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高性能服务器编程 游双

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值