假设 socket.sendto 是非阻塞操作是否安全?

Is it safe to assume that socket.sendto is non-blocking operation?

我有一个使用 socket(AF_INET, SOCK_DGRAM) 创建的套接字对象,我将在异步循环中使用它。但是我在 https://docs.python.org/3/library/asyncio-eventloop.html#low-level-socket-operations 中找不到 sendto 函数。

我可以安全地假设这个函数是一个可以在异步循环中调用的非阻塞系统调用吗?或者我应该用 run_in_executor 在另一个线程中将它提交给 运行?

** Documentation 声明它执行了一个系统调用,我担心它可能会阻塞整个循环。

不,你不能期望 socket.sendto 是非阻塞的。

而是使用 DatagramTransport.sendto:

Send the data bytes to the remote peer given by addr (a transport-> dependent target address). If addr is None, the data is sent to the target address given on transport creation.

This method does not block; it buffers the data and arranges for it to be sent out asynchronously.

数据报传输由 loop.create_datagram_endpoint 协程返回:

transport, protocol = await loop.create_datagram_endpoint(factory, sock=sock)

编辑 - 关于您的评论:

Is socket.sendto() equivalent to transport.sendto()?

不,不是,transport.sendto 使用 loop.add_writer 使操作成为非阻塞的。见 implementation.

I do not want to use this method because of it's implementation which enforce me to receive data through protocol with callback style.

asyncio 的底层是基于回调的,asyncio 不为 UDP 提供基于协程的对象。不过,我写了一个module that provides high-level UDP endpoints for asyncio.

用法:

async def main():
    local = await open_local_endpoint()
    remote = await open_remote_endpoint(*local.address)
    remote.write(b'Hey Hey, My My')
    data, addr = await local.read()
    message = "Got {data!r} from {addr[0]} port {addr[1]}"
    print(message.format(data=data.decode(), addr=addr))

输出:

Got 'Hey Hey, My My' from 127.0.0.1 port 45551

sendto() 是非阻塞的,如果 writer 当前不可用,可能会引发 (BlockingIOError, InterruptedError)。 socket.sendto()transport.sendto() 之间的区别是 transport.sendto() 将首先尝试调用 socket.sendto(),或者等到套接字准备好写入 loop.add_writer() 并调用 socket.sendto() 如果第一次尝试发送失败,请再次发送。

这是我从 Python 3.5.2(Windows 32 位)

asyncio 模块的源代码中观察到的

编辑:

在Windows中,套接字操作阻塞行为由socket.settimeout指定,因此请务必将超时值设置为0,这样它的操作就不会阻塞。