应用场景
- 在网络通信中,很多操作会使得进程阻塞:
- TCP套接字中的recv/accept
- UDP套接字中的recvfrom
- 超时检测的必要性
- 避免进程在没有数据时无限制地阻塞
- 实现某些特定协议要求,比如某些设备规定,发送请求数据后,如果多长时间后没有收到来自设备的回复,需要做出一些特殊处理
- 自带超时参数的函数
如使用select/poll/epoll函数最后一个参数可以设置超时。
1) select设置超时
struct` `timeval tm =` `{2,` `0};//设置2s打算阻塞`
`sret =` `select(maxfd +` `1,` `&tempfds,` `NULL,` `NULL,` `&tm);`
`第五个参数:`
`struct` `timeval` `{`
`long tv_sec;` `/*秒*/`
`long tv_usec;` `/*微秒*/`
`};`
`
2. poll
int poll(struct pollfd *fds, nfds_t nfds, int timeout);`
` 第三个参数:时间单位是毫秒 -1阻塞, 2000=2s`
` ret = poll(event, num, 2000);//超时检测时间为2s`
`
3. epoll 设置的是epoll_wait
`
` int epoll_wait(int epfd, struct epoll_event *events,`
` int maxevents, int timeout);`
` 第四个参数:时间单位是毫秒 -1阻塞, 2000=2s`
` ret = epoll_wait(epfd, events, 20, 2000);`
`设置超时后的返回值都为:<0 error`
` =0 超时`
` >0 正确 `
`
2.利用setsockopt属性设置
Linux中socket属性
|-------------------|-------------|----------------|
| 选项名称 | 说明 | 数据类型 |
| ==== SOL_SOCKET 应用层 ==== |||
| 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 | 服务类型 | int |
| IP_TTL | 生存时间 | int |
| IP_ADD_MEMBERSHIP | 将指定的IP加入多播组 | struct ip_mreq |
| ==== IPPRO_TCP 传输层 ==== |||
| TCP_MAXSEG | TCP最大数据段的大小 | int |
| TCP_NODELAY | 不使用Nagle算法 | int |
功能:设置/获取网络属性;`
`原型:`
`#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);`
`参数:`
`int sockfd:指定要设置/获取哪个套接字的属性;`
`int level:指定要控制的协议层次;`
` SOL_SOCKET:应用层 通用套接字选项; man 7 socket`
` IPPROTO_TCP:TCP选项 man 7 TCP`
` IPPROTO_UDP:UDP选项 man 7 UDP`
` IPPROTO_IP:IP选项; man 7 IP`
`int optname:指定要控制的内容,指定控制方式;`
`--- SOL_SOCKET: man 7 socket -----`
` SO_REUSEADDR:允许端口快速重用 optval:` `int*`
` SO_BROADCAST:允许广播 optval:` `int*`
` SO_RCVBUF/SO_SNDBUF:接收缓冲区 发送缓冲区大小`
` SO_RCVTIMEO/SO_SNDTIMEO:接收超时时间,发送超时时间`
`void` `*optval:根据optname不同,该类型不同;(数据类型)`
`socklen_t optlen/socklen_t` `*optlen:真实的optval指针指向的内存空间的大小;`
`返回值:`
` 成功,返回0;`
` 失败,返回-1,更新errno;`
`
3、利用alarm定时器设置
#include <unistd.h>`
`unsigned int alarm(unsigned int seconds);`
`功能:在进程中设置一个定时器`
`参数:seconds:定时时间,单位为秒`
`返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则`
` 返回上一个闹钟时间的剩余时间,否则返回0。`
`alarm(5) 闹钟 定时器`
`//5秒之后会,会有一个信号产生(SIGALRM)`
`int` `sigaction(int signum,` `const` `struct` `sigaction` `*act,`
`struct` `sigaction` `*oldact);`
` 功能:对接收到的指定信号处理`
` signum 信号 `
`struct` `sigaction`
`{`
`void` `(*sa_handler)(int);` `//信号处理函数`
`void` `(*sa_sigaction)(int,` `siginfo_t` `*,` `void` `*);` `//信号处理函数`
`sigset_t sa_mask;`
`int sa_flags;` `//信号属性; SA_RESTART自重启属性`
`#define SA_RESTART 0x10000000`
`void` `(*sa_restorer)(void);`
`};`
`//设置信号属性`
`struct` `sigaction act;`
`sigaction(SIGALRM,NULL,&act);//获取原属性`
` act.sa_handler=handler;//修改属性`
`sigaction(SIGALRM,&act,NULL);//将修改的属性设置回去`
`注:在recv前调用alarm函数`
` alarm的 SIGALRM信号产生后会打断(终端)下面的系统调用recv;`
` 打断后相当于recv错误返回。`
`信号改变行为后,当前进程所有行为都被改变,若想要再次改变回原行为,需要再次执行sigaction.`
`
注意:一个进程只能有一个闹钟时间。如果在调用alarm时已设置过闹钟时间,则之前的闹钟时间被新值所代替,时间到了会给进程发送一个SIGALRM信号,这个信号也有结束进程的功能
#include<stdio.h>`
`#include<signal.h>`
`#include<unistd.h>`
`void` `handler(int sig)`
`{`
`printf("timeout................\n");`
`}`
`int` `main(int argc,` `char` `const` `*argv[])`
`{`
`struct` `sigaction act;`
`sigaction(SIGALRM,NULL,&act);//获取原属性`
` act.sa_handler=handler;//修改属性`
`sigaction(SIGALRM,&act,NULL);//将修改的属性设置回去`
`// sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);`
`char buf[64]={};`
`while` `(1)`
`{`
`alarm(2);`
`printf("hello\n");`
`fgets(buf,sizeof(buf),stdin);`
`printf("fgets阻塞接触\n");`
`int ret=alarm(2);`
`printf("%d\n",ret);`
`read(0,buf,sizeof(buf));`
`printf("read阻塞接触\n");`
`printf("buf:%s\n",buf);`
`}`
`return` `0;`
`}`
`