NetworkStream 不刷新数据

Ird*_*des 2 c# sockets

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

发送“aaasdsd”

收到“aaasdsd”

发送“bb”

收到“ bb asdsd

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

Lua*_*aan 6

TCP 不是这样工作的。就这么简单。

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

您在这里尝试做的是发送两个单独的消息,并在另一端接收两个单独的消息。这在 UDP(基于消息的)上可以正常工作,但在 TCP 上不起作用,因为 TCP 是没有组织的流。

所以是的,Flush工作得很好。只是无论您Flush在一侧调用多少次,以及调用单个Sends多少次,Receive另一端的每个都将获得其缓冲区中可以容纳的尽可能多的数据,而与Send另一侧的s无关.

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

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

另一种方法对您的情况可能不是很有用 - 您可以使用固定长度的数据。例如,一条消息总是正好是 100 个字节。当与预定义的消息类型结合使用时,这非常强大 - 例如,消息类型 1 将包含两个整数,代表一些坐标。

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

编辑:

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