2013-12-09 4 views
7

Ich arbeite am Schreiben einer netzwerkorientierten Anwendung in Python. Ich hatte früher an blockierenden Sockets gearbeitet, aber nachdem ich die Anforderungen und Konzepte besser verstanden habe, möchte ich die Anwendung mit nicht blockierenden Sockets und somit einem ereignisgesteuerten Server schreiben.Wie funktioniert die select() - Funktion im Select-Modul von Python genau?

Ich verstehe, dass die Funktionen im Select-Modul in Python verwendet werden, um bequem zu sehen, welche Socket uns interessiert und so weiter. Auf dem Weg zu, dass ich im Grunde versucht, durch ein paar Beispiele von einem ereignisgesteuerten Server zu spiegeln, und ich war gekommen, über diese:

""" 
An echo server that uses select to handle multiple clients at a time. 
Entering any line of input at the terminal will exit the server. 
""" 

import select 
import socket 
import sys 

host = '' 
port = 50000 
backlog = 5 
size = 1024 
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
server.bind((host,port)) 
server.listen(backlog) 
input = [server,sys.stdin] 
running = 1 
while running: 
    inputready,outputready,exceptready = select.select(input,[],[]) 

    for s in inputready: 

     if s == server: 
      # handle the server socket 
      client, address = server.accept() 
      input.append(client) 

     elif s == sys.stdin: 
      # handle standard input 
      junk = sys.stdin.readline() 
      running = 0 

     else: 
      # handle all other sockets 
      data = s.recv(size) 
      if data: 
       s.send(data) 
      else: 
       s.close() 
       input.remove(s) 
server.close() 

die Teile, die ich nicht zu verstehen schien, sind folgende:

Im Code-Snippet inputready,outputready,exceptready = select.select(input,[],[]) glaube ich, die select() Funktion gibt drei möglicherweise leere Listen von Waitable-Objekte für Eingabe, Ausgabe und außergewöhnliche Bedingungen. Es macht also Sinn, dass das erste Argument der select()-Funktion die Liste ist, die den Server-Socket und die Standardeingabe enthält. Allerdings, wo ich Verwirrung habe, ist in der else Block des Codes.

Da wir die Liste der inputready-Sockets for-loopen, ist klar, dass die select()-Funktion einen Client-Socket auswählen wird, der zum Lesen bereit ist. Nachdem wir jedoch Daten unter Verwendung von recv() gelesen haben und feststellen, dass der Socket tatsächlich Daten gesendet hat, möchten wir ihn zurück zum Client senden. Meine Frage ist, wie können wir in diesen Socket schreiben, ohne es zu der Liste als zweites Argument an die select() Funktionsaufruf übergeben? Bedeutung, wie können wir send() auf dem neuen Socket direkt aufrufen, ohne es mit select() als beschreibbare Socket "registrieren"?

Warum schleifen wir auch nur über die Buchsen, die zum Lesen bereit sind (in diesem Fall input_ready)? Ist es nicht notwendig, sogar die readyready-Liste zu durchlaufen, um zu sehen, welche Sockets zum Schreiben bereit sind? Offensichtlich vermisse ich hier etwas.

Es wäre auch sehr hilfreich, wenn jemand die Funktionsweise der select() Funktion etwas genauer erklären könnte oder auf eine gute Dokumentation verweisen könnte.

Vielen Dank.

+2

Ich glaube, Sie durch die Details des Beispielcodes verwechselt werden. Der Aufruf von send auf einem Socket, ohne zu warten, bis es schreibbereit ist, ist in Ordnung, wird jedoch blockiert, wenn der ausgehende Puffer voll ist. Sie ignorieren diesen Fall in Ihrem Beispielcode. Vielleicht möchten Sie die Manpage 'select' nachschlagen, da dies ein dünner Wrapper ist. Ich nehme an, Sie haben http://docs.python.org/2/library/select.html gefunden –

Antwort

1

Wahrscheinlich ist dieser Codeschnipsel nur ein einfaches Beispiel und daher nicht erschöpfend. Sie können in jedem Socket schreiben und lesen, auch wenn Sie nicht angeben, dass sie bereit sind. Aber wenn Sie das tun, können Sie natürlich nicht sicher sein, dass Ihr send() nicht blockiert. Also, ja, es wäre beste Praxis, sich auf select auch für Schreiboperationen zu verlassen. Es gibt auch viele andere Funktionen, die einen ähnlichen Zweck haben und in vielen Fällen besser sind als wählen (z.B. epoll), aber sie sind nicht auf allen Plattformen verfügbar. Informationen über die Auswahl, Epoll & andere Funktionen können in Linux-Manpages gefunden werden.

jedoch in Python gibt es viele nette Bibliotheken verwendet, um viele Verbindungen zu handhaben, einige davon sind: Twisted und gevent

+0

Danke @Faust.Wäre es eine schwierige Aufgabe, die App ohne die Hilfe von Twisted oder ähnlichem zu programmieren? – gravetii

+1

Es hängt von Ihren Bedürfnissen ab. Zum Beispiel kann eine lib wie gevent das beste Tool auswählen, das auf der aktuellen Plattform verfügbar ist (z. B. epoll unter Linux und kqueue unter FreeBSD). Sie können es selbst machen, aber wenn Ihr Projekt mehr als ein Experiment ist und Sie wirklich die beste Leistung benötigen, ist es wahrscheinlich besser, sich auf weit verbreitete und getestete Tools wie gevent oder Twisted zu verlassen. Das Erreichen der Optimierungsebene dieser Bibliothek wird nicht einfach sein. – smeso

Verwandte Themen