it-swarm.com.de

Wie gehe ich mit einer gebrochenen Pipe in Python um?

Ich habe in Python einen einfachen Multi-Threaded-Game-Server geschrieben, der für jede Client-Verbindung einen neuen Thread erstellt. Ich stelle fest, dass der Server gelegentlich wegen eines Pipe-/SIGPIPE-Fehlers abstürzt. Ich bin mir ziemlich sicher, dass es passiert, wenn das Programm versucht, eine Antwort an einen Client zu senden, der nicht mehr vorhanden ist.

Was ist ein guter Weg, um damit umzugehen? Meine bevorzugte Lösung würde einfach die serverseitige Verbindung zum Client schließen und fortfahren, anstatt das gesamte Programm zu beenden.

PS: Diese Frage/Antwort behandelt das Problem generisch; wie genau soll ich es lösen?

45
Adam Plumb

Informieren Sie sich über die try: Anweisung.

try:
    # do something
except socket.error, e:
    # A socket error
except IOError, e:
    if e.errno == errno.EPIPE:
        # EPIPE error
    else:
        # Other error
37
S.Lott

Unter der Annahme, dass Sie das Standard-Socket-Modul verwenden, sollten Sie die socket.error: (32, 'Broken pipe')-Ausnahme (nicht IOError, wie von anderen vorgeschlagen) erkannt haben. Dies wird in dem von Ihnen beschriebenen Fall angezeigt, d. H. Senden/Schreiben an eine Steckdose, für die die Gegenseite getrennt wurde.

import socket, errno, time

# setup socket to listen for incoming connections
s = socket.socket()
s.bind(('localhost', 1234))
s.listen(1)
remote, address = s.accept()

print "Got connection from: ", address

while 1:
    try:
        remote.send("message to peer\n")
        time.sleep(1)
    except socket.error, e:
        if isinstance(e.args, Tuple):
            print "errno is %d" % e[0]
            if e[0] == errno.EPIPE:
               # remote peer disconnected
               print "Detected remote disconnect"
            else:
               # determine and handle different error
               pass
        else:
            print "socket error ", e
        remote.close()
        break
    except IOError, e:
        # Hmmm, Can IOError actually be raised by the socket module?
        print "Got IOError: ", e
        break

Beachten Sie, dass diese Ausnahme beim ersten Schreiben in einen geschlossenen Socket nicht immer ausgelöst wird - normalerweise beim zweiten Schreiben (es sei denn, die Anzahl der beim ersten Schreiben geschriebenen Bytes ist größer als die Puffergröße des Sockets). Sie müssen dies im Hinterkopf behalten, falls Ihre Anwendung der Meinung ist, dass das entfernte Ende die Daten vom ersten Schreibzugriff empfangen hat, obwohl die Verbindung möglicherweise bereits getrennt wurde.

Sie können die Häufigkeit reduzieren (aber nicht vollständig beseitigen), indem Sie select.select() (oder poll) verwenden. Prüfen Sie vor dem Schreiben, ob Daten vom Peer gelesen werden können. Wenn select meldet, dass Daten zum Lesen aus dem Peer-Socket verfügbar sind, lesen Sie sie mit socket.recv(). Wenn dies eine leere Zeichenfolge zurückgibt, hat der Remote-Peer die Verbindung geschlossen. Da es hier immer noch eine Wettlaufsituation gibt, müssen Sie die Ausnahme noch abfangen und behandeln.

Twisted eignet sich hervorragend für diese Art von Dingen, es klingt jedoch so, als hätten Sie schon einiges an Code geschrieben.

50
mhawke

SIGPIPE (obwohl ich denke, dass Sie vielleicht EPIPE? meinen), tritt auf Sockets auf, wenn Sie einen Socket herunterfahren und dann Daten an ihn senden. Die einfache Lösung besteht darin, den Socket nicht vor dem Senden von Daten herunterzufahren. Dies kann auch bei Pipes passieren, aber es hört sich nicht so an, als würden Sie es erfahren, da es sich um einen Netzwerkserver handelt.

Sie können die Bandhilfe auch einfach anwenden, um die Ausnahme in einem Top-Level-Handler in jedem Thread abzufangen.

Wenn Sie Twisted verwenden, anstatt für jede Clientverbindung einen neuen Thread zu erzeugen, hätten Sie dieses Problem wahrscheinlich nicht. Es ist wirklich schwierig (möglicherweise unmöglich, abhängig von Ihrer Anwendung), die Reihenfolge der Schließ- und Schreibvorgänge korrekt zu bestimmen, wenn mehrere Threads denselben E/A-Kanal verwenden.

4
Glyph

Ich stehe vor der gleichen Frage. Aber ich sende den gleichen Code beim nächsten Mal, es funktioniert einfach ...

$ packet_write_wait: Connection to 10.. port 22: Broken pipe

Beim zweiten Mal funktioniert es:

[1]   Done                    Nohup python -u add_asc_dec.py > add2.log 2>&1

Ich denke, der Grund liegt möglicherweise in der aktuellen Serverumgebung.

0
yuan