查找最大UDP负载 - python套接字发送/发送

时间:2016-10-13 22:38:31

标签: python sockets networking udp

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

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

背景

我不是在问什么

演示代码

要查看错误:

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))

2 个答案:

答案 0 :(得分:3)

您的部分场所需要进行小幅修正。

由于IP头包含16位字段的长度,IPv4消息的最大大小可以是65535字节,并且包括IP头本身。

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

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

但是如果你的系统向IP头添加了更多的头(通过socket ioctls或其他任何选项字段),那么UDP应用程序有效负载的限制将减少相应的数量。

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

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

答案 1 :(得分:1)

嗯,总是有尝试和看到的方法......我不会称之为优雅,但它与平台无关:

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()