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网络编程之套接字类型与协议设置
套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...
随机推荐
- 【Git】CentOS7 通过源码安装Git
yum源仓库里的Git版本更新不及时,最新版的Git是1.8.3,但是官方的最新版早已经更新到2.9.5.想要安装最新版本Git,只能下载源码进行安装 建议最好更新git为较新版本,便于使用 1.查看 ...
- 【OCP|052】OCP最新题库解析(052)--小麦苗解答版
[OCP|052]OCP最新题库解析(052)--小麦苗解答版 OCP最新题库解析历史连接(052):http://mp.weixin.qq.com/s/bUgn4-uciSndji_pUbLZfA ...
- Maven之自定义archetype生成项目骨架
Maven之自定义archetype生成项目骨架(一) http://blog.csdn.net/sxdtzhaoxinguo/article/details/46895013
- 值得珍藏的HTTP协议详解
转自:http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html 引言 HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式 ...
- 消息中间件--ActiveMQ&JMS消息服务
### 消息中间件 ### ---------- **消息中间件** 1. 消息中间件的概述 2. 消息中间件的应用场景 * 异步处理 * 应用解耦 * 流量削峰 * 消息通信 --------- ...
- Uncaught (in promise) TypeError:的错误
1.错误 创建一个vue实例,在data定义一些变量,如activityTime. 在methods里面用了axios发送请求. 在then的回调里使用this.activityTime 报错! 2. ...
- Python盗号原理-代码实现截屏键盘记录远程发送(不要做坏事)
这年头盗号的从出不穷,不是脱裤就是社工钓鱼.今天呢我们就以前面的几篇技术文章做铺垫,来讲一下python盗号的原理(学习用途,请勿用于违法犯罪),知己知彼方能百战不殆嘛. 脱裤我们就不多提了,无非就是 ...
- java.lang.ClassCastException: oracle.sql.CLOB cannot be cast to oracle.sql.CLOB
错误现象: [framework] 2016-05-26 11:34:53,590 -INFO [http-bio-8080-exec-7] -1231863 -com.dhcc.base.db.D ...
- Angular4.0入门
angular与其他的差别 angular cli安装 cnpm install -g @angular/cli 最新版本 cnpm uninstall -g @angular/cli 卸载全局版本 ...
- 疯狂的Django 之深度外键跨表查找之疯狂INNER JOIN
定义Model: from django.db import models class Moreinfo(models.Model): weight = models.FloatField() hei ...