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函数。

相关问题拓展阅读:

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平台的表现

先看头文件中的声明:

view plaincopy在CODE上查看代码片派生到我的代码片

int

WSAAPI

recvfrom(

_In_ SOCKET s,

_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf,

_In_ int len,

_In_ int flags,

_Out_writes_bytes_to_opt_(*fromlen, *fromlen) struct sockaddr FAR * from,

_Inout_opt_ int FAR * fromlen

);

再看MSDN说明:

If the datagram or message is larger than the buffer specified, the buffer is filled with the first part of the datagram, and recvfrom generates the error WSAEMSGSIZE. For unreliable protocols (for example, UDP) the excess data is lost.

可以看出,buf大小小于UDP包大小的时候,recvfrom()会返回-1,并设置错误WSAEMSGSIZE。

实际编程测试验证确实是这样的表现。

3 Linux平台的表现

先看头文件中的声明:

view plaincopy在CODE上查看代码片派生到我的代码片

__extern_always_inline ssize_t

recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags,

__SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len)

可以看出与Windows平台的函数原型相同。但是在其man手册里,没有看到UDP包大于接收缓冲区情况的特殊说明。

写代码测试表明,buf小于UDP包大小的时候,recvfrom()仍然返回复制到缓冲区的字节数,调用者无法得知UDP包被截断的情况。

4 写代码注意事项

UDP包更大是多大呢?UDP头部大小字段占16字节,所以理论上是65535个字节大小。但是UDP如果是通过IP(大多数情况)来传送,由于UDP本身不支持分片,所以一个UDP包只能通过一个IP包来传送,一个IP包大大小理论上也是用16字节表示,这样UDP更大大小就是(65535-IP头部)。

而现实中如果IP包大小大于底层链路层帧的更大数据区大小,则必须对IP包进行分片传送。分片会严重影响传送效率,而且增大不稳定性,所以实际的网络程序发送的IP包都封装到单一的链路层帧中,从而避免分片。问题是链路层帧是多大呢?答案是不一定,因为不同的物理网络的帧大小不一样,如以太网是1500字节,但是其他物理网络可能更小,Internet上的有个最小的限制,那就是576字节。如果UDP程序运行在只运行在以太网中,那么为了避免IP分片,可以采用的更大大小为()=1472字节。如果UDP程序需要运行在Internet上,那么建议更大大小为()=548字节。

linux recvfrom超时无效的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux recvfrom超时无效,Linux中recvfrom超时无效的解决方法,Linux udp通信不成功,linuxsend返回值为9,使用recvfrom接收UDP包在Windows和Linux平台的不同表现的信息别忘了在本站进行查找喔。


数据运维技术 » Linux中recvfrom超时无效的解决方法 (linux recvfrom超时无效)