服务端使用IO多路复用(select,poll等)时, 为什么监听套接字不能是阻塞IO的?

预备知识

RST

if (FD_ISSET(listen_fd, &readset)) { // 监听套接字上触发事件,说明有连接建立完成,此时调用 accept 肯定可以返回已连接套接字。这样看来,似乎把监听套接字设置为非阻塞,没有任何好处。
printf("listening socket readable\n");    
sleep(5); // 在这5秒期间,已完成连接的套接字(已连接套接字),因为对方发送RST使得本服务端删除掉该连接。
struct sockaddr_storage ss;    
socklen_t slen = sizeof(ss);    
int fd = accept(listen_fd, (struct sockaddr *) &ss, &slen); // 没有创建好的连接,此时accep会一直阻塞,直到有新的连接建立好。
// 更为严重的是,该线程再也没有机会对其他 I/O 事件进行分发,相当于该服务器无法对新连接和其他 I/O 进行服务。// TODO 有的问题

Q: 如果accept传入的监听套接字listen_fd是非阻塞IO的,那么accept就不会阻塞总是会返回值吗?

 int fd = accept(listen_fd, (struct sockaddr *) &ss, &slen); // 如果listen_fd是非阻塞IO的套接字,accept是不是就不会阻塞,但是会多几种返回值?

A: 应该是总是能返回,但是会返回异常值,需要忽略掉。
A:
所以解决的办法就是把监听套接字设为非阻塞,再用accept配合IO复用。
然后对于accept的返回值,忽略EWOULDBLOCK(BSD中的客户终止连接)、EAGAIN、ECONNABORTED(POSIX实现中的客户终止连接)、EPROTO(SVR4中的客户终止连接)、EINTR(被信号中断)。// 附录2

可能阻塞的套接字调用可分为以下四类

// 附录3
套接字的默认状态是阻塞的,这就意味着当发出一个不能立即完成的套接字调用时,其进程将被投入睡眠,等待响应操作完成,可能阻塞的套接字调用可分为以下四类:
(1) 输入操作,包括read,readv,recv,recvfrom,recvmsg;
(2) 输出操作,包括write,writev,send,sendto,sendmsg;
(3) 接受外来连接,即accept函数。
(4) 发起外出连接,即tcp的connect函数;

参考

  1. https://time.geekbang.org/column/article/141573
  2. 非阻塞TCP套接字的要点
  3. TCP之非阻塞connect和accept
posted @ 2020-04-18 21:15  sicnu-yudidi  阅读(366)  评论(0编辑  收藏  举报