如何以二进制帧格式发送数据(TCP)

我需要将数据流传输到TCP服务器并且之前已经完成了。 数据应采用二进制帧格式。 我用Google搜索并找到了我认为我需要做的教程:

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient(v=vs.71).aspx

但我不知道如何以所需的格式创建数据,因此我可以以正确的方式发送它。 它应该采用以下格式:

Field|Offset| Type | size(octets) id | 0 |unsign int| 1 name | 1 |Byte Array| 40 grade| 41 |sign float| 8 

例:

 Field| Data | InBytes | id | 133 | 133 | name | 247 | 247 0 | grade| 0 | 0 | 

什么是数据类型我应该存储int,字节数组,浮点数以及在哪里指定偏移和大小,最后应该如何发送到服务器(在示例中它们只发送一个字节数组)。

AC #-如何使用上面提供的链接中的代码发送这样的数据的示例将是值得赞赏的。

要表示要序列化(和反序列化)的数据,您可以使用结构并设置正确的元数据,以便CLR为您完成剩下的工作。 像其他人说的那样,你需要处理端点上的数据包接收。 此外,您还必须考虑接收器所期望的字符集,因为您的数据中有一个字符串字段。 以下代码是一个示例,说明如何使用注释实现将托管数据转换为二进制格式的结构。

 // setting the layout to sequential will prevent the compiler/JIT // to reorder the struct fields // NOTE: Observe here that the Charset used is Ansi. You may need to // change this depending on the format expected by the receiver. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] struct DataPacket { [MarshalAs(UnmanagedType.U4)] public uint Id; // As I understood from your question, the Name field // has a prefixed size of 40 bytes. Attention here: // the SizeConst actually means '40 characters', not bytes // If you choose to use the Unicode charset, set SizeConst = 20 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)] public String Name; // This will be serialized in little endian format // in your question this field is 8 bytes long, which // in c# corresponds to the double type. If you really mean // the float type (4 bytes), change here. public double Grade; // Calling this method will return a byte array with the contents // of the struct ready to be sent via the tcp socket. public byte[] Serialize() { // allocate a byte array for the struct data var buffer = new byte[Marshal.SizeOf(typeof(DataPacket))]; // Allocate a GCHandle and get the array pointer var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned); var pBuffer = gch.AddrOfPinnedObject(); // copy data from struct to array and unpin the gc pointer Marshal.StructureToPtr(this, pBuffer, false); gch.Free(); return buffer; } // this method will deserialize a byte array into the struct. public void Deserialize(ref byte[] data) { var gch = GCHandle.Alloc(data, GCHandleType.Pinned); this = (DataPacket)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(DataPacket)); gch.Free(); } } 

用法:

 DataPacket packet; packet.Id = 1234; packet.Name = "Marvin the paranoid robot"; packet.Grade = 9.2; // serialize var bytes = packet.Serialize(); // send via tcp var tcp = new TcpClient(...); tcp.GetStream().Write(bytes, 0, bytes.Length); // deserializing; DataPacket receivedPacket; receivedPacket.Deserialize(bytes); 

你已经拥有了数据包,现在你需要处理接收器上的数据包接收。 那部分你不需要手工完成,你可以使用一些工具,比如@jgauffin说。

我将采用的方法是为我的其余代码和正在发送的数据的物理表示之间的接口提供两个函数。 这将允许我封装从TCP连接发出的物理数据表示,并允许它在需要时进行更改。

所以你可能想要的是有一个TCP读者类,它提供帧组合function和从帧的原始字节到数据结构的转换和TCP编写器类,它采用数据结构并创建一个原始字节帧然后将其写入TCP连接。

我会有一个消息缓冲区,它将是一个字节数组。 这是将通过TCP连接发送的消息缓冲区。

接下来将是一个数据结构,其中包含在系统其余部分中使用的数据。

然后我会有一个函数,比如FromTcpToDataStruct (dataStruct, messageArray)将原始字节串转换为我正在使用的数据结构,然后从FromDataStructToTcp (messageArray, dataStruct)转换为从数据结构转换为原始字符串。

您有一些问题需要考虑。 首先是目标服务器上的unsign int和float的表示, Little Endian Big Endian问题 。 第二种是将一串字节从TCP连接转换为离散帧(从TCP连接读取字节,并使用临时缓冲区组成一个完整的原始字节帧,以便读取提供完整的字节帧)。

您不能只发送该数据。 TCP是基于流的,这意味着您可以使用一个操作( socket.Receive() )或使用多个操作来接收您的信息。

解决这个问题的最简单方法是使数据包格式包含一个标题(一个简单的长度标题就足够了)和一个正文(您指定的信息)。

我不知道你是否必须自己实现一切,或者你是否可以使用网络库。 我已经创建了一个库,它可以为您处理所有套接字IO。 您需要做的就是处理编码和解码。 我为一个简单的二进制协议编写了​​一个小例子(未完成)。

编码器/解码器类可以在这里找到: https : //github.com/jgauffin/griffin.networking/tree/master/Source/Protocols/SimpleBinary/Griffin.Networking.Protocol.SimpleBinary/Handlers

Griffin.Networking简介: http : //blog.gauffin.org/2012/05/griffin-networking-a-somewhat-performant-networking-library-for-net/

我只是使用二进制编写器将数据转换为字节数组,然后像示例中一样发送它。

您可能需要检查字符串以确保它形成40个字节

  MemoryStream MS = new MemoryStream(); BinaryWriter Writer = new BinaryWriter(MS); Writer.Write(MyByte); Writer.Write(ASCIIEncoding.UTF8.GetBytes(MyString)); Writer.Write(MyDouble);