2010-11-18 12 views
5

Escribo un juego de plataforma cruzada con capacidades de red (usando SFML y RakNet) y he llegado al punto en que he compilado el servidor en mi servidor Ubuntu y tengo un cliente en funcionamiento en mi Mac. Todo el desarrollo se realiza en mi Mac, así que al principio he estado probando el servidor y funcionó bien.Creando y usando una estructura multiplataforma en C++

Estoy enviando struct s a través de la red y luego simplemente devolviéndolos desde char * a (por ejemplo) inet::PlayerAdded. Ahora esto ha estado funcionando bien (en su mayor parte), pero mi pregunta es: ¿Esto siempre funcionará? Parece un enfoque muy frágil. ¿La estructura siempre se distribuirá igual incluso en otras plataformas, Windows, por ejemplo? ¿Qué recomendarías?

#pragma pack(push, 1) 
struct Player 
{ 
    int dir[2]; 
    int left; 
    float depth; 
    float elevation; 
    float velocity[2]; 
    char character[50]; 
    char username[50]; 
}; 

// I have been added to the game and my ID is back 
struct PlayerAdded: Packet 
{ 
    id_type id; 
    Player player; 
}; 
#pragma pack(pop) 
+2

Para el downvoter serie, ¿alguna razón en particular? (El voto a favor de Steve parece particularmente atroz) – KevinDTimm

+0

Los puntos no tienen sentido en un lugar como este. Yo no me preocuparía por eso. –

+1

No me interesan los puntos, quiero saber por qué cada respuesta (excepto una, la menos útil de todas) obtuvo un voto negativo. Especialmente cuando una de las respuestas (downvoted) era claramente mejor. – KevinDTimm

Respuesta

5

Al igual que muchas de las otras respuestas, desaconsejaría enviar datos binarios brutos si se pueden evitar. Algo como Boost serial o Google Protobuf hará un buen trabajo sin demasiada sobrecarga.

Pero ciertamente puede crear estructuras binarias multiplataforma, está hecho todo el tiempo y es una forma muy válida de intercambiar datos. Poner en capas una "estructura" sobre esa información simplemente tiene sentido. Sin embargo, debes tener mucho cuidado con el diseño, afortunadamente la mayoría de los compiladores te dan muchas opciones para hacerlo. "paquete" es una de esas opciones y se ocupa de mucho.

También debe tener cuidado con el tamaño de los datos. Simple incluye stdint.h y usa los tipos de tamaño fijo como uint32_t. Tenga cuidado con los valores de coma flotante, ya que no todas las arquitecturas compartirán el mismo valor, por lo que es probable que lo hagan con flotadores de 32 bits. También para endianess, la mayoría de las arquitecturas usarán lo mismo, y si no lo hacen, simplemente puedes voltearlo en el cliente, que es diferente.

+0

Aunque la mayoría de las respuestas fueron realmente buenas, creo que esta es la que más me ayudó. Creo que voy a intentarlo, y si alguna vez toco una barrera con este método sabré qué usar. – ErikPerik

9

esto no funcionará si (entre otras cosas) intenta hacerlo desde la máquina ascendente hacia la izquierda a la máquina-endian grande como el int representación correcta se invertirá entre los dos.

Esto también podría fallar si la alineación o el empaque de su estructura cambia de una máquina a otra. ¿Qué sucede si tiene algunas máquinas de 64 bits y algunas de 32 bits?

Necesita utilizar una biblioteca de serialización portátil adecuada como Boost.Serialization o Google Protocol Buffers para asegurarse de tener un protocolo de conexión (también conocido como formato de datos transmisibles) que se puede decodificar con éxito independientemente del hardware.

Lo bueno de Protocol Buffers es que puede comprimir los datos de forma transparente utilizando una secuencia compatible con ZLIB que también es compatible con las secuencias protobuf. De hecho, he hecho esto, funciona bien. Imagino que otras secuencias de decorador se pueden usar de forma análoga para mejorar u optimizar su protocolo de cable básico según sea necesario.

2

La respuesta a "... presentado igual incluso en otras plataformas ..." es generalmente no. Esto es así incluso si se abordan problemas tales como CPUs diferentes y/o endianness diferente.

Los diferentes sistemas operativos (incluso en la misma plataforma de hardware) pueden usar representaciones de datos diferentes; esto normalmente se llama la "plataforma ABI" y es diferente entre, por ejemplo, Windows 32bit/64bit, Linux 32bit/64bit, MacOSX.

'#pragma pack' es solo la mitad del camino, porque más allá de las restricciones de alineación pueden existir diferencias en el tamaño del tipo de datos. Por ejemplo, "largo" en Windows de 64 bits es de 32 bits mientras que es de 64 bits en Linux y MacOSX.

Dicho esto, el problema obviamente no es nuevo y ya se ha abordado en el pasado: el estándar de llamada a procedimiento remoto (RPC) contiene mecanismos para definir estructuras de datos de forma independiente de la plataforma y cómo codificar/decodificar "búferes" que representan estas estructuras. Se llama "XDR" (representación de datos externa). Ver RFC1832. Según se va programando, esta rueda se ha reinventado varias veces; ya sea que convierta a XML, haga el trabajo de bajo nivel con XDR, use google :: protobuf, boost o Qt :: Variant, hay mucho para elegir.

En un lado puramente de implementación: Para simplificar, simplemente suponga que "unsigned int" en todas partes está alineado 32bit en un límite de 32 bits; Si puede codificar todos sus datos como una matriz de valores de 32 bits, entonces el único problema de externalización con el que debe lidiar es la endianidad.

Cuestiones relacionadas