在Linux系统中,recvfrom函数是常用的接收数据的函数。它可以在指定的时间内等待数据到达,超时则返回-1并设置errno为EWOULDBLOCK或EAGN错误。但是有时候我们在使用这个函数时却发现,超时设置似乎没有生效,无论等待多长时间,函数都不会返回,这是为什么呢?本文将介绍这个问题的原因和解决方法。
问题原因
recvfrom函数的超时是依赖于套接字选项SO_RCVTIMEO的,该选项设置了套接字的接收超时时间。我们在调用recvfrom函数前需要通过setsockopt函数设置套接字选项,如下所示:
“`C++
#include
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
“`
其中,sockfd表示要操作的套接字描述符;level表示选项定义的协议层。对于套接字选项SO_RCVTIMEO,level应该是SOL_SOCKET;optname表示要设置的选项名,对于SO_RCVTIMEO,应该设置为SO_RCVTIMEO;optval和optlen分别是设置选项的值和长度。
例如,我们可以将发送和接收超时时间设置为10秒:
“`C++
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
“`
然而,有一些情况下,我们会发现即使设置了超时时间,recvfrom函数依然会一直阻塞而不返回。这是因为Linux内核对SO_RCVTIMEO有一些限制,如果不满足这些限制,超时设置就会无效。下面我们来分析一下这些限制。
Linux内核的限制
1. 数据包长时间在网络中传输,但是没有到达接收方时,recvfrom会一直等待,超时设置失效。这是因为SO_RCVTIMEO只适用于数据已经到达了接收缓冲区的情况,对于还在网络中传输的数据,内核并不会考虑等待超时的情况。如果要在这种情况下使用超时设置,我们需要使用select函数等待,它能够等待一段时间并检查套接字是否有数据可读。
2. 数据包可能已经到达了接收缓冲区,但是仍然没有被recvfrom函数读取,这时recvfrom函数会一直等待,超时设置失效。如果我们在调用recvfrom函数时指定的缓冲区大小小于数据包大小,或是多次调用recvfrom函数时指定的缓冲区大小总和小于数据包大小,就会出现这种情况。解决方法是尽可能保证缓冲区的大小大于等于更大的数据包大小,或使用MSG_PEEK标志调用recvfrom函数,查看数据包大小后再次调用recvfrom函数读取数据。
3. SO_RCVTIMEO的最小超时时间为20毫秒,如果设置的超时时间小于20毫秒,超时设置会被忽略。这是因为Linux内核采用了一种轮询方式实现SO_RCVTIMEO,如果超时时间小于20毫秒,轮询的开销将过大,因此会忽略超时设置。如果要设置更小的超时时间,可以使用其他的方法,如使用select函数以及epoll函数。
解决方法
有了以上的分析,我们可以得出以下的解决方法:
1. 当数据包长时间在网络中传输时,使用select函数等待可读,而不是设置超时时间等待。
2. 确保指定的缓冲区大小大于等于更大的数据包大小,或使用MSG_PEEK标志调用recvfrom函数。
3. 设置的超时时间应该大于等于20毫秒,否则会被忽略。
4. 如果需要更细粒度的超时时间控制,可以使用其他的方法,如使用select函数以及epoll函数。
相关问题拓展阅读:
Linux udp通信不成功
server端绑定消渗余地拿滚址错误。通常喊枣是
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
可能是内存的问题,也可能是程序的兼容性问题.
linuxsend返回值为9
linuxsend返回值为9:
linuxsend返回值为9因为OS Linux Mint 14.客户端在启动时向服务器发送请求以获取一些会话参数,包括usec中的超时 . 客户端打开一个套接字并设置一个默认轿陪蠢超时值,以便在recvfrom上不被无限制地阻塞,然后发送对这些参数的请求,乱御关闭所述套接字,重新打开设置新超时值的套接字,最后询问用户用于命令 .
参数请求由子函数处理,该子函数除了其他之外还获得指向闭陪客户端套接字的指针,因此如果新的sockfd是不同的数字,则主函数也将能够引用新的套接字 . (返回值用作检查值,零或非零)
linuxsend返回值为9,这蠢或个的话根据厂商发布的数据以及我迟档山的朋友的反馈来看的话,这个是完码中全正常的。
使用recvfrom接收UDP包在Windows和Linux平台的不同表现
1 UDP接收原理
操作系统的UDP接收流程如下:收到一个UDP包后,验证没有错误后,放入一个包队列中,队列中的每一个元素就是一个完整的UDP包。当应用程序通过recvfrom()读取时,OS把相应的一个完整UDP包取出,然后拷贝到用户提供的内存中,物理用户提供的内存大小是多少,OS都会完整取出一个UDP包。如果用户提供的内存小于这个UDP包的大小,那么在填充慢内存后,UDP包剩余的部分就会被丢弃,以后再也无法取回。
这与TCP接收完全不同,TCP没有完整包的概念,也没有边界,OS只旁改会取出用户要求的大小,剩余的仍然保留在OS中,下次还可以继续取出。
socket编程虽然是事实上的标氏氏准,而且不同平台提供的接口函数也非常类似,歼启散但毕竟它不存在严格的标准。所以各个平台的实现也不完全兼容。下面就从recvfrom()这个函数看看Window平台和Linux平台的不同。
2 Windows平台的表现