网络编程之非阻塞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会把监 ...
随机推荐
- gcc学习(二)[第二版]
头文件与库文件 1.概要 在使用C语言和其他语言进行程序设计的时候,我们需要头文件来提供对常数的定义和对系统及库函数调用的声明. 库文件是一些预先编译好的函数集合,那些函数都是按照可重用原则编写的.它 ...
- 《java入门第一季》之面向对象(代码块一网打尽)
上一篇里面对代码块做出介绍,这里给出一个面试题,加深印象. 如有毁三观的地方,请见谅.拒绝黄赌毒 写程序的执行结果. class Student { static { System.out.print ...
- 【66】Scanner类用法详解
Scanner是新增的一个简易文本扫描器,在 JDK 5.0之前,是没有的. public final class Scanner extends Object implements Iterator ...
- cocos2D v3.x中动作回调函数的变化
cocos2D v3.x版本中的动作的回调函数不能再带任何参数并且不能返回任何值. 官方给出的传递参数的办法是: 选择器(selector)不能带有任何形参,选择器需要的参数必须通过ivar或prop ...
- LeetCode之“字符串”:最短回文子串
题目链接 题目要求: Given a string S, you are allowed to convert it to a palindrome by adding characters in f ...
- objc:NSDateFormatter使用备忘
NSDateFormatter类的实例可以将字符串的日期表示转换为NSDate对象或者反向转换. 如果只要显示日期不需要时间,则可以用-setDateStyle方法来设置显示日期的格式,有以下几种: ...
- HBase数据字典
数据字典用来存储了系统的元数据.HBase的元数据包括:用户表的定义.表的切分方案.分片的分布情况(即分片分布在哪个regionserver上).分片对应的数据文件和日志文件.其中,分片和数据文件的映 ...
- linux设备驱动模块引用和依赖
/modules/04 # lsmod test 787 0 - Live 0xbf010000 (PO) func 633 1 test, Live 0xbf00c000 (PO) test -&g ...
- 用Xstream时候遇到的两个小异常
第一个 com.thoughtworks.xstream.converters.ConversionException: Cannot construct ClassXXX as it does no ...
- LeetCode(36)- Implement Stack using Queues
题目: Implement the following operations of a stack using queues. push(x) -- Push element x onto stack ...