【问题标题】:Asyncios await reader.read() is waiting foreverAsyncios await reader.read() 一直在等待
【发布时间】:2020-12-11 10:43:15
【问题描述】:

我有一个这样写的小型服务器:

async def handle_client(reader, writer):
    request = (await reader.read()).decode('utf8') # should read until end of msg
    print(request)
    response = "thx"
    writer.write(response.encode('utf8'))
    await writer.drain()
    writer.close()


loop = asyncio.get_event_loop()
loop.create_task(asyncio.start_server(handle_client, socket.gethostname(), 8886))
loop.run_forever()

还有一个这样写的小客户:

async def tcp_echo_client(message):
    reader, writer = await asyncio.open_connection(
        my_ip, 8886)

    print(f'Send: {message!r}')
    writer.write(message.encode())
    await writer.drain()
    data = await reader.read() # should read until end of msg
    print(f'Received: {data.decode()!r}')
    print('Close the connection')
    writer.close()
    await writer.wait_closed()

asyncio.run(tcp_echo_client(f"Hello World!"))

客户端和服务器开始通信,但从未完成。 为什么阅读器无法识别味精结束?

如果我写

request = (await reader.read(1024)).decode('utf8')

相反,它可以工作,但我需要接收未定义的大量数据。

我尝试像这样修改服务器的代码:

    while True:
        request = (await reader.read(1024)).decode('utf8')
        if not request:
            break

它接收所有数据块,但在最后一个块之后仍然等待。为什么? 我如何从服务器告诉读者停止监听并继续在代码中发送答案?

【问题讨论】:

    标签: python-3.x sockets python-asyncio


    【解决方案1】:

    TCP 连接是基于流的,这意味着当您将“消息”写入套接字时,字节将被发送到对等方,而不包括消息之间的分隔符。连接另一端的对等方可以检索字节,但它需要自己弄清楚如何将它们切成“消息”。这就是读取最后一个块似乎挂起的原因:read() 只是等待对等方发送更多数据。

    为了能够检索单个消息,发件人必须对每条消息进行框架或分隔。例如,发送方可以在发送消息后关闭连接,这将允许另一方读取消息,因为它后面会跟随文件结束指示符。但是,这将允许发件人仅发送一条消息而无法读取响应,因为套接字将被关闭。

    更好的选择是编写者只关闭套接字的写入端,这种部分关闭有时被称为shutdown)。在 asyncio 中,这是通过调用 write_eof 来完成的:

    writer.write(message.encode())
    await writer.drain()
    writer.write_eof()
    

    这样发送,消息后面会跟着文件尾,服务器端的读取不会挂起。虽然客户端将能够读取响应,但仍将仅限于发送一条消息,因为在写入端已关闭的套接字上将无法进行进一步的写入。

    要实现由任意数量的请求和响应组成的通信,您需要对每条消息进行框架化。一个简单的方法是在每条消息前加上消息长度:

    writer.write(struct.pack('<L', len(request)))
    writer.write(request)
    

    接收方先读取消息大小,然后读取消息本身:

    size, = struct.unpack('<L', await reader.readexactly(4))
    request = await reader.readexactly(size)
    

    【讨论】:

    • 非常感谢!! writer.write_eof() 完成了这项工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多