3

我需要将数据流式传输到 TCP 服务器并且之前没有做过。数据应为二进制帧格式。我用谷歌搜索并找到了本教程,我认为它描述了我需要做的事情:

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#-如何使用上面提供的链接中的代码发送此类数据的示例将不胜感激。

4

4 回答 4

2

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

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

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

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

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

你有几个问题需要考虑。首先是目标服务器上无符号整数和浮点数的表示,即Little Endian Big Endian 问题。第二个是将来自 TCP 连接的字节串转换为离散帧(从 TCP 连接读取字节并使用临时缓冲区组成完整的原始字节帧,以便读取提供完整的字节帧)。

于 2012-08-15T13:34:45.863 回答
2

您不能只发送该数据。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/

于 2012-08-15T13:46:15.997 回答
2

要表示要序列化(和反序列化)的数据,您可以使用结构并设置正确的元数据,因此 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 说的。

于 2012-08-15T15:03:29.747 回答
1

我会简单地使用二进制写入器将数据转换为字节数组,然后像示例中一样发送它。

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

        MemoryStream MS = new MemoryStream();
        BinaryWriter Writer = new BinaryWriter(MS);
        Writer.Write(MyByte);
        Writer.Write(ASCIIEncoding.UTF8.GetBytes(MyString));
        Writer.Write(MyDouble);
于 2012-08-15T13:35:31.333 回答