Почему Python socket.recv () не возвращает -1 - PullRequest
0 голосов
/ 10 ноября 2019

Если я закрываю сокет сервера в цикле s.recv_into() от клиента, я продолжаю получать 0 из recv_into() бесконечно.

Как я понимаю, единственный способ узнать, что сокет закрыт, был не через сам механизм сокетов Python, а с использованием select / poll / и т. Д.

Что рационально не возвращать -1когда сокет закрыт другой стороной? Почему я не могу правильно отличить «в настоящее время нет данных» от «больше никого нет»?

РЕДАКТИРОВАТЬ:

Возможно, правильный вопрос - почему не исключениевыбрасывается при использовании recv(), если другая сторона прервала соединение?

Ответы [ 2 ]

1 голос
/ 10 ноября 2019

Если я закрываю сокет сервера в цикле s.recv_into() от клиента, я продолжаю получать 0 из recv_into() бесконечно.

Да, как и ожидалось. Закрытое соединение обозначается 0, а не -1. ​​

Как я понимаю, единственный способ узнать, что сокет закрыт, был не через сам механизм сокетов Python, а с помощью select / poll / etc.

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

Однако это не означает, что одноранговый узел все еще жив (по умолчанию таймаут TCP составляет 15 минут AFAIK). Это не может быть надежно обнаружено. Лучшее, что вы можете сделать, это реализовать какой-либо протокол проверки связи по проводам.

Какой смысл не возвращать -1, когда сокет закрыт другой стороной?

Для системных вызовов (например, recv) код возврата -1 (как правило) зарезервирован для ошибок. Однако Python переводит их в исключения (и поэтому AFAIK вы никогда не получите -1 в Python).

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

1 голос
/ 10 ноября 2019

recv_into - это просто оболочка системного вызова C по имени recv. Возвращаемое значение 0 не является ошибочным условием!

Из руководств Linux recv(2):

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Эти вызовы возвращают количество полученных байтов или -1, если произошла ошибка. В случае ошибки errno устанавливается для указания ошибки.

Когда одноранговый узел потокового сокета выполнил упорядоченное завершение, возвращаемое значение будет 0 (традиционное возвращение конца файла).

Сокеты дейтаграмм в различных доменах (например, в доменах UNIX и Internet) допускают дейтаграммы нулевой длины. Когда такая дейтаграмма получена, возвращаемое значение равно 0.

Значение 0 также может быть возвращено, если запрошенное число байтов для получения из сокета потока было 0.

В случае ошибок Python не вернет -1, вместо этого он вызовет соответствующее исключение.

Для потоковых сокетов вы должны прерывать цикл всякий раз, когда recv_* возвращает значение, которое указывает, что было получено 0 байтов. Нет смысла проверять -1, но если вы хотите быть более уверенным, вы можете естественным образом использовать while sock.recv_into(....) > 0: или аналогичный.

Различные вызовы read в Python работают аналогичным образом.

...