对象到网络的序列化-使用现有协议 [英] Object to Network serialization - with an existing protocol

查看:61
本文介绍了对象到网络的序列化-使用现有协议的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为用C ++编写的服务器程序编写客户端.与众不同的是,所有网络协议都采用一种格式,可以很容易地将数据包复制到C ++结构中(1字节数据包代码,然后每种数据包类型有不同的排列方式).

I'm writing a client for a server program written in C++. As is not unusual, all the networking protocol is in a format where packets can be easily memcopied into/out of a C++ structure (1 byte packet code, then different arrangements per packet type).

我可以在C#中做同样的事情,但是有没有更简单的方法,特别是考虑到很多数据是我想作为字符串使用的定长char数组?还是我应该吸收它并根据需要转换类型?我已经看过使用ISerializable接口,但是它看起来并不像所需的那样低.

I could do the same thing in C#, but is there an easier way, especially considering lots of the data is fixed-length char arrays that I want to play with as strings? Or should I just suck it up and convert types as needed? I've looked at using the ISerializable interface, but it doesnt look as low level as is required.

推荐答案

我在2004年对此发表了一篇文章,其中介绍了一些可用于将二进制流转换为.NET内存结构的选项.由于旧博客站点已不存在,我将其重新发布到了新博客中.

I wrote a post on this in 2004 which covers some of the options available for converting a binary stream to a .NET memory structure. I reposted it on my new blog since the old blog site no longer exists.

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

基本上,您有三个选择

  1. 在需要/unsafe开关的C#中使用C ++样式的内存指针
  2. 使用.NET封送处理分配非托管内存块,将字节复制到非托管内存,然后使用Marshal.PtrToStructure将数据封送回托管堆,将其映射到您的结构中.
  3. 使用BinaryReader手动读取字节流并将数据打包到结构中.就个人而言,这是我的首选.

在考虑选项时,还应考虑字节顺序可能如何影响您.

When you consider the options you should also take into account how the byte ordering might affect you.

作为示例,我将以IP标头作为示例,因为在发布本文时,我正在处理Raw TCP数据包.

As an example I will use the IP header as an example, since at the time of the post I was working with Raw TCP packets.

您需要定义二进制数据将映射到的.NET结构.例如,IP标头如下所示.

You need to define your .NET structure that the binary data will be mapped to. For example the IP Header looks like the following.

[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;
}

请注意,只有前两个选项才需要StructLayout属性,当然,您需要为正在从服务器序列化的结构设置适当的包装.

Note that the StructLayout attribute is only required for first two options, and of course you will need to set the packing as appropriate for the structure that is being serialized from the server.

因此,在C/C ++中,给定指向包含映射到C/C ++结构的数据字节的内存块的指针,您可以使用以下代码段将数据块视为结构内存块,其中数据包是内存的一个字节*.

So in C/C++, given a pointer to a block of memory that contains the data bytes that map to the C/C++ structure you can use the following bit of code to view the block of data as a structure piece of memory, where packet is a byte* to the memory.

IpHeader *pHeader = (IpHeader*)packet;

使用/unsafe选项使用C#进行相同的操作,上面所定义的结构使用以下代码.

Doing the same is C# using /unsafe option and the struct defined above you count use the following code.

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

封送处理选项如下所示

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

最后,您可以使用BinaryReader完全在托管代码中完成此操作.

And finally you can use the BinaryReader to do this entirely in managed code.

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();

正如我之前提到的,您可能需要考虑字节顺序.例如,上面的代码不是很正确,因为IpHeader使用的字节顺序与ReadInt16假定的字节顺序不同. ReadInt32等.解决上述问题的方法就像使用IPAddress.NetworkToHostOrder一样简单.

As I mentioned earlier, you might need to consider byte ordering. For example, the above code is not quite correct because the IpHeader does not use the same byte order as is assumed by ReadInt16. ReadInt32 etc. Resolving the issue with the above solution is as simple as using 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());

这篇关于对象到网络的序列化-使用现有协议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆