87-非阻塞 connect
来源:互联网 发布:白银理财投资实时数据 编辑:程序博客网 时间:2024/05/18 02:45
非阻塞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 连接:
- 连接建立成功:套接字描述符可写。
- 连接建立失败:套接字描述符可读可写。
根据上面两条规则,我们可以使用 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
- 非阻塞connect
- 非阻塞connect编程
- linux非阻塞connect
- 非阻塞CONNECT
- 创建非阻塞connect
- 非阻塞connect()
- 非阻塞connect
- 非阻塞connect学习
- 非阻塞connect
- 非阻塞connect
- 非阻塞connect流程
- 非阻塞connect
- 非阻塞connect()
- 非阻塞connect问题
- 非阻塞connect
- 非阻塞connect
- 非阻塞connect
- While 循环的各个部分
- Hadoop学习笔记(2)
- CodeForces 600CMake Palindrome
- HUNNU11628 真的是简单的题【水题】
- 消息队列应用
- 87-非阻塞 connect
- linux中cp复制、mv移动、重命名,SVN回滚命令的应用
- 《高性能MySQL》读书笔记--多版本并发控制算法
- Hadoop学习笔记(3)
- 数据库数组合并输出json
- python基础------日期time
- 今天火箭又输了!
- redis备份脚本(自用)
- URL中“#” “?” &“”号的作用