当非阻塞send()只传输部分数据时,我们可以假设它会在下次调用时返回EWOULDBLOCK吗?

Dav*_*uss 15 c sockets linux nonblocking

非阻塞套接字的手册页中详细记录了两个案例:

  • 如果send()返回与传输缓冲区相同的长度,则整个传输成功完成,并且套接字可能处于或不处于返回EAGAIN/EWOULDBLOCK的状态,下一次调用> 0字节进行传输.
  • 如果send()返回-1并且errno是EAGAIN/EWOULDBLOCK,则没有传输完成,并且程序需要等到套接字准备好接收更多数据(epoll情况下的EPOLLOUT).

没有记录的非阻塞套接字是:

  • 如果send()返回一个小于缓冲区大小的正值.

是否可以安全地假设send()甚至会在一个字节的数据上返回EAGAIN/EWOULDBLOCK?或者,非阻塞程序是否应该再次发送()以获得结论性的EAGAIN/EWOULDBLOCK?我担心在插座上放一个EPOLLOUT观察器,如果它实际上没有处于"阻塞"状态以响应它的出现.

显然,后一种策略(再次尝试获得某种结论)具有明确定义的行为,但它更加冗长并且会对性能产生影响.

Dam*_*mon 28

呼叫send有三种可能的结果:

  1. 发送缓冲区中至少有一个字节可用→ send成功并返回接受的字节数(可能少于您要求的字节数).
  2. 您打电话时发送缓冲区已满send.
    →如果套接字阻塞,send阻止
    →如果套接字是非阻塞的,则send使用EWOULDBLOCK/EAGAIN
  3. 发生错误(例如,用户拔出网络电缆,通过对等方重置连接)→ send失败并出现其他错误

如果接受的字节数send小于您要求的数量,则这意味着发送缓冲区现在已完全填满.然而,这对于未来的任何电话来说都是纯粹的间接和非授权send.
返回的信息send仅仅是您呼叫时当前状态的"快照" send.当send您返回时或send再次拨打电话时,此信息可能已经过时.当程序在内部时send,或者在一个纳秒之后,或者在任何其他时间,网卡可能会在数据报上放置数据报- 无法知道.您将知道下一个呼叫何时成功(或何时不成功).

换句话说,但这并不意味着下一次调用send将返回EWOULDBLOCK/ EAGAIN(或者套接字未无阻塞会阻止).尝试直到所谓的"得出结论EWOULDBLOCK"是正确的做法.

  • 几年前有关于新闻的讨论:comp.protocols.tcp-IP,普遍认为阻止send()阻塞直到所有数据都被缓冲.该新闻组由TCP的所有实现者填充.他们应该知道. (3认同)
  • 我认为这只适用于非阻塞:"发送成功并返回接受的字节数(可能比你要求的少)." 阻塞套接字应该阻塞,直到它完成所有数据的发送或由于其他原因而失败.我要么将阻塞方案添加到#1,要么从#2中删除阻塞方案(仅对非阻塞套接字做出回答). (2认同)
  • @DavidTimothyStrauss:令人惊讶的是,您的假设完全符合联机帮助页和POSIX的措辞(类似`write`系统调用中的措辞明确提到了部分写入).虽然这个措辞对于数据报套接字是有意义的(你不能使用`send`!),但对于面向连接的套接字来说没有任何意义.一次发送例如100kB肯定是允许的,但是如果整个消息必须适合"64kB的典型"缓冲区大小,则意味着你的程序会阻塞_forever_(因为它永远不会将完整的消息放入缓冲区) ). (2认同)
  • 当然,对于数据报套接字,它是有意义的,因为只能发送完整的消息,完整的消息必须适合缓冲区.但是数据报套接字需要有一个缓冲区,可以容纳至少一个最大尺寸的数据报.这意味着`sendto`不会阻止_forever_.对于流套接字,部分发送已经存在了30年(无论手册中的措辞是否混乱),例如参见[Beej的指南](http://www.beej.us/guide/bgnet/output/html/ multipage/advanced.html #sendall)关于这个主题(Beej的网络指南通常是一个非常好的阅读). (2认同)
  • @Damon阻塞send()保证数据至少*被缓冲.它可以经历多个缓冲区发送缓冲区循环,并在整个64MiB完全发送或缓冲后返回. (2认同)

use*_*421 7

如果 send() 返回与传输缓冲区相同的长度,则整个传输成功完成,套接字可能处于阻塞状态,也可能不处于阻塞状态。

不。套接字保持在它所处的模式:在这种情况下,非阻塞模式,在下面始终假设。

如果 send() 返回 -1 并且 errno 是 EAGAIN/EWOULDBLOCK,则没有任何传输完成,程序需要等待套接字不再阻塞。

直到发送缓冲区不再满为止。套接字保持非阻塞模式。

如果 send() 返回一个小于缓冲区大小的正值。

套接字发送缓冲区中只有那么多空间。

假设 send() 会阻塞甚至多一个字节的数据是否安全?

“假设 [它] 会阻塞”根本不“安全”。不会。它处于非阻塞模式。EWOULDBLOCK 意味着它在阻塞模式下阻塞。

还是非阻塞程序应该再尝试一次 send() 以获得结论性的 EAGAIN/EWOULDBLOCK?

随你(由你决定。无论您决定如何,API 都可以工作。

我担心在套接字上放置一个 EPOLLOUT 观察器,如果它实际上没有阻塞的话。

这不是“阻止”。它没有阻止任何事情。它处于非阻塞模式。发送缓冲区在那一刻被填满。过一会可能就完全空了。

我看不出你在担心什么。如果您有待处理的数据并且上次写入没有全部发送,请选择可写性,并在获得时写入。如果这样的写入发送了所有内容,则下次不要选择可写性。

套接字通常是可写的,除非它们的发送缓冲区已满,所以不要一直选择可写性,因为您只会得到一个自旋循环。