20

非阻塞套接字的手册页中详细记录了两种情况:

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

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

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

假设 send() 会在多一个字节的数据上返回 EAGAIN/EWOULDBLOCK 是否安全?或者非阻塞程序是否应该再尝试一次 send() 以获得最终的 EAGAIN/EWOULDBLOCK?我担心在套接字上放置一个 EPOLLOUT 观察程序,如果它实际上没有处于“将阻塞”状态来响应它的出来。

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

4

2 回答 2

36

调用send有三种可能的结果:

  1. 发送缓冲区中至少有一个字节可用→send成功并返回接受的字节数(可能比您要求的要少)。
  2. 在您调用时,发送缓冲区已满send
    →如果套接字是阻塞的,send阻塞
    →如果套接字是非阻塞的,send失败并显示EWOULDBLOCK/EAGAIN
  3. 发生错误(例如用户拔掉网线,对等方重置连接)→send失败并出现另一个错误

如果接受的字节数send小于您要求的数量,那么这意味着发送缓冲区现在完全满了。但是,这纯粹是间接的,对于未来对send.
返回的信息send只是您调用时当前状态的“快照” send。当时间send返回或您send再次致电时,此信息可能已经过时。网卡可能会在您的程序在内部send时,或在纳秒后,或在任何其他时间将数据报放在网络上 - 无法知道。您将知道下一次调用何时成功(或何时不成功)。

换句话说,这并不意味着下一次调用send将返回EWOULDBLOCK/ EAGAIN(或者如果套接字不是非阻塞的,则会阻塞)。尝试直到你所谓的“得出结论EWOULDBLOCK”才是正确的做法。

于 2013-10-16T09:42:16.533 回答
7

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

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

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

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

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

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

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

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

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

随你(由你决定。API 的工作方式取决于您的决定。

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

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

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

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

于 2013-10-15T22:21:26.583 回答