jtpereyda
jtpereyda

Reputation: 7385

Find max UDP payload -- python socket send/sendto

How can I find the maximum length of a UDP payload in Python (Python 2), preferably platform-independent?

Specifically, I want to avoid [Errno 90] Message too long AKA errno.EMSGSIZE.

Background

What I am not asking

Demo Code

To see the error in action:

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

Upvotes: 1

Views: 7624

Answers (2)

selbie
selbie

Reputation: 104514

Some of your premises need a minor correction.

As a result of the IP header containing a 16 bit field for length, the largest size an IPv4 message can be is 65535 bytes And that includes the IP header itself.

The IP packet itself has at least a 20 byte header. Hence 65535 - 20 == 65515 is the largest size of the payload of an IP message. The payload can be a UDP datagram.

The UDP datagram itself is typically an 8 byte header. Hence 65515 - 8 == 65507. So even if the UDP header could theoretically contain an amount higher than 65507 in its own length field, the IPv4 message can't contain it.

But if your system adds any more headers to the IP header (option fields via socket ioctls or whatever), then the limit of the UDP application payload will get reduced by a corresponding amount.

In practice, any IP message above the MTU size of your network adapter (~1500 bytes), will trigger the UDP packet to undergo IP fragmentation. So if your ethernet card has a 1500 byte message size, a a UDP datagram containing 65507 bytes of application data will get fragmented into approximately 43 separate ethernet frames. Each frame is a fragmented IP packet containing a subset of the UDP bytes, but with a seperate header. When all the IP fragments are received on the remote end, it logically gets delivered to the application as 65507 byte datagram. Fragmentation is transparent to applications.

I would suggest running your code with Wireshark and send to a real IP address out of the network. You can observe and study how IP fragmentation works.

Upvotes: 4

Jeremy Friesner
Jeremy Friesner

Reputation: 73071

Well, there's always the try-it-and-see approach... I wouldn't call this elegant, but it is platform-independent:

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

Upvotes: 3

Related Questions