Is there a way to shorten connect_ex()'s timeout?

Viewed 859

I'm trying to troubleshoot connection issues across a network, trying to see why some users can see a box right as it's powered on and others can take nearly 20 seconds to see it. It's causing some fatal issues in our local programs.

To that end, I ran up a simple minimum verifiable python script to try and figure out what's up:

import socket
import time
import errno

HOST = '192.168.1.1'
PORT = 12345

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)

while(True):
   err = s.connect_ex((HOST, PORT))
   if err == 0:
      print("CONNECTED")
   else:
      print("NO CONNECTION: {0}".format(errno.errorcode[err]))
   time.sleep(2)

When I run this code, I see a consistent pattern: EAGAIN, followed by 10 instances of EALREADY. When it DOES connect, 'CONNECTED' replaces the EAGAIN bit of the cycle.

I thought that setting the timeout to 1 second, and waiting for 2 seconds, should let me abort the connect call in the background faster, but it seems to have no effect whatsoever. Is there a TCP option I can set, or Windows setting I can tweak, to ensure that I don't have these awkward 20 second blocks while I'm polling the remote box for connectivity?

1 Answers

By setting a timeout on the socket, socket.connect_ex() becomes a non-blocking operation. That is why you are getting EAGAIN and EALREADY errors when calling connect_ex() repeatedly in a loop - EAGAIN on the first call indicates the connection cannot be completed immediately and is in progress in the background, and then EALREADY on subsequent calls indicates that an existing connection is still in progress and has not completed yet.

Your loop simply runs until the connection finally ends with success or failure, no matter how long that takes. The socket timeout you are setting does not play into that logic.

To do what you are asking for, run your own timer in parallel to the connection loop, and then close the socket if the timer elapses before socket.connect_ex() returns its final result.

Alternatively, get rid of the loop altogether.

Call socket.connect_ex() one time, and if it returns EAGAIN then use select.select() to wait for the connection to complete. It has a timeout parameter. A TCP socket will enter a writable state if a pending connection attempt successfully connects to the server. If select() times out, or reports an error, close the socket instead.

Or, try using socket.create_connection(), which also has a timeout parameter.

Related