I/O複用的高級應用----非阻塞connect

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章