1 非阻塞connect
1.1 阻塞模式与非阻塞
调用connect会发起三次连接。
阻塞模式下,connect的返回结果:
- 发出syn分节后,对方没有收到对端的syn分节,这时会返回ETIMEOUT。超时时间大概在75秒左右。
- 发出syn分节后,对方没有在监听指定的端口,便会回复RST。这时返回ECONNREFUSED。
- 发出syn分节后,引发路由器一个ICMP目的不可达的错误。多次尝试后仍无法成功发送,这时会返回EHOSTUNREACH或者ENETUNREACH。
非阻塞模式下,调用connect后会立刻返回EINPROGRESS,同时三次握手还在进行。
1.2 非阻塞模式的实现
int socfd = socket();
//set non-blocking
int ret = connect(sockfd, ...);
if(ret < 0)
{
return -1;
}
if(errno == EINPRPGRESS)
{
fd_set rdset, wrset;
FD_SET(sockfd, rdset);
FD_SET(sockfd, wrset);
ret = select(sockfd+1, &rdset, &wrset, NULL, timeout);
if(FD_ISSET(sockfd, &wrset))
{
if(FD_ISSET(sockfd, rdset))
{
//清楚sock error
}
//成功连接
return sockfd.
}
}
1.3 非阻塞connect的意义
非阻塞connect的意义在于提高并发度。阻塞connect下,完成一个三次握手需要耗费一个RTT时间。RTT时间波动很大。从局域网内的几时毫秒到广域网的几十秒。阻塞模式下,进程被connect阻塞住,什么都干不了。非阻塞下,我们可以让select或者epoll来监听listenfd,直到完成三次连接再继续进行数据的手法。
2 非阻塞accept
2.1 非阻塞accept的实现
int sockfd = socket();
//set non-blocking
bind()
fd_set rdset;
FD_SET(sockfd, &rdset);
while(1)
{
int ret = select(sockfd+1, &rdset, NULL, NULL, timeout);
if(ret < 0)
{
// err log
}
if(errno == EPROTO || ...)
{
continue;
}
int accept(sockfd,...);
}
2.2 注意点
完成三次连接后,accept之前,select为可读,这个时候如果对端关闭连接,accept这个关闭的sockfd会阻塞。
所以select返回后,accept前,总是忽略EWOULDBLOCK和ECONNABORTED、EPROTO、EINTR。
2.3 非阻塞accept的意义
非阻塞accept的意义同样在于提高并发度。
网友评论