3

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

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

    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

4 回答 4

2

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

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

TCP 不知道也不关心程序的消息帧概念。它并不关心您发送 3 条“消息”以及单独的发送调用。一个有效的 TCP 实现可以为从流中读取的函数的每次调用返回一个字节,或者它可以在向您返回字节之前尽可能多地缓冲。你应该相应地编码。

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

我在这里更详细地处理了这个问题,尽管代码是 C++ 中的,但概念仍然适用。

于 2012-05-10T20:58:27.630 回答
1

看来您期望:

sender.NoDelay = true;

基本上意味着:

sender.Client.Send(buffer);

...成为阻塞调用,直到位从您的 NIC 上移开。不幸的是,这种情况并非如此。NoDelay只告诉网络层不要等待,但是您正在使用的底层调用Send具有一些异步行为,并且您的程序线程运行得足够快,以至于在您实质上附加第二个字符串之前网络层的线程不会唤醒。

阅读更多关于NoDelay 这里。阅读那里的注释以了解详细信息。

一些相关的引用:

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

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

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

于 2012-05-10T17:41:32.970 回答
1

此外sender.NoDelay = true,最终对我有用的技巧sender.Client.Send(...)是以Thread.Sleep(50).

实际数字没有研究,它只是从一开始就起作用(我猜测 50 毫秒是一个合理的等待时间,不会在我的发送队列中创建积压),因此请务必针对您的操作环境进行测试和调整。

题外话:接收我的数据的应用程序是一个闭路电视录像机,它可以在视频上叠加文本,这样每个 TCP 数据包都是屏幕上的不同行。我被迫对 Nagle 的算法做一些事情,否则我的“行”被显示为“捆绑”在一起(更不用说明显与机器使用缓冲区有关的其他工件)。

于 2012-06-26T15:50:16.957 回答
0

如果您想确保在 TCP 上单独接收具有您提到的约束的消息,您应该在每条消息之后关闭连接。如果您不期待回复,您可以关闭读写操作,如果您想要回复,请关闭仅写入操作。关闭写入将导致接收器收到 0 个字节,因此接收器可以检测到消息的结尾。例如,http 就是这样工作的。

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

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

于 2012-05-10T17:47:06.613 回答