Python не обнаруживает закрытый сокет, пока вторая передача

Когда я закрываю сокет на одном конце соединения, другой конец получает ошибку во второй раз, когда он отправляет данные, но не в первый раз:

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("localhost", 12345))
server.listen(1)

client = socket.create_connection(("localhost",12345))
sock, addr = server.accept()
sock.close()

client.sendall("Hello World!")    # no error
client.sendall("Goodbye World!")  # error happens here

Я попытался установить TCP_NODELAY, используя send вместо sendall, проверив fileno(), я не могу найти способ получить первую отправку, чтобы выбросить ошибку или даже обнаружить впоследствии, что она не удалась, EDIT: вызов sock.shutdown до sock.close не помогает. РЕДАКТИРОВАТЬ № 2:, даже добавив time.sleep после закрытия и до написания не имеет значения. РЕДАКТИРОВАТЬ № 3: проверка количества байтов, возвращаемого send, не помогает, так как он всегда возвращает количество байтов в сообщении.

Поэтому единственное решение, которое я могу решить, если я хочу обнаружить ошибки, - это следовать за каждым sendall с помощью client.sendall(""), который вызовет ошибку. Но это кажется хакерским. Я на Linux 2.6.x, поэтому, даже если решение работает только для этой ОС, я был бы счастлив.

Ответы

Ответ 1

Ожидается, и как реализуются API-интерфейсы TCP/IP (так что это похоже на почти все языки и во всех операционных системах)

Короче говоря, вы не можете ничего сделать, чтобы гарантировать, что вызов send() возвращает ошибку напрямую, если вызов send() каким-то образом не может доставить данные на другой конец. вызовы отправки/записи просто доставляют данные в стек TCP, и до TCP-стека доставляются, когда это возможно.

TCP также является только транспортным протоколом, если вам нужно знать, достигли ли ваши сообщения "сообщения" на другом конце, вы должны реализовать это самостоятельно (какая-то форма ACK), как часть вашего протокола приложения - там нет другой бесплатный обед.

Однако - если вы читаете() из сокета, вы можете получать уведомление немедленно при возникновении ошибки или когда другой конец закрывает сокет - вам обычно нужно делать это в виде формы цикла мультиплексирования (то есть, используя выбор/опрос или другое средство мультиплексирования ввода-вывода).

Просто помните, что вы не можете читать() из сокета, чтобы узнать, удалось ли выполнить самую последнюю отправку/запись. Вот несколько случаев из-за того, почему (но это случаи, о которых никто не думает об этом, всегда получите вас)

  • несколько вызовов write() забуферированы из-за перегрузки сети или потому, что окно tcp было закрыто (возможно, медленное считывающее устройство), а затем другой конец закрывает сокет или возникает жесткая сетевая ошибка, поэтому вы не можете сказать если бы была последняя запись, которая не прошла, или написать, что вы сделали 30 секунд назад.
  • Сетевая ошибка или брандмауэр тихо отбрасывает ваши пакеты (никакие ответы ICMP не генерируются), вам придется подождать, пока TCP не выйдет из соединения, чтобы получить ошибку, которая может быть много секунд, обычно несколько минут.
  • TCP занят повторной передачей по вызову send - возможно, эти повторные передачи генерируют ошибку. (действительно то же, что и в первом случае)

Ответ 2

В соответствии с docs попробуйте позвонить sock.shutdown() перед вызовом sock.close().