Linux中recvfrom超时无效的解决方法 (linux recvfrom超时无效)
在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函数。