非阻塞connect
connect出错时有一种error值为EINPROGRESS。这种错误发生在对非阻塞的socket调用connect,而连接又没有立即建立时。在这个时候我们可以调用select、poll等函数来监听这个连接失败的socket上的可写事件。当select、poll等函数返回后,再利用getsockopt函数来读取错误码并清除该socket上的错误。如果错误码为0,表示连接成功建立,负责连接失败。
通过以上的非阻塞connect方式,我们就能够同时发起多个连接并一起等待,具体代码实现如下:
//blokTimeMs表明非阻塞时的毫秒数,若值为-1表明阻塞
int connect(std::string ip, int port, int blockTimeMs = -1)
{
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
inet_pton(AF_INET, ip.c_str(), &addr.sin_addr);
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
//阻塞
if(blockTimeMs == -1)
{
int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
if(ret < 0)
{
perror("connectBloking");
close(sockfd);
return -1;
}
close(sockfd);
return 1;
}
//非阻塞
int oldOption = fcntl(sockfd, F_GETFL);
int newOption = oldOption | O_NONBLOCK;
//设置sockfd非阻塞
fcntl(sockfd, F_SETFL, newOption);
int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
if(ret == 0)
{
//连接成功
fcntl(sockfd, F_SETFL, oldOption);
return sockfd;
}
else if(errno != EINPROGRESS)
{
//连接没有立即返回,此时errno若不是EINPROGRESS,表明错误
perror("connect error != EINPROGRESS");
return -1;
}
fd_set readFds;
fd_set writeFds;
struct timeval timeout;
FD_ZERO(&readFds);
FD_ZERO(&writeFds);
FD_SET(sockfd, &writeFds);
FD_SET(sockfd, &readFds);
timeout.tv_sec = blockTimeMs/1000;
timeout.tv_usec = (blockTimeMs%1000)*1000;
ret = select(sockfd+1, &readFds, &writeFds, NULL, &timeout);
if(ret <= 0)
{
perror("select timeout or error");
close(sockfd);
return -1;
}
if(FD_ISSET(sockfd, &writeFds))
{
//可读可写有两种可能,一是连接错误,二是在连接后服务端已有数据传来
if(FD_ISSET(sockfd, &readFds))
{
if(connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) != 0)
{
int error=0;
socklen_t length = sizeof(errno);
//调用getsockopt来获取并清除sockfd上的错误.
if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &length) < 0)
{
printf("get socket option failed\n");
close(sockfd);
return -1;
}
if(error != EISCONN)
{
perror("connect error != EISCONN");
close(sockfd);
return -1;
}
}
}
//此时已排除所有错误可能,表明连接成功
fcntl(sockfd, F_SETFL, oldOption);
return sockfd;
}
else
{
perror("connect failed");
close(sockfd);
return -1;
}
}