Подтвердить что ты не робот

Когда и почему socket.send() возвращает 0 в python?

python3 сокет программирует howto представляет этот фрагмент кода

class MySocket:
    """demonstration class only
      - coded for clarity, not efficiency
    """

    def __init__(self, sock=None):
        if sock is None:
            self.sock = socket.socket(
                            socket.AF_INET, socket.SOCK_STREAM)
        else:
            self.sock = sock

    def connect(self, host, port):
        self.sock.connect((host, port))

    def mysend(self, msg):
        totalsent = 0
        while totalsent < MSGLEN:
            sent = self.sock.send(msg[totalsent:])
            if sent == 0:
                raise RuntimeError("socket connection broken")
            totalsent = totalsent + sent

    def myreceive(self):
        chunks = []
        bytes_recd = 0
        while bytes_recd < MSGLEN:
            chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048))
            if chunk == b'':
                raise RuntimeError("socket connection broken")
            chunks.append(chunk)
            bytes_recd = bytes_recd + len(chunk)
        return b''.join(chunks)

где цикл отправки прерывается, если метод socket send возвращает 0.

Логика этого фрагмента заключается в том, что когда метод send возвращает "0 отправленных байт", отправляющая сторона соединения сокета должна отказаться от своих усилий по отправке данных. Это верно для метода recv, где нулевые байты, считанные для сокета в режиме блокировки, должны интерпретироваться как EOF и поэтому сторона чтения должна отказаться.

Однако я не могу понять, в каких ситуациях метод send мог возвращать ноль. Мое понимание сокетов python заключается в том, что send немедленно возвращается из-за буферизации на уровне ОС. Если буфер заполнен, send будет заблокирован, или если соединения будут закрыты на удаленной стороне, будет создано исключение.

Наконец, предположим, что send возвращает ноль без повышения исключения: действительно ли это означает, что все будущие вызовы send будут возвращать ноль?

Я провел некоторое тестирование (хотя использовал только сокет, подключенный к ::1 в OS X) и не смог найти ситуацию, в которой send возвращает 0.

Edit

В HOWTO указано:

Но если вы планируете повторно использовать ваш сокет для дальнейших передач, вам нужно чтобы понять, что на сокете нет EOT. Повторяю: если сокет send или recv возвращает после обработки 0 байтов, соединение было сломана. Если соединение не было сломано, вы можете подождать на recv навсегда, потому что сокет не скажет вам, что ничего нет больше читать (пока).

Очень легко найти ситуацию, в которой recv возвращает 0: когда удаленная (посылающая) сторона вызывает socket.shutdown(SHUT_WR), далее recv на принимающей стороне вернет 0 и не приведет к возникновению каких-либо исключений.

Я ищу конкретный пример, где вы можете показать, что прием 0 нуля из send указывает на сломанное соединение (которое будет продолжать возвращать 0 при отправке.)

4b9b3361

Ответ 1

Увидев вопрос, я был как-то ошеломлен, потому что вызов send C может возвращать 0 байтов, и соединение, конечно, все еще живое (сокет не может просто отправить больше байтов в данный данный момент времени)

Я решил "использовать источник", и если я не ошибаюсь (что всегда бывает и часто бывает), это ошибка в HOWTO.

Сеть:

  • send является псевдонимом для sock_send
  • sock_send вызывает по очереди sock_call
  • sock_call вызывает по очереди sock_call_ex
  • sock_call вызывает по очереди sock_send_impl (который был передан по цепочке, начинающейся с sock_send)

разматывать:

  • sock_send_impl возвращает true или false (1 или 0) с помощью return (ctx->result >= 0)

  • sock_call_ex возвращает

    • -1, если sock_send_impl возвращает false
    • 0, если sock_send_impl возвращает true
  • sock_call прозрачно передает это значение.

  • sock_send

    • возвращает NULL для a -1 (потому что была установлена ​​ошибка и возникает исключение)

    • возвращает ctx->result для 0 из sock_call

      И ctx->result - это количество байтов, написанных вызовом C send в sock_send_impl.

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

Если моя логика неправильная, кто-то, пожалуйста, дайте мне знать.

Ответ 2

Возможно, я ошибаюсь, но я думаю, что вы ищете невозможную ситуацию...

Как пояснил @mementum в своем ответе, теоретически возможно, что сокет будет возвращать ноль, если нет ошибки, но также не отправлено данных.

Однако, как показано в другом месте на SO, это может произойти только в очень специфических сценариях. По моему опыту (а также в комментариях к принятому ответу), вы только когда-либо получите нулевой результат в неблокирующем сокете, когда сеть перегружена. Теперь сокеты Python блокируются по умолчанию, а это значит, что ядро ​​должно подождать, пока не будет места, чтобы взять еще несколько данных, а затем вернуть количество байтов в очереди. По определению это никогда не может быть нулевым.

Итак, все вместе, так как ваш фрагмент не reset тип сокета - например. используя функцию set_blocking - она ​​использует блокирующие сокеты и поэтому не может возвращать нуль и, следовательно, не может ударить по указанному пути.

Это подтверждается тем фактом, что вы не смогли запустить определенную строку кода независимо от того, что вы делаете.