关于非阻塞connect的若干细节性问题
2016-03-08 11:33
429 查看
我们用man connection命令查看手册,如下:
EINPROGRESS
The socket is nonblocking and the connection cannot be completed immediately. It
is possible to select(2) or poll(2) for completion by selecting the socket for
writing. After select(2) indicates writability, use getsockopt(2) to read the
SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error
codes listed here, explaining the reason for the failure).
当connect可写时还可能是发生了错误,我们必须判断这种情况。发生错误时套接字既可读又可写,而connect连接成功时套接字也可能即可读又可写(select之前有可能连接已经建立并有来自对端的数据到达).
如何区分这两种情况呢?因此,必须调用
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)
另一个问题,对于非阻塞式套接字,如果其上的connect调用被信号中断并且不会由内核自动重启,那么它将返回EINTR,此时我们不能再次调用connect等待未完成的连接继续完成,这样做将导致返回EADDRINUSE错误。我们只能调用select检测其状态,就像上面说的那样。
EINPROGRESS
The socket is nonblocking and the connection cannot be completed immediately. It
is possible to select(2) or poll(2) for completion by selecting the socket for
writing. After select(2) indicates writability, use getsockopt(2) to read the
SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error
codes listed here, explaining the reason for the failure).
当connect可写时还可能是发生了错误,我们必须判断这种情况。发生错误时套接字既可读又可写,而connect连接成功时套接字也可能即可读又可写(select之前有可能连接已经建立并有来自对端的数据到达).
如何区分这两种情况呢?因此,必须调用
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)
另一个问题,对于非阻塞式套接字,如果其上的connect调用被信号中断并且不会由内核自动重启,那么它将返回EINTR,此时我们不能再次调用connect等待未完成的连接继续完成,这样做将导致返回EADDRINUSE错误。我们只能调用select检测其状态,就像上面说的那样。
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) { int ret; socklen_t addrlen = sizeof(struct sockaddr_in); if (wait_seconds > 0) activate_nonblock(fd); //设为非阻塞 ret = connect(fd, (struct sockaddr*)addr, addrlen); if (ret < 0 && errno == EINPROGRESS) { //printf("11111111111111111111\n"); fd_set connect_fdset; struct timeval timeout; FD_ZERO(&connect_fdset); FD_SET(fd, &connect_fdset); timeout.tv_sec = wait_seconds; timeout.tv_usec = 0; do { // 一但连接建立,则套接字就可写 所以connect_fdset放在了写集合中 ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout); } while (ret < 0 && errno == EINTR); if (ret == 0) { ret = -1; errno = ETIMEDOUT; } else if (ret < 0) return -1; else if (ret == 1) { //printf("22222222222222222\n"); /* ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/ /* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */ int err; socklen_t socklen = sizeof(err); int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen); if (sockoptret == -1) { return -1; } if (err == 0) { //printf("3333333333333\n"); ret = 0; } else { //printf("4444444444444444:%d\n", err); errno = err; ret = -1; } } } if (wait_seconds > 0) { deactivate_nonblock(fd); //设回阻塞 } return ret; }
相关文章推荐
- [面试系列]HTTPS交互流程
- java中 Static 用法
- android RecyclerView 完全解析
- 隐藏在微信支付中的坑
- 虚拟机VirtualBox和Ubutu
- Nginx负载均衡配置实例详解
- Eclipse+Maven创建webapp项目<一>
- 经典洗牌算法(Knuth-Durstenfeld Shuffle)
- Wrtnode 配置方法:
- windows 服务
- 同步原语,无共享架构
- c语言结构体数组的应用问题
- 如何将纯色背景的图片转换为背景透明的图片
- 自定义view你需要知道的
- Redis入门很简单之一【简介与环境搭建】
- Error retrieving parent for item: No resource found that matches the given name 'android:TextAppearance.Material.Widget.Button.Inverse'.
- C#索引器2 字符串作为索引号
- python 类属性初始化
- iOS 开发技巧-制作环形进度条
- /var目录满导致启动glusterd服务失败