UDP缓冲区,它们在哪里,有什么保证?

3

我正在尝试使用Python套接字库(3.5,在Linux Mint 18上),尝试理解UDP。我是一个涉猎软件的硬件人员,UDP似乎比TCP更容易理解。我非常清楚UDP不能保证一对一地传递数据包。

到目前为止,我可以按照教程将数据从服务器回显到客户端。

然而,我喜欢推动事情,看看当应用程序没有按预期路径进行时会发生什么,我不喜欢写那些在意外情况下“挂起”的东西。

如果服务器将套接字绑定到端口号,然后客户端发送多个消息到该端口,然后服务器多次调用recvfrom(),我发现每次调用都返回一个消息,并且消息按顺序排列。换句话说,消息已经被缓冲,后来的消息没有覆盖队列中先前的消息。我并不惊讶看到这种情况发生,但也不会感到惊讶,即只有最后接收到的消息可用,即缓冲区长度为1。

这个缓冲区及其深度是Python实现细节、Linux Mint/Ubuntu细节还是由UDP协议定义的?


2
这一切都在您的操作系统网络堆栈中处理。 - chepner
据我所知,UDP没有任何保证。数据包可能不会到达,它们可能会被复制,它们可能会乱序到达。通常情况下,“无法保证”的事情99.9%的时间看起来都很好。在现实世界中,复制失败案例可能会很困难。 - Ned Batchelder
现代计算机有很多内存,因此很难溢出缓冲区。如果您20年前尝试过这样做,您可能会轻松丢失数据包。 - Barmar
3个回答

4
这个UDP套接字的缓冲区大小是操作系统网络栈的实现细节。每个操作系统都会尝试基于其预期的使用情况设置合理的默认大小,但您可以通过调用socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, newSizeInBytes)和/或socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, newSizeInBytes)来针对每个套接字覆盖操作系统的默认大小(无论如何,最多只能达到某个最大值)。缓冲区将排队等待尽可能多的数据包,然后丢弃任何无法完全适应剩余空间的传入数据包。

1
UDP缓冲区位于操作系统的网络堆栈中。缓冲区的大小取决于计算机的内存和内核配置设置。在具有多GB内存的现代计算机上,操作系统很可能有足够的空间用于UDP缓冲区,除非计算机极度过载,否则很难溢出它们。
您可能有办法配置操作系统以限制用于UDP缓冲区的内存量,以便您可以导致溢出,并查看测试应用程序中的症状。我不知道配置设置,您可以尝试在Unix & LinuxAskUbuntu.com中提问。

实际上,溢出默认大小的UDP缓冲区并不太困难--每个套接字都有自己的缓冲区,而套接字的缓冲区是固定大小且不是特别大的(例如32-128KB,而不是兆字节),因此计算机总共有多少RAM并不重要。 - Jeremy Friesner
我同意上面的评论,即使在具有32 GiB RAM的Linux系统上,udp发送缓冲区也仅为212KB。 通过执行以下操作进行检查:cat /proc/sys/net/core/wmem_default 并获得212992 其中wmem_default是“套接字发送缓冲区的默认设置(以字节为单位):” - Ankit
@Ankit 谢谢。我在90年代曾经与频繁使用的Solaris DNS服务器一起工作过,有时会遇到UDP缓冲区限制,但我认为默认缓冲区大小现在应该已经增加了。 - Barmar

0
网络I/O由操作系统内核执行,您的代码运行在用户空间,所以当网络数据到达时,内核必须将该数据存储在内核内存中,直到您的用户空间代码能够获取它为止。
请记住,即使您从套接字尽可能快地读取数据,当您读取一个数据包并处理它时,另外两个数据包可能会在您仍在忙于处理时到达,您肯定不希望丢失这些数据包,对吧?当然,UDP协议不是可靠的协议,因此数据包可能根本不会到达,但如果它们确实到达了您的计算机,那么因为当前没有人主动等待这些数据包,就把它们丢弃掉是多么愚蠢的行为呢?而且编写网络代码也应该可以不被迫编写多线程代码。即使是单线程程序也必须能够进行网络通信,但这样的程序永远无法同时监听新数据包和处理先前的数据包。
通常情况下,协议标准并不详细定义协议实现的工作方式,它们只定义了数据包在传输线上的外观以及如果协议定义了一种行为,它们会从外部实体的角度定义这种行为(比如说:“如果输入你的系统,期望得到以下输出”)。然而,具体如何实现这个行为取决于实际的实现方案。即使标准定义了实现必须如何做到这一点,又有谁会注意到你违反了标准呢?只要从系统外部看起来行为正确,没有人会注意到系统在内部是否按照标准要求工作。而且,为什么有人会关心呢?只要行为与遵循标准的效果完全一样,无论你是否真正内部遵循标准,有什么区别呢?
UDP不需要任何缓冲,但如果没有缓冲,单线程程序几乎无法使用UDP,因为它会不断使用大量的数据包。想象一下一个水龙头一直滴水的情况。你的任务是用杯子接住这些水并倒入管道中。当你的杯子装满了并且你跑向管道时,水龙头的水就会流失,因为你不在那里接住它。因此,最好将水龙头放在一个桶里,这样桶就能接住水,而你可以用杯子从桶里取水。只要你比水从水龙头滴出的速度更快地将水从桶中引走,桶就永远不会溢出,即使你不是一直紧挨着水龙头。
套接字在现代操作系统中总是有一个缓冲区,唯一的区别是,在UDP的情况下,这个缓冲区是一个内部实现细节,而在TCP的情况下,协议要求两端都存在缓冲区,否则TCP无法正常工作。然而,如果TCP缓冲区等于套接字缓冲区,或者在套接字缓冲区前面有一个独立的缓冲区,或者在协议实现中有这样一个缓冲区,因此根本没有套接字缓冲区,这又是一个实现细节,协议标准并不关心(通常套接字缓冲区只是简单地用作TCP缓冲区,因为拥有两个缓冲区没有实际优势,而且套接字缓冲区已经存在)。
操作系统会为每个套接字选择一个默认的缓冲区大小,可能取决于套接字的协议,也可能不取决于协议。一些系统在使用套接字时会动态调整缓冲区大小(例如根据吞吐量和延迟),大多数系统允许您自行调整缓冲区大小,如果需要的话。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接