pythonsocketsnonblockingrecv

Why does this function block?


I have some code that is reading data (if there is any) from a non-blocking socket and printing it, but somehow when this happens (data is received & printed) the function then blocks and does not return unless I kill the sending process (which also uses a non-blocking stream to send the data).

Obviously I have done something wrong but for the life of me I can't see it - the socket is non-blocking and I think the continue statements are somewhat redundant. Python isn't my main language so I'm assuming I've made a daft mistake in this somehow.

This is the socket setup which accepts connections & appends them to the list - I am assuming the non-blocking nature of the socket is retained on accept():

# Open socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setblocking(False) # Make port non-blocking
s.bind((HOST, PORT))
s.listen(MAX_CONN)
#print("Listening on port %d" % PORT)

connection_list = []
connection_list.append(s)

while True:
    # Get the list sockets which are ready to be read through select
    readable, writable, errored = select.select(connection_list, [], [], 0) # 0 = No timeout / do not block

    for sock in readable:
        # New connection
        if sock == s:
            # New connection received through server_socket
            sockfd, addr = s.accept()
            connection_list.append(sockfd)
            print("Client (%s, %s) connected" % addr)

This is the receiver-side Python code that I believe is at fault, it's polled from a main loop:

    def handle_replies():
        global connection_list
        for sock in connection_list:
            if sock != s: # Ignore our open listening socket
                try:
                    data = sock.recv(1024)
                except socket.error as e:
                    err = e.args[0]
                    if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
                        print("Would block") # Socket has no data for us
                    else:
                        # a "real" error occurred
                        print("Error: %s" % e)
                    continue # To next sock in list
                else:
                    if data:
                        print("<%s> sent %s" % (str(sock.getpeername()), data))
                    continue

If I send some data to the socket from another process, I get (for example): <('127.0.0.1', 33196)> sent b'[HELLO:STATUS]' but then it just stops until I kill the sending process.

This is the [relevant parts of] sending-side code (GTK3 C code) which I think is correct and doesn't block or hang at the send call:

        GSocket *sock = g_socket_connection_get_socket(connection);
        g_socket_set_blocking( sock, FALSE);

        /* use the connection */
        GInputStream * istream = g_io_stream_get_input_stream (G_IO_STREAM (connection));
        GOutputStream * ostream = g_io_stream_get_output_stream (G_IO_STREAM (connection));

        bytes = sprintf(buffer, "[HELLO:STATUS]");
        g_output_stream_write (ostream, buffer, (gsize)bytes, NULL, NULL);

So, what have I missed or done wrong in all this?


Solution

  • Well @Max's comment led me straight to it:

        for sock in readable:
            # New connection
            if sock == s:
                # New connection received through server_socket
                sockfd, addr = s.accept()
                sockfd.setblocking(False) # This isn't inherited - set it again!
                connection_list.append(sockfd)
                print("Client (%s, %s) connected" % addr)
    

    The non-blocking state of the port isn't inherited on accept(), adding sockfd.setblocking(False) fixed it.