查找最大 UDP 有效负载——python 套接字发送/发送

Posted

技术标签:

【中文标题】查找最大 UDP 有效负载——python 套接字发送/发送【英文标题】:Find max UDP payload -- python socket send/sendto 【发布时间】:2017-02-23 05:41:20 【问题描述】:

如何在 Python (Python 2) 中找到 UDP 有效负载的最大长度,最好与平台无关?

具体来说,我想避免[Errno 90] Message too long AKA errno.EMSGSIZE

背景

UDP 数据包格式允许的最大值is 65536)。

IPv4数据包格式seems to be 65507所允许的最大值。

但似乎some systems set the limit lower。

我没有问什么

What is the largest Safe UDP Packet Size on the Internet? 一个 IPv4 数据报可以容纳的最大 UDP 负载是多少? 一个以太网帧可以容纳的最大 UDP 负载是多少?

演示代码

查看操作中的错误:

import socket

msg_len = 65537  # Not even possible!

ip_address = "127.0.0.1"
port = 5005
msg = "A" * msg_len

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg, (ip_address, port))

【问题讨论】:

相关:***.com/questions/25841/… 我认为没有一种可移植的方式来获取此信息。这不是套接字 API 的特性。 【参考方案1】:

您的某些前提需要稍作修正。

由于 IP 标头包含 16 位长度字段,因此 IPv4 消息的最大大小为 65535 字节,其中包括 IP 标头本身。

IP 数据包本身至少有一个 20 字节的标头。因此 65535 - 20 == 65515 是 IP 消息 有效负载 的最大大小。有效载荷可以是 UDP 数据报。

UDP 数据报本身通常是一个 8 字节的报头。因此 65515 - 8 == 65507。因此,即使理论上 UDP 标头在其自身的长度字段中可以包含高于 65507 的数量,IPv4 消息也不能包含它。

但是,如果您的系统向 IP 标头添加更多标头(通过套接字 ioctls 或其他方式的选项字段),那么 UDP 应用程序有效负载的限制将相应减少。

实际上,任何超过网络适配器 MTU 大小(约 1500 字节)的 IP 消息都会触发 UDP 数据包进行 IP 分段。因此,如果您的以太网卡的消息大小为 1500 字节,则包含 65507 字节应用程序数据的 UDP 数据报将被分成大约 43 个单独的以太网帧。每个帧都是一个分段的 IP 数据包,其中包含 UDP 字节的子集,但具有单独的标头。当远程端接收到所有 IP 片段时,它在逻辑上以 65507 字节数据报的形式传递给应用程序。分片对应用程序是透明的。

我建议使用 Wireshark 运行您的代码并将其发送到网络外的真实 IP 地址。您可以观察和研究 IP 分片的工作原理。

【讨论】:

"分片对应用程序是透明的。" 没错,但分片和重组需要时间。此外,许多路由器和大多数防火墙只允许第一个分片通过,因为分片用于攻击。如果整体的任何一个片段丢失,整体将被丢弃。碎片化真的不再实用了。 Ron,我正要写一些类似的细节,关于 IP 分片的可靠性问题,以及为什么将过多的数据重载到 UDP 字段中不是一个好主意。现代路由器会限制碎片的数量是可以理解的,但如果将其限制在 1 个,我会感到非常惊讶。(但是,嘿,我每天都学到新东西)。 我写的更多是关于目标路由器或防火墙。默认情况下,大多数防火墙不再允许碎片。企业通常在路由器上配置相同的东西。我总是很惊讶人们想用尽可能多的数据加载不可靠的协议,因为他们知道丢失一个数据包会丢失更多的数据。 谢谢罗恩。因为我维护Stuntman,所以我真的很想测试这个。但是我还没有实现对fragment handling detection 的 PADDING 属性支持,因为我从来没有看到需要它(以及公共服务器的一些安全问题)。测试一些我正在使用的各种网络会很有趣。【参考方案2】:

嗯,总有试一试的方法......我不会称之为优雅,但它是独立于平台的:

import socket

def canSendUDPPacketOfSize(sock, packetSize):
   ip_address = "127.0.0.1"
   port = 5005
   try:
      msg = "A" * packetSize
      if (sock.sendto(msg, (ip_address, port)) == len(msg)):
         return True
   except:
      pass
   return False

def get_max_udp_packet_size_aux(sock, largestKnownGoodSize, smallestKnownBadSize):
   if ((largestKnownGoodSize+1) == smallestKnownBadSize):
      return largestKnownGoodSize
   else:
      newMidSize = int((largestKnownGoodSize+smallestKnownBadSize)/2)
      if (canSendUDPPacketOfSize(sock, newMidSize)):
         return get_max_udp_packet_size_aux(sock, newMidSize, smallestKnownBadSize)
      else:
         return get_max_udp_packet_size_aux(sock, largestKnownGoodSize, newMidSize)

def get_max_udp_packet_size():
   sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
   ret = get_max_udp_packet_size_aux(sock, 0, 65508)
   sock.close()
   return ret

print "Maximum UDP packet send size is", get_max_udp_packet_size()

【讨论】:

以上是关于查找最大 UDP 有效负载——python 套接字发送/发送的主要内容,如果未能解决你的问题,请参考以下文章

576 字节是 UDP 有效负载消除碎片的安全大小吗?

UDP Socket 接收垃圾值

Python网络编程—UDP套接字广播

在 UDP 套接字上多次调用 connect() 以发送到不同的目标地址 - 有效吗?

Python 之 Socket编程(TCP/UDP)

基于UDP协议通信的套接字/UDP协议介绍/基于UDP协议查找DNS/进程介绍/操作系统与进程发展史