Wie kann ich nicht-blockierenden Socket erhalten connect () 's?
-
05-07-2019 - |
Frage
Ich habe ein ganz einfaches Problem hier. Ich muß mit vielen Hosts gleichzeitig kommunizieren, aber ich brauche eigentlich keine Synchronisation, da jede Anfrage ziemlich autark ist.
Aus diesem Grund entschied ich mich mit asynchronen Sockets zu arbeiten, anstatt Threads Spamming. Jetzt habe ich ein kleines Problem:
Die Asynchron-Sachen wie ein Zauber funktioniert, aber wenn ich zu 100 Hosts zu verbinden, und ich bekomme 100 Timeouts (Timeout = 10 Sekunden), dann warte ich 1000 Sekunden, nur um herauszufinden, alle meine Verbindungen fehlgeschlagen.
Gibt es eine Möglichkeit auch nicht blockierenden Socket Connects zu bekommen? Meine Buchse ist bereits auf nicht-blockierenden, fordert jedoch () zu verbinden blockiert noch.
das Timeout zu reduzieren, ist keine akzeptable Lösung.
ich dies in Python tue, aber ich denke, die Programmiersprache tut in diesem Fall wirklich wichtig ist.
Muss ich wirklich Threads verwenden?
Lösung
Sie müssen die Verbindung herstellt und parallelisieren, da die Steckdosen sperren, wenn Sie ein Timeout. Alternativ könnten Sie nicht ein Timeout gesetzt, und das Auswahlmodul verwenden.
Sie können dies tun, mit der Dispatcher-Klasse in der asyncore Modul . Werfen Sie einen Blick auf die grundlegenden http-Client Beispiel . Mehrere Instanzen dieser Klasse werden sie beim Verbinden nicht blockieren. Sie können dies genauso einfach Threads, und ich denke, macht Tracking-Buchse Timeouts einfacher, aber da Sie bereits asynchrone Methoden können Sie auch auf der gleichen Spur bleiben.
Als Beispiel folgende Arbeiten auf allen meinen Linux-Systemen
import asyncore, socket
class client(asyncore.dispatcher):
def __init__(self, host):
self.host = host
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, 22))
def handle_connect(self):
print 'Connected to', self.host
def handle_close(self):
self.close()
def handle_write(self):
self.send('')
def handle_read(self):
print ' ', self.recv(1024)
clients = []
for i in range(50, 100):
clients.append(client('cluster%d' % i))
asyncore.loop()
Wo in cluster50 - cluster100 gibt es zahlreiche Maschinen, die nicht mehr reagiert, oder gar nicht vorhanden sind. Dieser beginnt sofort Druck:
Connected to cluster50
SSH-2.0-OpenSSH_4.3
Connected to cluster51
SSH-2.0-OpenSSH_4.3
Connected to cluster52
SSH-2.0-OpenSSH_4.3
Connected to cluster60
SSH-2.0-OpenSSH_4.3
Connected to cluster61
SSH-2.0-OpenSSH_4.3
...
Dies dauert jedoch nicht berücksichtigt Getaddrinfo, die zu blockieren hat. Wenn Sie Fragen sind mit den DNS-Abfragen zu lösen, hat alles zu warten. Sie müssen wahrscheinlich die DNS-Abfragen separat auf eigene Faust, sammeln und die IP-Adressen in Ihrem Asynchron-Schleife verwenden,
Wenn Sie einen größeren Toolkit als asyncore wollen, werfen Sie einen Blick auf Twisted-Matrix . Es ist ein bisschen schwer zu bekommen, aber es ist die beste Netzwerk-Programmier-Toolkit Sie für Python bekommen.
Andere Tipps
Mit dem select
Modul. Auf diese Weise können Sie für E / A-Abschluss auf mehrere nicht-blockierende Sockets warten. Hier einige weitere Informationen zu ausgewählten . Von der verlinkten Seite:
In C
select
Codierung ist ziemlich komplex. In Python, es ist ein Stück Kuchen, aber es ist nahe genug an der C-Version dass, wenn Sie verstehen, wählen Sie in Python, werden Sie keine Probleme haben, mit ihm in C.
ready_to_read, ready_to_write, in_error = select.select(
potential_readers,
potential_writers,
potential_errs,
timeout)
Sie übergeben
select
drei Listen: die erste alle Steckdosen enthält, die Sie vielleicht wollen versuchen, zu lesen; die zweit alle die Buchsen möchten Sie vielleicht versuchen, Schreiben auf, und die letzte (normalerweise links leer) diejenigen, die Sie wollen auf Fehler prüfen. Sie sollten beachten, dass eine Buchse kann mehr als einen gehen in Liste. Derselect
Aufruf blockiert, aber Sie können es ein Timeout geben. Das ist im Allgemeinen eine vernünftige Sache zu tun - geben ihm einen schönen langen Timeout (sagen wir ein Minute), es sei denn Sie haben guten Grund zu sonst tun.Im Gegenzug werden Sie drei Listen erhalten. Sie haben die Steckdosen, die tatsächlich lesbar, beschreibbar und in Error. Jede dieser Listen ist eine Teilmenge (Möglicherweise leere) des entsprechenden Liste, die Sie übergeben. Und wenn Sie ein setzen Buchse in mehr als eine Eingabeliste, es wird nur (höchstens) wird in einer Ausgabe Liste.
Wenn eine Buchse ist in der Ausgabe lesbar Liste können Sie sein as-close-to-sicher-als-wir-immer-man-in-diesem-Business dass ein
recv
auf diesem Sockel zurückkehren etwas. Gleiche Idee für die beschreibbaren Liste. Sie werden in der Lage zusend
etwas. Vielleicht nicht alles, was Sie wollen, aber etwas ist besser als nichts. (Eigentlich jeder einigermaßen gesund Buchse wird als beschreibbar zurückkehren - es bedeutet nur, ausgehende Netzwerkpuffer Platz zur Verfügung steht.)Wenn Sie einen „Server“ Buchse haben, setzen Sie es in der potential_readers Liste. Wenn es kommt in der lesbaren Liste verschicken, akzeptieren (fast sicher) Arbeit. Wenn Sie einen neuen Socket erstellt eine Verbindung zu einem anderen Person, steckt es in dem potential_writers Liste. Wenn es zeigt sich in der beschreibbaren Liste, Sie haben ein gute Chance, dass es angeschlossen ist.
Leider gibt es kein Beispiel-Code, der den Fehler zeigt, so ist es ein bisschen schwer zu sehen, wo dieser Block kommt.
Er tut so etwas wie:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(0)
s.connect(("www.nonexistingname.org", 80))
Das Socket-Modul verwendet intern Getaddrinfo, die ein blockierende Betrieb ist, vor allem, wenn der Hostname existiert nicht. Ein Standard-konformes DNS-Client wird noch einige Zeit warten, um zu sehen, ob der Name wirklich existiert nicht, oder wenn es nur einig langsamer DNS-Server beteiligt ist.
Die Lösung ist die Verbindung nur zu-IP-Adressen oder DNS-Client verwenden, die nicht blockierende Anfragen erlaubt, wie pydns .
Verwenden Sie verdrehten .
Es ist ein asynchrones Networking-Engine in Python geschrieben, zahlreiche Protokolle unterstützt, und Sie können Ihre eigenen hinzufügen. Es kann verwendet werden, Clients und Server zu entwickeln. Es blockiert nicht auf verbinden.
Haben schauen Sie auf der asyncore Modul? Könnte sein, was Sie brauchen.