繁体   English   中英

异步TCP服务器-邮件构架建议

[英]Async TCP Server - Message Framing Advice

我在C#中有一个异步TCP套接字服务器,该服务器使用Socket的TcpListener / TcpClient包装器制成。 我一般对网络还很陌生,因此我不知道TCP如何处理发送的数据以及它如何不保留消息边界。 经过一番研究,我认为我已经提出了一个可靠的解决方案,但我想知道是否有人对我有更多建议。

目前,我要发送的数据是使用protobuf(Google的数据交换库) https://code.google.com/p/protobuf/的序列化类对象的byte []

在序列化数据之后,在发送数据之前,我决定在字节数组的开头附加一个4字节的Int32。 我的想法是,当数据包发送到服务器时,它将解析出Int32,然后等待直到接收到该字节数,然后再对数据进行任何处理,否则只需再次调用BeginRead。

这是在我编写数据之前运行过的代码,它似乎可以正常工作,但是我可以接受任何性能建议:

public byte[] PackByteArrayForSending(byte[] data)
{
    // [0-3] Packet Length 
    // [3-*] original data

    // Get Int32 of the packet length
    byte[] packetLength = BitConverter.GetBytes(data.Length);
    // Allocate a new byte[] destination array the size of the original data length plus the packet length array size
    byte[] dest = new byte[packetLength.Length + data.Length];

    // Copy the packetLength array to the dest array
    Buffer.BlockCopy(packetLength, 0, dest, 0, packetLength.Length);
    // Copy the data array to the dest array
    Buffer.BlockCopy(data, 0, dest, packetLength.Length, data.Length);

    return dest;
}

我在服务器端有些卡住。 我通过使用Buffer.BlockCopy复制前4个字节,然后使用BitConverter.ToInt32来读取我应该获取的长度,来读取packetLength变量。 我不确定是否应该不断将传入的数据读取到特定于客户端的Stream对象中,还是仅使用while循环。 到目前为止,这是我在服务器端拥有的代码示例:

NetworkStream networkStream = client.NetworkStream;
int bytesRead = networkStream.EndRead(ar);

if (bytesRead == 0)
{
  Console.WriteLine("Got 0 bytes from {0}, marking as OFFLINE.", client.User.Username);
  RemoveClient(client);
}

Console.WriteLine("Received {0} bytes.", bytesRead);

// Allocate a new byte array the size of the data that needs to be deseralized.
byte[] data = new byte[bytesRead];

// Copy the byte array into the toDeserialize buffer
Buffer.BlockCopy(
  client.Buffer,
  0,
  data,
  0,
  bytesRead);

// Read the first Int32 tp get the packet length and then use the byte[4] to get the packetLength
byte[] packetLengthBytes = new byte[4];
Buffer.BlockCopy(
  data,
  0,
  packetLengthBytes,
  0,
  packetLengthBytes.Length);

int packetLength = BitConverter.ToInt32(packetLengthBytes, 0);

// Now what do you recommend?

// If not all data is received, call 
// networkStream.BeginRead(client.Buffer, 0, client.Buffer.Length, ReadCallback, client);
// and preserve the initial data in the client object

感谢您的宝贵时间和建议,我希望能进一步了解该主题。

TCP确保在一端填充到流中的字节将以相同顺序掉落到另一端,并且不会丢失或重复。 仅此而已,当然不支持大于一个字节的实体。

通常,我在邮件头的前面加上了消息的长度,类型和子类型。 通常会提供一个关联ID,以将请求与响应进行匹配。

基本模式是获取字节并将其附加到缓冲区。 如果缓冲区中的数据足以包含消息头,则提取消息长度。 如果缓冲区中的数据足以包含该消息,则从缓冲区中删除该消息并进行处理。 重复所有剩余数据,直到没有完整的消息要处理。 根据您的应用程序,这可能是等待读取或检查流中是否有其他数据的关键。 某些应用程序可能需要继续从单独的线程读取流,以避免限制发送方。

请注意,您不能假定您具有完整的消息长度字段。 处理一条消息后,您可能还剩下三个字节,并且无法提取int

根据您的消息,使用循环缓冲区可能比每次处理消息时改组剩余字节的效率更高。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM