关于非阻塞connect的若干细节性问题
我们用man connection命令查看手册,如下:
- EINPROGRESS
- The socket is nonblocking and the connection cannot be completed immediately. It
- is possible to select(2) or poll(2) for completion by selecting the socket for
- writing. After select(2) indicates writability, use getsockopt(2) to read the
- SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error
- codes listed here, explaining the reason for the failure).
当connect可写时还可能是发生了错误,我们必须判断这种情况。发生错误时套接字既可读又可写,而connect连接成功时套接字也可能即可读又可写(select之前有可能连接已经建立并有来自对端的数据到达).
如何区分这两种情况呢?因此,必须调用
- getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in);
if (wait_seconds > 0)
activate_nonblock(fd); //设为非阻塞
ret = connect(fd, (struct sockaddr*)addr, addrlen);
if (ret < 0 && errno == EINPROGRESS)
{
//printf("11111111111111111111\n");
fd_set connect_fdset;
struct timeval timeout;
FD_ZERO(&connect_fdset);
FD_SET(fd, &connect_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do
{
// 一但连接建立,则套接字就可写 所以connect_fdset放在了写集合中
ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout);
} while (ret < 0 && errno == EINTR);
if (ret == 0)
{
ret = -1;
errno = ETIMEDOUT;
}
else if (ret < 0)
return -1;
else if (ret == 1)
{
//printf("22222222222222222\n");
/* ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/
/* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */
int err;
socklen_t socklen = sizeof(err);
int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen);
if (sockoptret == -1)
{
return -1;
}
if (err == 0)
{
//printf("3333333333333\n");
ret = 0;
}
else
{
//printf("4444444444444444:%d\n", err);
errno = err;
ret = -1;
}
}
}
if (wait_seconds > 0)
{
deactivate_nonblock(fd); //设回阻塞
}
return ret;
}
浙公网安备 33010602011771号