摘要:

    本文介绍在套接字的I/O操作上设置超时的三种方法。


图片可能有点宽,看不到的童鞋可以点击图片查看完整图片。。


1 调用alarm

使用SIGALRM为connect设置超时

设置方法:

  1. 监听SIGALRM信号,
  2. 设置sig_alrm处理函数,
  3. 在阻塞函数前调用alarm函数设置超时时间,
  4. 正常返回后,重置超时事件为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套接字设置超时的三种方法的更多相关文章

  1. Unix网络编程 高级IO套接字设置超时

    我们知道.对于一个套接字的读写(read/write)操作默认是堵塞的.假设当前套接字还不可读/写,那么这个操作会一直堵塞下去,这样对于一个须要高性能的server来说,是不能接受的.所以,我们能够在 ...

  2. linux设置变量的三种方法

    1在/etc/profile文件中添加变量对所有用户生效(永久的) 用VI在文件/etc/profile文件中增加变量,该变量将会对Linux下所有用户有效,并且是“永久生效”. 例如:编辑/etc/ ...

  3. linux下修改.bash_profile立即生效的三种方法

    1 . .bash_profile 2 source .bash_profile 3 exec bash --login

  4. 详解linux下批量替换文件内容的三种方法(perl,sed,shell)

    在建设本网站的时候,发现新建了很多的网页,突然发现,每个文件都需要进行修改一样的内容,一个一个打开很是麻烦,所以,总结了一下如何快速修改一个目录下多个文件进行内容替换.第三种方法用的不多 方法一 使用 ...

  5. linux清空文件内容的三种方法

    linux系统中清空文件内容的三种方法 1.使用vi/vim命令打开文件后,输入"%d"清空,后保存即可.但当文件内容较大时,处理较慢,命令如下:vim file_name:%d: ...

  6. linux 套接字编程入门--Hello World

    下述代码是linux套接字编程的入门代码.分为服务端和客户端源码. 服务端代码的主要流程是绑定ip地址和端口号建立套接字,等待客户端发起访问.接受客户端请求之后,向客户端发送字符串"hell ...

  7. Linux 套接字编程中的 5 个隐患(转)

    本文转自IBM博文Linux 套接字编程中的 5 个隐患. “在异构环境中开发可靠的网络应用程序”. Socket API 是网络应用程序开发中实际应用的标准 API.尽管该 API 简单,但是开发新 ...

  8. 第13讲 | 套接字Socket:Talk is cheap, show me the code

    第13讲 | 套接字Socket:Talk is cheap, show me the code 基于 TCP 和 UDP 协议的 Socket 编程.在讲 TCP 和 UDP 协议的时候,我们分客户 ...

  9. [转载] 读《UNIX网络编程 卷1:套接字联网API》

    原文: http://cstdlib.com/tech/2014/10/09/read-unix-network-programming-1/ 文章写的很清楚, 适合初学者 最近看了<UNIX网 ...

随机推荐

  1. mongodb启动失败:child process failed, exited with error number 100

    参考 http://www.dataguru.cn/thread-107361-1-1.html 里面的路径 根据自己的--dbpath的路径  和l--logpath路径去找

  2. 【数组】word search

    题目: Given a 2D board and a word, find if the word exists in the grid. The word can be constructed fr ...

  3. JavaScript数据结构-10.字典

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. 前端思想实现:面向UI编程_____前端框架设计开发

    引子,我去小说看多了,写博客竟然写引子了!!!不过,没引子不知道怎么写了.言归正传吧,前端这个职业,也就这几年刚刚火起来的职业,以前那个混乱的年代,前端要么是UI设计师代劳解决问题,要么就是后端程序员 ...

  5. 微服务Kong(六)——配置参考

    1. 配置加载 如果您通过其中一个官方软件包安装了Kong,Kong会附带默认配置文件,该文件可以在/etc/kong/kong.conf.default中找到.要开始配置Kong,您可以复制此文件: ...

  6. python跳出多重循环

    # -*- coding=utf-8 -*- """ 如何结束多重循环,在单层循环中,可以用break跳出循环,那两层,三层呢? """ # ...

  7. WPF EventAggregator(基于EventAggregator的事件发布及订阅)

    一.EventAggregator简介 EventAggregator是Prism中专门处理ViewModel与ViewModel之间事件传递的类对象,它提供了针对事件的发布方法和订阅方法,所以可以非 ...

  8. Redis之数据类型Sting字符串

    Redis String(字符串) string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value. string类型是二进制安全的.意思是redi ...

  9. JAVA标签

    java没有GOTO,可以通过标签实现跳转. 在 Java 里唯一需要用到标签的地方就是拥有嵌套循环,而且想中断或继续多个嵌套级别的时候. 使用位置:用在循环语句之前.----它实际需要紧靠在循环语句 ...

  10. C# 时间操作类

    using System; namespace DotNet.Utilities { /// <summary> /// 时间类 /// 1.SecondToMinute(int Seco ...