C#网络流碎片数据

时间:2012-08-12 23:01:28

标签: c# fragment networkstream beginread

我的上下文

我有一个TCP网络程序,它通过连接发送已经序列化并编码到base64的大对象。我写了一个客户端库和一个服务器库,他们都使用NetworkStream's Begin/EndReadBegin/EndWrite。这是我正在使用的代码(非常简化的版本):

对于服务器:

var Server = new TcpServer(/* network stuffs */);
Server.Connect();
Server.OnClientConnect += new ClientConnectEventHandler(Server_OnClientConnect);

void Server_OnClientConnect()
{
    LargeObject obj = CalculateLotsOfBoringStuff();
    Server.Write(obj.SerializeAndEncodeBase64());
}

然后是客户:

var Client = new TcpClient(/* more network stuffs */);
Client.Connect();
Client.OnMessageFromServer += new MessageEventHandler(Client_OnMessageFromServer);

void Client_OnMessageFromServer(MessageEventArgs mea)
{
    DoSomethingWithLargeObject(mea.Data.DecodeBase64AndDeserialize());
}

客户端库具有NetworkStream.BeginRead的回调方法,该方法触发事件OnMessageFromServer,该事件将数据作为字符串通过MessageEventArgs传递。

我的问题

但是,当通过BeginRead/EndRead接收大量数据时,它似乎在多条消息上分散。例如。假装这是一个很长的信息:

"This is a really long message except not because it's for explanatory purposes."

如果这确实是一个很长的消息,Client_OnMessageFromServer可能会被调用...说三次“长消息”的碎片部分:

"This is a really long messa"

"ge except not because it's for explanatory purpos"

"es."

Soooooooo .... 深呼吸

Begin/EndWrite的一次通话中,通过一个Client_OnMessageFromServer发送所有内容的最佳方法是什么?

2 个答案:

答案 0 :(得分:5)

你做不到。在TCP上,事物的到达方式不一定与发送方式相同。 您的代码的工作是知道什么构成完整的消息,并在必要时缓冲传入的数据,直到您 完整的消息为止小心不要丢弃下一个消息的开始我在这个过程中。

在文本协议中,这通常意味着“发现换行符/ nul-char”。对于二进制,它通常意味着“在消息的前导码中读取长度标题”。

答案 1 :(得分:4)

TCP是一种流协议,没有固定的消息边界。这意味着您可以接收部分信息或一部分的结尾和另一部分的开头。

有两种方法可以解决这个问题:

  1. 更改协议以添加消息结束标记。这样你就可以不断收到,直到找到特殊标记。但是,这可能会导致您有一个缓冲区,其中包含一条消息的结尾和另一条消息的开头,这就是我推荐下一条消息的原因。
  2. 更改协议以首先发送消息的长度。然后,您将确切地知道消息的长度,并且可以在接收时倒计时,这样您就不会读取下一条消息的开头。