Это похоже на ошибку закрытия записи, подробно описанную в этой статье .
Короче говоря, writer.close
не является сопрограммой, поэтому вы не можете ждать закрытия, чтобы фактически сброситьданные из буфера asyncio в ОС.Ожидание writer.drain()
до close()
не помогает, поскольку оно делает паузу только до тех пор, пока фоновые записи не уменьшат размер буфера до «низкого водяного знака», а не - как можно было ожидать - до тех пор, пока буфер не будет очищен.
ОБНОВЛЕНИЕ : Начиная с Python 3.7, выпущенного в июне 2018 года, простое исправление должно ждать writer.wait_closed()
в конце tcp_echo_writer
.
В то время, когда ответ был изначально написан, единственное доступное исправление состояло в том, чтобы скопировать реализацию asyncio.open_connection
(не так плохо, как кажется, поскольку это короткая удобная функция) и добавить вызов к transport.set_write_buffer_limits(0)
.Это заставит await writer.drain()
фактически ждать, пока все данные будут записаны в ОС (что, по мнению упомянутой статьи, в любом случае будет правильным решением):
@asyncio.coroutine
def fixed_open_connection(host=None, port=None, *,
loop=None, limit=65536, **kwds):
if loop is None:
loop = asyncio.get_event_loop()
reader = asyncio.StreamReader(limit=limit, loop=loop)
protocol = asyncio.StreamReaderProtocol(reader, loop=loop)
transport, _ = yield from loop.create_connection(
lambda: protocol, host, port, **kwds)
###### Following line added to fix buffering issues:
transport.set_write_buffer_limits(0)
######
writer = asyncio.StreamWriter(transport, protocol, reader, loop)
return reader, writer
Странно, что такаяБаг нас скрывать в основной библиотеке asyncio.
Я подозреваю, что большинство людей не видят эту ошибку, потому что они поддерживают цикл обработки событий в течение более длительного времени, делая другие вещи, поэтому после writer.close()
данные в конечном итоге записываются и сокет закрывается.