一、connect非阻塞编写

  TCP连接的建立涉及到一个三次握手的过程,且socket中connect函数需要一直等到客户接收到对于自己的SYN的ACK为止才返回,

这意味着每 个connect函数总会阻塞其调用进程至少一个到服务器的RTT时间,而RTT波动范围很大,从局域网的几个毫秒到几百个毫秒甚至广域网上的几秒。

这段 时间内,我们可以执行其他处理工作,以便做到并行。在此,需要用到非阻塞connect。

将一个阻塞connect变为非阻塞大概有如下几步:

第一步:将套接字设置为非阻塞模式

设置套接字可以用ioctl()或者fcntl()

(1)用ioctl设置

 /*创建套接字*/
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//.................
/*step 1: 将套接字设置为非阻塞*/
flags = ;
ioctl(sockfd, FIONBIO, &flags);

(2)用fcntl函数设置

  #include <unistd.h>
#include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ ); //int fcntl(int fd, int cmd, long arg);

参数fd

参数fd代表欲设置的文件描述符

参数cmd

参数cmd代表打算操作的指令
有以下几种情况:
F_DUPFD用来查找大于或等于参数arg的最小且仍未使用的文件描述符,并且复制参数fd的文件描述符。
F_GETFD取得close-on-exec旗标。若此旗标的FD_CLOEXEC位为0,代表在调用exec()相关函数时文件将不会关闭。
F_SETFD 设置close-on-exec 旗标。该旗标以参数arg 的FD_CLOEXEC位决定。
F_GETFL 取得文件描述符状态旗标,此旗标为open()的参数flags。
F_SETFL 设置文件描述符状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。
F_GETLK 取得文件锁定的状态。
F_SETLK 设置文件锁定的状态。此时flcok 结构的l_type 值必须是F_RDLCK、F_WRLCK或F_UNLCK。如果无法建立锁定,则返回-1,错误代码为EACCES 或EAGAIN。

F_SETLKW F_SETLK 作用相同,但是无法建立锁定时,此调用会一直等到锁定动作成功为止。若在等待锁定的过程中被信号中断时,会立即返回-1,错误代码为EINTR。

返回值

fcntl的返回值与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列四个命令有特定返回值:

F_DUPFD、F_GETFD、F_GETFL、F_GETOWN.

第一个返回新的文件描述符,接下来的两个返回相应标志,最后一个返回一个正的进程ID或负的进程组ID。

 /*创建套接字*/
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//.................
/*获取flags标志*/
int flags;
flags = fcntl(sockfd, F_GETFL, );
/*设置套接字为非阻塞*/
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

第二步:调用connect连接判断返回值

 ret = connect(sockfd, addr, addrlen);
if (ret == ) {
return TURE;
}
if (ret < && errno == EINPROGRESS) { /*如果调用connect函数会之间返回-1(表示出错),且错误为EINPROGRESS,表示连接建立,建立启动但是尚未完成,*/
}
else
// connect error 

实现非阻塞 connect ,首先把 sockfd 设置成非阻塞的。这样调用 connect 可以立刻返回,根据返回值和 errno 处理三种情况

(1) 如果返回 0,表示 connect 成功。

(2) 如果返回值小于 0, errno 为 EINPROGRESS, 表示连接建立已经启动但是尚未完成。这是期望的结果,不是真正的错误

(3) 如果返回值小于0,errno 不是 EINPROGRESS,则连接出错了。

建立connect连接,此时socket设置为非阻塞,connect调用后,无论连接是否建立立即返回-1,同时将errno(包含 errno.h就可以直接使用)设置为EINPROGRESS, 表示此时tcp三次握手仍旧进行,如果errno不是EINPROGRESS,则说明连接错误,程序结束。
当客户端和服务器端在同一台主机上的时候,connect回马上结束,并返回0;无需等待,所以使用goto函数跳过select等待函数,直接进入连接后的处理部分

第三步:将套接字添加到select集合中,判断是否可读可写

ret = connect(sockfd, addr, addrlen);
if (ret < && errno == EINPROGRESS) { /*如果调用connect函数会之间返回-1(表示出错),且错误为EINPROGRESS,表示连接建立,建立启动但是尚未完成;*/
FD_ZERO(&connectfd);
FD_SET(, &connectfd);
FD_SET(sockfd, &connectfd);//将套接字加入到集合中
   /*设置超时时间*/
timeout.tv_sec = ;
timeout.tv_usec = ;
if (select(sockfd+, NULL, &connectfd, NULL, &timeout) > )
{
len = sizeof(uint32);
       /*获取error值,并进行判断*/
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, (socklen_t*)&len);
if (err == )
{
ret = TURE;
}
else
ret = ERR;
}
else
ret = ERR;
}

  设置等待时间,使用select函数等待正在后台连接的connect函数,这里需要说明的是使用select监听socket描述符是否可读或者可写,如果只可写,说明连接成功,可以进行下面的操作。如果描述符既可读又可写,分为两种情况,第一种情况是socket连接出现错误,第二种情况是connect连接成 功,socket读缓冲区得到了远程主机发送的数据。需要通过connect连接后返回给errno的值来进行判定,或者通过调用 getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&error,&len); 函数返回值来判断是否发生错误.当网络发生错误的时候,getsockopt返回-1,return -1,程序结束。网络正常时候返回0,程序继续执行

第四步:恢复套接字描述符状态为非阻塞,并返回

 ()
/* 恢复套接字非阻塞*/
flags = ;
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); ()
/* 恢复套接字阻塞*/
flags = ;
ioctl(sockfd, FIONBIO, &flags);

下面是整个connect流程代码:

(1) 创建socket,并利用fcntl将其设置为非阻塞

(2) 调用connect函数,如果返回0,则连接建立;如果返回-1,检查errno ,如果值为 EINPROGRESS,则连接正在建立

(3) 为了控制连接建立时间,将该socket描述符加入到select的可写集合中,采用select函数设定超时

(4) 如果规定时间内成功建立,则描述符变为可写;否则,采用getsockopt函数捕获错误信息

(5) 恢复套接字的文件状态并返回

 int Connect(uint32 sockfd, struct sockaddr *addr,  socklen_t addrlen)
{
struct timeval timeout;
uint32 flags, len;
int err, ret;
fd_set connectfd; if (NULL == addr)
return ERR; /*step 1: 将套接字设置为非阻塞*/
flags = ;
ioctl(sockfd, FIONBIO, &flags); /*step 2: 调用connect连接*/
if ((ret = connect(sockfd, addr, addrlen)) < )
{
if (errno != EINPROGRESS)
ret = ERR; FD_ZERO(&connectfd);
FD_SET(, &connectfd);
FD_SET(sockfd, &connectfd); timeout.tv_sec = ;
timeout.tv_usec = ;
if (select(sockfd+, NULL, &connectfd, NULL, &timeout) > )
{
len = sizeof(uint32);
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, (socklen_t*)&len);
if (err == )
{
ret = TURE;
}
else
ret = ERR;
}
else
ret = ERR;
} 42   /*恢复套接字为阻塞模式*/
flags = ;
ioctl(sockfd, FIONBIO, &flags); return ret;
}

网络编程之非阻塞connect编写的更多相关文章

  1. UNIX网络编程——非阻塞connect:时间获取客户程序

    #include "unp.h" int connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec) ...

  2. UNIX网络编程-非阻塞connect和非阻塞accept

    1.非阻塞connect 在看了很多资料之后,我自己的理解是:在socket发起一次连接的时候,这个过程需要一段时间来将三次握手的过程走完,如果在网络状况不好或者是其他的一些情况下,这个过程需要比较长 ...

  3. UNIX网络编程——非阻塞connect: Web客户程序

    非阻塞的connect的实现例子出自Netscape的Web客户程序.客户先建立一个与某个Web服务器的HTTP连接,再获取一个主页.该主页往往含有多个对于其他网页的引用.客户可以使用非阻塞conne ...

  4. UNIX网络编程——非阻塞connect

    当在一个非阻塞的TCP套接字上调用connect时,connect将立即返回一个EINPROGRESS错误,不过已经发起的TCP三次握手继续进行.我们接着使用select检测这个连接或成功或失败的已建 ...

  5. linux 客户端 Socket 非阻塞connect编程

    开发测试环境:虚拟机CentOS,windows网络调试助手        非阻塞模式有3种用途        1.三次握手同时做其他的处理.connect要花一个往返时间完成,从几毫秒的局域网到几百 ...

  6. 面向连接的socket数据处理过程以及非阻塞connect问题

    对于面向连接的socket类型(SOCK_STREAM,SOCK_SEQPACKET)在读写数据之前必须建立连接,首先服务器端socket必须在一个客户端知道的地址进行监听,也就是创建socket之后 ...

  7. (转)非阻塞Connect对于select时应注意问题

    对于面向连接的socket类型(SOCK_STREAM,SOCK_SEQPACKET)在读写数据之前必须建立连接,首先服务器端socket必须在一个客户端知道的地址进行监听,也就是创建socket之后 ...

  8. 网络IO模型 非阻塞IO模型

    网络IO模型 非阻塞IO模型 同步 一件事做完后再做另一件事情 异步 同时做多件事情 相对论 多线程 多进程 协程 异步的程序 宏观角度:异步 并发聊天 阻塞IO 阻塞IO的问题 一旦阻塞就不能做其他 ...

  9. TCP非阻塞accept和非阻塞connect

    http://blog.chinaunix.net/uid-20751538-id-238260.html 非阻塞accept     当一个已完成的连接准备好被accept的时候,select会把监 ...

随机推荐

  1. Android中让多个线程顺序执行探究

    线程调度是指按照特定机制为多个线程分配CPU的使用权. 有两种调度模型:分时调度模型和抢占式调度模型. 分时调度模型:是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的CPU的时间片. ...

  2. 四种简单的图像显著性区域特征提取方法-----AC/HC/LC/FT。

    四种简单的图像显著性区域特征提取方法-----> AC/HC/LC/FT. 分类: 图像处理 2014-08-03 12:40 4088人阅读 评论(4) 收藏 举报 salient regio ...

  3. iOS中UITableView分割线左侧顶齐

    iOS 7开始UITableView的分割线不在从左侧边界开始了,而是默认空出了一段距离. 如果想要使用默认的分割线而且还要从左侧边界开始的话,有几种解决方式: 1.在tableView的代理方法中设 ...

  4. Unix/Linux中的grep命令(转)

    本文转载自:如何使用Unix/Linux grep命令——磨刀不误砍柴工系列.该博文条理很清晰. grep简介 grep在一个或多个文件中查找与模式字符串(pattern)匹配的行,并将搜索的结果打印 ...

  5. Java进阶(十七)ArrayList与LinkedList的区别

    ArrayList与LinkedList的区别 ArrayList ArrayList其实是包装了一个数组 Object[],当实例化一个ArrayList时,一个数组也被实例化,当向ArrayLis ...

  6. Java中如何封装自己的类,建立并使用自己的类库?

    from:http://blog.csdn.net/luoweifu/article/details/7281494 随着自己的编程经历的积累会发现往往自己在一些项目中写的类在别的项目中也会有多次用到 ...

  7. HFile

    HFile存储格式 HBase中的所有数据文件都存储在Hadoop HDFS文件系统上,主要包括两种文件类型: 1. HFile, HBase中KeyValue数据的存储格式,HFile是Hadoop ...

  8. ANSI控制码的说明

    例如: echo -ne "\33[32m" 可以将字符的显示颜色改为绿色 echo -ne "\33[3;1H" 可以将光标移到第3行第1列处 具体的摘抄一些 ...

  9. 总结一下 Spring的IOC、DI

    国庆节刚过,应一些朋友的提问,总结一下Spring中IOC也即DI的通俗理解. 网友wm5920解释: IOC控制反转:说的是创建对象实例的控制权从代码控制剥离到IOC容器控制,实际就是你在xml文件 ...

  10. 熊猫猪新系统测试之四:Ubuntu 14.04

    目前猫猪在办公室一般用的就是乌班图系统,一方面原因是老本本性能跑不起来Windows,更重要的是本猫觉得Linux系统更开放些.况且现在用的也比较熟了,完全可以脱离Windows鸟!这一系列4篇新系统 ...