在涉及套接字的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网络编程之套接字类型与协议设置
套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...
随机推荐
-
shell十三问
1) 为何叫做 shell ?在介绍 shell 是甚幺东西之前,不妨让我们重新检视使用者与计算机系统的关系:图(FIXME)我们知道计算机的运作不能离开硬件,但使用者却无法直接对硬件作驱动,硬件的驱 ...
-
解决安卓SDK更新连不通问题
http://wenku.baidu.com/link?url=d7t81OFF4_o2YF9iBne-azyovROGPGOozMgWKNyAIQK8vtI0mIjvzpfdOXg7KOobu202 ...
-
Cocos2d-JS场景树
场景树概念(Scene Graph) 场景树是Cocos2d-JS中用来管理场景中所有元素的一个数据结构,场景树之所以被称为一棵树是因为它将一个场景的所有子结点以树状图的形式组织在一起. Cocos2 ...
-
requestAnimationFrame兼容性扩展
/** * requestAnimationFrame兼容性扩展,两方面工作: * 1.把各浏览器前缀进行统一 * 2.在浏览器没有requestAnimationFrame方法时将其指向setTim ...
-
行列有序矩阵求第k大元素
问题来源:http://www.careercup.com/question?id=6335704 问题描述: Given a N*N Matrix. All rows are sorted, and ...
-
install cuda5 on ubuntu12.04
1. sudo apt-get install libglapi-mesa 2. sudo apt-get install freeglut3-dev build-essential libx11-d ...
-
JS中有关数组Array的常用方法函数
Array对象的方法主要有如下几种(我所知道的): concat()连接两个或多个数组,并返回结果,但是值得注意的是该方法并不改变数组本身,而仅仅返回一个数组连接的副本. push()在数组后面添加一 ...
-
python 中range numpy.arange 和 numpy.linspace 的区别
1.返回值不同 range返回一个range对象,numpy.arange和numpy.linspace返回一个数组. 2.np.arange的步长可以为小数,但range的步长只能是整数. 与Pyt ...
-
金融量化分析【day110】:IPython介绍及简单操作
一. IPython介绍 ipython是一个python的交互式shell,比默认的python shell好用得多,支持变量自动补全,自动缩进,支持bash shell命令,内置了许多很有用的功能 ...
-
为何SQL SERVER使用sa账号登录还原数据库BAK文件失败,但是使用windows登录就可以
今天发现一个问题,就是公司开发服务器上的sql server使用sa账号登录后,还原一个数据库bak文件老是报错,错误如下: TITLE: Microsoft SQL Server Managemen ...