2012-12-07 4 views
6
open_sockets = [] 

listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

listening_socket.bind(("", 1234)) 

listening_socket.listen(5) 

while True: 
    rlist, wlist, xlist = select.select([listening_socket] + open_sockets, [], []) 
    for i in rlist: 
     if i is listening_socket: 
      new_socket, addr = listening_socket.accept() 
      open_sockets.append(new_socket) 
     else: 
      data = i.recv(1024) 
      if data == "": 
       i.close() 
       open_sockets.remove(i) 
       print "Connection closed" 
      else: 
       i.send(data) 
       print repr(data) 

Jetzt weiß ich, das ist einfache Server-Code, ein paar Kunden umgehen kann - das einzige, was ich nicht verstehe, sind diese beiden Linien:recv() in Python

 data = i.recv(1024) 
     if data == "": 

Ich verstehe, dass Wenn der Client bereits akzeptiert hat, wird die andere Option aufgerufen, die Option, die überprüft, ob sich etwas im Puffer befindet. Ich verstand nicht, warum, wenn es nichts in dem Puffer ist, es geht weiter und überprüft nicht die Zeile:

if data == "": 

aber, wenn der Client drückt geben Sie einfach die zu "" entspricht es trennt

Warum, wenn nichts gedrückt wird, ist es nicht dasselbe wie ""?

+0

Be sicher, eine Antwort zu akzeptieren, wenn einige der zur Verfügung gestellten Antworten Ihre Frage beantwortet haben. Es ist eine nette Art, den Leuten zu danken, die zu dieser Seite beitragen. Wenn Ihre Frage nicht beantwortet wurde, können Sie jederzeit einen Kommentar abgeben. Vielen Dank! – GargantuChet

Antwort

6

Es beginnt mit dem select Aufruf. Diese Funktion überwacht die Sockets und wartet auf etwas Bemerkenswertes. Bei Sockets in der ersten Liste bedeutet "bemerkenswert", dass der Socket Daten zum Lesen zur Verfügung hat.

rlist, wlist, xlist = select.select([listening_socket] + open_sockets, [], []) 

Der Code jetzt iteriert durch die Liste der Steckdosen mit Daten zum Lesen bereit und wirkt auf die Art der Socket-Basis gehandhabt wird.

Verbindungsorientierte ("Listening") Sockets werden verwendet, um neue Verbindungen zu akzeptieren. Da es in rlist ist, wissen wir, dass es etwas für uns zum "Lesen" hat. Im Zusammenhang mit einem Abhören von Sockets bedeutet dies, dass eine neue Verbindung empfangen wurde. So akzeptieren wir die Verbindung und speichern den neuen Socket in der Liste open_sockets.

  new_socket, addr = listening_socket.accept() 
      open_sockets.append(new_socket) 

Wenn die Steckdose nicht listening_socket ist, dann ist es eine Buchse, welche (oder wurde) zu einem entfernten Client verbunden ist. Und wieder, da es in rlist ist, wissen wir, dass es etwas für uns zum "Lesen" hat. Im Zusammenhang mit Connected Sockets bedeutet dies entweder, dass Daten tatsächlich zum Lesen verfügbar sind oder dass der Socket geschlossen wurde.

So nennen wir recv alle verfügbaren Daten zu erhalten,

 else: 
      data = i.recv(1024) 

und sehen, ob wir tatsächlich etwas gelesen habe. Wenn keine Daten verfügbar waren, muss die Verbindung geschlossen worden sein, also schließen wir das Socket-Objekt und entfernen es aus open_sockets.

  if data == "": 
       i.close() 
       open_sockets.remove(i) 
       print "Connection closed" 

Wenn wir haben tatsächlich Daten erhalten, können wir es nur an den Client zurückschreiben und sie auf dem Bildschirm drucken.

  else: 
       i.send(data) 
       print repr(data) 

Der erste Aufruf von select wird warten, bis eine Verbindung eingeht.Sie können durch die Aktualisierung der Code als

print "About to call select" 
rlist, wlist, xlist = select.select([listening_socket] + open_sockets, [], []) 
print "Returned from select" 

Nach dem ersten Aufruf für sich selbst sehen, werden rlistlistening_socket umfassen. Wir wissen das, weil open_sockets leer ist, und wie select aufgerufen wird, wird nicht zurückgegeben, bis etwas gelesen wird, um zu lesen. Wir akzeptieren also die neue Verbindung und fügen sie zu open_sockets hinzu.

Wenn select erneut aufgerufen wird, gibt es drei mögliche Ereignisse. Erstens, listening_socket möglicherweise eine andere Verbindung erhalten haben. In diesem Fall wird wie zuvor verfahren: Wir akzeptieren die Verbindung und fügen sie zu open_sockets hinzu.

Zweitens könnte die neue Verbindung Daten empfangen haben. Da select in rlist enthalten ist, wissen wir, dass Daten bereit sind, aus dem Socket "gelesen" zu werden (was bedeutet, dass die Daten zum Lesen bereit sind oder der Socket geschlossen wurde). i.recv wird die neuen Daten zurückgeben.

Drittens könnte die neue Verbindung geschlossen worden sein. Da select in rlist enthalten ist, wissen wir, dass Daten bereit sind, aus dem Socket gelesen zu werden (mit der gleichen Bedeutung wie oben). Aber i.recv wird "" zurückgeben, da der Socket keine neuen Daten hat. Wir wissen also, dass die Steckdose geschlossen wurde, und bereinigen Sie entsprechend.

Wenn keine Daten vom Client gesendet werden (und die Verbindung noch offen ist), wird select sie nicht in rlist einschließen. Die Schleife wird also nicht verarbeitet, und i.recv wird nicht an diesem bestimmten Socket aufgerufen.

+0

Es ist erwähnenswert 'socket.setblocking()' und 'socket.settimeout()' hier denke ich, da es nicht streng zutreffend ist, dass 'recv' wird universell blockieren. –

+0

wird nicht blockiert. 'i in rlist' sagt' i' ist "bereit" zu lesen. 1024 ist bufsize, recv() kann weniger Bytes zurückgeben – jfs

+0

Ich hörte nicht zu verstehen. Was Sie sagten, ist, wenn nichts gedrückt wird, wird es die Linie nicht überschreiten, so wird es stecken und wird nicht durch den anderen Kunden oder Sie meinten, dass es wird Überspringe die if-Bedingung? – user1779374

3

Wenn ein Socket "" als Antwort sendet, heißt das normalerweise, dass der Socket geschlossen ist (oder herunterfahren?). Jemand korrigiert mich, wenn ich hier falsch liege. Ohne diese Anweisung könnte es in einer Endlosschleife gefangen werden, wenn der Remote-Server plötzlich nicht mehr reagiert.

+0

Das ist richtig. Das ist mir gerade passiert und es verursachte eine große Speicherüberlastung. Ich habe Daten an eine Liste angehängt, die nach einem Terminalmarker suchte. Danke, dass du darauf hingewiesen hast. –

+0

@MichaelDavidWatson Eine ziemlich populäre Netzwerkbibliothek hatte einen ähnlichen Fehler und verursachte verwirrende Endlosschleife in meinem Code. Ein einfaches 'if not data: raise Exception' ist normalerweise die beste Lösung. – Anorov

0

i (btw, ein unglücklicher Name für eine Buchse) nicht in der rlist sein wird, es sei denn es etwas gibt, das heißt zu lesen, wird i.recv(1024) zurückkehren etwas oder die Verbindung erfolgt das heißt i.recv(1024) kehrt b"".

Einmal .recv() zurückgegeben b"" Sie erhalten nichts von diesem Sockel.

Die Interpretation von "der Client (ein Mensch) drückt nur Enter" hängt vom Client (Software) ab. Es hat nichts mit dem Server zum Beispiel zu tun, kann der Client Eingabezeichenfolgen puffern, bis ein Newline angetroffen wird oder ein Timeout passiert ist oder es kann jedes Byte gesendet werden, sobald sie es von einem Benutzer empfängt, usw.

-1
[[email protected] ]# grep "banner_timeout = " /opt/panel-migrator/thirdparties/python/lib/python2.7/site-packages/paramiko/transport.py 

self.banner_timeout = 60  # how long (seconds) to wait for the SSH banner