2014-01-16 9 views
11

Windows-Sockets haben ein seltsames Verhalten, wenn es um WSAECONNREFUSED geht (was bedeutet, dass Backlog voll oder Port nicht verfügbar ist, siehe https://stackoverflow.com/a/10308338/851737). Wenn Windows eine dieser Bedingungen erkennt, wiederholt es (bis zu) zwei Mal mit einem Intervall von 0,5 Sekunden. Dies bedeutet, dass WSAECONNREFUSED bei einem Socket-Verbindungsversuch (http://support.microsoft.com/kb/175523/en-us) mindestens 1 Sekunde benötigt wird.Schnelle Erkennung oder Simulation von WSAECONNREFUSED

Gibt es eine Möglichkeit, diese Erkennung zu beschleunigen, ohne die Registrierungswerte zu verfälschen? Ich muss simulieren, Socket-Verbindungen in Unittests zu verweigern. Ein Workaround wie das Simulieren einer abgewiesenen Verbindung mit rohen Sockets wäre ebenfalls akzeptabel.

Hier ist ein einfacher Python-Skript, das Problem demonstriert:

import errno 
import socket 
import time 

PORT = 5


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() 

Antwort

3

Es ist nicht genau das, was Sie gefragt. Aber wenn Sie dies nur in Unitests benötigen, wäre mock Bibliothek nützlich.

import errno 
import socket 
import time 
import mock 

PORT = 5


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() 
+0

Eigentlich habe ich eine erneute Verbindung Routine bin Tests, so hat der zweite Anruf zu verbinden erfolgreich (weil ich den Server starte, nachdem ich festgestellt habe, dass der erste Verbindungsversuch fehlgeschlagen ist). Dies sollte jedoch mit einer intelligenteren Version von 'connect_mock' möglich sein. Ich werde das testen und meine Fortschritte melden. Danke bis jetzt für die Inspiration. – schlamar

+0

Ah das funktioniert perfekt in meinem Fall :) Sie haben definitiv das Kopfgeld verdient, aber ich halte das noch ein paar Tage offen, weil es eine allgemeinere Lösung geben könnte. – schlamar

2

Hier ist meine Lösung auf Basis von dmitry-Vakhrushev des answer, die die Methode connect intelligenter ist das Patchen:

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)