Laden...

TCP Keepalive / Polling

Erstellt von Tarion vor 14 Jahren Letzter Beitrag vor 13 Jahren 6.966 Views
T
Tarion Themenstarter:in
381 Beiträge seit 2009
vor 14 Jahren
TCP Keepalive / Polling

Gibt es eine saubere Lösung schnell zu detektieren ob eine TCP-Verbindung noch besteht.
Kann ich etwas senden und auf ACK warten ohne das die andere Seite auf Anwendungsebene Daten bekommt? Wie hoch sind die Timeouts für die TCP Verbindung fürs ACK oder wo stelle ich die ein.

Ich hab auch schon was gecoded, aber das kommt mir nicht sehr zuverlässig / schnell vor:


 bool blockingState = tcpClient.Client.Blocking;
            try
            {
                byte[] tmp = new byte[1];
                tcpClient.Client.
                tcpClient.Client.Blocking = false;
                tcpClient.Client.Send(tmp, 0, SocketFlags.None);
                // Connected!
            }
            catch (SocketException e)
            {
                // 10035 == WSAEWOULDBLOCK
                if (e.NativeErrorCode.Equals(10035))
                {
                    Logger.Log("Poll: Still Connected, but the Send block", LogLevel.Warning);
                }
                else
                {
                    Logger.Log(String.Format("Poll: Disconnect detected: error code {0}!", e.NativeErrorCode), LogLevel.Info);
                    InvokeDisconnect();
                }
            }
            finally
            {
                tcpClient.Client.Blocking = blockingState;
            }

S
72 Beiträge seit 2006
vor 14 Jahren

Ob du verbunden bist, kannst du mit einem nicht blockierenden, Zero-byte Send() prüfen und anschließend den Rückgabewert auswerten. Das hast du ja bereits in deinem Beispielcode gemacht. Dass das ganze Zeit in Anspruch nimmt, ist klar. Stell dir vor die Gegenstelle hat einen Stromausfall. Ein Verbindungsabbau mit FIN kommt so nie zustande. Du musst also explizit die Verbindung prüfen.

Bei einem TcpClient hast du kein Zugriff auf TCP-Flags (ACK, SYN, etc.), das ist gerade der Sinn von Protokollen. TcpClient hat auch keine Timeout Eigenschaft für synchrone Aufrufe. Da musst du schon direkt mit der Klasse Socket arbeiten und dort ist der Standardwert 0.

“Ene mene mu und raus bist Du!”

T
Tarion Themenstarter:in
381 Beiträge seit 2009
vor 14 Jahren

Ich such nochmal in der socketklasse nach Timeouts, im moment stellt das Programm erst nach mehreren Sekunden den Disconnect fest. Das müsste doch irgendwie schneller gehen.

S
72 Beiträge seit 2006
vor 14 Jahren

Ich such nochmal in der socketklasse nach Timeouts, im moment stellt das Programm erst nach mehreren Sekunden den Disconnect fest. Das müsste doch irgendwie schneller gehen.

Wer beendet die Verbindung eigentlich? Normalerweise läuft unter TCP der Verbindungsabbau (FIN) geordnet ab, so dass beide Seiten informiert werden. Dazu sendet der abbrechende Peer ein Nullbyte.

In deinem Beispielcode passt zudem folgende Zeile nicht.

tcpClient.Client.Send(tmp, 0, SocketFlags.None);

Das muss

tcpClient.Client.Send(tmp, 1, SocketFlags.None);

lauten.

“Ene mene mu und raus bist Du!”

T
Tarion Themenstarter:in
381 Beiträge seit 2009
vor 14 Jahren

Alles was ordentlich läuft ist kein Problem. Ich will merken wenn das W-Lan weg ist oder der Stecker gezogen wurde.

Funktioniert mein Code noch wenn ich byte[] tmp = new byte[0]; sende und die 0 lasse?

S
8.746 Beiträge seit 2005
vor 14 Jahren

Der Sender kann den Verbindungsabbruch nur merken, wenn er sendet, der Empfänger nur über einen Timeout. Theoretisch geht auch TCP-Keep-Alive, aber das funzt nur unter bestimmten Vorraussetzungen. Wenn du merken willst, ob das WLAN weg ist, dann musst du dir vom WLAN-Adapter die entsprechenden Events schicken lassen. Auf der TCP-Ebene merkst du das eben gar nicht oder erst später.

L
667 Beiträge seit 2004
vor 14 Jahren

Mit TcpClient / TcpListener hab ich es bei mir so realisiert, dass alle verbundenen Clients regelmäßig eine "Ping"-Message an den Server schicken, so eine Art Heartbeat quasi. Wenn ein Client nicht mehr "pingt", weiß der Server, dass der Client weg ist.

Nicht besonders schnell bzw. zeitnah, aber was Besseres hab ich nicht gefunden.

"It is not wise to be wise" - Sun Tzu

61 Beiträge seit 2009
vor 13 Jahren

Mit TcpClient / TcpListener hab ich es bei mir so realisiert, dass alle verbundenen Clients regelmäßig eine "Ping"-Message an den Server schicken, so eine Art Heartbeat quasi. Wenn ein Client nicht mehr "pingt", weiß der Server, dass der Client weg ist.

Nicht besonders schnell bzw. zeitnah, aber was Besseres hab ich nicht gefunden.

Genauso mache ich das bei mir auch. Will nicht sagen, dass es die beste Variante ist, aber es funktioniert. Das läuft bei mir über Timeouts.

Habe das so gelöst:
Der Client ist bei mir passiv und fragt den Server nie, ob er noch da ist, sondern wartet immer auf die Anfrage vom Server, der wissen will, ob der Client noch da ist.
Wenn der Client keine Anfrage mehr vom Server erhält (z.B. 10 Sekunden lang) dann gilt das für den Client als Timeout und die Verbindung zum Server wird getrennt.
Wenn der Client die Verbindung zum Server trennt, schickt dieser ihm einen Byte-Wert, dass er jetzt die Verbindung trennt und schließt den NetworkStream. Der Server erhält die Mitteilung und wirft ihn sozusagen "raus".

Aber habe dabei das Gegen-Problem:
Wenn der Server die Verbundung zum Client beendet, sendet dieser ebenfalls ein Disconnect-ByteWert und schließt dann die Verbindung.
Aber dieser Wert kommt nie beim Client an und der Client beendet mit Timeout.

Ich habe schon Flush() und BinaryWriter-Klasse versucht zu verwenden, aber nichts hat geholfen.

Wie stelle ich sicher, dass die Daten noch beim Client ankommen, bevor der Server dicht macht?

In der Zeit vor fünf Minuten ist Jetzt die Zukunft. Jetzt ist die Gegenwart. Die Zeit, in der ich zu erzählen begonnen habe, ist die Vergangenheit von Jetzt und die Zukunft von der Gegenwart der Zeit, fünf Minuten bevor ich zu erzählen begann.

T
Tarion Themenstarter:in
381 Beiträge seit 2009
vor 13 Jahren

Senden vor dem Disconnect sollte gehen und müsste ankommen. Mehr kann man dazu nur sagen wenn du etwas Beispielcode hast. Irgendwo scheint dein Server die Verbindung vor dem Senden der Disconnect Nachricht zu schließen.
Mach aber lieber einen neuen Thread mit genauer Problembeschreibung auf, dein problem hat nichts mehr mit polling oder keepalive zu tun.

61 Beiträge seit 2009
vor 13 Jahren

Lohnt sich nicht, habe das Problem gelöst.

Ich habe einmal vergleicht, wo der Unterschied zwischen dem Trennen der Verbindung vom Client zum Server mit dem Trennen der Verbindung vom Server zum Client liegt und habe dabei folgendes festgestellt:
Ich habe die Tickzahl jeweils für das Senden von Disconnect und das Schließen des NetworkStream genommen.
Beim Client-Disconnect gab es eine Differenz von gut 10000+ Ticks und beim Server 0 Ticks. Der Unterschied beim Client war, dass ich das Senden der Disconnect-Nachricht asynchron ausgeführt habe.

Beim Server habe ich das dann so gelöst:
Ich habe einen anonymen Delegaten als System.Threading.ThreadStart deklariert (weil ThreadStart ein einfacher Delegat ohne Parameter und ohne Rückgabewert ist) und das Senden des Disconnect über BeginInvoke (also asynchron) ausgeführt. Im eigentlichen Thread habe ich noch einen Wartepunkt benutzt und dann erst den Stream geschlossen. Und es funktioniert.

Soll ich am besten noch einen Thread als gelöst öffenen? ^^

In der Zeit vor fünf Minuten ist Jetzt die Zukunft. Jetzt ist die Gegenwart. Die Zeit, in der ich zu erzählen begonnen habe, ist die Vergangenheit von Jetzt und die Zukunft von der Gegenwart der Zeit, fünf Minuten bevor ich zu erzählen begann.