使用通用的启用FIONBIO阻塞和禁用阻塞达到目的。
代码如下:
#if !defined(INVALID_SOCKET)
#define INVALID_SOCKET (SOCKET)(~0)
#endif
#if !defined(SOCKET_ERROR)
#define SOCKET_ERROR (-1)
#endif
/*
*@brief 异步的socket connect
*@param s socket打开的描述符,同connect的第一个参数
*@param name 包含目标计算机的ip和端口的描述信息,同connect的第二个参数
*@param namelen name的长度,同connect的第三个参数
*@nTimeout 允许connet连接超时(最大可接受时间),单位毫秒。默认22秒。
*@return 成功返回0,失败返回错误码,含义参考errno的错误码表。
*@note 此函数调用类似于同步的connet。在使用此函数之前不要设置socket为异步,否则异步将失效。
*/
int synchronous_connect(
SOCKET s, const struct sockaddr * name, int namelen, int nTimeout = 22000 )
{
// 确保socket和目标地址信息正确
if (INVALID_SOCKET == s || NULL == name || 0 == namelen)
{
return -1;
}
// 启动异步
u_long nBlock(1); //为1则异步,0则取消异步
ioctlsocket(s, FIONBIO, &nBlock);
errno_t nRet = 0;
// 连接
if (SOCKET_ERROR == connect(s, name, namelen ))
{
fd_set fs;
FD_ZERO(&fs);
FD_SET(s, &fs);
TIMEVAL timeout;
timeout.tv_sec = nTimeout/1000;
timeout.tv_usec = nTimeout%1000;
int nTmp = select(s + 1, NULL, &fs, NULL, &timeout);
if ( nTmp<=0 )
{
//这里通常是由于超时
nRet = (0==nTmp?SOCKET_ERROR:errno);
goto EXIT_POINT;
}
int optval = 0;
int optlen = sizeof(optval);
if (0 != getsockopt(s, SOL_SOCKET, SO_ERROR,
(char*)&optval, (socklen_t*)&optlen))
{
// 其它错误
nRet = errno;
goto EXIT_POINT;
}
// 获取当前的socket错误状态,
// 如果此状态为0,则表示connet成功,否则是发生了错误。
nRet = optval;
};
EXIT_POINT:
nBlock = 0; // 取消异步
ioctlsocket(s, FIONBIO, &nBlock);
return nRet;
}