Sérialisation object à réseau – avec un protocole existant

J’écris un client pour un programme serveur écrit en C ++. Comme il n’est pas inhabituel, tout le protocole réseau est dans un format permettant de mémoriser facilement des paquets dans une structure C ++ (code de paquet sur 1 octet, puis dispositions différentes par type de paquet).

Je pourrais faire la même chose en C #, mais existe-t-il un moyen plus simple, en particulier si l’on considère qu’un grand nombre de données sont des tableaux de caractères de longueur fixe avec lesquels je veux jouer en tant que chaînes? Ou devrais-je simplement sucer et convertir les types au besoin? J’ai déjà envisagé d’utiliser l’interface ISerializable, mais elle ne semble pas être aussi faible que nécessaire.

J’ai écrit un article à ce sujet en 2004, qui couvre certaines des options disponibles pour la conversion d’un stream binary en une structure de mémoire .NET. Je l’ai republiée sur mon nouveau blog car l’ancien site de blog n’existe plus.

http://taylorza.blogspot.com/2010/04/archive-structure-from-binary-data.html

Fondamentalement, vous avez trois options

  1. Utiliser des pointeurs de mémoire de style C ++ en C # nécessitant le commutateur / unsafe
  2. Utilisez le marshaling .NET pour allouer un bloc de mémoire non géré, copiez les octets dans la mémoire non gérée, puis utilisez Marshal.PtrToStructure pour réorganiser les données vers le segment de mémoire géré en les mappant dans votre structure.
  3. Utilisez un BinaryReader pour lire manuellement le stream d’octets et comstackr les données dans la structure. Personnellement, c’était mon option préférée.

Lorsque vous envisagez les options, vous devez également prendre en compte la manière dont l’ordre des octets peut vous affecter.

À titre d’exemple, je vais utiliser l’en-tête IP comme exemple, car au moment de la publication, je travaillais avec des paquets TCP bruts.

Vous devez définir votre structure .NET à laquelle les données binarys seront mappées. Par exemple, l’en-tête IP ressemble à ce qui suit.

[StructLayout(LayoutKind.Sequential, Pack = 1)] struct IpHeader { public byte VerLen; public byte TOS; public short TotalLength; public short ID; public short Offset; public byte TTL; public byte Protocol; public short Checksum; public int SrcAddr; public int DestAddr; } 

Notez que l’atsortingbut StructLayout n’est requirejs que pour les deux premières options et vous devrez bien sûr définir le compactage approprié pour la structure en cours de sérialisation à partir du serveur.

Ainsi, en C / C ++, étant donné un pointeur sur un bloc de mémoire contenant les octets de données mappés sur la structure C / C ++, vous pouvez utiliser le bit de code suivant pour afficher le bloc de données en tant que morceau de mémoire de structure, où paquet est un octet * dans la mémoire.

 IpHeader *pHeader = (IpHeader*)packet; 

Faites la même chose avec C # en utilisant l’option / unsafe et la structure définie ci-dessus que vous comptez utilise le code suivant.

 IpHeader iphdr; unsafe { fixed ( byte *pData = packet) { iphdr = *(IpHeader*)pData; } } //Use iphdr... 

L’option de marshaling ressemblerait à ceci

 IntPtr pIP = Marshal.AllocHGlobal( len ); Marshal.Copy( packet, 0, pIP, len ); iphdr = (IpHeader)Marshal.PtrToStructure( pIP, typeof(IpHeader) ); Marshal.FreeHGlobal( pIP ); 

Et enfin, vous pouvez utiliser BinaryReader pour le faire entièrement en code managé.

 MemoryStream stm = new MemoryStream( packet, 0, len ); BinaryReader rdr = new BinaryReader( stm ); iphdr.VerLen = rdr.ReadByte(); iphdr.TOS = rdr.ReadByte(); iphdr.TotalLength = rdr.ReadInt16(); iphdr.ID = rdr.ReadInt16(); iphdr.Offset = rdr.ReadInt16(); iphdr.TTL = rdr.ReadByte(); iphdr.Protocol = rdr.ReadByte(); iphdr.Checksum = rdr.ReadInt16(); iphdr.SrcAddr = rdr.ReadInt32(); iphdr.DestAddr = rdr.ReadInt32(); 

Comme je l’ai mentionné plus tôt, vous devrez peut-être envisager l’ordre des octets. Par exemple, le code ci-dessus n’est pas tout à fait correct car IpHeader n’utilise pas le même ordre d’octet que celui supposé par ReadInt16. ReadInt32 etc. Résoudre le problème avec la solution ci-dessus est aussi simple que d’utiliser IPAddress.NetworkToHostOrder.

 iphdr.VerLen = rdr.ReadByte(); iphdr.TOS = rdr.ReadByte(); iphdr.TotalLength = IPAddress.NetworkToHostOrder(rdr.ReadInt16()); iphdr.ID = IPAddress.NetworkToHostOrder(rdr.ReadInt16()); iphdr.Offset = IPAddress.NetworkToHostOrder(rdr.ReadInt16()); iphdr.TTL = rdr.ReadByte(); iphdr.Protocol = rdr.ReadByte(); iphdr.Checksum = IPAddress.NetworkToHostOrder(rdr.ReadInt16()); iphdr.SrcAddr = IPAddress.NetworkToHostOrder(rdr.ReadInt32()); iphdr.DestAddr = IPAddress.NetworkToHostOrder(rdr.ReadInt32());