connect有默认的超时时间,非阻塞connect的实现依赖于select,select的一个参数可以用来指定超时时间。
非阻塞connect函数的实现,
1.调用fcntl将套接字设置为非阻塞
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
connect(sockfd, (SA*)&saddr, sizeof(saddr));
if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { socklen_t len = sizeof(error); /*SO_ERROR 获取错误状态*/ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) { close(sockfd); return -1; } } if (error) { close(sockfd); return -1; } }
示例程序
/* *connect_nowait 非阻塞connect建立连接并返回连接好的套接字 *addr 连接到的ipv4地址 *port 连接到的端口 *nsec 超时时间 *fp 函数指针 *返回值:成功返回套机字,出错返回-1 */ int connect_nowait(const char* addr,const unsigned int port, const int nsec, void (*fp)()) { int sockfd; struct sockaddr_in saddr; int flags; int r; int error; fd_set rset, wset; struct timeval tval; if (addr == NULL) return -1; /*地址格式不合法*/ if (inet_addr(addr) == -1) return -1; /*端口超出范围*/ if (port > 65535) return -1; /*时间不合法*/ if (nsec < 0) return -1; if (fp == NULL) return -1; bzero((void*)&saddr, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = inet_addr(addr); saddr.sin_port = htons(port); /*socket异常*/ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) return -1; if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) { close(sockfd); return -1; } /*设置为非阻塞*/ if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) { close(sockfd); return -1; } /*对非阻塞的TCP套接字调用connect,期待返回EINPROGRESS错误,表示引发三路握手,但连接尚未完成*/ if ((r = connect(sockfd, (SA*)&saddr, sizeof(saddr))) == -1) { if (errno != EINPROGRESS) { close(sockfd); return -1; } } /*建立连接期间,这里可以做一些其他的处理,提高时间利用率*/ fp(); /*如果客户-服务器都在一台主机上,那么这里可能会立即建立连接*/ if (r == 0) goto done; /*将套接字添加到读写描述符集中*/ FD_ZERO(&rset); FD_SET(sockfd, &rset); wset = rset; /*初始化超时时间*/ tval.tv_sec = nsec; tval.tv_usec = 0; /*select 异常*/ if ((r = select(sockfd+1, &rset, &wset, NULL, &tval)) == -1) { close(sockfd); return -1; } /*select 超时*/ if (r == 0) { close(sockfd); return -1; } /* *三种情况 *1.套接字成功建立连接,此时可写 *2.连接出错,套接字既可读又可写 *3.成功建立连接,并有对端数据到达,套接字既可读又可写 */ if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { socklen_t len = sizeof(error); /*SO_ERROR 获取错误状态*/ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) { close(sockfd); return -1; } } if (error) { close(sockfd); return -1; } done: if ((r = fcntl(sockfd, F_SETFL, flags)) == -1) { close(sockfd); return -1; } return sockfd; }
#include <iostream> #include "net.h" using namespace std; void while_connect() { for(int i=0; i<10; i++) cout << "while_connect" << endl; } int main() { int sockfd; char *addr = "182.92.78.165"; unsigned int port = 33333; int nsec = 3; sockfd = connect_nowait(addr, port, nsec, while_connect); if (sockfd == -1) cout << "连接失败" << endl; cout << "连接成功" << endl; return 0; }
执行结果:
总结:
非阻塞connect实际上与普通建立套接字并连接的步骤没有太大差别,只是调用fcntl将套接字设置为非阻塞后调用connect,同样引发三路握手,并不等待connect返回,,而去利用这段时间处理其他操作,更好的利用时间,之后再利用select检测套接字来判断连接是否完成,并且可以指定一个超时时间。
原文:http://blog.csdn.net/aspnet_lyc/article/details/29179501