Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When and why socket.send() returns 0 in python?

The python3 socket programming howto presents this code snippet

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)

where the send loop is interrupted if the socket send method returns 0.

The logic behind this snippet is that when the send method returns '0 bytes sent', the sending side of a socket connection should give up its efforts to send data. This is for sure true for the recv method, where zero bytes read for a socket in blocking mode should be interpreted as EOF, and therefore the reading side should give up.

However I cannot understand under which situations the send method could return zero. My understanding of python sockets is that send returns immediately due to buffering at the OS level. If the buffer is full send will block, or if the connections is closed at the remote side, an exception is raised.

Finally suppose send returns zero without raising an exception: does this really indicate that all future send calls will return zero?

I've done some testing (although using only socket connected to ::1 on OS X) and was not able to find a situation in which send returns 0.

Edit

The HOWTO states:

But if you plan to reuse your socket for further transfers, you need to realize that there is no EOT on a socket. I repeat: if a socket send or recv returns after handling 0 bytes, the connection has been broken. If the connection has not been broken, you may wait on a recv forever, because the socket will not tell you that there’s nothing more to read (for now).

It is pretty easy to find a situation in which recv returns 0: when the remote (sending) side calls socket.shutdown(SHUT_WR), further recv on the receiving side will return 0 and not raise any exception.

I'm looking for a concrete example where you can show that receiving 0 zero from send indicates a broken connection (which will continue to return 0 on send.)

like image 961
Stefano M Avatar asked Jan 21 '16 09:01

Stefano M


1 Answers

Upon seeing the question I was somehow stunned, because a send C call can return 0 bytes and the connection is of course still alive (the socket cannot simply send more bytes at that given moment in time)

  • https://github.com/python/cpython/blob/master/Modules/socketmodule.c

I decided to "use the source" and unless I am very wrong (which can always be and often is) this is a bug in the HOWTO.

Chain:

  • send is an alias for sock_send
  • sock_send calls in turn sock_call
  • sock_call calls in turn sock_call_ex
  • sock_call calls in turn sock_send_impl (which has been passed down the chain starting with sock_send)

Unwinding:

  • sock_send_impl returns true or false (1 or 0) with return (ctx->result >= 0)

  • sock_call_ex returns

    • -1 if sock_send_impl returns false
    • 0 if sock_send_impl returns true
  • sock_call returns this value transparently.

  • sock_send

    • returns NULL for a -1 (because an error has been set and an exception will be raised)

    • returns ctx->result for 0from sock_call

      And ctx->result is the number of bytes written by the C call send in sock_send_impl.

The chain shows that if 0 bytes have been sent, there is no error and this actually is a potential real life socket situation.

If my logic is wrong, someone please let me know.

like image 106
mementum Avatar answered Sep 19 '22 14:09

mementum