Frage

Ich habe ein kleines Server-Programm, das Verbindung auf einem TCP oder lokalen UNIX-Socket akzeptiert, liest einen einfachen Befehl, und je nach dem Befehl sendet eine Antwort. Das Problem ist, dass der Kunde manchmal kein Interesse an der Antwort hat und verläßt früh, so zu diesem Socket schreibt einen SIGPIPE verursachen und meinen Server zum Absturz bringen. Was ist die beste Praxis den Absturz hier zu verhindern? Gibt es eine Möglichkeit, wenn die andere Seite der Linie zu überprüfen, ist nach wie vor zu lesen? (Select () scheint nicht, hier zu arbeiten, wie es immer sagt der Buchse beschreibbar ist). Oder sollte ich nur die SIGPIPE mit einem Handler fangen und sie ignorieren?

War es hilfreich?

Lösung

Sie möchten im Allgemeinen die SIGPIPE und behandeln den Fehler direkt im Code ignorieren. Dies liegt daran, Signal-Handler in C haben viele Beschränkungen auf, was sie tun können.

Die meisten tragbaren Weg, dies zu tun ist, um die SIGPIPE Handler SIG_IGN einzustellen. Dies verhindert, dass jede Buchse oder ein Rohr aus Schreib verursacht ein SIGPIPE Signal.

das SIGPIPE Signal zu ignorieren, verwenden Sie den folgenden Code ein:

signal(SIGPIPE, SIG_IGN);

Wenn Sie die send() Anruf verwenden, eine weitere Option ist es, die MSG_NOSIGNAL Option zu verwenden, die das Verhalten SIGPIPE off auf einer Basis pro Anruf wenden. Beachten Sie, dass nicht alle Betriebssysteme die MSG_NOSIGNAL Flag unterstützen.

Schließlich Sie können auch die SO_SIGNOPIPE Buchse Flagge zu berücksichtigen, die mit setsockopt() auf einigen Betriebssystemen eingestellt werden kann. Dadurch wird verhindert, SIGPIPE von nur an die Buchsen durch Schreiben verursacht wird es eingestellt auf.

Andere Tipps

Eine andere Methode ist die Steckdose zu ändern, damit es nie SIGPIPE auf Schreib erzeugt (). Dies ist bequemer, in Bibliotheken, in denen Sie keine globalen Signal-Handler für SIGPIPE wollen könnten.

Bei den meisten BSD-basierte (MacOS, FreeBSD ...) -Systemen (vorausgesetzt, Sie sind mit C / C ++), können Sie dies mit:

int set = 1;
setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));

diese mit in der Tat statt das SIGPIPE Signal erzeugt wird, wird EPIPE zurückgegeben werden.

Ich bin super spät zur Party, aber SO_NOSIGPIPE ist nicht tragbar und kann auf Ihrem System nicht funktionieren (es scheint, eine BSD Sache zu sein).

Eine gute Alternative, wenn Sie auf, sagen wir, ein Linux-System ohne SO_NOSIGPIPE wäre die MSG_NOSIGNAL Flagge auf send (2) Anruf einzustellen.

Beispiel write(...) durch send(...,MSG_NOSIGNAL) ersetzen (siehe nobar 's Kommentar)

char buf[888];
//write( sockfd, buf, sizeof(buf) );
send(    sockfd, buf, sizeof(buf), MSG_NOSIGNAL );

In diesem Post ich mögliche Lösung beschrieben für Solaris Fall, wenn weder SO_NOSIGPIPE noch MSG_NOSIGNAL verfügbar ist.

  

Stattdessen müssen wir vorübergehend SIGPIPE in dem aktuellen Thread unterdrücken, die Bibliothek Code ausführt. Hier ist, wie dies zu tun: SIGPIPE zu unterdrücken wir zuerst, ob sie anhängig ist. Ist dies der Fall, bedeutet dies, dass es in diesem Thread blockiert ist, und wir müssen nichts tun. Wenn die Bibliothek zusätzliche SIGPIPE erzeugt, wird es mit den anstehenden einem verschmolzen werden, und das ist ein No-op. Wenn SIGPIPE nicht anhängig ist dann blockieren wir es in diesem Thread, und auch prüfen, ob es bereits gesperrt wurde. Dann sind wir frei unsere schreibt auszuführen. Wenn wir SIGPIPE in seinen ursprünglichen Zustand wiederherzustellen, haben wir folgendes: wenn SIGPIPE ursprünglich anhängig war, wir nichts zu tun. Andernfalls überprüfen wir, ob es nun anhängig ist. Ist dies der Fall (was das bedeutet geführte Aktionen erzeugen eine oder mehr SIGPIPEs), dann warten wir es in diesem Thread, damit seinen ausstehenden Status Clearing (dies zu tun, wir sigtimedwait () mit Null-Timeout verwenden, diese Blockierung in zu vermeiden ist ein Szenario, in die böswilligen Benutzer SIGPIPE manuell auf einen gesamten Prozess gesendet: wir werden sehen, es fehlt noch, aber auch andere Gewinde können damit umgehen, bevor wir eine Änderung mussten warten, bis es) in diesem Fall. Nach dem Löschen Status anhängig entsperren wir SIGPIPE in diesem Thread, aber nur, wenn sie ursprünglich nicht blockiert wurde.

Beispiel-Code an https://github.com/kroki /XProbes/blob/1447f3d93b6dbf273919af15e59f35cca58fcc23/src/libxprobes.c#L156

Handle SIGPIPE Lokal

Es ist normalerweise am besten, um den Fehler vor Ort und nicht in einem globalen Signal-Ereignishandler zu handhaben, da vor Ort werden Sie mehr Kontext haben, was los ist und welche Rückgriff zu nehmen.

Ich habe eine Kommunikationsschicht in einem meinem apps, dass mein App ermöglicht es mit einem externen Zubehör zu kommunizieren. Wenn ein Schreibfehler auftritt Ich werfe und Ausnahme in der Kommunikationsschicht und lassen Sie sie sprudelt zu einem Block Try-Catch es dort zu behandeln.

Code:

Der Code ein SIGPIPE Signal zu ignorieren, so dass Sie es lokal behandeln können, ist:

// We expect write failures to occur but we want to handle them where 
// the error occurs rather than in a SIGPIPE handler.
signal(SIGPIPE, SIG_IGN);

Mit diesem Code wird das SIGPIPE Signal von angehoben verhindern, aber Sie werden einen Lese- / Schreibfehler erhalten, wenn die Steckdose zu verwenden versuchen, so dass Sie für die überprüfen müssen.

Sie können den Prozess nicht auf dem fernen Ende eines Rohres am Austreten aus, verhindern und wenn es beendet, bevor Sie mit dem Schreiben fertig sind, werden Sie ein SIGPIPE Signal erhalten. Wenn Sie das Signal SIG_IGN, dann schreiben wird mit einem Fehler zurück - und Sie müssen beachten und zu diesem Fehler zu reagieren. Nur fangen und das Signal in einem Handler zu ignorieren, ist keine gute Idee - Sie müssen beachten, dass das Rohr nun nicht mehr existierenden und das Verhalten des Programms ändern, damit es nicht auf das Rohr wieder (nicht schreiben, weil das Signal wieder erzeugt werden, und ignoriert wieder, und Sie werden wieder versuchen, und der gesamte Prozess für eine lang Zeit und verschwenden viel CPU-Leistung weitergehen könnte).

  

Oder sollte ich nur die SIGPIPE mit einem Handler fangen und sie ignorieren?

Ich glaube, dass direkt an ist. Sie wollen wissen, wenn das andere Ende ihren Descriptor geschlossen hat und das ist, was SIGPIPE sagt.

Sam

Linux Handbuch sagt:

  

EPIPE Die lokale Ende wurde auf einer Verbindung herunterzufahren orientierte                 Steckdose. In diesem Fall wird der Prozess erhält auch eine SIGPIPE                 es sei denn, MSG_NOSIGNAL eingestellt ist.

Aber für Ubuntu 12.04 ist es nicht richtig. Ich schrieb einen Test für diesen Fall und ich immer EPIPE withot SIGPIPE erhalten. SIGPIPE ist genereated wenn ich versuche, auf die gleiche gebrochene Buchse zum zweiten Mal zu schreiben. So brauchen Sie nicht SIGPIPE zu ignorieren, wenn dieses Signal passiert, bedeutet es, Logikfehler in Ihrem Programm.

  

Was ist die beste Praxis den Absturz hier zu verhindern?

So oder deaktivieren sigpipes nach jedem, oder fängt und die Fehler ignorieren.

  

Gibt es eine Möglichkeit, wenn die andere Seite der Linie zu überprüfen, noch lesen?

Ja, verwenden Sie wählen ().

  

select () scheint nicht, hier zu arbeiten, wie es immer sagt der Buchse ist beschreibbar.

Sie müssen auswählen, die auf dem lesen Bits. Sie können sich wahrscheinlich ignorieren die write Bits.

Wenn das ferne Ende seiner Datei-Handle geschlossen, wählen Sie werden Ihnen sagen, dass es Daten bereit zu lesen. Wenn Sie das gehen und lesen, werden Sie 0 Byte zurück, das ist, wie das Betriebssystem Sie sagt, dass die Datei-Handle geschlossen wurde.

Das einzige Mal, wenn Sie die Schreib Bits nicht ignorieren können, wenn Sie große Mengen senden, und es besteht die Gefahr des anderen Endes immer im Rückstand, die Ihre Puffer verursachen können zu füllen. Wenn das passiert, dann versuchen, die Datei-Handle schreiben kann Ihr Programm / Thread führen zu blockieren oder zu versagen. Testen wählen Sie vor dem Schreiben werden Sie von dem schützen, aber es garantiert nicht, dass das andere Ende ist gesund oder dass Ihre Daten ankommen wird.

Beachten Sie, dass Sie eine SIGPIPE aus der Nähe zu bekommen (), sowie, wenn Sie schreiben.

Schließen spült alle gepufferten Daten. Wenn das andere Ende bereits geschlossen worden ist, dann wird in der Nähe fehl, und Sie werden eine SIGPIPE erhalten.

Wenn Sie gepuffert TCPIP verwenden, dann ein erfolgreicher Schreib nur bedeutet, dass Ihre Daten zu senden Warteschlange ist, bedeutet es nicht, es wurde abgeschickt. Bis Sie erfolgreich Nähe nennen, wissen Sie nicht, dass Ihre Daten gesendet wurden.

SIGPIPE sagt etwas schief gegangen ist, ist es Ihnen nicht sagen, was, oder was soll man dagegen tun können.

Unter einem modernen POSIX-System (das heißt Linux), können Sie die sigprocmask()-Funktion verwenden.

#include <signal.h>

void block_signal(int signal_to_block /* i.e. SIGPIPE */ )
{
    sigset_t set;
    sigset_t old_state;

    // get the current state
    //
    sigprocmask(SIG_BLOCK, NULL, &old_state);

    // add signal_to_block to that existing state
    //
    set = old_state;
    sigaddset(&set, signal_to_block);

    // block that signal also
    //
    sigprocmask(SIG_BLOCK, &set, NULL);

    // ... deal with old_state if required ...
}

Wenn Sie später den vorherigen Zustand wiederherstellen möchten, stellen Sie sicher, dass die old_state irgendwo sicher zu speichern. Wenn Sie diese Funktion mehrmals aufrufen, müssen Sie entweder einen Stapel verwenden oder nur die erste oder letzte old_state speichern ... oder vielleicht eine Funktion haben, die eine bestimmte blockierte Signal entfernt.

Für weitere Informationen lesen Sie bitte die Manpage rel="nofollow.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top