且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用 asyncio 的 Python 简单套接字客户端/服务器

更新时间:2022-06-18 22:35:47

线程代码最接近的字面翻译会像以前一样创建套接字,使其非阻塞,并使用 asyncio 低级套接字操作来实现服务器.这是一个例子,坚持更相关的服务器部分(客户端是单线程的,可能按原样运行):

The closest literal translation of the threading code would create the socket as before, make it non-blocking, and use asyncio low-level socket operations to implement the server. Here is an example, sticking to the more relevant server part (the client is single-threaded and likely fine as-is):

import asyncio, socket

async def handle_client(client):
    loop = asyncio.get_event_loop()
    request = None
    while request != 'quit':
        request = (await loop.sock_recv(client, 255)).decode('utf8')
        response = str(eval(request)) + '
'
        await loop.sock_sendall(client, response.encode('utf8'))
    client.close()

async def run_server():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('localhost', 15555))
    server.listen(8)
    server.setblocking(False)

    loop = asyncio.get_event_loop()

    while True:
        client, _ = await loop.sock_accept(server)
        loop.create_task(handle_client(client))

asyncio.run(run_server())

以上有效,但不是使用 asyncio 的预期方式.它非常低级,因此容易出错,需要您记住在套接字上设置适当的标志.此外,没有缓冲,所以像从客户端读取一行这样简单的事情变成了一件令人厌烦的苦差事.此 API 级别实际上仅适用于替代事件循环的实现者,它们将提供他们sock_recvsock_sendall 等的实现.

The above works, but is not the intended way to use asyncio. It is very low-level and therefore error-prone, requiring you to remember to set the appropriate flags on the socket. Also, there is no buffering, so something as simple as reading a line from the client becomes a tiresome chore. This API level is really only intended for implementors of alternative event loops, which would provide their implementation of sock_recv, sock_sendall, etc.

Asyncio 的公共 API 提供了两个用于消费的抽象层:旧的 transport/protocol层仿照 Twisted 和较新的 流层.在新代码中,您几乎肯定要使用流 API,即调用 asyncio.start_server 并避免原始套接字.这显着减少了行数:

Asyncio's public API provides two abstraction layers intended for consumption: the older transport/protocol layer modeled after Twisted, and the newer streams layer. In new code, you almost certainly want to use the streams API, i.e. call asyncio.start_server and avoid raw sockets. That significantly reduces the line count:

import asyncio, socket

async def handle_client(reader, writer):
    request = None
    while request != 'quit':
        request = (await reader.read(255)).decode('utf8')
        response = str(eval(request)) + '
'
        writer.write(response.encode('utf8'))
        await writer.drain()
    writer.close()

async def run_server():
    server = await asyncio.start_server(handle_client, 'localhost', 15555)
    async with server:
        await server.serve_forever()

asyncio.run(run_server())