53 votes

Comment gérer un tuyau cassé (SIGPIPE) en python?

J'ai écrit un simple multi-thread serveur de jeu en python qui crée un nouveau thread pour chaque connexion client. Je trouve que chaque maintenant et puis, le serveur crash à cause d'un bris de tuyau/SIGNAL d'erreur. Je suis assez sûr qu'il se passe lorsque le programme tente d'envoyer une réponse à un client qui n'est plus présent.

Ce qui est une bonne façon de traiter ce problème? Mon préféré de résolution, il suffit de fermer la connexion côté serveur pour le client et de passer, plutôt que de quitter le programme en entier.

PS: Cette question/réponse traite du problème de manière générique; comment doit-je le résoudre?

55voto

mhawke Points 10385

En supposant que vous êtes à l'aide de la douille standard module, vous devriez être en rattrapage de l' socket.error: (32, 'Broken pipe') d'exception (pas IOError comme d'autres l'ont suggéré). Ce sera soulevée dans le cas que vous avez décrit, c'est à dire l'envoi/l'écriture d'un socket pour lesquels la distance est déconnecté.

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

Notez que cette exception ne sera pas toujours être soulevées lors de la première écriture pour une socket fermée - plus généralement la deuxième écrire (à moins que le nombre d'octets écrits dans la première écriture est plus grande que la prise de la taille de la mémoire tampon). Vous devez garder cela à l'esprit dans le cas où votre application pense que l'extrémité distante reçu les données de la première écrire quand on peut déjà avoir déconnecté.

Vous pouvez réduire le taux d'incidence (mais ne pas éliminer entièrement) en utilisant select.select() (ou poll). Vérifier les données prêt à lire de par les pairs avant de tenter une écriture. Si select des rapports qu'il y a à disposition des données à lire à partir de la prise par les pairs, le lire en utilisant socket.recv(). Si cela renvoie une chaîne vide, l'homologue distant a fermé la connexion. Car il y a encore une condition de concurrence ici, vous aurez toujours besoin de l'attraper et de gérer l'exception.

Twisted est idéal pour ce genre de chose, cependant, il semble que vous avez déjà écrit un peu juste de code.

41voto

S.Lott Points 207588

Lisez sur l'essai: déclaration.

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

3voto

Glyph Points 17756

SIGPIPE (bien que je pense que peut-être vous voulez dire EPIPE?) se produit sur les sockets lorsque vous arrêtez une socket, puis envoyer les données. La solution la plus simple est de ne pas fermer la socket vers le bas avant de tenter d'envoyer des données. Cela peut également se produire sur les tuyaux, mais il n'a pas l'air comme ça de ce que vous êtes en train de vivre, puisque c'est un serveur de réseau.

Vous pouvez aussi appliquer la bande-aide de la capture de l'exception dans certains haut-gestionnaire de niveau dans chaque thread.

Bien sûr, si vous avez utilisé Tordu plutôt que de fraie un nouveau thread pour chaque client de connexion, vous ne serait probablement pas ce problème. C'est vraiment dur (peut-être impossible, en fonction de votre application) pour obtenir la commande de proximité et les opérations d'écriture correcte si plusieurs threads traitant du même I/O canal.

-4voto

Kirk Strauser Points 12087

Ma réponse est très proche de celle de S.Lott, sauf que je serais encore plus précise:

 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
 

où "23" est le numéro d'erreur que vous obtenez d'EPIPE. De cette façon, vous ne tenterez pas de traiter une erreur de permissions ou quoi que ce soit pour lequel vous n’êtes pas équipé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X