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

时间:2013-10-15 21:14:01

标签: c linux sockets nonblocking

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

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

非阻塞套接字未记录的内容是:

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

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

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

2 个答案:

答案 0 :(得分:27)

致电send有三种可能的结果:

  1. 发送缓冲区中至少有一个字节可用→send成功并返回接受的字节数(可能比您要求的少)。
  2. 当您致电send时,发送缓冲区已完全 →如果套接字阻塞,send阻止
    →如果套接字是非阻止的,send会因EWOULDBLOCK / EAGAIN
  3. 而失败
  4. 发生错误(例如,用户拔出网线,通过对等方重置连接)→send因其他错误而失败
  5. 如果send接受的字节数小于您要求的数量,那么这意味着发送缓冲区现在已满。但是,对于未来对send的任何调用,这纯粹是间接的,非授权的 send返回的信息仅仅是您调用send时当前状态的“快照”。当send返回时或再次拨打send时,此信息可能已经过时。当您的程序在send内,或者在纳秒之后,或者在任何其他时间,网卡可能会在线上放置数据报 - 无法知道。您将知道下一个呼叫何时成功(或何时不成功)。

    换句话说,暗示下一次调用send将返回EWOULDBLOCK / EAGAIN(或者如果套接字没有,则会阻止) t非阻塞)。尝试直到所谓的“获得结论EWOULDBLOCK”是正确的事情。

答案 1 :(得分:4)

  

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

没有。套接字保持其所处的模式:在这种情况下,非阻塞模式,假设在下面。

  

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

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

  

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

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

  

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

“假设[它]会阻止”是不安全的。它不会。它处于非阻塞模式。 EWOULDBLOCK意味着阻止阻止模式。

  

或者非阻塞程序是否应该再次发送()以获得最终的EAGAIN / EWOULDBLOCK?

这取决于你。 API适用于您决定的任何内容。

  

我担心在插座上放一个EPOLLOUT观察器,如果它实际上并没有阻塞它。

这不是'阻止'。它不会阻止任何事情。它处于非阻塞模式。发送缓冲区在那一刻被填满。片刻之后它可能完全是空的。

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

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