非阻塞socket的连接
引用自:http://blog.csdn.net/cccallen/article/details/6619994
连接套接字,阻塞的套接字超时时间很长无法接受,而是用非阻塞套接字时使用的方案也有多种。后者是个比较好的方法
方案1:不断重试,直到连接上或者超时:
- int connect_socket_timeout(int sockfd,char *dest_host, int port, int timeout)
- {
- struct sockaddr_in address;
- struct in_addr inaddr;
- struct hostent *host;
- int err, noblock=1 , connect_ok=0, begin_time=time(NULL);
- log_debug("connect_socket to %s:%dn",dest_host,port);
- if (inet_aton(dest_host, &inaddr))
- {
- log_debug("inet_aton ok now gethostbyaddr %sn",dest_host);
- memcpy(&address.sin_addr, &inaddr, sizeof(address.sin_addr));
- }
- else
- {
- log_debug("inet_aton fail now gethostbyname %s n",dest_host);
- host = gethostbyname(dest_host);
- if (!host) {
- /* We can't find an IP number */
- log_error("error looking up host %s : %dn",dest_host,errno);
- return -1;
- }
- memcpy(&address.sin_addr, host->h_addr_list[0], sizeof(address.sin_addr));
- }
- address.sin_family = AF_INET;
- address.sin_port = htons(port);
- /* Take the first IP address associated with this hostname */
- ioctl(sockfd,FIONBIO,&noblock);
- /** connect until timeout */
- /*
- EINPROGRESS A nonblocking socket connection cannot be completed immediately.
- EALREADY The socket is nonblocking and a previous connection attempt has not been completed.
- EISCONN The socket is already connected.
- */
- if (connect(sockfd, (struct sockaddr *) &address, sizeof(address)) < 0)
- {
- err = errno;
- if (err != EINPROGRESS)
- {
- log_error("connect = %d connecting to host %sn", err,dest_host);
- }
- else
- {
- // log_notice("connect pending, return %d n", err);
- while (1) /* is noblocking connect, check it until ok or timeout */
- {
- connect(sockfd, (struct sockaddr *) &address, sizeof(address));
- err = errno;
- switch (err)
- {
- case EISCONN: /* connect ok */
- connect_ok = 1;
- break;
- case EALREADY: /* is connecting, need to check again */
- //log_info("connect again return EALREADY check again...n");
- usleep(50000);
- break;
- default: /* failed, retry again ? */
- log_error("connect fail err=%d n",err);
- connect_ok = -1;
- break;
- }
- if (connect_ok==1)
- {
- //log_info ("connect ok try time =%d n", (time(NULL) - begin_time) );
- break;
- }
- if (connect_ok==-1)
- {
- log_notice ("connect failed try time =%d n", (time(NULL) - begin_time) );
- }
- if ( (timeout>0) && ((time(NULL) - begin_time)>timeout) )
- {
- log_notice("connect failed, timeout %d secondsn", (time(NULL) - begin_time));
- break;
- }
- }
- }
- }
- else /* Connect successful immediately */
- {
- // log_info("connect immediate success to host %sn", dest_host);
- connect_ok = 1;
- }
- /** end of try connect */
- return ((connect_ok==1)?sockfd:-1);
- }
方案2:
补充关于select在异步(非阻塞)connect中的应用,刚开始搞socket编程的时候我一直都用阻塞式的connect,非阻塞connect的问题是由于当时搞proxy scan
而提出的呵呵,通过在网上与网友们的交流及查找相关FAQ,总算知道了怎么解决这一问题.同样用select可以很好地解决这一问题.大致过程是这样的:
1.将打开的socket设为非阻塞的,可以用fcntl(socket, F_SETFL, O_NDELAY)完成(有的系统用FNEDLAY也可).
2.发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧在进行还没有完成.
3.将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视,如果可写,用getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int));
来得到error的值,如果为零,则connect成功.
在许多unix版本的proxyscan程序你都可以看到类似的过程,另外在solaris精华区->编程技巧中有一个通用的带超时参数的connect模块.
我们知道,缺省状态下的套接字都是阻塞方式的,这意味着一个套接口的调用不能立即完成时,进程将进入睡眠状态,并等待操作完成。对于某些应用,需要及时 可控的客户响应,而阻塞的方式可能会导致一个较长的时间段内,连接没有响应。造成套接字阻塞的操作主要有recv, send, accept, connect.
下面主要以connect为例,讲讲非阻塞的connect的工作原理。当一个TCP套接字设置为非阻塞后,调用 connect,会立刻返回一个EINPROCESS的错误。但TCP的三路握手继续进行,我们将用select函数检查这个连接是否建立成功。建立非阻 塞的connect有下面三个用途:
1.可以在系统做三路握手的时候做些其它事情,这段时间你可以为所欲为。
2 可以用这个技术同时建立多个连接,在web应用中很普遍。
3.可以缩短connect的超时时间,多数实现中,connect的超时在75秒到几分钟之间,累傻小子呢?
虽然非阻塞的conncet实现起来并不复杂,但我们必须注意以下的细节:
* 即使套接字是非阻塞的,如果连接的服务器是在同一台主机,connect通常会立刻建立。(connect 返回 0 而不是 EINPROCESS)
* 当连接成功建立时,描述字变成可写
* 当连接出错时,描述字变成可读可写
- int connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec)
- {
- int flags, n, error;
- socklen_t len;
- fd_set rset, wset;
- struct timeval tval;
- // 获取当前socket的属性, 并设置 noblocking 属性
- flags = fcntl(sockfd, F_GETFL, 0);
- fcntl(sockfd, F_SETFL, flags | O_NOBLOCK);
- errno = 0;
- if ( (n = connect(sockfd, saptr, salen)) < 0)
- if (errno != EINPROGRESS)
- return (-1);
- // 可以做任何其它的操作
- if (n == 0)
- goto done; // 一般是同一台主机调用,会返回 0
- FD_ZERO(&rset);
- FD_SET(sockfd, &rset);
- wset = rset; // 这里会做 block copy
- tval.tv_sec = nsec;
- tval.tv_usec = 0;
- // 如果nsec 为0,将使用缺省的超时时间,即其结构指针为 NULL
- // 如果tval结构中的时间为0,表示不做任何等待,立刻返回
- if ((n = select(sockfd+1, &rset, &west, NULL,nsec ?tval:NULL)) == 0) {
- close(sockfd);
- errno = ETIMEOUT;
- return (-1);
- }
- if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &west)) {
- len = sizeof(error);
- // 如果连接成功,此调用返回 0
- if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
- return (-1);
- }
- else
- err_quit(“select error: sockfd not set”);
- done:
- fcntl(sockfd, F_SETFL, flags); // 恢复socket 属性
- if (error) {
- close(sockfd);
- errno = error;
- return (-1);
- }
- return (0);
- }
现在在网络服务器编程中使用epoll比较普遍,创建一个socket,设为异步socket(fcntl),
由epoll负责监听fd的状态,epoll_wait之捕获到EPOLLOUT事件,收到EPOLLOUT也不能认为是
TCP层次上connect(2)已经成功,要调用getsockopt看SOL_SOCKET的SO_ERROR是否为0。若为0,
才表明真正的TCP层次上connect成功。至于应用层次的server是否收/发数据,那是另一回事了。
转载于:https://www.cnblogs.com/langqi250/archive/2012/12/04/2801407.html
非阻塞socket的连接相关推荐
- 深入理解阻塞socket和非阻塞socket
什么是阻塞socket,什么是非阻塞socket.对于这个问题,我们要先弄清什么是阻塞/非阻塞.阻塞与非阻塞是对一个文件描述符指定的文件或设备的两种工作方式. 阻塞的意思是指,当试图对该文件描述符进行 ...
- linux socket 阻塞服务端 非阻塞客户端,linux下异步RPC的阶段性总结-非阻塞SOCKET客户端...
尽可能使用非阻塞socket int flags, s; flags = fcntl (fd, F_GETFL, 0); if (flags == -1){ close(fd); return -1; ...
- 设置非阻塞socket收发数据
非阻塞式I/O包括非阻塞输入操作,非阻塞输出操作,非阻塞接收外来连接,非阻塞发起外出连接.包括的函数有:read, readv, recv, recvfrom, recvmsg, write, wri ...
- 使用select+非阻塞socket写的网络数据转发程序 « Xiaoxia[PG]
使用select+非阻塞socket写的网络数据转发程序 « Xiaoxia[PG] 使用select+非阻塞socket写的网络数据转发程序 « Xiaoxia[PG] 从实践之中,我又学到东西了! ...
- 使用NIO实现异步非阻塞Socket tcp通信
使用NIO实现异步非阻塞Socket tcp通信 一.BIO与NIO IO(BIO)和NIO区别:其本质就是阻塞和非阻塞的区别 阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,就会一直等 ...
- linux 非阻塞 socket - Google 搜索
linux 非阻塞 socket - Google 搜索 linux c实现超时.非阻塞socket的函数select - Yunlu Liu (刘云璐) sites.google.com/site/ ...
- python select實現非阻塞socket
python select實現非阻塞socket - liukeforever的专栏 - 博客频道 - CSDN.NET python select實現非阻塞socket 分类: Python 201 ...
- 对于非阻塞socket的可写事件
当socket设置成非阻塞时,并且将EPOLLOUT事件通过epoll_ctl添加时,可写事件总是会触发,可以通过写代码来验证 代码如下: #include <sys/socket.h> ...
- socket编程 —— 非阻塞socket (转)---例子已上传至文件中
在上一篇文章 <socket编程--一个简单的例子> http://blog.csdn.net/wind19/archive/2011/01/21/6156339.aspx 中写了一个简单 ...
最新文章
- iOS开发中各种关键字的区别
- php编程习惯,经验分享:PHP编程的5个良好习惯(二)
- 系统分析员备考之系统工程篇(系统工程基础)
- 基于c语言中调试工具的用法汇总(不包含gdb)【转】
- 自己动手用electron+vue开发博客园文章编辑器客户端【一】
- Ajax学习笔记-运行原理及实现-2
- 开机自动启动程序的操作(就是这么简单)
- 小区物业管理系统计算机科学技术,智能化小区物业管理系统关键技术研究
- 图像拼接和图像融合技术(基于Opencv)
- X99主板2011-3接口E5 CPU一览表
- 母版页的详细使用介绍
- mp2格式怎么转换mp3?
- 学java被“劝退”的第六天
- nginx中配置二级域名和ssl
- 战略决定结构是什么意思?
- python从键盘上输入一个字符、当输入的是英文字母时_以下程序的功能:从键盘上输入一个字符,当输入的是英文字母时,输出\'输入的是英文字母\';
当输入的...
- 权值线段树+动态开点(学习小结)
- 机器学习问与答(二):线性学习
- npm优雅的卸载npm包
- Linux开机报错,提示Welcome to emergency mode, After logging in, type “journalctl -xb“ to view system log