问题由来:使用CSoket连接未知的服务端IP或未开启的服务端时,直接调用BOOL bRet =CSocket::Connect(strServerIP, nPort)
发生长时间的阻塞,导致程序退出时存在进程残留的问题。
问题原因:
BOOL CSocket::ConnectHelper(const SOCKADDR* lpSockAddr, int nSockAddrLen)
{
if (m_pbBlocking != NULL)
{
WSASetLastError(WSAEINPROGRESS);
return FALSE;
}
m_nConnectError = -1;
if (!CAsyncSocket::ConnectHelper(lpSockAddr, nSockAddrLen))
{
if (GetLastError() == WSAEWOULDBLOCK)
{
while (PumpMessages(FD_CONNECT))
{
if (m_nConnectError != -1)
{
WSASetLastError(m_nConnectError);
return (m_nConnectError == 0);
}
}
}
return FALSE;
}
return TRUE;
}
CSocket::Connect()
内部调用CSocket::ConnectHelper()
发送链接请求(瞬间执行完毕),之后在线程队列中轮询抓取FD_CONNECT消息,在此阻塞。
解决方法:直接调用ConnectHelper发送链接请求,然后通过select设定自定义时间,根据返回值确认是否真正链接。参考此处5楼
可以使用CAsyncSocket类中的OnConnect(int nErrorCode)函数
如果是超时的话,回返WSAETIMEDOUT (值为10060),可通过对nErrorCode进行判断来做出对用户提示超时信息。
但是此方法的返回时间无法设置,大概在20秒左右。
还可以通过一下步骤:
1,设置为非阻塞套接字 ;
2,connect, ;
3,select判断超时。
由于CAsyncSocket本身即为非阻塞套接字,所以只需在正常的connect语句后面添加select的判断语句来确定是否超时。
CAsyncSocket sock;
struct timeval tmo ;
fd_set r;
FD_ZERO(&r);
FD_SET(sock.m_hSocket, &r);
tmo.tv_sec = 3; //连接超时3秒
tmo.tv_usec = 0;
ret = select(0, 0, &r, 0, &tmo);
通过参考MSDN的select函数的返回值的说明:The select function returns the total number of socket handles that are ready and contained in the FD_SET structures, zero if the time limit expired, or SOCKET_ERROR if an error occurred.
可知,当ret==0时超时。
此外还有一种方法:
int TimeOut=6000; //设置发送超时6秒
if(::setsockopt(cClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
return 0;
}
TimeOut=6000;//设置接收超时6秒
if(::setsockopt(cClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
return 0;
}
使用setsockopt函数需要使用socket2
问题是我用的是socket2 但是没有成功实现这种方法。