select在异步(非阻塞)connect中的使用
socket编程中非阻塞connet调用的处理可以借助select来解决,大致步骤如下:
1. 将打开的socket设为非阻塞的
Windows设置方式
unsigned long unblock = 1;
ret = ioctlsocket(tSock, FIONBIO, (unsigned long *)&unblock);
if(ret == SOCKET_ERROR) {
printf("Set Socket NonBlock Failed!\n");
SockClose(tSock);
return INVALID_NODE;
}
Linux可以用
fcntl(socket, F_SETFL, O_NDELAY)
2. 调用connect,非阻塞形式下会返回-1,但是errno被设为EINPROGRESS,意即connect仍旧
在进行还没有完成.
3. 将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视, 如果可写,用 getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int)); 来得到error的值,如果为零,则connect成功.
4. 代码流程
flags = fcntl(tSock, F_GETFL, 0)
fcntl(tSock, F_SETFL, flags | O_NONBLOCK)
ret = connect(tSock, (SOCKADDR *)&tSvrINAddr, sizeof(tSvrINAddr))
if(ret < 0) {
/* if connect error */
if (errno != EINPROGRESS) {
printf("\nConnecting Failed!\n");
SockClose(tSock);
return INVALID_NODE;
} else {
fd_set rset, wset;
int error = 0;
socklen_t len = sizeof(error);
FD_ZERO(&rset);
FD_SET(tSock, &rset);
wset = rset;
bWait = select(tSock + 1, &rset, &wset, NULL, pTimeVal);
if (bWait <= 0) {
printf("Connect TimeOut!\n");
SockClose(tSock);
return INVALID_NODE;
}
if (getsockopt(tSock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
printf("Get Socket Error !\n");
SockClose(tSock);
return INVALID_NODE;
}
if (error > 0) {
printf("\nGet Socket Error %d\n", error);
SockClose(tSock);
return INVALID_NODE;
}
}
}
5. 注意:
22:参数错误,比如ip地址不合法,没有目标端口等
101:网络不可达,比如不能ping通
111:链接被拒绝,比如目标关闭链接等
115:当链接设置为非阻塞时,目标没有及时应答,返回此错误,socket可以继续使用