2009-12-18 37 views
6

He leído un poco en la programación cliente/servidor en C#. Estoy suficientemente familiarizado con este proceso para hacer la siguiente pregunta:C# enviar objetos de estructura a través del socket

¿cómo puedo transmitir objetos de estructura a través de tcp/ip en lugar de solo cadenas?

mi aplicación es un juego en red con capacidades de chat. entonces, en lugar de simplemente transmitir texto, me gustaría implcar una estructura de datos o una estructura de clases que tendrá dos campos: i. paquete tipo ii. los datos para el tipo de paquete

y lo transmitiría cuando sea necesario durante la ejecución de la aplicación, y decodificaría el objeto de datos en el extremo receptor y lo colocaría donde pertenece.

im no buscando código, solo algunas ideas y declaraciones de búsqueda que puedo enviar a google, así lo haré; tener una mejor comprensión

ive leído sobre la serialización/de serialización, ¿es ése el camino a seguir?

gracias.


He comprobado los mensajes que se presentaron temas relacionados como, pero todavía desea más guidence.


+0

acabo de leer lo siguiente: El formateador binario: el formateador binario proporciona codificación binaria para serialización compacta, ya sea para almacenamiento o para flujos de red basados ​​en sockets. La clase BinaryFormatter generalmente no es apropiada cuando los datos se deben pasar a través de un firewall. -------------------- ¿La serialización Xml es buena para pasar por los firewalls? – iTEgg

Respuesta

3

Puede crear un NetworkStream basado en un Socket y utilizar cualquier mecanismo Stream para transportar sus datos. Eso traduce su pregunta a: ¿Cómo puedo leer/escribir una estructura desde/hacia un flujo?

Puede utilizar la serialización pero también un BinaryWriter/BinaryReader. Por una pequeña estructura (como se describe) Me gustaría escribir algunos métodos personalizados:

var netStream = new NetworkStream(clientSocket, true); 
var writer = new BinaryWriter(netStream); 

writer.Write(data.Value1); 
writer.Write(data.Value2); 

para estructuras más grandes que yo consideraría la opción de cálculo de referencias de Cheeso.

+0

La clasificación simplemente convierte una estructura en una matriz de bytes. Suponía que querría transmitir los bytes en una transmisión de red, tal como lo demostró. – Cheeso

+0

Sí, estoy de acuerdo con su respuesta. Pero asignar un búfer no administrado para 2 valores es una sobrecarga significativa. –

5

La serialización es la forma más sencilla de hacerlo ya que el sistema la admite directamente. Sin embargo, hay algunos problemas de rendimiento con objetos grandes y complicados. En su caso, parece que la serialización es el camino a seguir. Si quieres algo de nivel inferior, puedes echar un vistazo a BinaryWriter/BinaryReader, que te permite hacer el trabajo tú mismo.

+3

Quizás desee detallar la diferencia entre la serialización binaria y de otro tipo para explicar por qué el binario sería más eficiente. – BlueTrin

+3

En última instancia, toda serialización es "binaria"; la diferencia es cómo. –

1

. La serialización binaria de .NET sería probablemente la opción más rápida lista para usar, asumiendo que ambos lados del mecanismo de comunicación son C# y pueden cargar el mismo conjunto, que contiene los tipos de mensajes. Sin embargo, su propia serialización probablemente esté bien, si sus estructuras son muy simples. Simplemente defina su estructura de datos en una clase y también un método para convertirla en una cadena y desde ella.

5

Si no necesita la riqueza de la serialización, si solo quiere escribir una estructura en una matriz de bytes, considere la clase Marshal.

Por ejemplo, considere una aplicación de alquitrán en C#. El formato tar está basado en bloques de 512 bytes, y el primer bloque en una serie tiene una estructura regular. Idealmente, la aplicación solo desea blitt los datos del archivo de disco, directamente en una estructura. El método Marshal.PtrToStructure hace esto. Aquí está la estructura.

[StructLayout(LayoutKind.Sequential, Size=512)] 
    internal struct HeaderBlock 
    { 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)] 
     public byte[] name; // name of file. 

     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
     public byte[] mode; // file mode 

     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
     public byte[] uid;  // owner user ID 

     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
     public byte[] gid;  // owner group ID 

     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] 
     public byte[] size; // length of file in bytes 

     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] 
     public byte[] mtime; // modify time of file 

     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
     public byte[] chksum; // checksum for header 

     // ... more like that... up to 512 bytes. 

Luego aquí hay una clase genérica que hace el blitting.

internal class RawSerializer<T> 
{ 
    public T RawDeserialize(byte[] rawData) 
    { 
     return RawDeserialize(rawData , 0); 
    }  

    public T RawDeserialize(byte[] rawData , int position) 
    { 
     int rawsize = Marshal.SizeOf(typeof(T)); 
     if(rawsize > rawData.Length) 
      return default(T); 

     IntPtr buffer = Marshal.AllocHGlobal(rawsize); 
     Marshal.Copy(rawData, position, buffer, rawsize); 
     T obj = (T) Marshal.PtrToStructure(buffer, typeof(T)); 
     Marshal.FreeHGlobal(buffer); 
     return obj; 
    } 

    public byte[] RawSerialize(T item) 
    { 
     int rawSize = Marshal.SizeOf(typeof(T)); 
     IntPtr buffer = Marshal.AllocHGlobal(rawSize); 
     Marshal.StructureToPtr(item, buffer, false); 
     byte[] rawData = new byte[ rawSize ]; 
     Marshal.Copy(buffer, rawData, 0, rawSize); 
     Marshal.FreeHGlobal(buffer); 
     return rawData; 
    } 
} 

puede utilizar esa clase con cualquier estructura . Debe usar LayoutKind.Sequential y restringirse a tipos blittables (básicamente primitivas y matrices de los mismos) para utilizar este enfoque. Es rápido y eficiente, en términos de código, rendimiento y memoria, pero está un poco restringido en cómo se puede usar.

Una vez que tenga la matriz de bytes, puede transmitirla a través de un NetworkStream, etc., y luego deserializar usando la misma clase en el otro extremo.

+0

¡Gracias! Excelente para implementaciones de ICD. Tener el parámetro genérico en los métodos en lugar de la clase sería un poco mejor en mi humilde opinión, ya que el compilador puede inferir T al serializar la mayor parte del tiempo. –

7

En última instancia, sí: está hablando de serialización. Esto puede tomar muchas formas, especialmente en .NET, pero en última instancia debe elegir entre:

  • text vs binary; el binario directo tiende a ser más pequeño que el texto, ya que generalmente implica menos análisis, etc .; el texto (xml, json, etc.) generalmente se representa en la transmisión como UTF8 (aunque es posible cualquier codificación). Son ampliamente legibles por humanos, y a pesar de ser más prolijo, usualmente se pueden comprimir bastante bien.
  • contrato vs metadatos; Los serializadores basados ​​en contratos se enfocan en representar datos - se asume que el otro extremo de la tubería entiende la estructura, pero no se supone que compartan una implementación. Esto tiene limitaciones en cuanto a que no se puede introducir repentinamente una subclase completamente inesperada, sino que la hace independiente de la plataforma. Por el contrario, los serializadores basados ​​en metadatos envían tipo información en la secuencia (es decir, "esta es una instancia My.Namespace.FooBar). Esto hace que sea realmente fácil trabajar, pero rara vez funciona entre diferentes plataformas (y a menudo no entre versiones) y todo ese tipo de información puede ser prolija
  • manual vs automático; hecho: los serializadores manuales a menudo pueden ser los más eficientes en términos de ancho de banda, ya que puede personalizar a mano el diablo con el flujo, pero toma lotes de esfuerzo y necesita comprender lotes de serialización. Los serializadores automáticos son mucho mejores para usos generales (en realidad: la mayoría de los escenarios). Evite el manual a menos que no tenga otra opción. Los serializadores automáticos manejan todas las complejidades de preocuparse por diferentes tipos de datos, etc.

enfoques Manual serializador incluyen (sólo mencionar la palabra clave "serializador"): TextWriter, XmlWriter, IXmlSerializable, BinaryWriter, ISerializable. No quieres hacer eso ...

Centrándose más en los serializadores automáticas:

   | Contract    | Metadata 
===============+========================+=========================== 
    Text   | XmlSerializer   | SoapFormatter 
       | DataContractSerializer | NetDataContractSerializer 
       | Json.NET    | 
---------------+------------------------+--------------------------- 
    Binary  | protobuf-net   | BinaryFormatter 

Puesto que usted está hablando corrientes primas, mi preferencia sería un serializador basado en contratos binario - pero entonces, me escribió protobuf-net, por lo que puede no ser imparcial; - p

Para la comparación con RPC común pilas:

  • "interacción remota" utiliza BinaryFormatter
  • "asmx" de servicios web (incluyendo WSE *) utilizan XmlSerializer
  • WCF pueden utilizar muchos, más comúnmente DataContractSerializer o NetDataContractSerializer, ya veces XmlSerializer (que también puede ser configurado para utilizar, por ejemplo, protobuf-net)

puedo felizmente escribo un ejemplo del uso protobuf-red en una corriente para representar diferentes mensajes de diferentes tipos, pero un sencilla ejemplo de la transformación de un conector usando protobuf-net es en uno de los proyectos de la muestra (here, in fact)

1

Estás en el camino correcto con el uso serialización de objetos.

Una cosa que pensaría que no he mencionado aún es que los serializadores binarios generalmente crearán menos bytes para enviar a través del socket, sin embargo, si usa un serializador XML o JSON y luego comprime los resultados usando un CompressionStream (GZipStream?) Antes de enviarlo a través de su transmisión de red, puede obtener incluso tamaños más pequeños según el tipo de datos en su objeto (esto funciona mejor cuando tiene muchas cadenas).

Esto requerirá más tiempo de CPU para enviar y leer los mensajes, por lo que es una compensación si necesita reducir los requisitos de ancho de banda.

Cuestiones relacionadas