如何在套接字IO操作上设置超时机制
主要有三种方案:
(1)调用Alarm,它在指定超时期满时产生SIGALRM信号,此方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的ALARM调用。
(2)在SELECT,即多路复用中阻塞等待IO,因为select有内置的时间限制,以此代替直接阻塞在read或write上的调用。
(3)使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项,这个方法的问题在于并非所有的实现都支持这两个套接字选项。
一、首先看一个如何调用Alarm,它主要是通过对慢系统调用产生中断信号来完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | int connect_timeo( int sockfd, const SA *saptr, socklen_t salen, int nsec){ Sigfunc *sigfunc; int n; sigfunc = Signal(SIGALRM, connect_alarm); if (alarm(nsec) != 0) err_msg( "Alarm has already been set!" ); if ( ( n = connect(sockfd, saptr, salen)) < 0){ //如果超过nsec秒数,会被中断产生EINTR close(sockfd); //关闭此套接字,防止三次握手继续进行 if ( errno == EINTR) //慢系统调用产生中断信号 errno = ETIMEDOUT; } alarm(0); Signal(SIGALRM, sigfunc); return n; } static void connect_alarm( int signo){ return ; } |
1 |
二、再来看一Select多路复用是如何做到这一点的,它主要是通过第五个参数来设置所阻塞的描述符超时情况。
1 2 3 4 5 6 7 8 9 10 11 | int readable_timeo( int fd, int sec){ fd_set rset; struct timeval tv; //主要是设置此结构体,之后将该结构体以参数形式传给Select函数 FD_ZERO(&rset); FD_SET(fd, &rset); tv.tv_sec = sec; tv.tv_usec = 0; return (select(fd+1, &rset, NULL, NULL, &tv)); } |
这样,在每次调用read之前就可以先调用此函数来设置超时秒数。如下:
1 2 3 4 5 6 7 8 9 10 11 | int readable_timeo( int fd, int sec){ fd_set rset; struct timeval tv; //主要是设置此结构体,之后将该结构体以参数形式传给Select函数 FD_ZERO(&rset); FD_SET(fd, &rset); tv.tv_sec = sec; tv.tv_usec = 0; return (select(fd+1, &rset, NULL, NULL, &tv)); } |
三、使用SO_RCVTIMEO套接字选项为recvfrom设置超时
本选项一旦设置到某个描述符上,其超时设置将应用于该描述符上的所有读操作,本方法的优势在于一次设置,一直生效,而前面两种方法需要在每个操作发生之前设置。另外,本套接字选项仅用于读操作,类似SO_SNDTIMEO仅用于写操作,但两者均不能用于为connect设置超时,从名字就可以看出,一个是接收,一个是发送。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; Setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)); //设置超时,通过设定套接字选项 while ( fgets (sendline, MAXLINE, stdin) != NULL){ sendto(sockfd, sendline, strlen (sendline), 0, pservaddr, servlen); n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); if ( n < 0){ if ( errno == EWOULDBLOCK){ //这里有点类似于非阻塞套接字,即无数据可读的返回值 fprintf (stderr, "Socket read timeout!" ); continue ; } else err_sys( "recvfrom error!" ); } recvline[n] = 0; fputs (recvline, stdout); } |
分类:
Linux/Unix
, 网络编程
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?