在使用非阻塞connect时,常常会发生EINPROGRESS错误。这是在对非阻塞的socket调用connect,而连接又没有立即建立的情况下发生的错误,在这种情况下,我们可以调用select、poll等函数来监听这个连接失败的socket上的可写事件。当select、poll等函数返回后,再利用getsockopt来读取错误码并清除该socket上的错误。如果错误码是0,表示连接成功建立,否则连接失败。
非阻塞connect的实现:
#include<stdio.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/select.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
int set_nonblock(int fd)
{
int old_flag = fcntl(fd,F_GETFL);
int new_flag = old_flag | O_NONBLOCK;
fcntl(fd,F_SETFL,new_flag);
return old_flag;
}
int unblock_connect(const char *ip,int port,int outtime)
{
struct sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_port= htons(port);
inet_pton(AF_INET,ip,&sockaddr.sin_addr);
int sockfd = socket(AF_INET,SOCK_STREAM,0);
int oldflag = set_nonblock(sockfd);
int ret = connect(sockfd,(struct sockaddr*)&sockaddr,sizeof(sockaddr));
if(ret == 0)
{
/*如果连接成功,则恢复sockfd的属性,并立即返回*/
printf("connect with server immediately\n");
fcntl(sockfd,F_SETFL,oldflag);
return sockfd;
}
else if(errno != EINPROGRESS)
{
perror("connnet error:");
close(sockfd);
return -1;
}
struct timeval timeout;
timeout.tv_sec = outtime;
timeout.tv_usec = 0;
fd_set writefd;
FD_ZERO(&writefd);
FD_SET(sockfd,&writefd);
ret = select(sockfd + 1,NULL,&writefd,NULL,&timeout);
if(ret == -1)
{
perror("select error:");
close(sockfd);
return -1;
}
if( ! FD_ISSET(sockfd,&writefd))
{
printf("no events on sockfd found\n");
close(sockfd);
return -1;
}
int error = 0;
socklen_t length = sizeof(error);
/*调用getsockopt来获取并清除sockfd上的错误*/
if(getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&error,&length) < 0)
{
perror("getsockopt error:");
close(sockfd);
return -1;
}
/*错误号不为0表示连接出错*/
if(error != 0)
{
printf("connection failed after select with the error: %d\n",error);
close(sockfd);
return -1;
}
/*连接成功*/
printf("connetion ready after select with the socket: %d\n",sockfd);
fcntl(sockfd,F_SETFL,oldflag);
return sockfd;
}
int main(int argc,const char *argv[])
{
if(argc < 3)
{
printf("usage: %s ip_adrres port_number\n",argv[0]);
exit(1);
}
const char *ip = argv[1];
int port = atoi(argv[2]);
int sockfd = unblock_connect(ip,port,5);
if(sockfd < 0)
{
return -1;
}
close(sockfd);
return 0;
}
参考:Linux高性能服务器编程 游双