在涉及套接字的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网络编程——设置套接字超时的更多相关文章

  1. UNIX网络编程——原始套接字(dos攻击)

    原始套接字(SOCK_RAW).应用原始套接字,我们可以编写出由TCP和UDP套接字不能够实现的功能. 注意原始套接字只能够由有 root权限的人创建. 可以参考前面的博客<<UNIX网络 ...

  2. UNIX网络编程——原始套接字的魔力【续】

    如何从链路层直接发送数据帧 上一篇里面提到的是从链路层"收发"数据,该篇是从链路层发送数据帧. 上一节我们主要研究了如何从链路层直接接收数据帧,可以通过bind函数来将原始套接字绑 ...

  3. UNIX网络编程——原始套接字的魔力【上】

    基于原始套接字编程 在开发面向连接的TCP和面向无连接的UDP程序时,我们所关心的核心问题在于数据收发层面,数据的传输特性由TCP或UDP来保证: 也就是说,对于TCP或UDP的程序开发,焦点在Dat ...

  4. UNIX网络编程——原始套接字SOCK_RAW

    实际上,我们常用的网络编程都是在应用层的报文的收发操作,也就是大多数程序员接触到的流式套接字(SOCK_STREAM)和数据包式套接字(SOCK_DGRAM).而这些数据包都是由系统提供的协议栈实现, ...

  5. UNIX网络编程——通用套接字选项

    1. SO_BROADCAST 套接字选项 本选项开启或禁止进程发送广播消息的能力.只有数据报套接字支持广播,并且还必须是在支持广播消息的网络上(例如以太网,令牌环网等).我们不可能在点对点链路上进行 ...

  6. unix网络编程——TCP套接字编程

    TCP客户端和服务端所需的基本套接字.服务器先启动,之后的某个时刻客户端启动并试图连接到服务器.之后客户端向服务器发送请求,服务器处理请求,并给客户端一个响应.该过程一直持续下去,直到客户端关闭,给服 ...

  7. UNIX网络编程——原始套接字的魔力【下】

    可以接收链路层MAC帧的原始套接字 前面我们介绍过了通过原始套接字socket(AF_INET, SOCK_RAW, protocol)我们可以直接实现自行构造整个IP报文,然后对其收发.提醒一点,在 ...

  8. <unix网络编程>UDP套接字编程

    典型的UDP客户/服务器程序的函数调用如下: 1.缓冲区 发送缓冲区用虚线表示,任何UDP套接字都有发送缓冲区,不过该缓冲区仅能表示写到该套接字的UDP数据报的上限.如果应用进程写一个大于套接字缓冲区 ...

  9. TCP/IP网络编程之套接字类型与协议设置

    套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...

随机推荐

  1. bzoj1926[Sdoi2010]粟粟的书架 二分 主席树

    1926: [Sdoi2010]粟粟的书架 Time Limit: 30 Sec  Memory Limit: 552 MBSubmit: 1064  Solved: 421[Submit][Stat ...

  2. HDU 5506(GT and set)

    题意: 表示看了很久,然而发现还是没看懂题. 正解:给你a个集合,让你把他们合并成k个,当两个集合有公共数字时可以合并. (一直以为是合并后,每个集合至少有两个数字相同- -,这英语也是醉了) 思路: ...

  3. hdu5586 BestCoder Round #64 (div.2)

    问题描述 给n个数{A}_{1},{A}_{2}....{A}_{n}A​1​​,A​2​​....A​n​​,你可以选择一个区间(也可以不选),区间里每个数x变成f(x),其中f(x)=(1890x ...

  4. 浅谈MySQL中优化sql语句查询常用的30种方法

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索 ...

  5. C语言程序设计第一次作业 冯亚杰

    (一)实验总结 求圆面积和周长 (1)题目 输入圆的半径,计算圆的周长和面积. (2)流程图 (3)测试数据及运行结果 测试数据1:r=7 运行结果: 测试数据2:r=5 运行结果: (4)实验分析 ...

  6. SpringBoot中跨域问题

    项目中经常会遇到浏览器跨域的问题,解决方式在启动类中配置 @Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigur ...

  7. Python学习--课本程序练习(周更)

    1.绘制正方形螺旋线 import turtle turtle.setup(600,300,200,200) turtle.pensize(1) turtle.color('green') i=0 w ...

  8. Mysql数据库连接报错!1130:host XXX is not allowed to connect to this mysql server

    我猜想是可能是连接的用户权限问题.结果这样子操作mysql库,可以解决此问题.在本机登入mysql后,更改 “mysql” 数据库里的 “user” 表里的 “host” 项,从”localhost” ...

  9. Python中enumerate用法详解

    enumerate()是python的内置函数.适用于python2.x和python3.xenumerate在字典上是枚举.列举的意思enumerate参数为可遍历/可迭代的对象(如列表.字符串)e ...

  10. 在ubuntu上安装最新稳定版本的node及npm

    背景 通过ubuntu官方apt安装工具安装的node是最新LTS版本的,而本人是个有点强迫症的人,喜欢追求新的东西,也就是想方设法想要去安装最新版本的node,所以本文也就产生了,附上ubuntu安 ...