UNIX网络编程——设置套接字超时
在涉及套接字的I/O操作上设置超时的方法有以下3种:
- 调用alarm,它在指定超时期时产生SIGALRM信号。这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm调用。
- 在select中阻塞等待I/O(select有内置的时间限制),以此代替直接阻塞在read或write调用上。
- 使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。这个方法的问题在于,并非所有实现都支持这两个套接字选项。
1.使用SIGALRM为connect设置超时
如下给出我们的connect_timeo函数,它以由调用者指定的超时上限调用connect。它的前3个参数用于调用connect,第四个参数是等待的秒数。
#include "unp.h" static void connect_alarm(int); int
connect_timeo(int sockfd, const SA *saptr, socklen_t salen, int nsec)
{
sigfunc *sigfunc;
int n; sigfunc = signal(SIGALRM, connect_alarm);
if (alarm(nsec) != 0)
err_msg("connect_timeo: alarm was already set"); if ( (n = connect(sockfd, saptr, salen)) < 0) {
close(sockfd);
if (errno == EINTR)
errno = ETIMEDOUT;
}
alarm(0); /* turn off the alarm */
signal(SIGALRM, sigfunc); /* restore previous signal handler */ return(n);
} static void
connect_alarm(int signo)
{
return; /* just interrupt the connect() */
}
12-13 把本进程的报警时钟设置成由调用者指定的秒数。如果此前已经给本进程时钟过报警时钟,那么alarm的返回值是现在设置的新值,否则alarm的返回值为0.
15-19 调用connect,如果本调用被中断(即返回EINTR错误),那就把errno值改设为ETIMEOUT,同时关闭套接字,以防三路握手继续进行。
20-21 通过以0为参数值调用alarm关闭本进程的报警时钟,同时恢复原来的信号处理函数(如果有的话)。
就本例子我们指出两点:
- 使用本技术总能减少connect的超时期限,但是无法延长内核现有的超时。源自Berkeley的内核中connect的超时通常为75s。在调用我们的函数时,可以指定一个比75小的值,但是如果指定一个比75大的值,那么connect仍将在75s后发生超时。
- 我们使用了系统调用(connect)的可中断能力,使得他们能够在内核超时发生之前返回。这一点不成问题的前提是:我们执行的是系统调用,并且能够直接处理由他们返回的EINTR错误。
2.使用SIGALRM为recvfrom设置超时
dg_cli函数通过调用alarm使得一旦在5S内收不到任何应答就中断recvfrom。
#include "unp.h" static void sig_alrm(int); void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + 1]; signal(SIGALRM, sig_alrm); while (fgets(sendline, MAXLINE, fp) != NULL) { sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); alarm(5);
if ( (n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {
if (errno == EINTR)
fprintf(stderr, "socket timeout\n");
else
err_sys("recvfrom error");
} else {
alarm(0);
recvline[n] = 0; /* null terminate */
fputs(recvline, stdout);
}
}
} static void
sig_alrm(int signo)
{
return; /* just interrupt the recvfrom() */
}
11-28 为SIGALRM建立一个信号处理函数,并在每次调用recvfrom前通过调用alarm设置一个5S的超时。如果recvfrom被我们的信号处理函数中断了,那
就输出一个信息并继续执行。如果读到一行来自服务器的文本,那就关掉报警时钟并输出服务器的应答。
31-35 信号处理函数只是简单的返回,以中断被阻塞的recvfrom。
3.使用select为recvfrom设置超时
readable_timeo的函数等待一个描述符最多在指定的秒数内变为可读。
#include "unp.h" int
readable_timeo(int fd, int sec)
{
fd_set rset;
struct timeval tv; FD_ZERO(&rset);
FD_SET(fd, &rset); tv.tv_sec = sec;
tv.tv_usec = 0; return(select(fd+1, &rset, NULL, NULL, &tv));
/* 4> 0 if descriptor is readable */
}
9-13 在读描述符集中打开与调用者给定描述符对应的位。把调用者给定的等待秒数设置在一个timeval结构中。
15 select等待该描述符变为可读或者发生超时。本函数的返回值就是select的返回值:出错时为-1,超时发生时为0,否则返回的正值给出已就绪描述符的数目。
#include "unp.h" void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + 1]; while (fgets(sendline, MAXLINE, fp) != NULL) { sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); if (readable_timeo(sockfd, 5) == 0) {
fprintf(stderr, "socket timeout\n");
} else {
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
recvline[n] = 0; /* null terminate */
fputs(recvline, stdout);
}
}
}
4.使用SO_RCVTIMEO套接字选项为recvfrom设置超时
SO_RCVTIMEO套接字选项一旦设置到某个描述符(包括指定超时值),其超时设置将应用于该描述符上的所有读操作。本方法的优势就体现在一次性设置选项上,而前两个方法总是要求我们在欲设置时间限制的每个操作发生之前做些工作。本套接字选项仅仅应用于读操作,类似的SO_SNDTIMEO选项则仅仅应用于写操作,两者都不能用于为connect设置超时。
#include "unp.h" void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + 1];
struct timeval tv; tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); while (fgets(sendline, MAXLINE, fp) != NULL) { sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
if (n < 0) {
if (errno == EWOULDBLOCK) {
fprintf(stderr, "socket timeout\n");
continue;
} else
err_sys("recvfrom error");
} recvline[n] = 0; /* null terminate */
fputs(recvline, stdout);
}
}
9-11 setsockopt的第四个参数是指向某个timeval结构的一个指针,其中填入了期望的超时值。
18-21 如果I/O操作超时,其函数(这里是recvfrom)将返回一个EWOULDBLOCK错误。
UNIX网络编程——设置套接字超时的更多相关文章
- UNIX网络编程——原始套接字(dos攻击)
原始套接字(SOCK_RAW).应用原始套接字,我们可以编写出由TCP和UDP套接字不能够实现的功能. 注意原始套接字只能够由有 root权限的人创建. 可以参考前面的博客<<UNIX网络 ...
- UNIX网络编程——原始套接字的魔力【续】
如何从链路层直接发送数据帧 上一篇里面提到的是从链路层"收发"数据,该篇是从链路层发送数据帧. 上一节我们主要研究了如何从链路层直接接收数据帧,可以通过bind函数来将原始套接字绑 ...
- UNIX网络编程——原始套接字的魔力【上】
基于原始套接字编程 在开发面向连接的TCP和面向无连接的UDP程序时,我们所关心的核心问题在于数据收发层面,数据的传输特性由TCP或UDP来保证: 也就是说,对于TCP或UDP的程序开发,焦点在Dat ...
- UNIX网络编程——原始套接字SOCK_RAW
实际上,我们常用的网络编程都是在应用层的报文的收发操作,也就是大多数程序员接触到的流式套接字(SOCK_STREAM)和数据包式套接字(SOCK_DGRAM).而这些数据包都是由系统提供的协议栈实现, ...
- UNIX网络编程——通用套接字选项
1. SO_BROADCAST 套接字选项 本选项开启或禁止进程发送广播消息的能力.只有数据报套接字支持广播,并且还必须是在支持广播消息的网络上(例如以太网,令牌环网等).我们不可能在点对点链路上进行 ...
- unix网络编程——TCP套接字编程
TCP客户端和服务端所需的基本套接字.服务器先启动,之后的某个时刻客户端启动并试图连接到服务器.之后客户端向服务器发送请求,服务器处理请求,并给客户端一个响应.该过程一直持续下去,直到客户端关闭,给服 ...
- UNIX网络编程——原始套接字的魔力【下】
可以接收链路层MAC帧的原始套接字 前面我们介绍过了通过原始套接字socket(AF_INET, SOCK_RAW, protocol)我们可以直接实现自行构造整个IP报文,然后对其收发.提醒一点,在 ...
- <unix网络编程>UDP套接字编程
典型的UDP客户/服务器程序的函数调用如下: 1.缓冲区 发送缓冲区用虚线表示,任何UDP套接字都有发送缓冲区,不过该缓冲区仅能表示写到该套接字的UDP数据报的上限.如果应用进程写一个大于套接字缓冲区 ...
- TCP/IP网络编程之套接字类型与协议设置
套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...
随机推荐
- Python【第五课】迭代器,生成器,数据序列化
本节内容 列表生成式,生成器,迭代器 Json & pickle 数据序列化 1.列表生成式,生成器,迭代器 1.1 列表生成式 列表生成式?不就是生成个列表的表达式,恩~~~ 差不多. 一般 ...
- mysql 使用问题?
linux中安装了mysql客户端和服务器端,为什么无法使用,总是报错呢 解决办法:使用dpkg -r mysql命令删除掉mysql-client和mysql-server了,还是不行,而且查看软件 ...
- (转)SQL中的循环、for循环、游标
我们使用SQL语句处理数据时,可能会碰到一些需要循环遍历某个表并对其进行相应的操作(添加.修改.删除),这时我们就需要用到咱们在编程中常常用的for或foreach,但是在SQL中写循环往往显得那么吃 ...
- Cisco 的基本配置实例之四----vlan的规划及配置(核心交换机)
4.vlan的规划及配置 在本节中我们讲解vlan的规划及具体的配置命令.在此例中我们用的是vtp(VLAN Trunking Protocol)server的模式,在这种模式中我们需要配置核心交换机 ...
- kibana使用
最近,被老大叫写一个kibana的使用方法给所有人用. 注意的一点: 能不用空格表示OR或者AND就不用空格表示,因为要么全用要么全部不用,否则会因为解析搜索同级的时候,若出现空格和OR,会冲突覆盖意 ...
- 一口一口吃掉Hibernate(四)——多对一单向关联映射
hibernate对于数据库的操作,全部利用面向对象的思维来理解和实现的.一般的单独表的映射,相信大家都没有问题,但是对于一些表之间的特殊关系,Hibernate提供了一些独特的方式去简化它. 今天就 ...
- JavaScript正则表达式模式匹配(6)——常用的正则表达式
1.检查邮政编码 var pattern=/[1-9][0-9]{5}/; // 必须是6位,必须是数字,第一位不能为0 var str='274200'; alert(pattern.test(st ...
- easyui datagrid属性和方法
本文可以当做api来使用 使用方法(Usage Example) 从现有的表单元素创建数据表格,定义在html中的行,列和数据. <table class="easyui-datagr ...
- Noip2017 普及 T3 Chess
神奇的场上原码 #include<iostream> #include<cstdio> #include<cstring> #include<queue> ...
- vs2017 +CUDA 9.0配置
环境: 1.Win7 64位 旗舰版 2.VS2017 3.CUDA 9.0 安装过程比较简单,直接运行在官网下载的CUDA安装包就可以了. 建议先安装VS,再安装CUDA.这样安装完之后会在VS里直 ...