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 提供了一个SIOCOUTQ
ioctl() 来查询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套接字:检测对等方是不是在发送前关闭? (Linux)