本节来理论性的了解一下关于服务端 accept
非阻塞.
阻塞 accept
服务器在繁忙过程时, 在建立三次握手之后, 调用accept之前, 如果出现客户端突然断开连接的情况, POSIX
指出这种情况 errno 设置为 CONNABORTED.
如: 三次握手之后, 客户端发送 RST后断开连接, 之后服务端调用accept
准备执行连接但并不知道对端已经关闭, 这个时候accept就会阻塞, 直到有下一个已完成的连接准备好被accept为止. 这个问题在 TCP可能出现的异常[1] 中提到过, 只是实验没有成功.
上述描述的整个过程如下 :
- 服务器的监听的 socket 可读, 但服务器要在一段时间之后 (实验中采用
sleep
模仿) 才能调用accept.- 服务器调用accept之前, 收到从客户发送过来的 RST;
- 这个已经完成的连接被从已完成连接队列中被删除;
- 服务器调用 accept, 但是由于没有其它已完成的连接存在, 因而服务器被阻塞了.
服务器会被一直阻塞在accept调用上, 直到另外一个客户建立一个连接为止; 但如果一直没有其它客户建立连接, 那么服务器将仍然一直被阻塞在accept调用上, 不处理任何其他已就绪的 socket. 这样的就严重降低了服务器的利用率.
非阻塞 accept
上述的问题也很容易解决, 解决这个问题的办法:
如果使用 select 来获知何时有已完成连接时, 总是把监听 socket 设置为非阻塞模式,并且在 accept 调用中忽略以下错误 : EWOULDBLOCK (Berkeley : 客户放弃连接时出现的错误)、 ECONNABORTED (POSIX : 客户放弃连接时出现的错误)、 EPROTO (SVR4 : 客户放弃连接时出现的错误) 和 EINTR (信号中断).
小结
- 阻塞 accept 可能发生的错误