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

时间:2018-01-29 17:06:42

标签: python sockets python-asyncio

我想使用asyncio协同程序而不是多线程来重新实现我的代码。

server.py

def handle_client(client):
    request = None
    while request != 'quit':
        request = client.recv(255).decode('utf8')
        response = cmd.run(request)
        client.send(response.encode('utf8'))
    client.close()

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 15555))
server.listen(8)

try:
    while True:
        client, _ = server.accept()
        threading.Thread(target=handle_client, args=(client,)).start()
except KeyboardInterrupt:
    server.close()

client.py

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.connect(('localhost', 15555))
request = None

try:
    while request != 'quit':
        request = input('>> ')
        if request:
            server.send(request.encode('utf8'))
            response = server.recv(255).decode('utf8')
            print(response)
except KeyboardInterrupt:
    server.close()

我知道有一些合适的异步网络库可以做到这一点。但我只想在这种情况下使用asyncio核心库,以便更好地理解它。

在处理客户端定义之前只添加async关键字真是太好了......这里有一段似乎有效的代码,但我仍然对实现感到困惑。

asyncio_server.py

def handle_client(client):
    request = None
    while request != 'quit':
        request = client.recv(255).decode('utf8')
        response = cmd.run(request)
        client.send(response.encode('utf8'))
    client.close()

def run_server(server):
    client, _ = server.accept()
    handle_client(client)

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 15555))
server.listen(8)

loop = asyncio.get_event_loop()
asyncio.async(run_server(server))
try:
    loop.run_forever()
except KeyboardInterrupt:
    server.close()

如何以最佳方式调整此方法并使用async等待关键字。

1 个答案:

答案 0 :(得分:8)

线程代码的最接近的字面翻译将像以前一样创建套接字,并使用asyncio low-level socket operations来实现服务器。这是一个例子,坚持更相关的服务器部分(客户端是单线程的,可能很好):

import asyncio, socket

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

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

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

loop = asyncio.get_event_loop()
loop.run_until_complete(run_server())

上述工作,但不是使用asyncio的预期方式。虽然正确的抽象取决于应用程序,但您可能希望至少使用asyncio.start_server而不使用原始套接字。这大大减少了代码的行数:

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

loop = asyncio.get_event_loop()
loop.create_task(asyncio.start_server(handle_client, 'localhost', 15555))
loop.run_forever()

有关其他详细信息,请参阅the documentation