非阻塞connect的作用及代码示例

connect 函数的调用涉及到TCP连接的三次握手过程,通常阻塞的connect 函数会等待三次握手成功或失败后返回,0成功,-1失败。如果对方未响应,要隔6s,重发尝试,,可能要等待75s的尝试并最终返回超时,才得知连接失败。即使是一次尝试成功,也会等待几毫秒到几秒的时间,如果此期间有其他事务要处理,则会白白浪费时间,而用非阻塞的connect 则可以做到并行,提高效率。

而通常,非阻塞的connect 函数与 select 函数配合使用:在一个TCP套接口被设置为非阻塞之后调用connect,connect (函数本身返回-1)会立即返回EINPROGRESS或EWOULDBLOCK错误,表示连接操作正在进行中,但是仍未完成;同时TCP的三路握手操作继续进行;在这之后,我们可以调用select来检查这个链接是否建立成功。

若建立连接成功,select的结果中该描述符可写;若失败,则可写可读,此时可以使用getsockopt获取错误信息。

正常的三次握手时序:

(以下内容转自)

非阻塞connect有三种用途:1. 我们可以在三路握手的同时做一些其它的处理。connect 操作要花一个往返时间完成,而且可以是在任何地方,从几个毫秒的局域网到几百毫秒或几秒的广域网,在这段时间内我们可能有一些其他的处理想要执行;2. 可以用这种技术同时建立多个连接.在Web浏览器中很普遍;3. 由于我们使用select 来等待连接的完,因此我们可以给select设置一个时间限制,从而缩短connect 的超时时间。在大多数实现中,connect 的超时时间在75秒到几分钟之间。有时候应用程序想要一个更短的超时时间,使用非阻塞connect 就是一种方法。

非阻塞connect 听起来虽然简单,但是仍然有一些细节问题要处理:1. 即使套接口是非阻塞的。如果连接的服务器在同一台主机上,那么在调用connect 建立连接时,连接通常会立即建立成功,我们必须处理这种情况。2. 源自Berkeley的实现(和Posix.1g)有两条与select 和非阻塞IO相关的规则:A. 当连接建立成功时,套接口描述符变成可写; B. 当连接出错时,套接口描述符变成既可读又可写。

处理非阻塞connect的步骤(重点):1. 创建socket,返回套接口描述符;2. 调用fcntl 把套接口描述符设置成非阻塞;3. 调用connect 开始建立连接;4. 判断连接是否成功建立。

判断连接是否成功建立:A. 如果connect 返回0,表示连接成功(服务器和客户端在同一台机器上时就有可能发生这种情况);B. 调用select 来等待连接建立成功完成;如果select 返回0,则表示建立连接超时。我们返回超时错误给用户,同时关闭连接,以防止三路握手操作继续进行下去。如果select 返回大于0的值,则需要检查套接口描述符是否可写,如果套接口描述符可写,则我们可以通过调用getsockopt来得到套接口上待处理的错误(SO_ERROR)。如果连接建立成功,这个错误值将是0;如果建立连接时遇到错误,则这个值是连接错误所对应的errno值(比如:ECONNREFUSED,ETIMEDOUT等)。

代码示例int conn_nonb(int sockfd, const struct sockaddr_in *saptr, socklen_t salen, int nsec){int flags, n, error, code;socklen_t len;fd_set wset;struct timeval tval;flags = fcntl(sockfd, F_GETFL, 0);fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);error = 0;if ((n == connect(sockfd, saptr, salen)) == 0) {goto done;} else if (n < 0 && errno != EINPROGRESS){return (-1);}/* Do whatever we want while the connect is taking place */FD_ZERO(&wset);FD_SET(sockfd, &wset);tval.tv_sec = nsec;tval.tv_usec = 0;if ((n = select(sockfd+1, NULL, &wset,NULL, nsec ? &tval : NULL)) == 0) {close(sockfd); /* timeout */errno = ETIMEDOUT;return (-1);}if (FD_ISSET(sockfd, &wset)) {len = sizeof(error);code = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);/* 如果发生错误,Solaris实现的getsockopt返回-1,* 把pending error设置给errno. Berkeley实现的* getsockopt返回0, pending error返回给error.* 我们需要处理这两种情况 */if (code < 0 || error) {close(sockfd);if (error)errno = error;return (-1);}} else {fprintf(stderr, "select error: sockfd not set");exit(0);}done:fcntl(sockfd, F_SETFL, flags); /* restore file status flags */return (0);}

我想有一天和你去旅行。去那没有去过的地方,

非阻塞connect的作用及代码示例

相关文章:

你感兴趣的文章:

标签云: