Window+TCP+connect 超时阻塞解决办法

现象:一般情况下,connect是以阻塞模式进行工作的,但如果在S/C架构开发中,如果S端没有开启,而C端去connect一个未开启或不存在的S时,就
会出现“卡死”的现象。

原因:
客户端在连接服务器时,可能会出现问题,导致三次握手无法完成,持续重试,表现在客户端程序的行为就是卡在connect调用上无法返回。在一个
TCP套接口被设置为非阻塞之后调用connect,connect会立即返回EINPROGRESS错误,表示连接操作正在进行中,但是仍未完成;同时TCP的三路握手操作
继续进行;在这之后,我们可以调用select来检查这个链接是否建立成功;


解决方法
1.建立socket
2.将该socket设置为非阻塞模式
3.调用connect()
4. 判断返回值是否是WSAEWOULDBLOCK,(在vs2015或linux下,应该是EINPROGRESS,没有试)
5.如果4成立,使用select()检查该socket描述符是否可写
6.如果5成立,则说明连接成功,将socket重设置为阻塞模式

附,网上还有一种说法是,第5步使用getsockopt来获取错误,如果不存在错误(错误值为0),则连接成功,但本人没有成功。

VC6++参考代码

 1 int SKTCPSocket::connectHost(char *ip, const unsigned int port)
 2 {
 3     int ret=0;
 4     timeval tm={2,0};  
 5     fd_set rset; 
 6     fd_set wset; 
 7     unsigned long mode = 1;  
 8 
 9     SOCKADDR_IN addrSrv;
10     addrSrv.sin_addr.S_un.S_addr = inet_addr(ip);
11     addrSrv.sin_family = AF_INET;
12     addrSrv.sin_port = htons(port);
13 
14     mode = 1;
15     ret=ioctlsocket(m_clientSocket, FIONBIO, &mode); /*!<设置为非阻塞模式,成功返回0 */
16     if(ret)
17     {
18         closesocket(m_clientSocket);
19         return -1;  
20     }
21 
22     ret=connect(m_clientSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));/*!<成功返回0,但在非阻塞情况下,一般为-1*/
23     if(ret)
24     {
25         ret=WSAGetLastError();
26         if(WSAEWOULDBLOCK==ret)/*!<如果Resource temporarily unavailable.10035*/
27         {
28             FD_ZERO(&rset); 
29             FD_ZERO(&wset); 
30             FD_SET(m_clientSocket, &rset);
31             FD_SET(m_clientSocket, &wset);
32 
33             ret=select(m_clientSocket+1, &rset, &wset, NULL, &tm) ;
34             if(ret<0)
35             {
36                 closesocket(m_clientSocket);
37                 return -2; 
38             }
39             else if(ret==0)/*!<Time Out*/
40             {
41                 closesocket(m_clientSocket);
42                 return -3; 
43             }
44             else
45             {
46                 if(FD_ISSET(m_clientSocket,&wset))/*!<如果可写,说明连接好*/
47                 {
48                     mode = 0;  
49                     ret=ioctlsocket(m_clientSocket, FIONBIO, &mode); /*!<再次设置为阻塞模式 */ 
50                     if(ret)   
51                     {  
52                         closesocket(m_clientSocket);
53                         return -4;  
54                     } 
55                     /*!<正确退出*/
56                     return 0;  
57                 }
58                 else
59                 {
60                     /*!<不可写*/
61                     closesocket(m_clientSocket);
62                     return -5;
63                 }
64                 
65             }
66         }//    end if(WSAEWOULDBLOCK==ret)
67         else
68         {
69             /*!<网络出现其他错误*/
70         closesocket(m_clientSocket);
71         return -6; 
72         }
73     }
74     else
75     {
76         return 0;
77     }
78 }

参考:
http://olive101.blog.163.com/blog/static/2051263201011221915696/
http://blog.csdn.net/saspss/article/details/8487678
http://blog.csdn.net/u014805066/article/details/50592415

原文地址:https://www.cnblogs.com/gjianw217/p/6297023.html