Как я могу рефакторинг для приема нескольких клиентов? - PullRequest
0 голосов
/ 05 июня 2018

Я не понимаю, почему server.py Version 1 позволяет прерывать и перезапускать клиент, а server.py Version 2 этого не делает:

server.py Version1:

import asyncio

async def handle_client(reader, writer):
    while True:
        request = (await reader.read(128)).decode()
        writer.write('Received ok.'.encode())
        await writer.drain()

async def main():
    loop.create_task(asyncio.start_server(handle_client, 'localhost', 15555))

loop = asyncio.new_event_loop()
loop.create_task(main())
loop.run_forever()

server.py Версия 2:

import asyncio

async def handle_client(reader, writer):
    while True:
        request = (await reader.read(128)).decode()
        if request == "hello":
            writer.write('Received ok.'.encode())
            await writer.drain()

async def main():
    loop.create_task(asyncio.start_server(handle_client, 'localhost', 15555))

loop = asyncio.new_event_loop()
loop.create_task(main())
loop.run_forever()

client.py:

import asyncio

async def make_connections():
    reader, writer = await asyncio.open_connection('localhost', 15555, loop=loop)
    loop.create_task(connect(reader, writer))


async def connect(reader, writer):
    writer.write("hello".encode())
    await writer.drain()
    result = await reader.read(128)
    print(result.decode())


loop = asyncio.new_event_loop()
loop.create_task(make_connections())
loop.run_forever()

Версия 2 отлично работает для одного клиента, но если я отправлю прерывание клавиатуры наклиент я больше не могу подключиться после перезапуска клиента.Раздражает ssh в и убивать / перезапускать сервер каждый раз, когда я изменяю код в клиенте.Я не понимаю, почему вторая версия не принимает клиента во второй раз, когда он пытается подключиться.

1 Ответ

0 голосов
/ 06 июня 2018

Я не понимаю, почему server.py Версия 1 позволяет клиенту прерываться и перезагружаться с клавиатуры, в то время как server.py Версия 2 не

Обе версии имеютошибка в том, что они неправильно проверяют состояние конца файла.Когда вы прерываете клиента, сокет закрывается, и чтение из него возвращает EOF, в то время как запись в него вызывает исключение.Ожидание writer.drain() в версии 1 доставляет исключение и прерывает сопрограмму.(Это исключение, вероятно, отображается при стандартной ошибке сервера.)

Версия 2 имеет проблему, хотя: тест if request == "hello" ложен при EOF, потому что reader.read() продолжает возвращать пустую строку байтов, чтобы пометить EOFсостояние.Это препятствует выполнению и доставке await writer.drain() исключения, поэтому сопрограмма остается в бесконечном цикле.Простое исправление - добавить что-то вроде if not request: break после read.

Почему версия 2 застревает

Но приведенное выше не полностью объясняет, почему в Версии 2 весь серверсломанные и новые клиенты не могут подключиться.Конечно, можно ожидать, что await либо вернет результат, либо передаст управление другим сопрограммам.Но наблюдаемое поведение заключается в том, что, несмотря на то, что в цикле while содержится await, сопрограмма не позволяет другим сопрограммам запускаться!

Проблема в том, что await не означает "пропуск"контроль за циклом событий ", как это часто понимают.Это означает «запросить значение из предоставленного ожидаемого объекта, передавая управление циклу событий , если объект указывает, что у него нет готового значения».Часть после if имеет решающее значение: если у объекта действительно есть готовое значение, это значение будет использовано немедленно, не откладываясь на цикл обработки событий.Другими словами, await не гарантирует, что цикл событий получит шанс на запуск.

Поток в EOF всегда имеет данные для возврата - пустая строка, которая отмечаетEOF.В результате он никогда не приостанавливается, и цикл заканчивается тем, что полностью блокирует цикл обработки событий.Чтобы гарантировать возможность выполнения других задач, вы можете добавить await asyncio.sleep(0) в цикле - но это не должно быть необходимо в правильно написанном коде, где запрос данных ввода-вывода скоро приведет к ожиданию, после чего цикл события будетудар. Как только ошибка обработки EOF будет исправлена, сервер будет работать правильно.

...