非阻塞connect
通常的socket描述符是阻塞式的,connect连接时可能出现长时间没有连接成功的情形,若将socket描述符设置为非阻塞,那么调用connect后三次握手还可能没有完全建立connect立即返回EINPROGRESS,这个过程中可以设置超时等待或者做其它的事情。
非阻塞式有3种用途:
1.三次握手同时做其他的处理。connect要花一个往返时间完成,从几毫秒的局域网到几百毫秒或几秒的广域网。这段时间可能有一些其他的处理要执行,比如数据准备,预处理等。
2.用这种技术建立多个连接。这在web浏览器中很普遍.
3.由于程序用select等待连接完成,可以设置一个select等待时间限制,从而缩短connect超时时间。多数实现中,connect的超时时间在75秒到几分钟之间。有时程序希望在等待一定时间内结束,使用非阻塞connect可以防止阻塞75秒,在多线程网络编程中,尤其必要。 例如有一个通过建立线程与其他主机进行socket通信的应用程序,如果建立的线程使用阻塞connect与远程通信,当有几百个线程并发的时候,由于网络延迟而全部阻塞,阻塞的线程不会释放系统的资源,同一时刻阻塞线程超过一定数量时候,系统就不再允许建立新的线程(每个进程由于进程空间的原因能产生的线程有限),如果使用非阻塞的connect,连接失败使用select等待很短时间,如果还没有连接后,线程立刻结束释放资源,防止大量线程阻塞而使程序崩溃。
非阻塞式connect的实现方式:客户端发起对服务器的socket的连接请求(connect调用,如果客户端socket描述符为阻塞模式则会一直阻塞到连接建立或者连接失败,注意阻塞模式的超时时间可能为75秒到几分钟之间),而如果为非阻塞模式,则调用connect之后如果连接不能马上建立则返回-1(errno设置为EINPROGRESS,注意连接也可能马上建立成功比如连接本机的服务器进程),如果没有马上建立返回,此时TCP的三路握手动作在背后继续,而程序可以做其他的东西,然后调用select检测非阻塞connect是否完成(此时可以指定select的超时时间,这个超时时间可以设置为比connect的超时时间短),如果select超时则关闭socket,然后可以尝试创建新的socket重新连接,如果select返回非阻塞socket描述符可写则表明连接建立成功。给出一个实例:
客户端非阻塞connect代码:
#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<assert.h>#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<errno.h>#include<string.h>#include<iostream>#define BUF_SIZE 1024using namespace std;int main(int argc,char* argv[]){ if(argc<=2){ cout<<"argc<=2"<<endl; return 1; } const char* ip=argv[1]; int port=atoi(argv[2]); struct sockaddr_in address; bzero(&address,sizeof(address)); address.sin_family=AF_INET; inet_pton(AF_INET,ip,&address.sin_addr); address.sin_port=htons(port); int sock=socket(PF_INET,SOCK_STREAM,0); assert(sock>=0); int ret=bind(sock,(struct sockaddr*)&address,sizeof(address)); assert(ret!=-1); ret=listen(sock,5); assert(ret!=-1); struct sockaddr_in client; socklen_t client_addrlength=sizeof(client); int connfd=accept(sock,(struct sockaddr*)&client,&client_addrlength); if(connfd<0){ cout<<"errno is:"<<errno<<endl; } else{ char buf[BUF_SIZE]; memset(buf,'\0',BUF_SIZE); ret=recv(connfd,buf,BUF_SIZE-1,0); cout<<buf<<endl; close(connfd); } close(sock); return 0;}