Envío/Recibo de datos. [0.12.x/0.13.x]

Ir abajo

Envío/Recibo de datos. [0.12.x/0.13.x]

Mensaje por Toyz el 7/1/2017, 4:29 pm

La diferencia entre otras versiones es que esta identifica los paquetes con 1 sólo byte lo que simplifica muchísimo el envío/recibo de datos. También la data que enviamos nunca tiene que ser convertida a la data que nosotros queramos ni tampoco separarla en partes como sucede en otras versiones Razz

Lo siguiente es extraído de GS-Zone. (Corregidas todas las faltas de ortografía)

SERVIDOR A CLIENTE:

Acá les pienso explicar un poco el funcionamiento del protocolo binario. No lo use mucho pero con esto no me ha generado ningún error hasta el momento lo que indica que funciona.

Primero vamos a ver como se envía un Comando del Servidor hacia el Cliente. Es un poco mas largo que en versiones anteriores, pero no es mas complicado.

Lo primero que hacemos:

Declaramos el ServerPacketID

Existe un Enum llamado ServerPacketID


Aquí se guardan los identificadores de los paquetes.

Entonces lo que vamos a hacer es crear un nuevo identificador:

Código:

 
Prueba
Este lo agregamos al final del Enum.


Ahora llegamos al punto de tener que usar el identificador, ya sea para enviar mas datos o para accionar algo en el cliente.

Entonces lo que hacemos es crear el Sub correspondiente que enviara el dato.

Para esto se crea un Write (Se utiliza el Write para reconocer fácilmente la escritura de datos. Cambien se puede usar el que uno quiera) + Nombre del indetificador

Entonces nos quedaría algo como:

WritePrueba

Esto es un Sub en donde se graban los datos a enviar.

Lo que hay que hacer es bastante sencillo:

Código:

 
On Error GoTo Errhandler
    
        Call UserList(UserIndex).outgoingData.WriteByte(ServerPacketID.Ejemplo)
Call UserList(UserIndex).outgoingData.WriteLong(123123123)
Call UserList(UserIndex).outgoingData.WriteInteger(32000)
Call UserList(UserIndex).outgoingData.WriteASCIIString("Yo tengo un sapo que se llama pepe")
Exit Sub
 
Errhandler:
    If Err.Number = UserList(UserIndex).outgoingData.NotEnoughSpaceErrCode Then
        Call FlushBuffer(UserIndex)
        Resume
    End If
 
Explicación:

Código:

 
On Error GoTo Errhandler
Acá le estamos diciendo que si hay algún error valla al manejador de errores.

Código:

 
Call UserList(UserIndex).outgoingData.WriteByte(ServerPacketID.Ejemplo)

Acá estamos enviándole al cliente el serverpacketid. Con esto el cliente sabrá donde posicionarse para hacer la acción.

Código:

 
Call UserList(UserIndex).outgoingData.WriteLong(123123123)
Call UserList(UserIndex).outgoingData.WriteInteger(32000)
Call UserList(UserIndex).outgoingData.WriteASCIIString("Yo tengo un sapo que se llama pepe")
Acá le estamos enviando los datos al cliente.

Lo único a tener en cuenta es que tienen que saber que datos se están enviado para poder grabar con su correspondiente valor. Por ejemplo un WriteByte no va a enviar un Entero Largo.

Si tienen dudas en cual usar miren la clase ClsByteQueue

Ya con esto armamos el "ENVIÓ" del paquete, pero si el cliente no se entera para que le sirven estos datos no sirve para nada..

Entonces ahora vamos al cliente:

Del mismo modo que en el servidor declaramos el ServerPacketID justo en la misma posición que lo Pusieron en el servidor(Al final).


Ahora llega la parte en la que el cliente tiene que saber de que paquete se esta hablando:

Entonces buscamos el Select Case ServerPacketID

Y le agregamos el caso correspondiente:

Case ServerPacketID.Prueba

A continuación en una nueva linea debajo del case le agregamos:

call HandlePrueba (Puede ser Handle como pepito. )

Pero.. qué tiene el handle?

No es mas ni menos que la lectura de los valores que le envio el servidor:

Código:

 
Public Sub HandleEjemplo()
 
    Call incomingData.ReadByte
 
Oro = incomingData.ReadLong()
Vida = incomingData.ReadInteger()
Nombre = incomingData.ReadASCIIString()
 
 
End Sub
  

Lo que estamos haciendo acá es lo siguiente:


Código:

 
    Call incomingData.ReadByte
    
Ahí removemos el ServerPacketID que le enviamos para identificar al paquete.

Código:

 
Oro = incomingData.ReadLong()
 
Aca estamos leyendo el primer valor que mandamos. Como mandamos un Long leemos un Long

Código:

 
Vida = incomingData.ReadInteger()
 
Acá estamos leyendo el segundo valor que le enviamos. Como era un Integer, leemos un integer.

Código:

Nombre = incomingData.ReadASCIIString()
Aquí leemos el ultimo valor enviado y como era texto lo leemos como tal.

Ahora ya le asignamos los valores de los datos enviados a variables propias.

Y ahora solo queda hacer lo que harían normalmente:

Ejemplo:

Después de esas líneas agregas:

Código:

 
Call RobarYMatar(Nombre, Oro, Vida)
Bueno ahí finaliza la guía para enviar un paquete del servidor al cliente.


CLIENTE AL SERVIDOR:

1º : Hay un enum donde se guardan todos los ID(Identificadores) de los paquetes que se envian desde el cliente al servidor, el mismo está en ambos lados(cliente-servidor) llamado "Private Enum ClienTpacketid".

2º : La información que enviamos desde el cliente al servidor, se envía por funciones que están dentro de una clase (clsByteQueue) y el cliente la usa como "outgoingdata" para enviar paquetes y "incomingdata" para recibirlos.
vamos a ejemplificar esto, lo primero que debemos hacer es en el enum que aclaré anteriormente agregar un nuevo identificador , para hacer esto nos vamos al cliente y buscaremos
Código:

 
Private Enum ClientPacketID
y ahí dentro ubicaremos un nuevo paquete , al cual le pondremos
Código:

 
   Prueba
tiene que ser obviamente antes del "End Enum"
ahora que ya tenemos declarado el identificador, vamos a crear una rutina que envie al servidor datos.
al final del módulo protocol.bas pondremos esto
Código:

 
Public Sub Prueba()
Call outgoingdata.writebyte(ClientPacketID.Prueba)
Call outgoingdata.writeasciistring("Esto es una prueba!")
End Sub
NOTA: Por cuestiones de prolijidad se le pone un "Write" adelante del nombre de la rutina y seguido el nombre del paquete, pero podemos usar el que nos guste.
aquí les explicaré más o menos que hicimos en esta rutina(sub)
Código:

 
Call outgoingdata.writebyte(ClientPacketID.Prueba)
con esto le enviamos al servidor(mediante outgoingdata) el packetID , que el servidor recibirá y hará lo que nosotros le indiquemos.
Código:

 
Call outgoingdata.writeasciistring("Esto es una prueba!")
con esto le enviamos una cadena de caracteres (string) al servidor, en este caso sería "Esto es una prueba!"
con esto listo, nos posicionamos en el servidor, y haremos lo siguiente.
Buscaremos el enum "Private Enum ClientPacketID", y agregaremos antes del end enum , y presten atención : SIEMPRE TIENE QUE TENER EL MISMO ORDEN TANTO EN CLIENTE-SERVIDOR NUESTRO PAQUETE IDENTIFICADOR, por cuestiones que explicaré más abajo.
bueno, agregamos en el mismo orden que en el cliente
Código:

 
   Prueba
  
ahora el servidor ya sabrá que hay un nuevo packetID, pero... si ya lo sabe, tiene que realizar una acción cuando lo recibe.. y para lograr esto necesitamos dirigirnos al :
Código:

 
Private Sub HandleIncomingdata(ByVal userIndex As Integer)
End Sub
ahí es donde se "maneja"(handle) , la "información"(data) , "entrante"(incoming), buscaremos donde está el ..
Código:

 
Select Case PacketID
ahí tenemos donde el servidor al recibir 1 "Identificador" realizará lo que nosotros le indiquemos, abajo de el select case agregaremos
Código:

 
 Case ClientPacketID.Prueb
 
a
  Call Prueba(userIndex)
NOTA: acá es lo mismo que el cliente , frecuentemente para la comprensión y prolijidad del código se le suele agregar un "Handle" y seguido el nombre del paquete, aunque también podemos usar el nombre que nos guste.
bueno, ahí ya el servidor sabe que al recibir X identificador, tiene que llamar a x rutina, y ahora, nos falta hacer esta rutina(sub) y hacer lo que nos guste, al final del módulo protocol.bas, agregaremos
al final del módulo protocol pondremos
acá les dejaré 2 formas de manejar nuestra data.
primera forma y más recomendada en paquetes qe contienen strings
Código:

 
Private Sub Prueba(ByVal Indice As Integer)
Dim Bufer as new clsbytequeue  \'declaramos el buffer auxiliar que usaremos
 
call bufer.copybuffer(userlist(indice).incomingdata) \'acá hacemos un copiado de nuestros datos
 
call bufer.readbyte    \'acá borraremos de la memoria el byte identificador.
 
dim MiCadena as string    \'en esta variable guardaremos el string que nos mandó el cliente.
 
micadena = bufer.readasciistring       \'borramos de la memoria el string que envió el cliente, y lo guardamos en una variable para utilizarlo.
 
\'LISTO, Ya tenemos la memoria del bufer vacia, a partir de acá haremos lo que quisieramos con nuestro string recibido
 
\'******************* esto tiene que estar antes del end sub
 
call userlist(indice).incomingdata.copybuffer(bufer) \'si llegó el paquete entero, y no hubo error alguno, destruiremos el bufer.
 
End Sub
de esa forma(la más recomendada) lograremos recibir nuestros datos, ahora la segunda opción
Código:

 
Private Sub Prueba(ByVal userIndex As Integer)
 
call userlist(userindex).incomingdata.readbyte  \'borraremos de la memoria el byte identificador.
 
dim cadena as string \'en esa variable nos guardamos lo que nos envió el cliente.
 
cadena = userlist(userindex).incomingdata.readasciistring  \'guardamos en la variable, y removemos de la memoria el string
 
\' APARTIR DE ACA HASTA EL END SUB, HAREMOS LO QUE NOS GUSTE.
End Sub
con las 2 formas lograremos lo mismo, pero la primera se recomienda más por que si hay un error(no llegan completos los datos , etc) no revienta todo, cosa que sí pasaría con la segunda(opción).
listo, tan simple como eso ya tenemos una comunicación entre un cliente y el servidor
ahora voy a aclarar diversas cosas :
1 : para enviar datos de diferente tipo, byte/integer/long/double/single, en el cliente usaremos
Código:

Call outgoingdata.writeTIPODEDATO(valor)
cambiando tipodedato por byte/integer/long/double/single y "valor" por lo que queremos enviarle al servidor.
2 : Esto es muy importante, jamás , de ninguna forma podemos tener siquiera 1 PAQUETE que no esté en la misma posición(en el enum) tanto en cliente-servidor, por esto que voy a explicar.
calculemos que tenemos esto así
En el clientpacketid del cliente tenemos
abajo de este paquete
Código:

 
    LoginNewChar            \'NLOGIN
tenemos el nuestro
Código:

 
    Prueba
y en el servidor en el enum clientpacketid
tenemos arriba de este paquete
Código:

 
    LoginNewChar            \'NLOGIN
el nuestro
Código:

 
    Prueba
entonces, que pasaría si hacemos esto?, fácil, el enum clientpacketid(como todos los ENUMs que no inician desde un valor) , el primer paquete es 0, el segundo es 1 el tercero 2 y así.
entonces tenemos en el cliente
Código:

 
Private Enum ClientPacketID
    LoginExistingChar       \'OLOGIN
    ThrowDices              \'TIRDAD
    LoginNewChar            \'NLOGIN
    Prueba
    
y en el servidor
Código:

 
Private Enum ClientPacketID
    LoginExistingChar       \'OLOGIN
    ThrowDices              \'TIRDAD
    Prueba
    LoginNewChar            \'NLOGIN
    
entonces quedaría así
cliente:
loginexistingchar = identificador 0\nthrowdices = identificador 1\nloginnewchar = identificador 2\nprueba(nuestro paquete id) = identificador 3
servidor:
loginexistingchar = identificador 0\nthrowdices = identificador 1\nprueba(nuestro paquete id) = identificador 2\nloginnewchar = identificador 3
ahora cuando nosotros hacemos esto
Código:

 
Call outgoingdata.writebyte(ClientPacketID.Prueba)
le estamos enviando al servidor un identificador(numero) que sería un 3, hasta acá todo bárbaro, el problema radica cuando...
el servidor recibe ese "3" y lo maneja como si fuera el "loginexistingchar"(que es el 3 para el servidor), osea , esto ocurre cuando hay un desorden y el cliente al enviar un 3 y el server recibirlo y manejarlo como un paquete que no es el que se espera, el protocolo revienta(si se escriben más/menos datos de los que el servidor espera) o simplemente si no enviamos datos adicionales, hará lo inverso, calculemos que tenemos invertido en el cliente y servidor el paquete de meditar, al enviar nuestro paquete "prueba", el servidor nos expulsará , dado a que el servidor no está esperando un "string"

Por qué no usar exit sub si enviamos strings:


Estamos acostumbrados a poner la típica:
Código:

 
If UserList(UserIndex).flags.Muerto = 1 Then
                      Call WriteConsoleMsg(UserIndex, "¡¡Estás muerto!!", FONTTYPE_INFO)
                      Exit Sub
              End If
Ahora, ¿cuál es el problema con esto?
Ninguno, dependiendo de qué paquete se trate. Si el paquete únicamente contiene bytes y variables númericas, no es necesario realizar una copia al buffer ni nada. Por eso, los paquetes que contengan esto:
Código:

 
        Call .incomingData.ReadByte
Y no hagan ninguna copia al buffer, no tiene problemas en utilizar Exit Sub. Pero la cosa cambia si utilizamos un paquete que contiene strings. Un paquete que contiene más de un dato y cadenas strings utiliza esto:
Código:

        Dim buffer As New clsByteQueue
        Call buffer.CopyBuffer(.incomingData)
Esto realiza una copia de los datos en el buffer, y por ende debe ser copiado al paquete original una vez que el paquete cumplió su función y llegó por completo al servidor-cliente:
Código:

 
        Call .incomingData.CopyBuffer(buffer)
Luego, el buffer auxiliar es destruido:
Código:

 
    Set buffer = Nothing
Por eso, si agregamos Exit Sub, no se destruye el buffer ni la copia vuelve al paquete original, y se puede generar un bucle infinito que termine por explotar el socket.


Última edición por Toyz el 30/8/2017, 1:36 am, editado 1 vez


_______________

Como saben yo aporto bastante en Servers-Argentum y les quiero decir que todos tienen derecho de usar y de aportar en otra comunidad mis aportes pero sólo con un requisito y es tan simple que dejar los créditos. Gracias.
Si conocen otra comunidad y en esa misma mis aportes no están aportados, si tenés tiempo y ganas, aportalo! A mucha gente le puede servir. No te olvides del requisito Razz

MIS MEJORES APORTES:

Compra/venta de personajes:
https://goo.gl/A44Av0

Juegos del Hambre:
https://goo.gl/u6aNUG

Deathmatch automático:
https://goo.gl/GQF4fK

Sistema de retos vía MMR con búsqueda, emparejamiento, etc:
https://goo.gl/40uP0f

Sistemas de eventos automáticos de 1vs1 hasta 10vs10:
https://goo.gl/XYKygc

1VS1 - AIM - MELEE - Gana Sigue (Se liberan cupos)
https://goo.gl/qq83wx

Retos 3vs3 - [Items, 2 rounds, múltiples arenas, oro]
https://goo.gl/x8SMnV

Eliminar sistema de Skill (COMPLETO)
https://goo.gl/mKxHzC
avatar
Toyz
Administrador
Administrador

¿BANEADO? : No.
Premios :
[1er puesto] Usuario del año 2016
Cantidad de envíos : 5045
Edad : 17
Localización : Global.
Fecha de inscripción : 28/09/2014

Ver perfil de usuario

Volver arriba Ir abajo

Re: Envío/Recibo de datos. [0.12.x/0.13.x]

Mensaje por Metrosersuah el 9/1/2017, 7:51 am

ademas de que ya ahí banda de estas cosas vi uno igualito que necesidad
avatar
Metrosersuah
Nivel 5
Nivel 5

¿BANEADO? : No.
Premios : Ninguno.
Cantidad de envíos : 234
Localización : 4 Paredes
Fecha de inscripción : 13/02/2016

Ver perfil de usuario http://www.argentumonline.com.ar

Volver arriba Ir abajo

Re: Envío/Recibo de datos. [0.12.x/0.13.x]

Mensaje por Toyz el 9/1/2017, 4:22 pm

No hay una banda, hay uno sólo y es el de GS-Z el cual lo puse acá. Es más hay usuarios como vos y otros que no saben el protocolo binario así que les va a venir de 10.


_______________

Como saben yo aporto bastante en Servers-Argentum y les quiero decir que todos tienen derecho de usar y de aportar en otra comunidad mis aportes pero sólo con un requisito y es tan simple que dejar los créditos. Gracias.
Si conocen otra comunidad y en esa misma mis aportes no están aportados, si tenés tiempo y ganas, aportalo! A mucha gente le puede servir. No te olvides del requisito Razz

MIS MEJORES APORTES:

Compra/venta de personajes:
https://goo.gl/A44Av0

Juegos del Hambre:
https://goo.gl/u6aNUG

Deathmatch automático:
https://goo.gl/GQF4fK

Sistema de retos vía MMR con búsqueda, emparejamiento, etc:
https://goo.gl/40uP0f

Sistemas de eventos automáticos de 1vs1 hasta 10vs10:
https://goo.gl/XYKygc

1VS1 - AIM - MELEE - Gana Sigue (Se liberan cupos)
https://goo.gl/qq83wx

Retos 3vs3 - [Items, 2 rounds, múltiples arenas, oro]
https://goo.gl/x8SMnV

Eliminar sistema de Skill (COMPLETO)
https://goo.gl/mKxHzC
avatar
Toyz
Administrador
Administrador

¿BANEADO? : No.
Premios :
[1er puesto] Usuario del año 2016
Cantidad de envíos : 5045
Edad : 17
Localización : Global.
Fecha de inscripción : 28/09/2014

Ver perfil de usuario

Volver arriba Ir abajo

Re: Envío/Recibo de datos. [0.12.x/0.13.x]

Mensaje por Contenido patrocinado


Contenido patrocinado


Volver arriba Ir abajo

Volver arriba


 
Permisos de este foro:
No puedes responder a temas en este foro.