Question

J'ai écrit un simple serveur de jeux multi-thread en python qui crée un nouveau thread pour chaque connexion client. Je constate que de temps en temps, le serveur tombe en panne à cause d'une erreur SIGPIPE / canal cassé. Je suis presque sûr que cela se produit lorsque le programme tente d'envoyer une réponse à un client qui n'est plus présent.

Quel est un bon moyen de gérer cela? Ma résolution préférée consiste simplement à fermer la connexion côté serveur au client et à passer à autre chose, plutôt que de quitter le programme complet.

PS: Cette question / réponse concerne le problème de manière générique; comment spécifiquement dois-je le résoudre?

Était-ce utile?

La solution

Lisez la déclaration try:

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

Autres conseils

En supposant que vous utilisiez le module de socket standard, vous devriez capturer l'exception socket.error: (32, 'Tube cassé') (et non pas IOError comme d'autres l'ont suggéré). Ceci sera soulevé dans le cas que vous avez décrit, c'est-à-dire l'envoi / l'écriture dans un socket pour lequel le côté distant 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 déclenchée lors de la première écriture dans un socket fermé - plus généralement lors de la seconde écriture (sauf si le nombre d'octets écrits dans la première écriture est supérieur à la taille de la mémoire tampon du socket). N'oubliez pas cela au cas où votre application pense que l'extrémité distante a reçu les données de la première écriture alors qu'elle s'est peut-être déjà déconnectée.

Vous pouvez en réduire l'incidence (mais pas l'éliminer entièrement) en utilisant select.select () (ou poll ). Vérifiez que les données sont prêtes à être lues par l'homologue avant de tenter une écriture. Si select indique qu'il existe des données pouvant être lues à partir du socket homologue, lisez-les à l'aide de socket.recv () . Si cela renvoie une chaîne vide, l'homologue distant a fermé la connexion. Comme il existe toujours une condition de concurrence critique, vous devez toujours capturer et gérer l'exception.

Twisted est idéal pour ce genre de choses, cependant, on dirait que vous avez déjà écrit pas mal de code.

SIGPIPE (même si je pense que vous voulez peut-être dire EPIPE ?) se produit sur les sockets lorsque vous fermez un socket, puis lui envoyez des données. La solution simple est de ne pas éteindre la prise avant d'essayer de lui envoyer des données. Cela peut également se produire sur les pipes, mais cela ne ressemble pas à ce que vous rencontrez, car c'est un serveur de réseau.

Vous pouvez également simplement appliquer la méthode de récupération de l'exception dans un gestionnaire de niveau supérieur dans chaque thread.

Bien sûr, si vous utilisiez Twisted au lieu de créer un nouveau fil pour chaque connexion client, vous ne le feriez probablement pas. pas ce problème. Il est très difficile (peut-être impossible, en fonction de votre application) d'obtenir la commande correcte des opérations de fermeture et d'écriture si plusieurs threads traitent avec le même canal d'E / S.

Je suis confronté à la même question. Mais je soumets le même code la prochaine fois, cela fonctionne. La première fois, il a éclaté:

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

La deuxième fois que cela fonctionne:

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

Je suppose que la raison peut être liée à l'environnement de serveur actuel.

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é.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top