Schnellere Erkennung eines defekten Sockets in Java / Android

10

Hintergrund

Meine Anwendung sammelt Daten vom Telefon und sendet sie an einen Remote-Server.
Die Daten werden zuerst im Speicher (oder in der Datei, wenn sie groß genug ist) gespeichert und alle X Sekunden oder so werden die Daten durch die Anwendung gelöscht und an den Server gesendet.

Es ist geschäftskritisch, dass jedes einzelne Datenelement erfolgreich gesendet wird. Ich möchte die Daten lieber zweimal senden als überhaupt nicht.

Problem

Als Test habe ich die App so eingerichtet, dass alle 5 Sekunden Daten mit einem Zeitstempel gesendet werden. Das bedeutet, dass alle 5 Sekunden eine neue Zeile auf dem Server erscheint.
Wenn ich den Server töte, erwarte ich, dass die Zeilen aufhören, sollten sie stattdessen in den Speicher geschrieben werden.

Wenn ich den Server wieder aktiviere, sollte ich bestätigen können, dass keine Ereignisse fehlen.

Das Problem ist jedoch, dass es nach dem Töten des Servers etwa 20 Sekunden dauert, bis die IO-Operationen fehlschlagen. Während dieser 20 Sekunden sendet die App glücklich die Ereignisse und entfernt sie aus dem Speicher, aber sie erreichen den Server nie und sind verloren für immer.

Ich muss sicherstellen, dass die Daten tatsächlich den Server erreichen.

Dies ist möglicherweise eine der grundlegenderen TCP Fragen, aber trotzdem habe ich keine Lösung gefunden.

Sachen, die ich versucht habe

Zusätzliche Informationen

Ich kann nicht ändern, wie der Server reagiert, was bedeutet, dass ich dem Server nicht sagen kann, dass er die Daten bestätigen soll (mehr als die TCP-Mechanismen). Der Server akzeptiert die Daten stillschweigend, ohne etwas zurückzusenden.

Codeschnipsel

Initialisierung der Klasse:

%Vor%

Wo Daten gesendet werden:

%Vor%

Aktualisieren 1

Ich sage das noch einmal, ich kann die Art, wie sich der Server verhält, nicht ändern.
Er empfängt Daten über TCP und UPD und sendet keine Daten zurück, um den Empfang zu bestätigen. Dies ist eine Tatsache und in einer perfekten Welt würde der Server die Daten bestätigen, aber das wird einfach nicht passieren.

Update 2

Die von Fraggle gepostete Lösung funktioniert perfekt (Schließen des Sockets und Warten auf das Schließen des Eingabestroms).

Dies kommt jedoch mit einer neuen Reihe von Problemen.
Da ich an einem Telefon bin, muss ich davon ausgehen, dass der Benutzer nicht unendlich viele Bytes senden kann und ich möchte den Datenverkehr möglichst gering halten.

Ich mache mir keine Sorgen wegen des Overheads, einen neuen Socket zu öffnen, diese paar Bytes werden keinen Unterschied machen. Ich mache mir jedoch Sorgen, dass ich jedes Mal, wenn ich mich mit dem Server verbinde, eine kurze Zeichenfolge senden muss, die angibt, wer ich bin.

Die Zeichenfolge selbst ist nicht so lang (etwa 30 Zeichen), aber das summiert sich, wenn ich den Socket zu oft schließe und öffne.

Eine Lösung besteht nur darin, die Daten alle X Bytes zu "leeren", das Problem ist, dass ich X weise wählen muss; Wenn es zu groß ist, werden zu viele doppelte Daten gesendet, wenn der Socket ausfällt und wenn es zu klein ist, ist der Overhead zu groß.

Endgültiges Update

Meine letzte Lösung besteht darin, den Socket zu "leeren", indem ich ihn alle X Bytes schließe, und wenn alles nicht gut geworden ist, werden diese X Bytes erneut gesendet.

Dies wird möglicherweise einige doppelte Ereignisse auf dem Server erstellen, die aber dort gefiltert werden können.

    
Nicklas A. 11.06.2011, 22:29
quelle

6 Antworten

2

Siehe die akzeptierte Antwort hier: Java Sockets und gelöschte Verbindungen

  1. socket.shutdownOutput ();
  2. warte auf inputStream.read (), um -1 zurückzugeben, was darauf hinweist, dass der Peer auch seinen Socket heruntergefahren hat
Fraggle 12.06.2011, 03:01
quelle
3

Die schlechte Nachricht: Sie können ' t eine fehlgeschlagene Verbindung erkennen , außer indem versucht wird, Daten über diese Verbindung zu senden oder zu empfangen.

Die gute Nachricht: Wie Sie sagen, ist es in Ordnung, wenn Sie doppelte Daten senden. Ihre Lösung besteht also nicht darin, Fehler in weniger als 20 Sekunden zu erkennen. Speichern Sie stattdessen einfach einen Ringpuffer, der die Daten der letzten 30 oder 60 Sekunden enthält. Jedes Mal, wenn Sie einen Fehler feststellen und dann erneut verbinden, können Sie die Sitzung starten, indem Sie diese gespeicherten Daten erneut senden.

(Dies könnte problematisch werden, wenn der Server in weniger als einer Minute wiederholt hoch- und runterläuft; wenn dies jedoch geschieht, haben Sie andere Probleme zu bewältigen.)

    
Dan Breslau 12.06.2011 00:25
quelle
3

Dans Lösung ist diejenige, die ich vorschlagen würde, nachdem Sie Ihre Frage gelesen haben, er hat meine Stimme.

Jetzt kann ich vorschlagen, um das Problem zu arbeiten? Ich weiß nicht, ob das mit deinem Setup möglich ist, aber eine Möglichkeit, mit schlecht entworfener Software umzugehen (das ist dein Server, sorry), ist es, es zu umhüllen, oder in einer fancy-design-pattern-talk Fassade oder in plain-talk stellt einen Proxy für Ihren Pain-in-the-behind Server zur Verfügung. Entwerfen Sie ein aussagekräftiges ack-basiertes Protokoll, lassen Sie den Proxy genügend Datensamples im Speicher behalten, um unterbrochene Verbindungen erkennen und tolerieren zu können usw. Kurz gesagt, lassen Sie die Telefon-App mit einem Proxy verbinden, der sich irgendwo auf einem "Server-Level" befindet. Maschine, die das "gute" Protokoll verwendet, dann muss sich der Proxy mit dem Serverprozess verbinden, indem er das "schlechte" Protokoll verwendet. Der Client ist verantwortlich für die Generierung von Daten. Der Proxy ist verantwortlich für den Umgang mit dem Server.

Nur eine andere Idee.

Bearbeite 0:

Vielleicht finden Sie das unterhaltsam: Die ultimative SO_LINGER-Seite, oder: Warum ist mein TCP nicht zuverlässig .

    
Nikolai Fetissov 12.06.2011 04:44
quelle
0

Funktioniert nicht: Server kann nicht geändert werden

Kann der Server nicht jede empfangene Nachricht mit einem anderen Paket bestätigen? Der Client wird die Nachrichten, die der Server noch nicht bestätigt hat, nicht entfernen.

Dies wird Auswirkungen auf die Leistung haben. Um eine Verlangsamung zu vermeiden, können Sie weiterhin Nachrichten senden, bevor eine Bestätigung empfangen wird, und mehrere Nachrichten in einer Rückmeldung bestätigen.

Wenn Sie alle 5 Sekunden eine Nachricht senden und 30 Sekunden lang keine Verbindung vom Netzwerkstapel erkannt wird, müssen Sie nur 6 Nachrichten speichern. Wenn 6 gesendete Nachrichten nicht bestätigt werden, können Sie die Verbindung als unterbrochen betrachten. (Ich nehme an, dass die Logik der Wiederverbindung und des Backlog-Versands bereits in Ihrer App implementiert ist.)

    
9000 12.06.2011 00:08
quelle
0

Was ist mit dem Senden von UDP-Datagrammen in einem separaten UDP-Socket, während der Remote-Host auf diese reagiert, und wenn der Remote-Host nicht antwortet, wird die TCP-Verbindung beendet? Es erkennt einen Verbindungsbruch schnell genug:)

    
Chris Dennett 12.06.2011 00:17
quelle
0

Verwenden Sie http POST statt Socket-Verbindung, dann können Sie eine Antwort auf jeden Beitrag senden. Auf der Client-Seite entfernen Sie nur die Daten aus dem Speicher, wenn die Antwort Erfolg anzeigt.

Sicher ist es mehr Overhead, aber gibt Ihnen was Sie wollen, 100% der Zeit.

    
Fraggle 12.06.2011 02:39
quelle

Tags und Links