NetworkStream不会刷新数据

时间:2015-05-28 06:50:16

标签: c# sockets

我正在使用套接字编写一个简单的聊天程序。当我发送长消息时,请在之后刷新流和短消息,将长消息的末尾附加到短消息中。它看起来像这样:

发送" aaasdsd"

收到" aaasdsd"

发送" bb"

接受" bb asdsd "

通过调试,我发现Flush方法(它应该清除流中的所有数据)不会这样做。根据mdsn,这是预期的行为,因为NetworkStream没有缓冲。在这种情况下如何清除流?我可以用一个相同长度的空(由0个字符组成)来跟踪每条消息,但我不认为这样做是正确的,而且,它会搞砸我需要的一些功能

1 个答案:

答案 0 :(得分:4)

TCP无法正常工作。就这么简单。

TCP是基于流的协议。这意味着您不应该永远将其视为基于消息的协议(与UDP不同)。如果需要通过TCP发送消息,则必须在TCP上添加自己的消息传递协议。

您在此处尝试执行的操作是发送两条单独的消息,并在另一侧接收两条单独的消息。这在UDP(基于消息)上可以正常工作,但它不适用于TCP,因为TCP是没有组织的流。

所以是的,Flush工作正常。只是无论你多少次拨打Flush一次,以及你拨打多个Send多少次,另一端的每个Receive都会获得尽可能多的数据。适合其缓冲区,与另一方的Send无关。

您设计的解决方案(几乎 - 只用单个\0分隔字符串)实际上是处理此问题的正确方法之一。通过这样做,您再次处理流上方的消息。这称为消息框架 - 它允许您区分各个消息。在您的情况下,您已在消息之间添加了分隔符。考虑在文件中写入相同的数据 - 再次,您需要自己的某种方式来分隔各个消息(例如,使用结束行)。

处理邮件框架的另一种方法是使用长度前缀 - 在发送字符串本身之前,发送它的长度。然后,当你在另一边阅读时,你知道字符串之间应该总是有一个长度前缀,所以读者知道消息何时结束。

另一种方式对您的情况可能不太有用 - 您可以使用固定长度的数据。因此,例如,消息将始终为100个字节。当与预定义的消息类型结合使用时,这非常强大 - 因此消息类型1将包含正好两个整数,例如代表一些协调。

但在任何一种情况下,您都需要在接收端进行自己的缓冲。这是因为(正如您已经看到的)单个接收可以一次读取多个消息,同时,不能保证在一次读取中读取整个消息。编写自己的网络实际上非常棘手 - 除非你这样做才能实际学习网络编程,我建议使用一些现成的技术 - 例如,Lindgren(一个很好的网络库,针对游戏进行了优化)但也适用于一般网络)或WCF。对于聊天系统,简单的HTTP(特别是使用双向WebSockets)也可能没问题。

修改

正如Damien正确指出的那样,您的代码似乎还存在另一个问题 - 您似乎忽略了Read的返回值。返回值告诉您实际读取的字节数。由于接收端有一个固定大小的持久缓冲区(显然),这意味着之后之后的每个字节仍然包含数据。要解决此问题,请确保您只使用返回的Read字节数。此外,由于这似乎表明您完全忽略了Read返回值,因此请确保在Read返回0时正确处理这种情况 - 这意味着另一方已正常关闭其连接 - 接收方应该这样做。