服务器编程入门(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-select异步IO
#实现多任务在同一个线程切换 #!/usr/bin/python from socket import * from select import * from time import ctime so ...
- python-无名管道进程通信
#!/usr/bin/python #coding=utf-8 import sys,os from time import sleep (r,w)=os.pipe() #创建无名管道,返回两个整数, ...
- CNN-利用1*1进行降维和升维
降维: 比如某次卷积之后的结果是W*H*6的特征,现在需要用1*1的卷积核将其降维成W*H*5,即6个通道变成5个通道: 通过一次卷积操作,W*H*6将变为W*H*1,这样的话,使用5个1*1的卷积核 ...
- Perl入门
Perl 是一门开源的脚本语言,由 Larry Wall 所创造,该语言以实用,快速开发为主要目标,与当前流行的面向对象结构化编程有些格格不入,但这并不妨碍 Perl 被广泛流传和使用,世界范围内围绕 ...
- python实例:在列表,字典,集合中,根据条件筛选数据
1. 从列表中过滤掉 负数 from random import randint # 随机生成列表 data = [randint(-10, 10) for _ in range(10)] print ...
- leetcode简单题目两道(5)
Problem Given an integer (signed bits), write a function to check whether it . Example: Given num = ...
- jquery选择器【总结】
本文总结整理了jquery里和选择器相关的所有方法,通过这篇文章,可以让你学习到在jquery里使用选择器的所有方法. 一:基本选择器: $("#aijquery") 选择id值等 ...
- xcode开启后,每次调试运行要输入密码
1.contorl+空格 打开终端 2.输入DevToolsSecurity --status查看状态,如果是Developer mode is currently disabled.那就对了 3.输 ...
- css的四种书写方式
优先级: 外部样式 < 内部样式表 < 内联样式表: 优先级,即:同名的选择器右边的会覆盖左边 1.内部样式表 <head> <style> /*内部样式表,一般用 ...
- Cheatsheet: 2018 01.01 ~ 02.28
JAVA How to Improve the Performance of a Java Application Java Memory Management Writing Java Micros ...