网络编程之非阻塞connect编写
一、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
参数cmd
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编写的更多相关文章
- UNIX网络编程——非阻塞connect:时间获取客户程序
#include "unp.h" int connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec) ...
- UNIX网络编程-非阻塞connect和非阻塞accept
1.非阻塞connect 在看了很多资料之后,我自己的理解是:在socket发起一次连接的时候,这个过程需要一段时间来将三次握手的过程走完,如果在网络状况不好或者是其他的一些情况下,这个过程需要比较长 ...
- UNIX网络编程——非阻塞connect: Web客户程序
非阻塞的connect的实现例子出自Netscape的Web客户程序.客户先建立一个与某个Web服务器的HTTP连接,再获取一个主页.该主页往往含有多个对于其他网页的引用.客户可以使用非阻塞conne ...
- UNIX网络编程——非阻塞connect
当在一个非阻塞的TCP套接字上调用connect时,connect将立即返回一个EINPROGRESS错误,不过已经发起的TCP三次握手继续进行.我们接着使用select检测这个连接或成功或失败的已建 ...
- linux 客户端 Socket 非阻塞connect编程
开发测试环境:虚拟机CentOS,windows网络调试助手 非阻塞模式有3种用途 1.三次握手同时做其他的处理.connect要花一个往返时间完成,从几毫秒的局域网到几百 ...
- 面向连接的socket数据处理过程以及非阻塞connect问题
对于面向连接的socket类型(SOCK_STREAM,SOCK_SEQPACKET)在读写数据之前必须建立连接,首先服务器端socket必须在一个客户端知道的地址进行监听,也就是创建socket之后 ...
- (转)非阻塞Connect对于select时应注意问题
对于面向连接的socket类型(SOCK_STREAM,SOCK_SEQPACKET)在读写数据之前必须建立连接,首先服务器端socket必须在一个客户端知道的地址进行监听,也就是创建socket之后 ...
- 网络IO模型 非阻塞IO模型
网络IO模型 非阻塞IO模型 同步 一件事做完后再做另一件事情 异步 同时做多件事情 相对论 多线程 多进程 协程 异步的程序 宏观角度:异步 并发聊天 阻塞IO 阻塞IO的问题 一旦阻塞就不能做其他 ...
- TCP非阻塞accept和非阻塞connect
http://blog.chinaunix.net/uid-20751538-id-238260.html 非阻塞accept 当一个已完成的连接准备好被accept的时候,select会把监 ...
随机推荐
- Javascript和BHO的相互调用简介
v:* { } o:* { } w:* { } .shape { }p.MsoNormal,li.MsoNormal,div.MsoNormal { margin: 0cm; margin-botto ...
- 深度剖析linux内核万能--双向链表,Hash链表模版
我们都知道,链表是数据结构中用得最广泛的一种数据结构,对于数据结构,有顺序存储,数组就是一种.有链式存储,链表算一种.当然还有索引式的,散列式的,各种风格的说法,叫法层出不穷,但是万变不离其中,只要知 ...
- 【cocos 2d-x】VS2013+cocos2d-x3.3Final+Adriod交叉编译环境配置(超详细版)
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.com ...
- how tomcat works 5 servlet容器 下
上一节,我们主要说的是Wrapper容器,这一节我们说Context容器. 再重申一遍,一个Context容器可以包含多个Wrapper容器; 一个Wrapper容器就表示一个独立的servlet. ...
- mybatis配置开发
以mysql为例: 一.需要的架包:mybatis.jar和mysql-connector-java.jar 二.一般会有两类配置文件:数据库配置文件和要执行的sql语句 数据库配置文件(配置文件中有 ...
- ITU-T Technical Paper: NP, QoS 和 QoE的框架以及它们的区别
本文翻译自ITU-T的Technical Paper:<How to increase QoS/QoE of IP-based platform(s) to regionally agreed ...
- Oracle EBS 预警系统管理(可用于配置工作流发审批邮件)
本章主要讲述配置和设置Oracle EBS预警系统管理, 它比较方便和及时发用户或系统对数据库操作情况.下面讲一操作步聚: 1.预警系统管理-->系统-->选项 名称"Unix ...
- 根据Facebook内存的管理使用,浅谈在iOS上自动检测内存泄漏问题
分装库下载:https://github.com/facebook/FBMemoryProfiler FBMemoryProfiler类库使用教程:http://ifujun.com/fbmemory ...
- Redis客户端ServiceStack.Redis的简单使用
在nuget中下载ServiceStack.Redis,但是运行之后会出现一个问题: Exception: "Com.JinYiWei.Cache.RedisHelper"的类型初 ...
- VS2017安装包不占用C盘空间的方法,亲试
问题:普通VS2017的安装方式,不论是在线安装还是下载的离线安装包,都会在安装过程中将安装包保存在C:\ProgramData\Microsoft\VisualStudio\Packages文件夹下 ...