Linux:在 TCP 套接字上发送整个消息或不发送任何消息

Posted

技术标签:

【中文标题】Linux:在 TCP 套接字上发送整个消息或不发送任何消息【英文标题】:Linux: send whole message or none of it on TCP socket 【发布时间】:2012-03-25 23:09:33 【问题描述】:

我正在通过非阻塞 TCP 套接字发送各种自定义消息结构。我想在一次 send() 调用中发送整个结构,或者如果发送缓冲区中只有部分消息的空间(即 send() 返回 EWOULDBLOCK),则返回不发送任何字节的错误。如果没有足够的空间,我会丢弃整个结构并报告溢出,但我希望之后可以恢复,即接收者只接收到一系列有效的完整结构。有没有办法检查发送缓冲区的可用空间,或者告诉 send() 调用按照描述的方式执行?基于数据报的套接字不是一个选项,必须是基于连接的 TCP。谢谢。

【问题讨论】:

简而言之:没有。即使您能够将其一件件发送,也不能保证收件人会一件件收到它。 (甚至不能保证他会得到它) Check that write()/send() can process whole buffer without block, fail otherwise (no partial write) 的可能重复项(看起来答案是SOCK_SEQPACKET 可能在 Linux 上做你想做的事,但我找不到任何证据证明这是由POSIX) wildplasser 是正确的,但是...如果您的消息小于路径 MTU,您可以在绝大多数时间得到您想要的。然而。无论如何,根据我的经验,您不能依靠 PMTU 在公共互联网上保持不变。 【参考方案1】:

Linux 提供了一个SIOCOUTQioctl() 来查询TCP 输出缓冲区中有多少数据:

http://www.kernel.org/doc/man-pages/online/pages/man7/tcp.7.html

您可以使用它加上SO_SNDBUF 的值来确定传出缓冲区是否有足够的空间容纳任何特定消息。所以严格来说,你的问题的答案是“是”。

但是这种方法有两个问题。首先,它是特定于 Linux 的。其次,当没有足够的空间来发送您的全部信息时,您打算怎么做?循环并再次致电select?但这只会告诉您套接字已准备好再次写入,从而导致您忙于循环。

为了效率,你应该硬着头皮只处理部分写入;让网络堆栈担心将您的流分成数据包以获得最佳吞吐量。

【讨论】:

如果无法发送整条消息,则将其丢弃。这对于用例来说很好。丢失消息是允许的,但重要的是客户端永远不会收到消息的一部分,然后是另一个消息的开始。我可能必须在发送端实现一个额外的应用程序级缓冲区。只是希望避免这种情况。 @gimmeamilk 阅读您的评论让我认为您误解了 TCP 的工作原理......这与数据报无关,它是一个流:如果您完全编写它,客户端将不会收到部分消息(并且不要遭受连接丢失的困扰)【参考方案2】:

TCP 不支持事务;这是您必须在layer 7(应用程序)上处理的事情。

【讨论】:

以上是关于Linux:在 TCP 套接字上发送整个消息或不发送任何消息的主要内容,如果未能解决你的问题,请参考以下文章

发送消息保证后,是否与TCP套接字断开连接?

使用 Qt 通过 TCP 套接字部分发送 xml 消息

TCP套接字:检测对等方是不是在发送前关闭? (Linux)

如何在 C# 中使用 TCP 客户端读取整个输入缓冲区?

如何使用 Perl 通过 TCP 和 UDP 连接到远程机器?

套接字阻止在 Java 服务器和 Python 客户端之间发送消息