非阻塞i/o 上调用 connect 比非阻塞 i/o 上调用 read/write 要麻烦一点,一方面 connect 函数不能像 read/write 那样反复调用,它只能调用一次;另一方面,connect 函数返回错误,并不代表连接建立不成功。

1. 非阻塞 connect

对于 TCP 协议,在非阻塞 i/o 上调用 connect,意味着 connect 会发送 SYN 段给服务器:

  • 如果在 connect 返回时,收到了服务器的 ACK,则 connect 返回 0,意味着连接建立成功,这通常只会发生在本机连接上。
  • 如果在 connect 返回时,未收到服务器的 ACK,则 connect 返回 -1,同时 errno 置为 EINPROGRESS,这个错误表示“正在进行……”.
  • 如果 connect 返回错误,errno 不是 EINPROGRESS 可以立即判断连接建立失败。

1.1 如何判断连接成功或失败?

对于 TCP 连接:

  • 连接建立成功:套接字描述符可写
  • 连接建立失败:套接字描述符可读可写

注1:上面说的可读可写的含义是 select 函数返回的读集合和写集合是否存在这个描述符。若描述符在读集合里,说明可读;如果在写集合里,说明可写。
注2:一般来说,新创建的描述符,在执行 connect 前既不可读也不可写,连接成功后,则变得可写(这是一定的),但是不一定可读。如果新创建的描述符变得可读了,大概率意味着出错,因为错误是通过 read 系统调用间接返回的。

根据上面两条规则,我们可以使用 select 来处理这两种情况。事先建立读写集合,然后使用 select 监听。

但是,反过来根据套接字描述符可读可写来判断连接成功是不可行的。换句话说,下面这样做是不对的

  • 如果可写不可读:连接建立成功(可以这样判断)
  • 如果可写可读:连接建立失败(不可以这样判断)

原因很简单,可写可读,并不一定就是失败,也许是对端发来数据了呢?所以,得使用另外一种办法来判断——使用套接字选项 SO_ERROR. 这也是 SO_ERROR 极少能派上用场的地方之一。

只要套接字描述符变得可读或可写,直接使用 getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) 取得套接字的状态。

注意:不同版本实现 getsockopt 行为有差异。如果真的产生错误:

  • Berkeley 实现让 getsockopt 返回 0,同时 error 中保存错误码(linux 也是这样的)
  • Solaris 实现让 getsockopt 返回 -1,同时 errno 中保存错误码(不是 error 参数)

1.2 伪代码

int nbioConnect(int sockfd, sockaddr *addr, socklent_t addrlen, int nsec) {int ret, error;socklent_t len;fd_set rfds, wfds;setNonblock(sockfd, 1); // 设置为非阻塞error = 0;// 发起连接ret = connect(sockfd, addr, addrlen);if (ret < 0) {if (errno != EINPROGRESS) {// 立即返回错误。close(sockfd);return -1;}}else if (ret == 0) {// 这种一般出现在本机连接上setNonblock(sockfd, 0); // 重新设置为阻塞return 0;}rfds = {sockfd};wfds = {sockfd};tv.tv_sec = nsec;tv.tv_usec = 0;// 在连接建立成功或失败前,或者超时时间未到,select 是不会返回的。ret = select(sockfd + 1, &rfds, &wfds, NULL, nsec ? &tv : NULL);if (ret < 0) {// 小概率事件close(sockfd);return -1;}else if (ret == 0) {// 超时errno == ETIMEDOUT;close(sockfd);return -1;}// 如果执行到这里了,说明连接已经建立成功或者失败,也就是说套接字描述符一定是可写或可读的或两者兼有。if (FD_ISSET(sockfd, &rfds) || FD_ISSET(sockfd, &wfds)) {// 这个 if 判断显的多余len = sizeof(error);ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);if (ret < 0) {// Solaris 版本可能会发生这种情况close(sockfd);return -1;}}if (error) {// Linux 版本如果错误会执行到这里errno = error;close(sockfd);return -1;}setNonblock(sockfd, 0); return 0;
}

2. 实验

这一次,只是将上篇写的时间获取客户端中的 connect 函数修改为了 nbioConnect 函数,并添加了一个超时参数进行控制。

  • 程序路径

本文使用的程序托管在 gitos 上:http://git.oschina.net/ivan_allen/unp

如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/nonblockio/nbiotimecli.

  • 实验结果


图1 设置超时时间为 10 s,结果超时


图2 连接成功

3. 总结

  • 掌握非阻塞 i/o 上调用 connect 函数
  • 知道如何判断非阻塞 connect 函数调用成功还是失败

87-非阻塞 connect相关推荐

  1. 【网络编程】非阻塞connect详解

    一.为什么使用非阻塞connect TCP连接的建立涉及一个在三路握手过程,阻塞的connect一直等到客户收到自己的SYN的ACK才返回,这需要至少一个RTT时间,RTT时间波动很大从几毫秒到几秒. ...

  2. 网络编程学习笔记(非阻塞connect)

    设置非阻塞,如果返回EINPROGRESS,表示正在建立连接,还没有完成 非阻塞的三个用途: 1.我们可以在三路握手的同时做其它的处理.connect要花一个往返时间完成,而且可以是在任何地主,从几个 ...

  3. 非阻塞connect的实现

    步骤1: 设置非阻塞,启动连接 实现非阻塞 connect ,首先把 sockfd 设置成非阻塞的.这样调用 connect 可以立刻返回,根据返回值和 errno 处理三种情况: (1) 如果返回 ...

  4. linux下网络编程设置非阻塞,UNIX网络编程 非阻塞connect的实现

    一.<UNIX网络编程>-非阻塞connect 在一个TCP套接口被设置为非阻塞之后调用connect,connect会立即返回EINPROGRESS错误,表示连接操作正在进行中,但是仍未 ...

  5. 由select/epoll返回的非阻塞connect还会是EINPROGRESS状态吗?

    一般情况下,我们像下面代码中所示的这样使用非阻塞connect: #include <stdio.h> #include <stdlib.h> #include <str ...

  6. linux网络编程-----非阻塞connect

    libevent在内部由于使用io多路复用函数进行的事件监控,所以它所有的io读写操作都是非阻塞的,换句话说,libevent提供的接口函数evconnlistener_new_bind()和buff ...

  7. 非阻塞connect

    在 socket 是阻塞模式下 connect 函数会一直到有明确的结果才会返回(或连接成功或连接失败),如果服务器地址"较远",连接速度比较慢,connect 函数在连接过程中可 ...

  8. Linux高性能服务器I/0高级应用:非阻塞connect(15)

    前言 首linux系统下,connect函数是阻塞的,阻塞时间的长度与系统相关.而如果把套接字设置成非阻塞,调用connect函数时会报错Operation now in progress,且errn ...

  9. Linux 非阻塞connect,错误码:EINPROGRESS

    当我们以非阻塞的方式来进行连接的时候,返回的结果如果是 -1,这并不代表这次连接发生了错误,如果它的返回结果是 EINPROGRESS,那么就代表连接还在进行中. 后面可以通过poll或者select ...

  10. 非阻塞connect,错误码:EINPROGRESS

    当我们以非阻塞的方式来进行连接的时候,返回的结果如果是 -1,这并不代表这次连接发生了错误,如果它的返回结果是 EINPROGRESS,那么就代表连接还在进行中. 后面可以通过poll或者select ...

最新文章

  1. mysql ls命令,Linux 常用 ls命令详解
  2. 七秘诀工作效率与薪水翻番-转
  3. 我的R之路:参数假设检验
  4. python企业级框架_Python六大开源框架对比:Web2py略胜一筹
  5. 从字到词,大词典中文BERT模型的探索之旅
  6. 如何将git分支上的标记移动到其他提交?
  7. 利用ModelBinder防止XSS一次尝试
  8. 【转】使用AIDL实现进程间的通信之复杂类型传递
  9. Redis基础、应用场景、数据结构及案例
  10. Allegro走等长线设置
  11. c语言保留三位小数用float,float保留三位小数
  12. CentOS7 WordPress无法将上传的文件移动至wp-content/uploads/ ApacheNginx解决方案
  13. C# 创建桌面快捷方式
  14. 如何查看自己的外网 IP 地址
  15. uniapp微信小程序授权登录和获取微信绑定的手机号码
  16. 红米ac2100有ipv6吗_Redmi路由器AC2100开箱:六天线简单直接,功能丰富对玩家友好...
  17. 企业如何借助工具分析用户画像
  18. 计算相似度的LLR算法
  19. vue使用腾讯地图定位以及地图组件使用
  20. WebStorm license server

热门文章

  1. 全新在线制图网站源码在线制作横幅广告
  2. python sorted函数倒序_Python sorted函数
  3. 克隆巴赫系数 Cronbach‘s alpha 及 R, Python 实现
  4. sql注入总结(一)
  5. 三四线城市咖啡店的光荣与梦想
  6. 在ps中用3d来做阴影
  7. 【Linux】如何设置静态IP地址
  8. 树莓派装Aria2和YAAW实现无人值守远程离线下载服务
  9. 黑马程序员与兄弟连兄弟会的对比
  10. 黑马程序员—一张帖看完黑马所有学科、班级就业薪资贴