J'ai un petit programme serveur qui accepte les connexions sur un socket TCP ou UNIX local, lit une commande simple et, selon la commande, envoie une réponse. Le problème est que le client peut parfois ne pas avoir d'intérêt pour la réponse et se termine tôt, donc écrire sur ce socket provoquera un SIGPIPE et fera planter mon serveur. Quelle est la meilleure pratique pour éviter le plantage ici? Existe-t-il un moyen de vérifier si l'autre côté de la ligne lit toujours? (select () ne semble pas fonctionner ici car il dit toujours que le socket est accessible en écriture). Ou devrais-je simplement attraper le SIGPIPE avec un gestionnaire et l'ignorer?
Une autre méthode consiste à changer le socket pour qu'il ne génère jamais de SIGPIPE sur write (). C'est plus pratique dans les bibliothèques, où vous ne voudrez peut-être pas de gestionnaire de signal global pour SIGPIPE.
Sur la plupart des systèmes basés sur BSD (MacOS, FreeBSD ...), (en supposant que vous utilisez C / C ++), vous pouvez le faire avec:
Avec cela en vigueur, au lieu du signal SIGPIPE généré, EPIPE sera retourné.
la source
Je suis super en retard à la fête, mais je ne suis
SO_NOSIGPIPE
pas portable et peut ne pas fonctionner sur votre système (cela semble être un problème BSD).Une bonne alternative si vous êtes sur, disons, un système Linux sans
SO_NOSIGPIPE
serait de définir l'MSG_NOSIGNAL
indicateur sur votre appel send (2).Exemple remplacé
write(...)
parsend(...,MSG_NOSIGNAL)
(voir le commentaire de nobar )la source
QTcpSocket
objet Qt pour envelopper l'utilisation des sockets, j'ai dû remplacer l'write
appel de méthode par un système d'exploitationsend
(en utilisant lasocketDescriptor
méthode). Quelqu'un connaît une option plus propre pour définir cette option dans uneQTcpSocket
classe?Dans cet article, j'ai décrit une solution possible pour le cas Solaris lorsque ni SO_NOSIGPIPE ni MSG_NOSIGNAL n'est disponible.
Exemple de code sur https://github.com/kroki/XProbes/blob/1447f3d93b6dbf273919af15e59f35cca58fcc23/src/libxprobes.c#L156
la source
Gérer le SIGPIPE localement
Il est généralement préférable de gérer l'erreur localement plutôt que dans un gestionnaire d'événement de signal global, car localement, vous aurez plus de contexte sur ce qui se passe et sur les recours à prendre.
J'ai une couche de communication dans l'une de mes applications qui permet à mon application de communiquer avec un accessoire externe. Lorsqu'une erreur d'écriture se produit, je lance et exception dans la couche de communication et le laisse bouillonner jusqu'à un bloc try catch pour le gérer là-bas.
Code:
Le code pour ignorer un signal SIGPIPE afin que vous puissiez le gérer localement est:
Ce code empêchera le signal SIGPIPE d'être élevé, mais vous obtiendrez une erreur de lecture / écriture lorsque vous essayez d'utiliser le socket, vous devrez donc vérifier cela.
la source
Vous ne pouvez pas empêcher le processus à l'extrémité d'un tuyau de se terminer, et s'il se termine avant que vous ayez fini d'écrire, vous obtiendrez un signal SIGPIPE. Si vous SIG_IGN le signal, alors votre écriture reviendra avec une erreur - et vous devez noter et réagir à cette erreur. Attraper et ignorer le signal dans un gestionnaire n'est pas une bonne idée - vous devez noter que le tuyau est maintenant éteint et modifier le comportement du programme afin qu'il n'écrive plus dans le tuyau (car le signal sera à nouveau généré et ignoré encore une fois, et vous essaierez à nouveau, et tout le processus pourrait durer longtemps et gaspiller beaucoup de puissance CPU).
la source
Je pense que c'est vrai. Vous voulez savoir quand l'autre extrémité a fermé son descripteur et c'est ce que SIGPIPE vous dit.
Sam
la source
Le manuel Linux disait:
Mais pour Ubuntu 12.04, ce n'est pas correct. J'ai écrit un test pour ce cas et je reçois toujours EPIPE withot SIGPIPE. SIGPIPE est généré si j'essaye d'écrire sur le même socket cassé une deuxième fois. Vous n'avez donc pas besoin d'ignorer SIGPIPE si ce signal se produit, cela signifie une erreur logique dans votre programme.
la source
Soit désactiver les sigpipes comme pour tout le monde, soit intercepter et ignorer l'erreur.
Oui, utilisez select ().
Vous devez sélectionner les bits de lecture . Vous pouvez probablement ignorer les bits d' écriture .
Lorsque l'extrémité distante ferme son descripteur de fichier, sélectionnez vous indiquera que des données sont prêtes à être lues. Lorsque vous allez lire cela, vous récupérerez 0 octet, c'est ainsi que le système d'exploitation vous indique que le descripteur de fichier a été fermé.
La seule fois où vous ne pouvez pas ignorer les bits d'écriture, c'est si vous envoyez de gros volumes et qu'il y a un risque que l'autre extrémité soit en retard, ce qui peut entraîner le remplissage de vos tampons. Si cela se produit, alors essayer d'écrire dans le descripteur de fichier peut entraîner le blocage ou l'échec de votre programme / thread. Tester select avant l'écriture vous protégera de cela, mais cela ne garantit pas que l'autre extrémité est saine ou que vos données vont arriver.
Notez que vous pouvez obtenir un sigpipe à partir de close (), ainsi que lorsque vous écrivez.
Fermer vide toutes les données mises en mémoire tampon. Si l'autre extrémité a déjà été fermée, la fermeture échouera et vous recevrez un sigpipe.
Si vous utilisez TCPIP en mémoire tampon, une écriture réussie signifie simplement que vos données ont été mises en file d'attente pour l'envoi, cela ne signifie pas qu'elles ont été envoyées. Tant que vous n'avez pas réussi à appeler close, vous ne savez pas que vos données ont été envoyées.
Sigpipe vous dit que quelque chose s'est mal passé, il ne vous dit pas quoi ou ce que vous devez faire à ce sujet.
la source
Sous un système POSIX moderne (c'est-à-dire Linux), vous pouvez utiliser la
sigprocmask()
fonction.Si vous souhaitez restaurer l'état précédent ultérieurement, assurez-vous d'enregistrer le
old_state
quelque part en sécurité. Si vous appelez cette fonction plusieurs fois, vous devez soit utiliser une pile, soit enregistrer uniquement la première ou la dernièreold_state
... ou peut-être avoir une fonction qui supprime un signal bloqué spécifique.Pour plus d'informations, lisez la page de manuel .
la source
sigprocmask
ajoute à l'ensemble des signaux bloqués, vous pouvez donc faire tout cela avec un seul appel.old_state
afin de pouvoir le restaurer plus tard, si je le souhaite. Si vous savez que vous n'aurez jamais besoin de restaurer l'état, il n'est en effet pas nécessaire de le lire et de le stocker de cette manière.sigprocmask()
lire l'ancien état, l'appel à lesigaddset
modifier, le deuxième appel à l'sigprocmask()
écrire. Vous pouvez supprimer le premier appel, initialiser avecsigemptyset(&set)
et remplacer le deuxième appel parsigprocmask(SIG_BLOCK, &set, &old_state)
.