Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OS X UDP send error: 55 No buffer space available

While I was implementing RUDP in python3.3 on OSX10.9.1 I noticed that the following code actually does not do what it does on linux: (it doesn't matter which language, same behavior for C, Java and C#/Mono)

from socket import *

udp = socket(AF_INET, SOCK_DGRAM)
udp.setsockopt(SOL_SOCKET, SO_REUSEADDR, True)

udp.bind(('0.0.0.0', 1337))
udp.setblocking(False)
udp.setsockopt(SOL_IP, IP_TTL, 4)
udp.connect(('8.8.8.8', 12345))

buf = b'x' * 400
for _ in range(1024 * 1024 * 10):
    udp.send(buf)

This code just keep writing a lot udp packets to 8.8.8.8, those packets get dropped after 4 hops so they should not reach the destination, it's just to simulate outgoing traffic.

The problem:

This code throws an OSError(55, 'No buffer space available') error, while on Linux(Windows, too) it throws a BlockingIOError, which is fine, because it's a nonblocking socket.

So while on linux and windows the socket behaves correctly on OSX this is a OSError, which is bad.

But the really interesting thing is that even if I put this socket into blocking mode, this code does still throw an error on OSX. While on linux and windows this throws no error at all, as expected, it just blocks.

Is this an implementation detail of BSD based systems ? Or am I missing some important network settings ?

[EDIT]

I forgot to mention that I was testing this behavior in a gigabit lan. And I think that was the problem. I connected to a 100mbit network and the problem was gone, even with 300mbit wlan the problem did not occur.

Now I think this is some OSX specific behavior when connected to high speed networks.

[EDIT - FINAL] I finally found the reason:

http://lists.freebsd.org/pipermail/freebsd-hackers/2004-January/005369.html

On BSD systems sendto NEVER blocks, the docs are wrong. Mixed with some OSX specific problem when connected to high speed lans. Overall: Just don't think that sendto blocks, it doesn't on BSD. The good thing: If you know that, you can take this into account.

like image 546
Kr0e Avatar asked Feb 23 '14 19:02

Kr0e


1 Answers

I don't think you mean to bind the socket. That should only be done on the server side that you are planning to listen to. What happens if you drop the call to bind? eg:

from socket import *
import time

udp = socket(AF_INET, SOCK_DGRAM)

buf = b'x' * 400
for _ in range(1024 * 1024 * 10):
    while True
        try:
            udp.sendto(buf, ('8.8.8.8', 12345))
            break
        except OSError, exc:
            if exc.errno == 55:
                time.sleep(0.1)
            else:
                raise
like image 135
user590028 Avatar answered Oct 16 '22 08:10

user590028