Я не понимаю, почему 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 будет исправлена, сервер будет работать правильно.