Reputation: 7385
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
.
The maximum allowed by the IPv4 packet format seems to be 65507.
Yet it seems that some systems set the limit lower.
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
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
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