套接字的默认状态时阻塞的
可能阻塞的套接字调用可分为以下4类:
1.输入操作,包括read、readv、recv、recvfrom和recvmsg。
2.输入操作,包括write、writev、send、sendto和sendmsg。
3.接受外来连接,即accept函数。
4.发起外出连接,即用于TCP的connect函数(该函数一直要等到客户收到对于自己的SYN的ACK为止才返回)
非阻塞connect
当在一个非阻塞的TCP套接字上调用connect时,connect将立即返回一个EINPROGRESS错误,不过已经发起的TCP三路握手继续执行。
非阻塞的connect有三个用途:
1.完成一个connect至少要花一个RTT时间,这段时间内也许我们想要执行的其他处理工作可执行
2.我们可以使用这个技术同时建立多个连接。这个用途已随着Web浏览器变得流行起来
3.既然使用select等待连接的建立,我们可以给select指定一个时间限制,使得我们能够缩短connect的超时
应用程序有时想要一个更短的超时时间,实现方法之一是使用非阻塞connect
非阻塞connect:时间获取客户程序
下面给出connect_nonb函数执行一个非阻塞connect,其中第四个参数是等待连接完成的秒数
1 #include "unp.h" 2 3 int 4 connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec) 5 { 6 int flags, n, error; 7 socklen_t len; 8 fd_set rset, wset; 9 struct timeval tval; 10 11 flags = Fcntl(sockfd, F_GETFL, 0); 12 Fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 13 14 error = 0; 15 if ( (n = connect(sockfd, saptr, salen)) < 0) 16 if (errno != EINPROGRESS) 17 return(-1); 18 19 /* Do whatever we want while the connect is taking place. */ 20 21 if (n == 0) 22 goto done; /* connect completed immediately */ 23 24 FD_ZERO(&rset); 25 FD_SET(sockfd, &rset); 26 wset = rset; 27 tval.tv_sec = nsec; 28 tval.tv_usec = 0; 29 30 if ( (n = Select(sockfd+1, &rset, &wset, NULL, 31 nsec ? &tval : NULL)) == 0) { 32 close(sockfd); /* timeout */ 33 errno = ETIMEDOUT; 34 return(-1); 35 } 36 37 if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { 38 len = sizeof(error); 39 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) 40 return(-1); /* Solaris pending error */ 41 } else 42 err_quit("select error: sockfd not set"); 43 44 done: 45 Fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ 46 47 if (error) { 48 close(sockfd); /* just in case */ 49 errno = error; 50 return(-1); 51 } 52 return(0); 53 }
select之后,如果描述符变为可读或可写,我们就调用getsockopt取得套接字的待处理错误。如果连接成功,该值将为0。
非阻塞connect:Web客户程序
在Web客户程序中。客户先建立一个与某个Web服务器的HTTP连接,再获取一个主页,该主页往往含有多个对于其他网页的引用。
客户可以使用非阻塞connect同时获取多个网页,以此取代每次只获取一个网页的串行获取手段。