OS X UDP发送错误:55 无可用缓冲区

6

当我在OSX10.9.1上使用Python3.3实现RUDP时,我注意到以下代码实际上并不像在Linux上那样工作:(不管是哪种语言,C、Java和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)

这段代码只是不断向8.8.8.8发送大量的udp数据包,这些数据包在经过4个跳点后就会被丢弃,因此它们不应该到达目的地,这只是为了模拟出站流量。

问题:

该代码在Linux(Windows也是如此)上抛出BlockingIOError错误,这是可以接受的,因为它是一个非阻塞套接字。但是在OSX上,它会抛出OSError(55, 'No buffer space available')错误,这是不好的。

但真正有趣的事情是,即使我将此套接字置于阻塞模式,这段代码在OSX上仍然会抛出错误。而在Linux和Windows上,这根本没有抛出任何错误,就像预期的那样,它只会阻塞。

这是基于BSD的系统的实现细节吗?还是我错过了一些重要的网络设置?

[编辑]

我忘记提到我是在千兆局域网中测试这种行为的。我认为这就是问题所在。我连接到了一个100mbit网络,问题消失了,甚至在300mbit WLAN上也没有出现问题。

现在我认为这是连接到高速网络时OSX特有的行为。

[编辑-最终]

我终于找到了原因:

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

在BSD系统上,sendto永远不会阻塞,文档是错误的。再加上连接高速局域网时出现的一些OSX特定问题。总体而言:不要认为sendto会阻塞,在BSD上它不会。好处是:如果你知道这一点,你可以考虑这个问题。


1
就我所知,在 Mac 上进行基于 C 的 UDP 套接字编程时,我也看到了相同的行为。我修改了我的代码,将其视为短暂错误(当它发生时只需再次尝试 send()),即与处理 EINTR 的方式相同,这似乎可以正常工作。 - Jeremy Friesner
2个回答

2

我认为你不需要绑定套接字。这个操作只需要在你计划监听的服务器端进行。如果你省略了绑定操作会发生什么呢?例如:

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

有趣的想法,但遗憾的是相同的行为。 - Kr0e
我对你的帖子关注错了要点。虽然你肯定不想使用bind()调用...但在使用udp时也不能使用send()。根据文档,send()仅适用于已连接的套接字(即:TCP)。你需要使用sendto()。我已经编辑了我的答案以反映正确的代码。 - user590028
1
其实,如果你连接了,使用send也完全没问题。在UDP上连接当然只是sendto的别名。如果你使用connect,就可以使用send。这是官方BSD套接字库的一部分。但说得也对,sendto解决方案实际上更好,因为在写入较大数据包后会出现异常(将大小从400更改为900后开始抛出异常)。 - Kr0e
1
嗯...我查了一下Unix的手册,你是对的。然后我检查了Python的socketmodule.c源代码(以确认它没有做任何奇怪的事情),看起来都很简单明了。接着我搜索了一下UDP写入错误55,并发现了一些有趣的细节。简而言之,你可能正在以比发送速度更快的速度写入数据包。请查看此链接--http://forums.ni.com/t5/LabVIEW/UDP-write-error-55-when-sending-video-over-network/td-p/746327。我已经修改了示例以融入建议。 - user590028
1
经过一夜的不眠,我终于找到了原因。虽然最初对我来说select函数起作用了,但它并不可靠。有时候套接字整个时间都是可写的,无论我写了多少数据(这显然会导致同样的问题),但有时候套接字是不可写的(正如预期的那样)。我使用了Python Tulip模块和其中包含的selectors。但实际上,它并没有起作用。我将更新我的问题,我发现这确实是一个BSD问题。您可以在源代码中进行检查。 - Kr0e
显示剩余3条评论

-1

欢迎提供解决方案的链接,但请确保您的答案即使没有链接也是有用的:在链接周围添加上下文,以便其他用户了解它的内容和原因,然后引用您链接的页面中最相关的部分,以防目标页面不可用。仅仅提供链接的答案可能会被删除。 - Bugs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接