这开始让我感到沮丧,因为无论我做什么,我的数据包都会被推到一起,这使得在另一端必须区分它们很烦人(对象很容易,变量字符串可能很麻烦)。有没有办法避免这种情况?
这是我正在使用的一些原始代码(是的,初始化发生在这里,这是粗略的测试代码,所以抱歉糟糕的编程约定)
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
我需要在分离时接收这两个,因为它们被自动加载到阻塞队列中,我不想处理对象大小和字符串解析。
答案 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。