Как обнаружить закрытый сокет на asyncio.StreamWriter.write? - PullRequest
1 голос
/ 27 января 2020

Я наконец-то приступил к изучению python asyncio и создаю простой клиент-сервер. Я могу легко использовать open_connection и start_server и заставить все говорить. Однако я столкнулся с некоторым неожиданным поведением, обнаруживающим, когда одна сторона неожиданно закрывает соединение.

На стороне клиента я могу легко обнаружить, что сервер закрыл соединение, потому что любой вызов к StreamReader.read ожидается либо ошибки или ничего не возвращает. Тем не менее, вызов StreamWriter.write / await StreamWriter.drain кажется успешным независимо от того, открыт ли сокет на другой стороне или нет, и это меня смущает.

Клиент (в сопрограмме, вызываемой через run_until_complete:

_, writer = await asyncio.open_connection('127.0.0.1', 1234)
_ = input('Connected.  Type and press enter to attempt send.')

try:
    writer.write(b'A message')
    await writer.drain()
    print('Sent successfully.')

except:
    print(traceback.print_exc())
finally:
    writer.close()
    await writer.wait_closed()

Сервер (синхронная версия, я также пробовал сервер asyn c):

with socket.socket() as s:
    s.bind(('0.0.0.0', 1234))
    s.listen(1)

    con, addr = s.accept()
    with con:
        print(f'Connected to {addr[0]}:{addr[1]}')
        _ = input('Type and press Enter to close.')

Если я запускаю сервер, подключите клиент, затем сервер прервет соединение, затем скажет клиенту написать, я ожидал бы, основываясь на документации и это сообщение StackOverflow для получения исключения, но вместо этого клиент просто печатает Sent successfully. Протестировано с Python 3.7.6 и Python 3.8.1.

Я знаю, что это не будет иметь значения во многих реальных приложениях, так как вы, вероятно, имеете какое-то чтение-l oop так что вы узнаете о проблеме из звонка на StreamReader.read. Как бы там ни было, я не знаю, почему не могу обнаружить сбой на write?

.

1 Ответ

0 голосов
/ 30 января 2020

То, что вы пытаетесь реализовать, несовместимо с работой сокетов TCP. Ваш drain() успешен не из-за асинхронной особенности, а потому, что базовый write() успешен. Запись на уровне ОС выполняется успешно, потому что она буферизируется ядром для эффективности.

Если вы хотите обнаружить, что клиент все еще там, вам нужно будет разработать протокол, который требует, чтобы он отвечал на запись, и обнаружить eof, читая тот ответ. Но даже в этом случае при чтении вам понадобится тайм-аут, поскольку нет гарантии, что отключение клиента всегда будет «чистым» и приведет к сигналу отключения.

...