tcp socket在阻塞模式下,调用connect超时时间很长,所以尝试添加超时时间,先设置非阻塞模式,再调用connect,如果返回0,则连接建立成功。如果返回非0,此时通过给select设置超时时间来测试指定时间内连接是否建立成功,调用select,如果select返回值为0,表示在 select 的超时时间内未能成功建立连接,则认为连接建立失败,相反此时我们可以通过调用 getsockopt 来检测集合中的套接口上是否存在待处理的错误,如果存在错误则认为连接建立失败,否则认为成功,再设置回阻塞模式。但在测试过程中发现windows和linux下表现不一样。
windows下(windows10),设置非阻塞模式后,调用connect后会返回10035的错误(代表windows下无法立即完成一个非阻挡性套接字操作),如果无法是无法建立连接的IP,在到达指定的超时时间后select返回值为0,此时认为连接建立失败。
但linux下(ubuntu18)经过测试发现如果是连接不可达的IP,connect失败很快返回错误码101( 网络不可达),此时可认为连接建立失败。如果是可达的IP,connect会返回错误码115( 代表linux下无法立即完成一个非阻挡性套接字操作),此时调用select,即使是不能正常建立连接的ip,select也不会直接返回0,此时无法判断,继续调用getsockopt,在第四个参数中返回了错误码,此时认为连接建立失败。
先记录着,有修改的再更新,这是续篇,其余代码参考
跨平台tcp socket通信C++类_JaneYu7777777的博客-CSDN博客
bool Connect(const char *ip, unsigned short port, int timeOut)
{
struct sockaddr_in serverAddr;
int length = 0;
m_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (m_sockfd == -1)
{
printf("************tcp socket error, ip=%s, port=%d***************\n", ip, port);
return false;
}
serverAddr.sin_family = AF_INET;
//连接服务器的地址
serverAddr.sin_addr.s_addr = inet_addr(ip);//ip地址的转换
serverAddr.sin_port = htons(port);
int nRet = -1;
int len = sizeof(int);
timeval tm;
fd_set set;
unsigned long ul = 1;
int nError = -1;
bool bRet = false;
//设置为非阻塞模式
#ifdef WIN32
ioctlsocket(m_sockfd, FIONBIO, &ul);
#else
ioctl(m_sockfd, FIONBIO, &ul);
#endif
nRet = connect(m_sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
nError = GetError();
printf("************CTcpClient connect nRet=%d errno=%d************\n", nRet, nError);
if (nRet == -1)
{
if ((nError == 10035) || (nError == 115)) // 10035:代表windows下无法立即完成一个非阻挡性套接字操作,115代表linux下无法立即完成一个非阻挡性套接字操作,其它错误认为已经失败
{
tm.tv_sec = timeOut;
tm.tv_usec = 0;
FD_ZERO(&set);
FD_SET(m_sockfd, &set);
nRet = select(m_sockfd + 1, NULL, &set, NULL, &tm);
printf("************CTcpClient select nRet=%d errno=%d************\n", nRet, GetError());
if (nRet > 0)
{
#ifdef WIN32
getsockopt(m_sockfd, SOL_SOCKET, SO_ERROR, (char *)&nRet, &len);
#else
getsockopt(m_sockfd, SOL_SOCKET, SO_ERROR, &nRet, (socklen_t *)&len);
#endif
printf("************CTcpClient getsockopt nRet=%d errno=%d************\n", nRet, GetError());
if (nRet == 0)
{
bRet = true;
}
else
{
bRet = false;
}
}
else
{
bRet = false;
}
}
else
{
bRet = false;
}
}
else
{
printf("************CTcpClient connect success************\n");
bRet = true;
}
ul = 0;
//设置为阻塞模式
#ifdef WIN32
ioctlsocket(m_sockfd, FIONBIO, &ul);
#else
ioctl(m_sockfd, FIONBIO, &ul);
#endif
if (!bRet)
{
printf("************CTcpClient error, ip=%s, port=%d************\n", ip, port);
Close();
return false;
}
printf("************CTcpClient success, ip=%s, port=%d************\n", ip, port);
return true;
}