在python 3 asyncio中是否有像socket.recv_into这样的操作?

时间:2015-01-20 11:29:29

标签: python python-asyncio

套接字模块有一个socket.recv_into方法,因此它可以使用用户定义的bytebuffer(如bytearray)进行零拷贝。但也许BaseEventLoop没有这样的方法。有没有办法在asyncio中使用像socket.recv_into这样的方法?

3 个答案:

答案 0 :(得分:1)

BaseEventLoop定义的低级套接字操作需要传入socket.socket个对象,例如BaseEventLoop.sock_recv(sock, nbytes)。因此,如果您有socket.socket,则可以致电sock.recv_into()。这样做是否是个好主意是另一个问题。

答案 1 :(得分:1)

可以实现自己的asyncio传输,它使用.recv_into()函数,但是,现在asyncio没有办法使用.recv_into()开箱即用。

我个人怀疑加速速度非常快:当你使用C进行开发时,零拷贝非常重要,但对于Python这样的高级语言来说,这些优势要小得多。

答案 2 :(得分:0)

更新:从Python 3.7.0开始,我在写这篇文章时处于alpha版本,标准库的asyncio模块文档AbstractEventLoop.sock_recv_into()。

编辑:按要求扩展答案......

对asyncio sock_recv_into()的调用通常如下:

byte_count = await loop.sock_recv_into(sock, buff)

buff是一个可变对象,它实现了Python的缓冲协议,其示例包括bytearray和bytearray上的内存视图。下面的代码演示了如何使用memoryview接收到bytearray。

asyncio套接字的工作演示代码必须包含一堆脚手架来设置连接的两端并运行事件循环。这里的要点是在下面的sock_read_exactly()协同例程中使用asyncio的sock_recv_into()。

#!/usr/bin/env python3

"""Demo the asyncio module's sock_recv_into() facility."""

import sys
assert sys.version_info[:2] >= (3, 7), (
        'asyncio sock_recv_into() new in Python 3.7')

import socket
import asyncio

def local_echo_server(port=0):
    """Trivial treaded echo server with sleep delay."""
    import threading
    import time
    import random
    ssock = socket.socket()
    ssock.bind(('127.0.0.1', port))
    _, port = ssock.getsockname()
    ssock.listen(5)
    def echo(csock):
        while True:
            data = csock.recv(8192)
            if not data:
                break
            time.sleep(random.random())
            csock.sendall(data)
        csock.shutdown(1)
    def serve():
        while True:
            csock, client_addr = ssock.accept()
            tclient = threading.Thread(target=echo, args=(csock,), daemon=True)
            tclient.start()
    tserve = threading.Thread(target=serve, daemon=True)
    tserve.start()
    return port


N_COROS = 100
nrunning = 0

async def sock_read_exactly(sock, size, loop=None):
    "Read and return size bytes from sock in event-loop loop."
    if loop is None: loop = asyncio.get_event_loop()
    bytebuff = bytearray(size)
    sofar = 0
    while sofar < size:
        memview = memoryview(bytebuff)[sofar:]
        nread = await loop.sock_recv_into(sock, memview)
        print('socket', sock.getsockname(), 'read %d bytes' % nread)
        if not nread:
            raise RuntimeError('Unexpected socket shutdown.')
        sofar += nread
    return bytebuff

async def echo_client(port):
    "Send random data to echo server and test that we get back the same."
    from os import urandom
    global nrunning
    loop = asyncio.get_event_loop()
    sock = socket.socket()
    sock.setblocking(False)
    await loop.sock_connect(sock, ('127.0.0.1', port))
    for size in [1, 64, 1024, 55555]:
        sending = urandom(size)
        await loop.sock_sendall(sock, sending)
        received = await sock_read_exactly(sock, size)
        assert received == sending
    nrunning -= 1
    if not nrunning:
        loop.stop()


if __name__ == '__main__':
    port = local_echo_server()
    print('port is', port)
    loop = asyncio.get_event_loop()
    for _ in range(N_COROS):
        loop.create_task(echo_client(port))
        nrunning += 1
    print('Start loop.')
    loop.run_forever()