Domanda

Ho scritto un semplice server di gioco multi-thread in Python che crea un nuovo thread per ogni connessione client. Sto scoprendo che di tanto in tanto il server si arresterà in modo anomalo a causa di un errore di pipe interrotto / SIGPIPE. Sono abbastanza sicuro che accada quando il programma tenta di inviare una risposta a un client che non è più presente.

Qual è un buon modo per affrontarlo? La mia risoluzione preferita sarebbe semplicemente chiudere la connessione lato server al client e andare avanti, anziché uscire dall'intero programma.

PS: Questa domanda / risposta riguarda il problema in modo generico; come dovrei risolverlo in modo specifico?

È stato utile?

Soluzione

Leggi sull'istruzione try:

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

Altri suggerimenti

Supponendo che tu stia utilizzando il modulo socket standard, dovresti rilevare l'eccezione socket.error: (32, 'Broken pipe') (non IOError come altri hanno suggerito). Ciò verrà sollevato nel caso in cui sia stato descritto, ovvero invio / scrittura a un socket per il quale il lato remoto si è disconnesso.

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

Notare che questa eccezione non verrà sempre sollevata sulla prima scrittura su un socket chiuso, più in genere sulla seconda scrittura (a meno che il numero di byte scritti nella prima scrittura sia maggiore della dimensione del buffer del socket). È necessario tenerlo presente nel caso in cui l'applicazione ritenga che l'estremità remota abbia ricevuto i dati dalla prima scrittura quando potrebbe essere già stata disconnessa.

Puoi ridurre l'incidenza (ma non eliminarla del tutto) usando select.select () (o poll ). Controllare i dati pronti per la lettura dal peer prima di tentare una scrittura. Se select segnala che sono disponibili dati da leggere dal socket peer, leggili usando socket.recv () . Se questo restituisce una stringa vuota, il peer remoto ha chiuso la connessione. Poiché esiste ancora una condizione di competizione qui, dovrai comunque catturare e gestire l'eccezione.

Twisted è ottimo per questo genere di cose, tuttavia, sembra che tu abbia già scritto un bel po 'di codice.

SIGPIPE (anche se penso che forse intendi EPIPE ?) si verifica sui socket quando si chiude un socket e quindi si inviano i dati. La soluzione semplice non è quella di chiudere il socket prima di provare a inviarlo dati. Questo può accadere anche su pipe, ma non sembra che sia quello che stai vivendo, dal momento che è un server di rete.

Puoi anche applicare il cerotto per catturare l'eccezione in un gestore di alto livello in ogni thread.

Ovviamente, se hai usato Twisted anziché generare un nuovo thread per ogni connessione client, probabilmente non ho questo problema. È davvero difficile (forse impossibile, a seconda della tua applicazione) ottenere l'ordinamento delle operazioni di chiusura e scrittura corrette se più thread hanno a che fare con lo stesso canale I / O.

Devo affrontare la stessa domanda. Ma invio lo stesso codice la prossima volta, funziona e basta. La prima volta che si è rotto:

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

La seconda volta che funziona:

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

Suppongo che il motivo potrebbe riguardare l'attuale ambiente del server.

La mia risposta è molto vicina a quella di S.Lott, tranne che sarei ancora più particolare:

try:
    # do something
except IOError, e:
    # ooops, check the attributes of e to see precisely what happened.
    if e.errno != 23:
        # I don't know how to handle this
        raise

dove " 23 " è il numero di errore che ricevi da EPIPE. In questo modo non tenterai di gestire un errore di autorizzazione o qualsiasi altra cosa per cui non sei attrezzato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top