即使在Tcpclient.NoDelay = true之后,socket.send也会将数据包附加在一起;

时间:2012-05-10 17:32:06

标签: c# sockets tcp

这开始让我感到沮丧,因为无论我做什么,我的数据包都会被推到一起,这使得在另一端必须区分它们很烦人(对象很容易,变量字符串可能很麻烦)。有没有办法避免这种情况?

这是我正在使用的一些原始代码(是的,初始化发生在这里,这是粗略的测试代码,所以抱歉糟糕的编程约定)

    sender.Connect("localhost", 8523);
    sender.Client.SendTimeout = 1000;
    sender.NoDelay = true;
    byte[] buffer = ASCIIEncoding.ASCII.GetBytes(message);
    sender.Client.Send(buffer);
    buffer = ASCIIEncoding.ASCII.GetBytes("DISCONNECT");
    sender.Client.Send(buffer);
    sender.Client.Close();

正如您所看到的,两个发送一个接一个地发生,并且套接字决定将它们发送到一起是最佳的。太好了,但我不想那样。因此,在另一端,它最终成为

   helloDISCONNECT

我需要在分离时接收这两个,因为它们被自动加载到阻塞队列中,我不想处理对象大小和字符串解析。

4 个答案:

答案 0 :(得分:2)

TCP向接收者提供一个字节流。

在流关闭之前,TCP套接字的每次读取都可以在1和您提供的缓冲区大小之间返回。你应该相应地编码。

TCP不了解或关心您的程序的消息框架概念。它不关心你发送3个“消息”与单独的发送调用。对于从流中读取的函数的每次调用,有效的TCP实现都可以返回单个字节,或者它可以在将字节返回给您之前缓冲起来。你应该相应地编码。

如果你想在通过TCP发送时处理消息,那么你必须应用你自己的框架,你必须在阅读你的消息时处理自己的框架。这通常意味着已知大小的长度前缀或消息终止符。

我更详细地处理了这个here,尽管代码是用C ++编写的,但这些概念仍然适用。

答案 1 :(得分:1)

看来你期待:

sender.NoDelay = true;

基本上意味着:

sender.Client.Send(buffer);

...成为阻塞呼叫,直到你的NIC关闭。不幸的是,这种情况并非如此。 NoDelay只告诉网络层没有等待,但是你使用Send进行的底层调用有一些异步行为,你的程序线程运行得足够快,以至于你的网络层线程没有唤醒ve基本上附加了第二个字符串。

详细了解NoDelay here。请阅读那里的评论以获取详细信息。

一些相关的引用:

  

获取或设置一个布尔值,该值指定流是否为Socket   正在使用Nagle算法。

     

Nagle算法旨在通过导致减少网络流量   套接字缓冲小数据包,然后组合并发送它们   在某些情况下一个包

BESIDES,即使你可以保证这些比特离开,客户的网络层也可以在你的客户端代码甚至有机会看到有两个“数据包”之前缓冲它们。

答案 2 :(得分:1)

除了sender.NoDelay = true之外,对我来说最终有效的诀窍是在sender.Client.Send(...)之后以Thread.Sleep(50)的形式使用了额外的延迟。

实际的数字没有研究过,它刚好从头开始工作(我估计50毫秒是一个合理的等待时间而没有在我的发送队列中创建积压),所以一定要测试并调整它以便你的操作环境。

Digression:接收我的数据的应用程序是一个CCTV录像机,它可以覆盖视频上的文本,以便每个TCP数据包在屏幕上是不同的行。我被迫对Nagle的算法做了一些事情,否则我的“线条”被显示为“聚集在一起”(更不用说显然与机器使用缓冲区有关的其他工件)。

答案 3 :(得分:0)

如果您希望确定在TCP上使用您提到的约束单独接收消息,则应在每条消息之后关闭连接。如果您不期待回复,可以关闭读取和写入,如果需要回复,请执行关闭以进行写入。执行关闭以进行写入将导致接收器接收0字节,因此接收器可以检测到消息结束。例如,http就像这样。

这里的权衡是你需要为每条消息打开一个新的TCP连接,这会导致一些开销。但是,在许多应用程序中,这并不重要。

免责声明:使用BSD套接字API很容易实现。我对C#的经验很少,不知道它是否提供了适合执行这些低级套接字操作的API。