为什么这个 Python 3 asyncio 客户端仅在 Python 进程中断后才发送字节?

Why is this Python 3 asyncio client sending the bytes only after the Python process is interrupted?

为什么我的客户端在代码行处超时:

response = yield from asyncio.wait_for(reader.read(), timeout=3.0)

而不是收到消息?

服务器部分仅在客户端超时后注册它从该客户端收到消息,我实际上发出 ctrl + d 以终止 Python 解释器


client.py

@asyncio.coroutine
def test_connection(host, port):
    # Get the streams for the socket
    print('Starting client connection')
    reader, writer = yield from asyncio.open_connection(host, port)

    message = 'Test Message'.encode('ascii')

    # Start communication
    print('Sending message to {}:{} - {}'.format(host, port, message))
    writer.write(message)

    print('Waiting 3 sec for response...')
    response = yield from asyncio.wait_for(reader.read(), timeout=3.0)
    print('Got response: {}'.format(response.decode('ascii')))

    writer.close()


def run():
    loop = asyncio.get_event_loop()
    task = asyncio.async(test_connection())
    loop.run_until_complete(task)
    loop.close()

问题很可能是您在服务器端(以及客户端)调用 reader.read(),这将阻塞直到从服务器发送 EOF。但大概你并没有这样做 - 你只是发送了一些字节并保持连接打开。

相反,您需要调用 readline() 并确保将 b'\n' 附加到您的混乱负载中,调用 read 一些大小来限制您等待的字节数,或者在编写消息负载后调用 writer.write_eof(),假设您不打算再使用 writer。使用 readline()write_eof 可能是最安全的选择。这是一个完整的示例,演示了如何使用 readline():

client.py

import asyncio

@asyncio.coroutine
def test_connection(host, port):
    # Get the streams for the socket
    print('Starting client connection')
    reader, writer = yield from asyncio.open_connection(host, port)

    message = 'Test Message\n'.encode('ascii')

    # Start communication
    print('Sending message to {}:{} - {}'.format(host, port, message))
    writer.write(message)

    print('Waiting 3 sec for response...')
    response = yield from asyncio.wait_for(reader.readline(), timeout=5.0)
    print('Got response: {}'.format(response.decode('ascii')))

    writer.close()


def run():
    loop = asyncio.get_event_loop()
    task = asyncio.async(test_connection('localhost', 5000))
    loop.run_until_complete(task)
    loop.close()

run()

server.py

import asyncio

@asyncio.coroutine
def got_connection(reader, writer):
    msg = yield from reader.readline()
    message = 'another Test Message\n'.encode('ascii')
    print('Sending message to {}:{} - {}'.format('localhost', 5000, message))
    writer.write(message)


def run():
    loop = asyncio.get_event_loop()
    server = loop.run_until_complete(asyncio.start_server(
                                        got_connection, 'localhost', 5000))
    loop.run_until_complete(server.wait_closed())
    loop.close()

run()

下面是使用 write_eof() 的更改:

client.py

@asyncio.coroutine
def test_connection(host, port):
    # Get the streams for the socket
    print('Starting client connection')
    reader, writer = yield from asyncio.open_connection(host, port)

    message = 'Test Message'.encode('ascii')

    # Start communication
    print('Sending message to {}:{} - {}'.format(host, port, message))
    writer.write(message)
    writer.write_eof()

    print('Waiting 3 sec for response...')
    response = yield from asyncio.wait_for(reader.read(), timeout=5.0)
    print('Got response: {}'.format(response.decode('ascii')))

    writer.close()

server.py

@asyncio.coroutine
def got_connection(reader, writer):
    msg = yield from reader.read()
    message = 'another Test Message'.encode('ascii')
    print('Sending message to {}:{} - {}'.format('localhost', 5000, message))
    writer.write(message)
    writer.write_eof()