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

时间:2016-10-17 11:33:53

标签: python-3.5 python-asyncio python-sockets

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

我可以安全地假设此函数是一个非阻塞系统调用,可以在asyncio循环中调用吗?或者我应该将其提交到另一个使用run_in_executor的线程中运行吗?

** Documentation表示它执行系统调用,这可能会阻止整个循环。

2 个答案:

答案 0 :(得分:4)

不,您不能指望socket.sendto是非阻止的。

相反,请使用DatagramTransport.sendto

  

将数据字节发送到addr给出的远程对等体(传输 - 依赖于目标地址)。如果addr为None,则将数据发送到传输创建时给出的目标地址。

     

此方法不会阻止; 它会缓冲数据并安排将数据异步发送出去。

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

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

编辑 - 关于您的评论:

  

socket.sendto()是否等同于transport.sendto()?

不,不是transport.sendto使用loop.add_writer来使操作无阻塞。请参阅implementation

  

我不想使用这种方法,因为它的实现强制我通过回调样式的协议接收数据。

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

答案 1 :(得分:-1)

sendto()是非阻止的,如果编写器当前不可用,则可能会引发(BlockingIOErrorInterruptedError)。 socket.sendto()transport.sendto()之间的区别在于transport.sendto()将首先尝试调用socket.sendto(),或者等到套接字准备好用loop.add_writer()写入并调用{{1}如果第一次尝试失败则再次发送。

这是我从Python 3.5.2(Windows 32位)的asyncio模块的源代码中观察到的

编辑:

在Windows中,套接字操作阻止行为由socket.sendto()指定,因此请确保将超时值设置为socket.settimeout,以便其操作不会阻止。