TCP-Sockets: Beendigung der Verbindung feststellen
-
Ich hab grade prinzipiell funktionstüchtige Client- und Server-Programme erstellt, die schön miteinander kommunizieren.
Der Server lauscht und akzeptiert eintrudelnde Verbindungen (TCP); er erstellt dann einen neuen Socket für die Kommunikation und übergibt diesen einem Handler, so dass der Server wieder auf weitere Verbindungen lauschen kann.
Ich finde nur nichts darüber, wie der Handler mit dem Kommunikationssocket feststellen kann, dass ein Client die Kommunikation beendet hat (ohne dass der Client irgendwelche Daten sendet, die den Verbindungsabbau mitteilen; der Client beendet sich bspw. mit Absturz oder schließt seinen Socket einfach mit close() ). Die Kommunikationssockets sollen ja nicht unendlich viele werden, ich möchte die wieder schließen sobald ich einen nicht mehr brauche.
Da müsste es doch eine Möglichkeit geben das festzustellen, wenn TCP verwendet wird?!
-
ich weiß ja nicht wie du die daten empfängst - also da gibts polling mit select() oder halt blockierend mit recv(). In jedem Fall bekommst du bei einer beendeten Verbindung ein bestimmten Rückgabewert. In der Man-Page stehen die entsprechenden Rückgabewerte. Ich glaube es ist -1.
blan
-
Beim Verbindungsaufbau müssen ja nicht zwangsläufig Daten gesendet werden. Ich frage, nachdem ich einen Komm.Socket erhalten habe, so ab:
while(cnt <= 0) cnt = commsocket->recvData(cBuf, 10);
int InetSocket::recvData(char* buffP, int CntP) { fd_set rfds; struct timeval tv; int iSelRet; ssize_t iReadRet; /* Watch m_fdSocket to see when it has input. */ FD_ZERO(&rfds); FD_SET(m_fdSocket, &rfds); /* Wait up to one second. */ tv.tv_sec = 1; tv.tv_usec = 0; iSelRet = select(m_fdSocket+1, &rfds, NULL, NULL, &tv); if (iSelRet) { iReadRet = read(m_fdSocket, buffP, CntP); iSelRet = static_cast<int>(iReadRet); } return iSelRet; // returns -1 on error }
Ich überwache den Socket also mit select. Aber nur weil keine Daten kommen heißt das ja nicht dass die Verbindung abgebaut worden ist.
Bei TCP wird dem Server der Verbindungsabbau durch den Client mitgeteilt.
Und auch wenn der Client abstürzt muss ein Serverprozess doch feststellen können ob die Verbindung noch steht. Okay, das könnte man durch einen eigenen Timeout regeln, falls eine gewisse Zeit keine Daten mehr kommen (regelmäßige "keep-alive"-Signale scheint es bei TCP nicht zu geben).
-
Du bekommst nur etwas mit, wenn die andere Seite die Verbindung sauber mit shutdown schliesst.
Wird ein Prozess abgeschossen (z.B. Speicherverletzung) bekommt das die Gegenseite nicht mit. Da liefert recv oder select keinen Fehler.
Für soetwas brauchst du ein "alive" Signal; also Pakete versenden und nach einem Timeout den Client als tot ansehen.
-
ihoernchen schrieb:
Du bekommst nur etwas mit, wenn die andere Seite die Verbindung sauber mit shutdown schliesst.
Okay, aber wie bekomme ich das mit wenn die andere Seite die Verbindung sauber beendet?
Für einen "Absturz" bräuchte ich dann halt einen Timeout.
-
recv liefert z.B. -1 und errno wird auf (?) ECONNRESET gesetzt.
http://www.opengroup.org/onlinepubs/009695399/functions/recv.html
find ich sehr gut zum Nachlesen.Bei select bin ich mir nicht 100% sicher: der socket sollte als lesbar markiert sein und recv wird sich wie oben verhalten (-1 und errno).
-
Auf Deinen letzten Beitrag hin habe ich jetzt mal folgendes ausprobiert (ich verwende read() anstelle von recv() ), das Ergebnis find ich überraschend:
Habe ich eine Verbindung, sende aber keine Daten, dann liefert
- select = 0
- read wird deshalb nicht aufgerufen
Unterbreche ich die Verbindung mit einem close(fdSocket) von der Client-Seite, dann liefert
- select = 1 (!!)
- read = 0
errno ist die ganze Zeit unverändert (SUCCESS). Naja, das Ergebnis könnte ich ja hernehmen um eine beendete Verbindung zu bemerken. Ich könnte es nur von "keine Daten" (0 Bytes) nicht unterscheiden.
-
Bis auf errno ist das Verhalten wie beschrieben.
select sagt ja nur, dass ein Aufruf von recv oder send bzw read oder write auf dem socket nicht blockieren wird; nicht, dass z.B. nur Daten vorhanden sind.
Bei einem return Wert von 0 ist der timeout abgelaufen.Die Sache mit errno wundert mich aber n bissl.
-
if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
} else {
perror("recv");
}
So mach ichs - recv liefert 0 by close und < 0 bei Fehler und es funktioniert
-
Radix schrieb:
Naja, das Ergebnis könnte ich ja hernehmen um eine beendete Verbindung zu bemerken. Ich könnte es nur von "keine Daten" (0 Bytes) nicht unterscheiden.
"keine Daten" (0 Bytes) wirst du außer bei einer geschlossenen Verbindung auch nicht bekommen (dazu müsste dir ja jemand was senden ohne etwas zu senden).
-
Afaik darf errno nicht zusammen mit (P)Threads verwendet werden. Das kann der Grund sein, weshalb errno trotz einseitigen Verbindungsschlusses immer auf SUCCESS blieb, oben.
-
Badestrand schrieb:
"keine Daten" (0 Bytes) wirst du außer bei einer geschlossenen Verbindung auch nicht bekommen (dazu müsste dir ja jemand was senden ohne etwas zu senden).
Ich hätte es schon für möglich gehalten dass ein
(write(fdSocket, buffP, 0)
prinzipiell funktioniert, aber das muss ich in meinem Fall jedenfalls nicht berücksichtigen.
pferdefreund schrieb:
if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
} else {
perror("recv");
}
So mach ichs - recv liefert 0 by close und < 0 bei Fehler und es funktioniertUnd so mach ich's mittlerweile auch.
Fabeltier schrieb:
Afaik darf errno nicht zusammen mit (P)Threads verwendet werden. Das kann der Grund sein, weshalb errno trotz einseitigen Verbindungsschlusses immer auf SUCCESS blieb, oben.
Ich habe nicht mit Threads programmiert, aber wenn man ich das richtig verstehe:
If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0. Otherwise, -1 shall be returned and errno set to indicate the error.
finde ich, errno verhält sich so wie es soll: errno wird nur gesetzt wenn -1 zurückgegeben wird. 0 ist kein Fehler, also SUCCESS.
-
Fabeltier schrieb:
Afaik darf errno nicht zusammen mit (P)Threads verwendet werden.
errno is thread-safe on all platforms that support POSIX threads, *provided* you compile your code correctly.
How to do so depends on the platform. On Solaris, compile with -D_REENTRANT, on AIX with -D_THREAD_SAFE. On Linux, compile with '-pthread' (though Linux errno is thread-safe by default).