服务器编程入门(13) Linux套接字设置超时的三种方法
摘要:
本文介绍在套接字的I/O操作上设置超时的三种方法。

图片可能有点宽,看不到的童鞋可以点击图片查看完整图片。。
1 调用alarm
使用SIGALRM为connect设置超时
设置方法:
- 监听SIGALRM信号,
- 设置sig_alrm处理函数,
- 在阻塞函数前调用alarm函数设置超时时间,
- 正常返回后,重置超时事件为0
void handle_msg(int sockfd) {
char sendbuf[BUFSIZE];
char recvbuf[BUFSIZE];
signal(SIGALRM, sig_alrm); //监听SIGALRM信号
while(1) {
memset( sendbuf, '\0', BUFSIZE );
memset( recvbuf, '\0', BUFSIZE );
printf("%s", "send msg:");
gets(sendbuf);
if (strlen(sendbuf) > 0)
send(sockfd,sendbuf,strlen(sendbuf),0);
if ( !strcmp(sendbuf, "exit"))
break;
alarm(5); //设置超时事件为5s,同时设置服务器回射前sleep 10秒,以让recv函数超时
if (recv(sockfd,recvbuf,BUFSIZE,0) > 0) {
alarm(0);
printf("recv back:%s\n\n", recvbuf);
}
else {
if (errno == EINTR)
fprintf(stderr,
"socket timeout\n");
else
fprintf(stderr,
"receive error\n");
}
}
close( sockfd );
return;
}
static void sig_alrm(int signo) {
fprintf(stderr,
"recv SIGALRM, return.\n");
return;
}
运行截图:

虽然设置了SIGALRM信号处理函数,但是如图所示,本例依然可以等待读取回射信息,因为信号处理函数里只是return。
2 使用select阻塞等待I/O
设置方法:
使用select的内置时间限制,阻塞在select代替recv函数的阻塞。
void handle_msg(int sockfd) {
char sendbuf[BUFSIZE];
char recvbuf[BUFSIZE];
while(1) {
memset( sendbuf, '\0', BUFSIZE );
memset( recvbuf, '\0', BUFSIZE );
printf("%s", "send msg:");
gets(sendbuf);
if (strlen(sendbuf) > 0)
send(sockfd,sendbuf,strlen(sendbuf),0);
if ( !strcmp(sendbuf, "exit"))
break;
if (readable_timeo(sockfd, 5) == 0) {
fprintf(stderr,
"socket timeout\n");
}
else{
recv(sockfd,recvbuf,BUFSIZE,0);
printf("recv back:%s\n\n", recvbuf);
}
}
close( sockfd );
return;
}
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);
}
运行截图:

由运行截图可以看到,超时警告运行正常。
需要注意一点的是,虽然客户端在超时之后继续发送消息,但是服务器回射的消息(hello world)依然被接收,这导致我们再次发送消息时,从缓冲区中读出了延迟收到的hello world。
这里只是演示超时技术,因此对此并不做进一步处理。
3 使用SO_RCVTIMEO套接字选项
使用SO_RCVTIMEO套接字选项为recv设置超时
设置方法:
- 使用setsockopt函数对套接字进行设置
- 一旦设置了某个描述符,其超时设置将应用于该描述符上的所有读操作
- SO_RCVTIMEO仅用于读操作,SO_SNDTIMEO仅用于写操作,两者都不能用于为connect设置超时
- 如果套接字超时,被阻塞的函数将返回一个EWOULDBLOCK错误
void handle_msg(int sockfd) {
char sendbuf[BUFSIZE];
char recvbuf[BUFSIZE + 1];
int n;
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO,
&tv, sizeof(tv) );
while(1) {
memset( sendbuf, '\0', BUFSIZE );
memset( recvbuf, '\0', BUFSIZE );
printf("%s", "send msg:");
gets(sendbuf);
if (strlen(sendbuf) > 0)
send(sockfd,sendbuf,strlen(sendbuf),0);
if ( !strcmp(sendbuf, "exit"))
break;
if ( (n=recv(sockfd,recvbuf,BUFSIZE,0)) < 0 ) {
if (errno == EWOULDBLOCK) {
fprintf(stderr,
"socket timeout\n");
continue;
}
else
fprintf(stderr,
"recv error");
}
else{
printf("recv back:%s\n\n", recvbuf);
}
}
close( sockfd );
return;
}
运行截图:

可以看到,超时警报成功运行,但依然有上一例中的延迟接收的情况发生,不作处理。
示例源码上传到了github上,地址:https://github.com/zs634134578/UNP/tree/tryTimeout
参考资料:
《UNIX网络编程 卷1:套接字联网API(第3版)》
服务器编程入门(13) Linux套接字设置超时的三种方法的更多相关文章
- Unix网络编程 高级IO套接字设置超时
我们知道.对于一个套接字的读写(read/write)操作默认是堵塞的.假设当前套接字还不可读/写,那么这个操作会一直堵塞下去,这样对于一个须要高性能的server来说,是不能接受的.所以,我们能够在 ...
- linux设置变量的三种方法
1在/etc/profile文件中添加变量对所有用户生效(永久的) 用VI在文件/etc/profile文件中增加变量,该变量将会对Linux下所有用户有效,并且是“永久生效”. 例如:编辑/etc/ ...
- linux下修改.bash_profile立即生效的三种方法
1 . .bash_profile 2 source .bash_profile 3 exec bash --login
- 详解linux下批量替换文件内容的三种方法(perl,sed,shell)
在建设本网站的时候,发现新建了很多的网页,突然发现,每个文件都需要进行修改一样的内容,一个一个打开很是麻烦,所以,总结了一下如何快速修改一个目录下多个文件进行内容替换.第三种方法用的不多 方法一 使用 ...
- linux清空文件内容的三种方法
linux系统中清空文件内容的三种方法 1.使用vi/vim命令打开文件后,输入"%d"清空,后保存即可.但当文件内容较大时,处理较慢,命令如下:vim file_name:%d: ...
- linux 套接字编程入门--Hello World
下述代码是linux套接字编程的入门代码.分为服务端和客户端源码. 服务端代码的主要流程是绑定ip地址和端口号建立套接字,等待客户端发起访问.接受客户端请求之后,向客户端发送字符串"hell ...
- Linux 套接字编程中的 5 个隐患(转)
本文转自IBM博文Linux 套接字编程中的 5 个隐患. “在异构环境中开发可靠的网络应用程序”. Socket API 是网络应用程序开发中实际应用的标准 API.尽管该 API 简单,但是开发新 ...
- 第13讲 | 套接字Socket:Talk is cheap, show me the code
第13讲 | 套接字Socket:Talk is cheap, show me the code 基于 TCP 和 UDP 协议的 Socket 编程.在讲 TCP 和 UDP 协议的时候,我们分客户 ...
- [转载] 读《UNIX网络编程 卷1:套接字联网API》
原文: http://cstdlib.com/tech/2014/10/09/read-unix-network-programming-1/ 文章写的很清楚, 适合初学者 最近看了<UNIX网 ...
随机推荐
- python笔记04-----字典、元组、集合操作
1.字典 是一种key-value的数据类型,使用就像字典 无序的因为无下标 创建一个字典: info = { 'stu1':'qq', 'stu2':'ww', 'stu3' ...
- ubuntu 16.04安装好后没声音的解决方法
刚安装好Ubuntu16.04 后没声音,找了好多方法都不行,看到网上说通过安装pavucontrol和alsamixer调节解决,最后无意发现一个方法,总算是可以用了,在此记录一下.可能有的可以解决 ...
- 更改SQLServer实例默认字符集
转自http://www.cnblogs.com/fygh/archive/2012/05/15/2501598.html 需求 安装数据库时,将字符集安装成了“SQL_Latin1_General_ ...
- [转]如何配置Log4Net使用Oracle数据库记录日志
本文转自:http://www.cnblogs.com/PatrickLiu/p/6012153.html 最近在做一个项目的时候,需要增加一个日志的功能,需要使用Log4Net记录日志,把数据插入到 ...
- 一个简单的json解析器
实现一个简单地json解析器. 两部分组成,词法分析.语法分析 词法分析 package com.mahuan.json; import java.util.LinkedList; import ja ...
- BTREE索引和HASH索引的区别
从本质上理解,BTREE是一种有序树,而hash是无序的.所以最关键的区别在于: 1,BTREE可以用来做范围查询,比如大于,小于,而HASH索引仅在"=","IN&qu ...
- this说明
这个This就表示当前实例的对象,用this可访问属性,this.Fist:
- webpack工具、Vue、react模块化
一.为什么要有webpack print('hello,world') fsdl fdsf title2 title3 引用 斜体字 加粗 有序列表1 有序列表2 无序列表1 无序列表2 行内code ...
- 关于JAVA是值传递还是引用传递的问题
1.概念 值传递:方法调用时,实际传入的是它的副本,在方法中对值的修改,不影响调用者的值. 引用传递:方法调用时,实际传入的是参数的实际内存地址,调用者和调用方法所操作的参数都指向同一内存地址,所以方 ...
- linux的目录结构及文件基本操作
1. linux的文件组织目录结构 linux的目录与window的区别 win以存储介质为主,主要以盘符及分区实现文件 管理,再下面才是目录. linux以树形目录结构的形式来构造整个系统,每一个目 ...