1.网络接收超时
一般情况下网络接收数据都采用阻塞属性,就等于一直等待对方数据到达。有的场合中我们可能不需要一直等待,因为可能会没有结果,这是可以使用超时接收,在规定的时间内如果没有数据到达。,则超时退出。
主要方法有三种:
- 使用多路复用的slect 函数设置超时时间
- 设置闹钟,当设置时间到达时将会产生一个SIGALRM信号,表示超时
- 设置套接字的属性为超时属性
2.多路复用
例题: 写一个服务器,使用select函数监听客户端的状态,如果客户端在3秒内没有发送数据,则超时接收。
思路:
使用select函数设置监听客户端状态
如果客户端在规定时间内没有数据到达则超时退出
关键代码 :
// 配置超时时间
struct timeval time_val ;
time_val.tv_sec = 5 ;
time_val.tv_usec = 0 ;
// 设置多路复用集合
fd_set set ;
FD_ZERO(&set); // 清空 集合
FD_SET(connect_fd , &set); // 添加 套记字到集合中
FD_SET(STDIN_FILENO , &set); // 添加标准输入到集合中
// 找到描述符最大值
max_fd = connect_fd > STDIN_FILENO ? connect_fd : STDIN_FILENO ;
// 等待描述符状态变化并设置超时 5秒
int ret_val = select(max_fd+1, &set , NULL ,NULL, &time_val);
if( ret_val == 0 )
{
printf("接收数据超时!!\n");
return -1 ;
}
3.设置闹钟
闹钟这种方式类似于信号驱动,信号驱动当收到信号时,证明有数据发送过来。闹钟使用alarm函数来提前设定一个时间,当时间到达时,回自动产生一个信号 14) SIGALRM。
思路:
- 使用signal函数设置好捕获闹钟信号,并设置好响应处理函数。
- 使用alarm()函数设置一个闹钟时间
- 当闹钟时间结束时产生一个 SIGALRM 信号
- 当捕获到闹钟信号则运行对应的响应函数
- 重新设置闹钟
API 接口
头文件:
#include <unistd.h>
函数原型:
unsigned int alarm(unsigned int seconds);
参数分析:
seconds --> 闹钟时间 (秒)
返回值:
成功返回设置的剩余时间
失败 0
4.设置套接字超时属性
在Linux下,建立套接字都是阻塞属性.我们设置一个超时属性给套接字,这样读取套接字数据时,在规定的时间会阻塞,但是在规定的时间之外,读取失败。
思路:
- 获得一个连接套接字
- 使用函数设置超时属性setsockopt()
- 正常玩耍,当接收函数返回-1时就是超时的时候了
接口API
获得属性/设置属性
头文件:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
函数原型:
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
参数分析:
sockfd --> 需要设置的已连接套接字
level --> 优先级
SOL_SOCKET:套接字
IPPROTO_IP:IP优先级
IPPRO_TCP:TCP优先级
optname --> 属性名
optval --> 属性的值 使能为1,失能为0
optlen --> 属性的值类型的长度
返回值:
成功 0
失败 -1
时间结构体:
struct timeval {
long tv_sec; /* seconds */ 秒
long tv_usec; /* microseconds */ 微秒
};
补充optname:
===========================SOL_SOCKET====================================:
optname选项名字 optlen的大小
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSEADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
=========================IPPROTO_IP=======================================
IP_HDRINCL 在数据包中包含IP首部 int
IP_OPTINOS IP首部选项 int
IP_TOS 服务类型
IP_TTL 生存时间 int
IP_ADD_MEMBERSHIP 加入组播 struct ip_mreq
=========================IPPRO_TCP======================================
TCP_MAXSEG TCP最大数据段的大小 int
TCP_NODELAY 不使用Nagle算法 int
核心代码:
struct timeval v;
v.tv_sec = 5;
v.tv_usec = 0;
setsockopt(connfd,SOL_SOCKET,SO_RCVTIMEO,&v,sizeof(v));
n = read(connfd,buf,sizeof(buf));
if(n == -1) ---> 超时,5秒内没有数据
{
printf("timeout!\n");
}
if(n > 0) --> 5秒内有数据
{
printf("from client:%s",buf);
if(strncmp(buf,"quit",4) == 0)
{
break;
}
}