在使用非阻塞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高性能服務器編程 遊雙