Empaquetado de datos eficiente para una red cliente-servidor

8

Lenguaje: C ++

Mi pregunta es la siguiente: me gustaría saber cuál sería la mejor o al menos una buena forma de empacar y enviar datos del cliente al servidor y viceversa. Habrá algunos datos componiendo un solo paquete. Un paquete tendrá un "id", que define para qué sirve, luego los datos en un orden predeterminado para esa "acción" que corresponde al paquete.

Para sistemas menos dependientes del rendimiento, simplemente enviaría cadenas, que estarían separadas por un espacio, siendo ellos los datos de la "acción" y la primera "palabra", el identificador del paquete y simplemente encadenar si las declaraciones verifican cuando hay una coincidencia .

Ahora, para un sistema más crítico, lo que hasta ahora había pensado era algo como esto:

Haga una cadena con la identificación del paquete y los datos, y envíela. Luego, para desempaquetar, podría extraer el primer número entero en la cadena, y al tener una matriz de controladores de paquetes, con índices correspondientes a la identificación del paquete que manejan, y simplemente hacer algo como packetHandlers [packetID] .Process (packetData).

¿Qué opinas de él, alguna sugerencia? muy apreciado!

Grimshaw
fuente

Respuestas:

10

Primero, confirme que realmente necesita un protocolo elegante y eficiente antes de desperdiciar recursos para desarrollarlo. No olvides que depurar / modificar tu juego será más difícil y requerirá más tiempo debido a tu elegante protocolo. Simplemente abstraería la comunicación de la red para que la implementación real pueda cambiarse fácilmente por una más eficiente si fuera necesario en el futuro. Use el protocolo más simple posible hasta que encuentre problemas de rendimiento. Otra ventaja de diseñar su protocolo más adelante es que el protocolo se puede optimizar para los datos reales transportados frente a lo que predice que se transportará.

Después de confirmar que necesita un protocolo elegante, observe los protocolos que otros han dedicado un tiempo considerable a desarrollar. Algunos ejemplos:

  • (actualización) El desarrollador original de Protocol Buffers (v2) desarrolló un nuevo protocolo llamado Cap'n Proto. Explica sus decisiones de diseño y se compara con otras bibliotecas similares que se han lanzado recientemente: Cap'n Proto, FlatBuffers y SBE .
  • El principal cuello de botella de Google es la comunicación de red entre computadoras, por lo que probablemente consideraron la eficiencia al desarrollar Protocol Buffers . Con gracia maneja la compatibilidad hacia adelante / hacia atrás ( cuando decide alterar sus estructuras de datos). Usado por todos los principales productos de Google (Gmail, Búsqueda, etc.)
  • Apache Thrift es un protocolo similar utilizado por Facebook.
  • RakNet es una biblioteca de red de código abierto diseñada específicamente para el desarrollo de juegos.
  • ZoidCom es otra biblioteca de redes orientada al desarrollo de juegos. No es de código abierto, pero aún puede estudiarlo para obtener sugerencias de diseño.

La primera regla de optimización del programa: no lo hagas.
La segunda regla de optimización del programa (¡solo para expertos!): No lo hagas todavía.

[ Michael A. Jackson ]

En otras palabras: su parámetro de optimización principal debe ser: vida (años de vida por implementación del programa). [ Cómo programar juegos independientes. Diapositiva 21. Jonathan Blow. ]

Leftium
fuente
1
buena lectura hombre! muchas gracias por la respuesta completa, sin duda ayudó, y estoy siguiendo su consejo por completo, simplemente resumen esto, haciéndolo lo suficientemente simple por ahora :)
Grimshaw
Estoy bastante seguro de que el caso de uso de Google para las memorias intermedias de protocolo no fue la eficiencia en el sentido estándar, sino maximizar la compatibilidad entre todas las plataformas posibles y cualquier versión de datos futura (que es la eficiencia en otra perspectiva). Estaré aquí leyendo sus otras notas, una muy buena colección para que me familiarice con el tema.
Patrick Hughes
RakNet no es de código abierto.
Gerstmann
@Gerstmann: Raknet es de código abierto (una vez más): github.com/OculusVR/RakNet
Leftium
0

¿Por qué usar dos esquemas de codificación diferentes? Simplemente use el segundo para cada sistema. Solo mantenlo simple.
Considere usar la compresión delta. Es decir, enviar un valor completo y después de eso solo las cosas que cambiaron. Después de algunas iteraciones del juego, envíe un valor completo nuevamente.
Otro encodig que podría considerar es Base 128 Varint. Google Protobufs lo usa. Eche un vistazo a la página "Codificación" de su guía para desarrolladores: la codificación de búferes de protocolo podría ahorrar algunos bytes.

Lurca
fuente
El primer sistema del que hablé fue un mero ejemplo de otros proyectos :) Ciertamente disfruté la idea de compresión delta, ¡gracias amigo!
Grimshaw
0

¿Cuál podría ser un ejemplo de los datos que está enviando? No veo ninguna razón para hacer algo demasiado elegante. Una vez que los datos están completamente cargados en el búfer del receptor, inspeccione el primero en intfunción de su valor, luego sabrá cómo procesar el resto de los datos.

Por lo que un paquete que consta de cuatro partes de datos id, val1, val2, y val2podría tener este aspecto:

// I'm using words (one byte) so my sample data is short
00000001 00101000 00010110 00010100 

A medida que lee el primer byte (que sabe que siempre estará allí), decide cómo procesar el siguiente conjunto de datos. Si la primera palabra (id) es 00000001que sabe que le siguen tres dwords más, y ese es el final del paquete. Para continuar con el ejemplo, es posible que tenga id = 00000010y su especificación se dice que para el valor de ID 2, procesar float, float, floaten ese orden, lo que podría indicar una posición del jugador en el espacio mundial.

Piense en ello como si escribiera su propio sistema de archivos binarios, tiene un valor de encabezado, que describe el resto de los datos, dónde se encuentra (qué posición) y qué tipo de datos se deben tratar.

Nate
fuente
Lo tengo, la única pregunta que me queda es: ¿está empaquetando todos esos ints y flotantes en una cadena estándar suficiente, o debería ir con tipos de datos más livianos?
Grimshaw
Como lo han dicho otros, comience con la cadena y vea qué sucede. Lo más probable es que tenga un cuello de botella en otra parte de su código.
Nate