1、非阻塞connect
在看了很多资料之后,我自己的理解是:在socket发起一次连接的时候,这个过程需要一段时间来将三次握手的过程走完,如果在网络状况不好或者是其他的一些情况下,这个过程需要比较长的时间,我们在连接之前将socket设置为非阻塞模式之后,调用connect函数之后,立即返回,如果成功返回0,如果不成功则返回EINPROGRESS,这个值表明连接正在进行,我们可以设置一个超时时间,然后在这个时间段内不停的检查socket是否连接上了,如果在这个时间段内还没有连上,则返回失败。在这个检查的过程中我们可以做一些其他事情,而不必等待连接。
实现这个检查过程,有两种方式:一种方法是用一个while循环来做,另一种方法是通过select来做。
方法一:
1 if (connect(sockfd, (struct sockaddr *) &address, sizeof(address)) < 0) 2 { 3 err = errno; 4 5 if (err != EINPROGRESS) 6 { 7 socket_ok = -1; 8 } 9 else 10 { 11 while (true) /* is noblocking connect, check it until ok or timeout */ 12 { 13 connect(sockfd, (struct sockaddr *) &address, sizeof(address)); 14 15 err = errno; 16 switch (err) 17 { 18 case EISCONN: /* connect ok */ 19 connect_ok = 1; 20 break; 21 22 case EALREADY: /* is connecting, need to check again */ 23 usleep(100); 24 break; 25 26 default: 27 connect_ok = -1; 28 break; 29 } 30 31 if (connect_ok == 1) 32 { 33 break; 34 } 35 36 if (connect_ok == -1) 37 { 38 break; 39 } 40 41 if ( (timeout > 0) && ((time(NULL) - begin_time) > timeout) ) 42 { 43 break; 44 } 45 } 46 } 47 } 48 else /* Connect successful immediately */ 49 { 50 connect_ok = 1; 51 }
方法二:
1 int CTCPConn::AsynConnectToServer(timeval tmConnTimeout) 2 { 3 int nRet = -1; 4 5 if( m_nEntityID <= 0 ) 6 { 7 return -1; 8 } 9 10 if( m_stSocket.CreateClient() ) 11 { 12 LOG("Conn create client of DBServer %d failed.\n", m_nEntityID); 13 return -1; 14 } 15 16 nRet = m_stSocket.AsynConnectTo(m_ulIPAddr, m_unPort); 17 18 // 返回-1表示正在连接 19 if ( nRet != -1 ) 20 { 21 return nRet; 22 } 23 24 fd_set writeSets; 25 SOCKET iSocketFD = m_stSocket.GetSocketFD(); 26 FD_ZERO(&writeSets); 27 FD_SET(iSocketFD, &writeSets); 28 29 nRet = select(iSocketFD + 1, NULL, &writeSets, NULL, &tmConnTimeout); 30 31 if ( nRet <= 0 ) 32 { 33 m_stSocket.Close(); 34 return -4; 35 } 36 37 if ( !FD_ISSET(iSocketFD, &writeSets) ) 38 { 39 m_stSocket.Close(); 40 return -5; 41 } 42 43 #ifdef WIN32 44 int nLen = sizeof(nRet); 45 getsockopt(iSocketFD, SOL_SOCKET, SO_ERROR, (char*)&nRet, &nLen); 46 #else 47 socklen_t nLen = sizeof(socklen_t); 48 getsockopt(iSocketFD, SOL_SOCKET, SO_ERROR, (char*)&nRet, &nLen); 49 #endif 50 51 if( nRet != 0 ) 52 { 53 m_stSocket.Close(); 54 return -6; 55 } 56 57 m_stSocket.SetConnected(); 58 59 return 0; 60 }
用select函数检查是否可写,这里可以设置一个超时时间,如果连接成功了的话,则socket变为可写的,select函数是可以检测到。检测到可写之后,用getsockopt函数来获取选项值,如果为0,则证明连接成功。
非阻塞连接的用途:
(1)可以在socket进行三次握手的时候干其他事情,不至于阻塞;
(2)可以同时进行多个连接,在web应用中比较常见(本人自己认为,浏览器就用到了这个,但具体怎么做的没有去了解)
(3)可以设置较短的超时时间,一般connect设计的时候都会有一个超时时间,会比较长,设置为非阻塞我们就可以自己来设定超时时间。
2、非阻塞accept
非阻塞accept主要用来解决以下问题:
用select检测socket状态,如果有连接就调用accept,这样如果在select检测到由连接请求,在调用accept之前,这个请求断开了,然后调用accept的时候就会阻塞在哪里,除非这时有另外一个连接请求,如果没有,则一直被阻塞在那里。(本人以为如果把accept单独放在一个线程中,不会出现以上问题,就是不明白这样做有什么坏处)。