套接字I/O超时设置方法和用select实现超时
注:如无特殊说明,sockfd 原始状态都是阻塞的。
一、使用alarm 函数设置超时
1
2 3 4 5 6 7 8 9 10 11 12 13 |
void handler(int sig)
{ } signal(SIGALRM, handler); alarm(5); |
程序大概框架如上所示,如果read在5s内被SIGALRM信号中断而返回,则表示超时,否则未超时已读取到数据,取消闹钟。但这种方法不常用,因为有时可能在其他地方使用了alarm会造成混乱。
二、使用套接字选项SO_SNDTIMEO、SO_RCVTIMEO
1
2 3 4 5 6 |
struct timeval timeout = {3,0};
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
int ret = read(sock, buf, sizeof(buf)); |
即使用setsockopt 函数进行设置,但这种方法可移植性比较差,不是每种系统实现都有这些选项。
三、使用select 实现超时
下面程序包含read_timeout、write_timeout、accept_timeout、connect_timeout 四个函数封装
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
/*************************************************************************
> File Name: sysutil.c > Author: Simba > Mail: dameng34@163.com > Created Time: Sat 02 Mar 2013 10:53:06 PM CST ************************************************************************/ #include "sysutil.h" /* read_timeout - 读超时检测函数,不含读操作 int read_timeout(int fd, unsigned int wait_seconds) fd_set read_fdset; FD_ZERO(&read_fdset); timeout.tv_sec = wait_seconds; do if (ret == 0) } return ret; /* write_timeout - 写超时检测函数,不含写操作 int write_timeout(int fd, unsigned int wait_seconds) fd_set write_fdset; FD_ZERO(&write_fdset); timeout.tv_sec = wait_seconds; do if (ret == 0) } return ret; /* accept_timeout - 带超时的accept int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) if (wait_seconds > 0) fd_set accept_fdset; timeout.tv_sec = wait_seconds; do if (ret == -1) if (addr != NULL) return ret; /* activate_nonblock - 设置IO为非阻塞模式 flags |= O_NONBLOCK; /* deactivate_nonblock - 设置IO为阻塞模式 flags &= ~O_NONBLOCK; /* connect_timeout - 带超时的connect if (wait_seconds > 0) ret = connect(fd, (struct sockaddr *)addr, addrlen); fd_set connect_fdset; timeout.tv_sec = wait_seconds; do if (ret == 0) else if (ret == 1) if (wait_seconds > 0) return ret; |
下面来解析一下这些函数的封装:
1
2 3 4 5 6 7 8 |
int ret;
ret = read_timeout(fd, 5); if (ret == 0) read(fd, buf, sizeof(buf)); else if (ret == -1 && errno == ETIMEOUT) printf("timeout...\n"); else ERR_EXIT("read_timeout"); |
如果 read_timeout(fd, 0); 则表示不检测超时,函数直接返回为0,此时再调用read 将会阻塞。
当wait_seconds 参数大于0,则进入if
括号执行,将超时时间设置为select函数的超时时间结构体,select会阻塞直到检测到事件发生或者超时。如果select返回-1且errno
为EINTR,说明是被信号中断,需要重启select;如果select返回0表示超时;如果select返回1表示检测到可读事件;否则select返回-1
表示出错。
2、write_timeout :此函数跟read_timeout 函数类似,只是select 关心的是可写事件,不再赘述。
3、accept_timeout :此函数是带超时的accept
函数,如果能从if (wait_seconds > 0) 括号执行后向下执行,说明select
返回为1,检测到已连接队列不为空,此时再调用accept 不再阻塞,当然如果wait_seconds == 0 则像正常模式一样,accept
阻塞等待,注意,accept 返回的是已连接套接字。
4、connect_timeout :在调用connect前需要使用fcntl
函数将套接字标志设置为非阻塞,如果网络环境很好,则connect立即返回0,不进入if
大括号执行;如果网络环境拥塞,则connect返回-1且errno ==
EINPROGRESS,表示正在处理。此后调用select与前面3个函数类似,但这里关注的是可写事件,因为一旦连接建立,套接字就可写。还需要注意的是当select
返回1,可能有两种情况,一种是连接成功,一种是套接字产生错误,由这里可知,这两种情况都会产生可写事件,所以需要使用getsockopt来获取一下。退出之前还需重新将套接字设置为阻塞。
我们可以写个小程序测试一下connect_timeout 函数,客户端程序如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include "sysutil.h"
int main(void) struct sockaddr_in servaddr; int ret = connect_timeout(sock, &servaddr, 5); struct sockaddr_in localaddr; printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); return 0; |
因为是在本机上测试,所以不会出现超时的情况,但出错的情况还是可以看到的,比如不要启动服务器端程序,而直接启动客户端程序,输出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echocli_timeout
connect_timeout: Connection refused
很明显是connect_timeout 函数返回了-1,我们也可以推算出connect_timeout 函数中,select返回1,但却是套接字发生错误的情况,errno = ECONNREFUSED,所以打印出Connection refused。
在这里可以粗略说下tcp connect 的机制,connect 只是完成发送 syn 的过程,后续的两次握手由协议栈完成。如果 fd 是 阻塞的,则 connect 会一直
等到超时或者连接成功返回;如果 fd 是非阻塞的,则 connect 会立刻返回,但此时协议栈是否已经完成连接要判断下返回值和 errno;无论 fd 阻塞还
是非阻塞,如果没有设置超时,则当重传 syn 次数达到 sysctl net.ipv4.tcp_syn_retries 时才超时结束,重传 syn 的时间采取指数退避的方式,假设
syn_retries 为5, 则分别为 1, 2, 4, 8, 16, 32 ... 即在目标 ip 不可达时要几十秒才 timeout(如果是ip 可达,但没有对应的监听端口,则在一次重试
后,对端机器会发送reset 标志,连接结束,耗时 1s 多),故:
如果是非阻塞方式,按照stevens 建议,如上面的做法即可;
如果是阻塞方式,可以用 setsockopt 设置 SO_SNDTIMEO 即可。
参考:
《Linux C 编程一站式学习》
《TCP/IP详解 卷一》
《UNP》
套接字I/O超时设置方法和用select实现超时的更多相关文章
- TCP/IP网络编程之套接字类型与协议设置
套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...
- 【TCP/IP网络编程】:02套接字类型与协议设置
本篇文章主要介绍创建套接字函数相关的3个输入参数的含义,它们最终确定了套接字通信所采用的协议.同时,也简单对比了TCP和UDP传输方式的区别. 什么是协议?协议是对话中使用的通信规则,而在计算机领域则 ...
- 使用SO_REVTIMEO套接字选项为recvfrom设置超时
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) { int n; ]; struct timeval ...
- 两个文件中的配置项设置方法和C比较程序处理
在实际的软件开发项目.程序经常需要翻阅了一些资料可能会改变从外部,我们需要读出的信息到一个统一的文件(一般ini档),而此文件被称为个人资料. 考虑这样一个场景,程序须要与多个数据库打交道,要从配置文 ...
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
Linux下测试代码: http://www.linuxhowtos.org/C_C++/socket.htm TCP模型 //TCPClient.c #include<string.h> ...
- 套接字IO超时设置和使用select实现超时管理
在涉及套接字IO超时的设置上有一下3种方法: 1.调用alarm,它在指定的时期满时产生SIGALRM信号.这个方法涉及信号的处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm ...
- Socket编程实践(9) --套接字IO超时设置方法
引:超时设置3种方案 1. alarm超时设置方法 //代码实现: 这种方式较少用 void sigHandlerForSigAlrm(int signo) { return ; } signal(S ...
- UNIX网络编程——设置套接字超时
在涉及套接字的I/O操作上设置超时的方法有以下3种: 调用alarm,它在指定超时期时产生SIGALRM信号.这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm ...
- 服务器编程入门(13) Linux套接字设置超时的三种方法
摘要: 本文介绍在套接字的I/O操作上设置超时的三种方法. 图片可能有点宽,看不到的童鞋可以点击图片查看完整图片.. 1 调用alarm 使用SIGALRM为connect设置超时 设置方法: ...
随机推荐
- OA系统权限管理设计方案
(转)OA系统权限管理设计方案 OA系统权限管理设计方案 不同职责的人员,对于系统操作的权限应该是不同的.优秀的业务系统,这是最基本的功能. 可以对“组”进行权限分配.对于一个大企业的 ...
- Cognos 报表在列表上面显示汇总
一直以来,Cognos Report Studio设计报表的时候,汇总默认显示在列表下方: 1如图,拖一个列表 2运行如下,数据显示正常按日期排序 3选中订单笔数.订单金额,添加自动汇总 4:运行,可 ...
- Cognos审核模块的导入与设置
Cognos审核模块:就是指针对Cognos在运行过程中的对象被执行和访问的日志记录做了一个对象包,在该包里面我们可以从报表里面看到一些日志记录.以方便我们对Cognos的执行记录进行查看,下面我来说 ...
- SpringApplicationConfiguration 这个不能用 解决方案
使用的test包的版本号要与spring的一致,避免jar包依赖冲突 直接用注解 @RunWith(SpringRunner.class)@SpringBootTest @SpringApplicat ...
- mac 下vim 配置文件
" Configuration file for vim set modelines=0 " CVE-2007-2438 " Normally we use vim-ex ...
- vue 通信方式汇总
1.10种通信方式 10种:https://juejin.im/post/5bd18c72e51d455e3f6e4334 2.除此之外,还有children和ref. 需要注意 $children ...
- 项目中用到的ext及js细节
1.js中无replaceAll方法,但能够用replace(regex," "),第一个參数是正則表達式,第二个參数是string,eg:str.replace(/\r\n/g, ...
- HTTP Header具体解释
HTTP(HyperTextTransferProtocol) 即超文本传输协议,眼下网页传输的的通用协议.HTTP协议採用了请求/响应模型,浏览器或其它client发出请求.server给与响应.就 ...
- 关于html中的doctype的重要性的认知以及IE的浏览器模式与文档模式
浏览器模式”用于切换IE针对该网页的默认文档模式.对不同版本浏览器的条件备注解析.发送给网站服务器的用户代理(User-Agent)字符串的值.网站可以根据浏览器返回的不同用户代理字符串判断浏览器的版 ...
- apachebench的简单使用
apachebench的简单使用 2013-03-08 15:48:47 分类: LINUX ApacheBench是 Apache 附带的一个小工具,专门用于 HTTP Server 的benchm ...