12

Windows sockets have some strange behavior when it comes to WSAECONNREFUSED (which means backlog full or port not available, see https://stackoverflow.com/a/10308338/851737). If Windows detects one of these conditions, it retries (up to) two times with an interval of 0.5s. This means it takes at least 1 second to detect WSAECONNREFUSED on a socket connection attempt (http://support.microsoft.com/kb/175523/en-us).

Is there any way to speed up this detection without messing with the registry values? I need to simulate refusing socket connections in unittests. A workaround like simulating a refused connection with raw sockets would be acceptable, too.

Here is a simple Python script demonstrating the issue:

import errno
import socket
import time

PORT = 50123


def main():
    s = socket.socket()
    s.bind(('127.0.0.1', PORT))
    s.listen(0)
    client = socket.socket()
    client.connect(('127.0.0.1', PORT))

    client2 = socket.socket()
    start = time.time()

    try:
        client2.connect(('127.0.0.1', PORT))
    except socket.error as e:
        assert e.errno == errno.WSAECONNREFUSED
        print 'connection attempt took', time.time() - start
    finally:
        client2.close()
        client.close()
        s.close()


if __name__ == '__main__':
    main()
Community
  • 1
  • 1
schlamar
  • 9,238
  • 3
  • 38
  • 76

2 Answers2

3

It's not exactly what you asked about. But if you need this in the unittests only, mock library would be useful.

import errno
import socket
import time
import mock

PORT = 50123


def connect_mock(*agrs):
    raise socket.error(errno.WSAECONNREFUSED, "Testing")


def main():
    s = socket.socket()
    s.bind(('127.0.0.1', PORT))
    s.listen(0)
    client = socket.socket()
    client.connect(('127.0.0.1', PORT))

    client2 = socket.socket()
    start = time.time()

    with mock.patch('socket.socket.connect', connect_mock):
        try:
            client2.connect(('127.0.0.1', PORT))
            print "done"
        except socket.error as e:
            assert e.errno == errno.WSAECONNREFUSED
            print 'connection attempt took', time.time() - start
        finally:
            client2.close()
            client.close()
            s.close()


if __name__ == '__main__':
    main()
Dmitry Vakhrushev
  • 1,382
  • 8
  • 12
  • Actually, I'm testing a reconnection routine, so the second call to connect has to succeed (because I start the server after I noticed that the first connection attempt failed). But this should be possible with a more intelligent version of `connect_mock`. I'll test this and report back my progress. Thanks so far for the inspiration. – schlamar Jan 22 '14 at 14:19
  • Ah this works perfectly in my case :) You definitely earned the bounty but I'm keeping this open a few more days because there might be a more general solution. – schlamar Jan 22 '14 at 14:42
2

Here is my solution based on dmitry-vakhrushev's answer which is patching the connect method more intelligent:

if sys.platform == 'win32':
    n_calls = [0]
    org_connect = socket.socket.connect

    def refusing_connect(*args):
        if n_calls[0] < 2:
            n_calls[0] += 1
            raise socket.error(errno.WSAECONNREFUSED, "Testing")
        return org_connect(*args)

    # patch socket.connect to speed up WSAECONNREFUSED detection
    patcher = mock.patch('socket.socket.connect', refusing_connect)
    patcher.start()
    self.addCleanup(patcher.stop)
schlamar
  • 9,238
  • 3
  • 38
  • 76