Reuse Socket-Problem

04/06/2010 - 12:32 von Edzard Egberts | Report spam
Ich krieg's einfach nicht hin:

Gegeben Fedora Linux 12 und ein Server, der über einen Listen-Socket
Clienten annimmt. Das làuft alles auf einem System, also als IPC.
Wenn ich den Server starte und ohne Clienten wieder beende, kann ich den
direkt wieder neu starten, wenn dagegen vorher Clienten verbunden waren,
ist der entsprechende Port immer für kurze Zeit blockiert. Der Code im
Konstruktor meiner Server-Klasse sieht folgendermaßen aus:

SOCKADDR_IN AdrLokal; // Lokale Socket-Adresse
AdrLokal.sin_family= AF_INET;
AdrLokal.sin_port= htons (PortNum); // Verbindung auf PortNum
AdrLokal.sin_addr.s_addr= htonl(Bind);
// mit angegebenem Adapter
// Socket an lokale Adresse binden:
if (bind(Socket(), reinterpret_cast< sockaddr* >(&AdrLokal),
sizeof(AdrLokal)) == SOCKET_ERROR)
{ // Bindung der Adresse hat nicht geklappt
Fehler(seBind);
}
else
{ // Weitermachen, wenn bind geklappt hat und auf Anfragen lauschen
if (listen (Socket(), SOMAXCONN) == SOCKET_ERROR)
{ // Versuch des Lauschens hat nicht geklappt
Fehler(seListen);
}
else
{ // Socket wartet jetzt auf Verbindungen
#ifndef WIN32
bool Reuse_An= true;
setsockopt(Socket(), SOL_SOCKET, SO_REUSEADDR,
&Reuse_An, sizeof(Reuse_An));
#endif
sm_Mem.Set_Rd(Socket()); // Socket für lesen anmelden
Fehler(seStartListen); // Socket-Start melden
}
}
}

Der Port wird also auf SO_REUSEADDR gesetzt und meiner Meinung nach
sollte das diese Wartezeit verhindern. Beim Neustart scheitert aber das
bind(Socket).

Beim Trennen der Clienten tritt immer ein Fehler beim Senden auf, der zu
einem SIGPIPE führt. Ich dachte bisher, dass die Verzögerung trotz
REUSEADDR an diesem Fehler lag. Jetzt habe ich das aber abgestellt,
indem ich beim Senden MSG_NOSIGNAL angebe und trotzdem noch die Verzögerung:

Len= send(Socket(), Buf, Len, MSG_NOSIGNAL);
// Daten auf Socket schreiben, kein SIGPIPE bei Fehler auslösen

Jetzt weiß ich nicht mehr weiter, wie ich diese Wartezeit endgültig los
werde. Den Timeout-Wert für REUSE habe ich auch schon versucht zu
manipulieren, das hat aber nicht geklappt, jedenfalls nicht für Werte
von ein bis drei Sekunden. Außerdem gefàllt mir das nicht und sollte
auch laufen, ohne dass man am System herumschrauben muss.
Was tun?
 

Lesen sie die antworten

#1 Rainer Weikusat
04/06/2010 - 13:39 | Warnen spam
Edzard Egberts writes:

[...]

{ // Weitermachen, wenn bind geklappt hat und auf Anfragen lauschen
if (listen (Socket(), SOMAXCONN) == SOCKET_ERROR)
{ // Versuch des Lauschens hat nicht geklappt
Fehler(seListen);
}
else
{ // Socket wartet jetzt auf Verbindungen
#ifndef WIN32
bool Reuse_An= true;
setsockopt(Socket(), SOL_SOCKET, SO_REUSEADDR,
&Reuse_An, sizeof(Reuse_An));
#endif



[...]

Der Port wird also auf SO_REUSEADDR gesetzt und meiner Meinung nach
sollte das diese Wartezeit verhindern. Beim Neustart scheitert aber
das bind(Socket).



Bist Du sicher, dass er das wird? Systemaufrufe sind letzen Endes auf
Maschinensprachebene definierte Interfaces und das heisst, dass Du Dir
grundsaetzlich nicht einfach Argument-Datentypen nach Massgabe der
politischen Korrektheit aussuchen kannst, sondern die benutzen
musst, die die Interfacedefinition vorgibt. In diesem Fall waere das
'int' und nicht 'bool'.

Beim Trennen der Clienten tritt immer ein Fehler beim Senden auf, der
zu einem SIGPIPE führt. Ich dachte bisher, dass die Verzögerung trotz
REUSEADDR an diesem Fehler lag.



Das ist, mit Verlaub gesagt, vollkommener Unfug. Der kernel schickt
ein SIGPIPE an eine Anwendung, nachdem diese versucht hat, Daten ueber
eine virtuelle Verbindung ('virtual circuit') zu schicken, deren
'anderes Ende' sich verabschiedet hat (zB wegen Absturz des Servers).
Der Gedanke dahinter ist, dass naive Anwendungen, die von stdin lesen
und nach stdout und stderr schreiben, Netztwerkverbindungen benutzen
koennen, ohne darauf vorbereitet zu sein. Um das in netzwerkbewussten
Anwendungen zu umgehen, kann man entweder entweder das Signal selber
behandeln oder die zugehoerige signal disposition explizt auf SIG_IGN
setzen. Dann bekommt man die selbe Benachrichtigung 'in band' als
EPIPE-Fehler.

[...]

Was tun?



Das, was ich oben geschrieben habe, ist essentiell 'geraten': Falls
bestimmte Dinge, von denen Du nichts geschrieben hast, sich so
verhalten, wie ich das annehme, dann koennte es zutreffen. Es waere
generell eine gute Idee, bei solchen Problemen erstmal strace zu
bemuehen, wenn man Code hat, der keine Fehlerbehandlung macht, und
mitzuteilen, welcher Systemaufruf mit welchem Fehlercode abbricht.

Ähnliche fragen