在python 3 asyncio中是否有类似socket.recv_的操作?

在python 3 asyncio中是否有类似socket.recv_的操作?,python,python-asyncio,Python,Python Asyncio,Socket模块有一个Socket.recv_into方法,因此它可以使用用户定义的bytebuffer(如bytearray)进行零拷贝。但是,BaseEventLoop可能没有这样的方法。有没有一种方法可以在asyncio中使用类似于socket.recv_进入的方法?为BaseEventLoop定义的低级套接字操作需要传入一个socket.socket对象,例如BaseEventLoop.sock_recv(sock,nbytes)。因此,假设您有一个socket.socket,您可以调用

Socket模块有一个
Socket.recv_into
方法,因此它可以使用用户定义的
bytebuffer
(如
bytearray
)进行零拷贝。但是,
BaseEventLoop
可能没有这样的方法。有没有一种方法可以在asyncio中使用类似于
socket.recv_进入
的方法?

BaseEventLoop
定义的低级套接字操作需要传入一个
socket.socket
对象,例如
BaseEventLoop.sock_recv(sock,nbytes)
。因此,假设您有一个
socket.socket
,您可以调用
sock.recv\u into()
。这样做是否是一个好主意是另一个问题。

您可以实现自己的asyncio传输,它利用
.recv\u into()
函数,但是是的,现在asyncio没有现成的方法使用
.recv\u into()


就个人而言,我对很大的加速速度表示怀疑:当您使用C开发时,零拷贝非常重要,但对于像Python这样的高级语言来说,好处要小得多。

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

编辑:按要求展开答案

调用asyncio的
sock\u recv\u into()
通常如下所示:

byte_count = await loop.sock_recv_into(sock, buff)
buff是一个实现Python缓冲区协议的可变对象,其示例包括bytearray和bytearray上的memoryview。下面的代码演示如何使用memoryview接收到bytearray中

asyncio套接字的工作演示代码必然包括一组脚手架,用于设置连接的两侧并运行事件循环。这里的要点是在下面的sock_read_justice()协同例程中使用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()
#/usr/bin/env蟒蛇3
“”“演示异步IO模块的sock\u recv\u into()功能。”“”
导入系统
断言系统版本信息[:2]>=(3,7)(
“asyncio sock_recv_into()在Python 3.7中新增”
导入套接字
导入异步
def local_echo_服务器(端口=0):
“”“具有睡眠延迟的普通踩踏式回显服务器。”“”
导入线程
导入时间
随机输入
ssock=socket.socket()
ssock.bind('127.0.0.1',端口))
_,port=ssock.getsockname()
ssock.听(5)
def回波(csock):
尽管如此:
数据=csock.recv(8192)
如果没有数据:
打破
time.sleep(random.random())
csock.sendall(数据)
C锁定关闭(1)
def serve():
尽管如此:
csock,client_addr=ssock.accept()
tclient=threading.Thread(target=echo,args=(csock,),daemon=True)
tclient.start()
tserve=threading.Thread(target=service,daemon=True)
tserve.start()
返回端口
N_COROS=100
nrunning=0
异步def sock_读取(sock、size、loop=None):
“从事件循环中的sock读取并返回大小字节。”
如果循环为None:loop=asyncio.get\u event\u loop()
bytebuff=bytearray(大小)
sofar=0
当sofar<尺寸:
memview=memoryview(bytebuff)[sofar:]
nread=wait循环。sock_recv_进入(sock,memview)
打印('socket',sock.getsockname(),'读取%d字节'%nread]
如果未重新阅读:
引发运行时错误('意外的套接字关闭')
sofar+=nread
返回bytebuff
异步def echo_客户端(端口):
“将随机数据发送到echo服务器,并测试我们是否返回相同的数据。”
从操作系统导入urandom
全球运转
loop=asyncio.get\u event\u loop()
sock=socket.socket()
袜子锁定(错误)
等待循环。sock_连接(sock,('127.0.0.1',端口))
对于[1,64,1024,55555]中的大小:
发送=urandom(大小)
等待循环。sock_sendall(sock,sending)
已接收=等待袜子读取(袜子,尺寸)
断言接收==发送
n运行-=1
如果未重新运行:
loop.stop()
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
端口=本地回波服务器()
打印('端口是',端口)
loop=asyncio.get\u event\u loop()
对于范围内的(N_COROS):
创建_任务(echo_客户端(端口))
n运行+=1
打印('开始循环')
loop.run_forever()

我已经使用了
loop.sock\u recv
。但是
loop.sock\u recv
返回字节对象。所以我也使用
bytearray.extend方法
ex)
ba=bytearray();bytes_object=从loop.sock_recv(some_sock)产生的收益;ba.extend(bytes\u object)
sock\u recv
不要使用预先创建的固定大小
bytearray
。我想要一种类似
预创建的缓冲区=bytearray(b'*100)的方法;loop.sock\u recv\u into(一些套接字,预创建的\u缓冲区,100)
@seunghyunhang:我理解,但是,我建议您可以在套接字上调用
sock.recv\u into()
,而不是
loop.sock\u recv()
。我不知道在asyncio框架内这样做是否安全——我怀疑不是。谢谢你的评论@mhawke:)我会尝试一下!因为
sock.recv\u into
不是协同程序,它很可能会失败:(@seunghyunhang:您可以将
BaseEventLoop
子类化,并提供
recv\u into()
的协同程序实现。您能否扩展您的答案来解释如何使用
AbstractEventLoop.sock\u recv\u into()
?可能包括一个代码示例。