Search code examples
pythonsocketsnetwork-programmingnonblocking

How can I determine if a non-blocking socket is really connected?


This question is not limited to Python. Its a general socket question. I have a non-blocking socket and want to connect to a machine which is reachable - on the other side the port does not exist. Why does select(...) succeed anyway? I expected a timeout. sock.send(...) fails with a broken pipe. How can I determine if the socket is really connected after select(...)? Thanks in advance.

import socket, errno, os, time, select

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(0)
err = sock.connect_ex(('192.168.178.21', 12345))
ready_to_read, ready_to_write, in_error = select.select([], [sock], [], timeout=5)
#ready_to_write is set even 192.168.178.21:12345 does not exist.

sock.setblocking(1)
sock.send('foo') #this fails
sock.close()

Solution

  • Try checking the return values. Here's what I get in a similar situation:

    >>> import socket, errno, os, time, select
    >>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    >>> sock.setblocking(0)
    >>> err = sock.connect_ex(('10.0.0.1', 12345))
    >>> import errno
    >>> print errno.errorcode[err]
    EINPROGRESS
    >>> print sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
    61
    >>> print errno.errorcode[61]
    ECONNREFUSED
    

    The select() call is probably returning because there was an exceptional condition on the socket - that is, its connection was refused.

    If I do this, the select() returns immediately:

    >>> ready_to_read, ready_to_write, in_error = select.select([], [sock], [])
    

    And interestingly enough, the return values match exactly what you passed in (if you had more than one socket, that would likely be different):

    >>> print in_error
    []
    >>> print ready_to_read
    []
    >>> print ready_to_write
    [<socket._socketobject object at 0x10ccd0980>]
    

    The designers of select() are expecting you to run select() in a loop, and if a socket returns an error when you attempt the write, remove it from the list when the write fails.

    So you have a couple of options, off the top of my head:

    • Wait until you try to read the socket and fail, then take the appropriate action.
    • Use getsockopt() as I did above to check if the socket is OK, or is in an error state, before attempting to do anything with it.