TCP Protokoll auf Softwareebene

Bereits seit ein paar Jahren ist mir bekannt das mein geschriebener Server Probleme mit Android Smartphone Clients hat.
Das Problem ist das sich manche Smartphones irgendwie nicht richtig an das TCP Protokoll halten oder anderst herum.
Kein Problem für die Clienten aber auf Serverseite treten Probleme auf, z.B. bekomme ich keine Nachricht wenn sich ein Client abmeldet oder auch nicht wenn die Verbindung abbricht (kein Internet).
Und andere solche Kleinigkeiten.

Auf Serverseite bedeutet das eine Liste voll mit Clients die nicht mehr existieren, nicht antworten obwohl sie noch da sind oder anderweitig Exceptions auslösen.

Funktioniert zwar aber ein bisschen nervig.
TCP ist hardwareseitig umgesetzt aber die Frage wäre ob man das vielleicht softwareseitig einheitlich umsetzen kann?

Bist du dir sicher, dass es daran liegt? Kleinere Abweichungen von den RFCs gibt es eigentlich immer. Das sind in der Regel aber Randbereiche oder unkritische Stellen, sodass das im Alltag nicht auffällt.

Bist du dir auch da sicher? Ich habe mich selbst noch nicht damit auseinandergesetzt, wie ein TCP/IP-Stack bei den gängigen Betriebssystemen implementiert ist. Allerdings gibt es betriebssystemspezifische Unterschiede, weshalb der TCP/IP-Stack in Software umgesetzt sein müsste.

Grundsätzlich hört sich dein Problem recht merkwürdig an und ich kann mir kaum vorstellen, dass es tatsächlich im TCP/IP-Stack liegt.

Aber: Ja, natürlich kann man TCP auch in Software umsetzen. Ein Beispiel hierfür ist uIP, ein TCP/IP-Stack für 8-Bit-Mikrocontroller: GitHub - adamdunkels/uip: The historical uIP sources

Naja, anderst kann ich mir das nicht erklären. Es sind nur manche Android Smartphones betroffen was ich bisher erlebt habe, die anderen Clients kommunizieren normal.
Eventuell weil über das mobile Netz kommuniziert wird oder wegen der Stromsparoption?
Wie gesagt besteht das Problem schon seit längerem. Dass der Server am laufen bleibt setzt vorraus die Clients hin und wieder rauszuschmeisen; sonst kann man wegen Fehler in der Kommunikation irgendwann nicht mehr miteinander kommunizieren geschweige denn sich verbinden.

Aber an was könnte das denn sonst liegen wenn nicht an der TCP Umsetzung?

Ich hab jetzt überlegt evt. TCP über UDP umzusetzen, vorrausgesetzt das UDP ganz normal funktioniert :smiley:

Ich würde auf die entsprechenden Stromsparfunktionen tippen. Kann aber natürlich auch ein Bug im „Netzwerkchip“ sein. Der TCP/IP Stack ist zu großen Teilen in Software implementiert und greift dann über HAL auf den Hardware zu, die dann nur noch Ethernet-Frames in Hardware macht. Daher kann z.B. bei Solarflarekarten der OS Stack übersprungen werden um die Latenz extrem zu reduzieren.

Zurück zum Problem: Wann tritt es auf? Bewegen sich die Leute? Könnten es Handovers aus dem Mobilfunknetz sein? Können die Geräte mit Problem evtl nur 3G oder nur 4G? Haben sie Dual-SIM und damit (höchst wahrscheinlich) Dual-SIM-Standby? usw usw usw :slight_smile:

Ich würde darauf tippen, der TCP Stack ist es nicht, der kommt von Android (von Linux) und ist, bis auf Versionsunterschiede, gleich. Die Treiber könnten natürlich ein Problem sein, genau wie die Hardware.

TCP over UDP nennt sich normalerweile Reliable UDP oder R(U)DP und Enet ist eine relativ bekannte Implementierung. Als Alternative könntest du dir QUIC anschauen (von Google). Aber das schützt nur vor Verbindungsabbrüchen, weil es keine Verbindung mehr gibt. Normal sollte dein App aber eh automatisch reconnecten :wink:

Ja und Nein,

das liegt nicht wirklich an den Handys. Nimm einfach einen PC steck den an einen Switch. Den steckst Du an einen anderen Switch, der wieder einen 2. PC bekommt. Baue eine Verbindung zwischen beiden PC via TCP auf. Wenn Du die Verbindung zwischen beiden Switch trennst, merken beide PC nichts.

Dir bleibt nichts weiter übrig, als zwischen den beiden Endpunkten immer ein “ALive”-Paket zu senden. Dabei sollte der Timeout aber Deinen Anforderungen für die Anwendung entsprechen.

Ich mache das seit Jahren so, mit einem Timeout von 5s (teilweise zu langsam).

hand, mogel

Ja, würde auch vorschlagen einfach alle paar sekunden ne awake message oder so zu schicken. da reichen ja ein paar entsprechend festgelegte bits oder wie auch immer. falls dann 2 solcher dinger nicht ankommen, kickst du halt den client raus oder was auch immer je nach anwendungsfall.

könntest du bezüglich des eigentlichen probls irgendwie konkreter werden? evtl runnable code examples posten? Welche mobilen geräte benutzt du zum testen bzw bei welchen treten die fehlet auf? passiert das vllt auch auf nem emulator - dann dürfte es doch nicht an der hardware liegen? oder einfach mal den code kopieren und nen pc client schreiben und schauen ob du den fehler da reproduziert bekommst?..

also ich gehe davon aus, du schicks ne “abmeld” nachricht aber es kommt nichts an? oder hab ich iwas falsch verstanden?

Da braucht man sich gar nicht drum zu kümmern. TCP kümmert sich selbst um Aspekte der Verbindungssicherheit (wie Reihenfolge der Pakete, Korrektheit der Daten, Erreichbarkeit der Gegenstelle, …). Das heißt, wenn ein Paket abgeschickt wird, muss die Server- oder Clientanwendung das Paket nur entgegennehmen und gar nicht weiter verarbeiten, denn TCP erkennt den Verbindungsabbruch nach einigen automatischen Retransmissions von selbst.

Edit: was bei einem erkannten Verbindungsabbruch passieren soll, muss natürlich trotzdem in der Anwendung umgesetzt sein.

Konzentrieren wir uns mal auf das Problem, dass disconnects nicht bemerkt werden - weder absichtlich noch durch äußere Einflüsse.
Das mit den Code-Tags hab ich noch nicht raus wie man die auf Java umstellt.
Auf Client Seite:

output.close(); input.close(); socket.close();
Im Moment lasse ich eine Disconnect-Message kurz vorher abschicken, ob die zuverlässig ankommt wenn man kurz danach den Socket schließt?

Soweit ich weis ist das egal. Da müsste ich mal eine Statistik mitlaufen lassen.
Also mein Smartphone das ich mir jetzt gekauft habe, hat das gleiche Problem, selbst im eigenen lokalen WLan.

Kein Emulator, echte Geräte; auch PC Clients und die funktionieren einwandfrei, zumindest konnte ich keine unregelmäßigkeiten feststellen.
(Achja über iOS kann ich übrigens nichts sagen, darauf hab ich die App nicht portiert.)

Aber sollte TCP nicht eine IOException werfen wenn keine Verbindung mehr besteht?

try { publishLog("[Server] " + message); writer.write(message + DELIMITER); writer.flush(); } catch (IOException e) { e.printStackTrace(); }
Wie gesagt, PC clients melden sich hier praktisch ab.
Bei (einigen) Android Geräten geht das aber durch. Dass die Nachricht nie ankommt, scheint auf Server Seite nicht registriert zu werden.

Das kann nur passieren, wenn TCP den Verbindungsabbruch feststellt. Das kann es über TCP-Keepalive-Pakete, die aber gemäß Keepalive - Wikipedia standardmäßig deaktiviert sind.

Du kannst erzwingen, dass TCP den Verbindungsabbruch feststellst, indem du einfach leere Pakete zum Server bzw. vom Server zum Client schickst.

Vom Protokoll ja, praktisch ist das so eine Sache. Schau mal nach Nagle-Algorithm.

Ja, weil Android einfach das WLAN abschaltet. Damit kommen vom Android auch nicht mehr die Reset-Meldungen des TCP durch. Der WLAN-Router ist nur für das Weiterleiten der Pakete zuständig. Den Zustand des Protokolls ist dem Router nicht bekannt bzw. völlig egal.

Zieh einfach ein Kabel - aber nicht das was in der Netzwerkkarte stecke. Sonst erzeugt das Betriebssystem die entsprechende TCP-Meldung.

Wie ich oben schon schrieb: wenn irgendwo zwischendurch das Kabel getrennt wird, gibt es keine Meldungen. Wie auch? Die beiden Switche bekommen nur mit das Physikalisch nichts mehr auf dem Port geht. Und über den Zustand irgend welcher Pakte bzw. Verbindungen haben die Switch auch keine Ahnung. Wen sollen die Switch informieren?

Nur wenn Du liest oder sendest. Machst Du nichts auf der Verbindung, bekommst Du nur eine Meldung wenn die Gegenstelle den Socket schließt und die Physik dir die Meldung durch reicht. Reist dir ein Bagger zwischen durch das Kabel ab, passiert nichts. Im schlimmsten Fall läuft dir dein Server mit toten Clients voll.

nur kurz:

  • Timeout kommt erst nach 2 Stunden
  • KeepAlive ist optional und ist abhängig von der Implementierung des TCP-Stacks

Aber genau das passiert eben nicht. Ich kann eine Millionen Nachrichten an den Client senden, sie kommen an… obwohl die App auf Android den Socket bereits geschlossen hat. Damit finde ich nicht heraus ob der Client noch existiert.
Einzige Möglichkeit wäre es, wie bereits erwähnt, einen Timeout manuell zu implementieren und auf eine Antwort zu warten. Das ist schwerer oder eher gesagt “nerviger” als es sich anhört, weil man ein Haufen Threads braucht um jeden Clienten zu bearbeiten und so leistungsfähig ist der Server halt auch nicht.

Im Moment ist es zudem so, dass ein Android Client sich nach einem Verbindungsabbruch nicht mehr anmelden kann. Ich kriege auch überhaupt keine Meldung über einen wartenden Clienten solange ich auf Serverseite den Serversocket nicht schließe.

Hast du mal mit tcpdump / wireshark den Traffic vom Server überwacht und analysiert, ob vom Clienten ACK-Pakete zurück kommen?

klingt danach, als wenn Du den Nagle-Algo nicht ausgeschaltet hast

C&P aus meiner Netzwerk Implementierung

/** ein neuer Client auf Serverseite */
    public NetClient(Socket socket) {
            logger.info("Neuer Server-Client");
            this.socket = socket;
            ip = "0.0.0.0";
            port = -1;
            try {
                    this.socket.setTcpNoDelay(true);
                    this.socket.setSoLinger(true, 0);
            } catch(SocketException e) {
                    e.printStackTrace();
            }
            setConnected(true);
            Init();
    }