Hallo,
ich bastle an einem Kommunikationsprotokoll zwischen Clients und einem
Server. Die Kommunikation erfolgt über UDP (kann nicht geändert werden).
Da kann es vorkommen, dass Pakete verlorengehen oder beschädigt
ankommen.
In einem Teil der Pakete werden Anweisungen zum Eintragen von Daten in
in eine Datenbanken zum Server gesendet. Diese Pakete beantwortet der
Server mit einer Quittung. Sollte eine solche Quittung beim Client nicht
ankommen, kann das zwei Ursachen haben:
a) das ursprüngliche Paket ist beim Server nicht angekommen, also wird
auch keine Quittung gesendet
b) die Quittung ist verlorengegangen
Der Client wartet immer eine gewisse Zeit auf die Quittung und sendet
dann das ursprüngliche Paket evtl. nochmal. Im Falle, dass nur die
Quittung verlorengegenagen ist, erfolgt dann aber die Eintragung in die
Datenbank doppelt - sofern man keine zusätzliche Absicherung betreibt.
Um das zu verhindern gebe ich jedem Paket einen Timestamp mit, der im
Falle einer Wiederholung unverändert bleibt und die Kennung, dass es
sich um ein wiederholtes Paket handelt. So kann der Server nachsehen, ob
er das Paket schon mal vor Kurzem verarbeitet hat.
Das funktioniert auch soweit ganz gut, ist aber ziemlich aufwändig, z.B.
muss eine Liste der zuletzt verarbeiteten Pakete geführt werden. Hat
hier evlt. jmd. noch einen Tip für einen celevereren Algorithmus? Danke.
Frank
"(kann nicht geändert werden)."
Das ist allerdings bloed, weil eigentlich willst du ein hoeheres
RPC-Protokoll (DCE, ONCRPC, SOAP, whatever...) fuer so etwas nutzen.
Frank schrieb:> Um das zu verhindern gebe ich jedem Paket einen Timestamp mit, der im> Falle einer Wiederholung unverändert bleibt und die Kennung, dass es> sich um ein wiederholtes Paket handelt. So kann der Server nachsehen, ob> er das Paket schon mal vor Kurzem verarbeitet hat.>> Das funktioniert auch soweit ganz gut, ist aber ziemlich aufwändig, z.B.> muss eine Liste der zuletzt verarbeiteten Pakete geführt werden. Hat> hier evlt. jmd. noch einen Tip für einen celevereren Algorithmus? Danke.
Schau mal, wie es bei TCP gemacht wird, nämlich mittels Sequenz-Nummern.
Wenn du dazu die Annahme machen kannst, dass sich Pakete niemals
"überholen" oder das nur sehr wenige Pakete betrifft, scheint mir das
viel effizienter zu sein, als mit Timestamps und Retransmit-Kennungen zu
hantieren.
Frank schrieb:> Um das zu verhindern gebe ich jedem Paket einen Timestamp mit, der im> Falle einer Wiederholung unverändert bleibt und die Kennung, dass es> sich um ein wiederholtes Paket handelt. So kann der Server nachsehen, ob> er das Paket schon mal vor Kurzem verarbeitet hat.
Das mit der expliziten Retransmit-Kennung als Signal "schau in der
Liste" solltest du lassen, wenn die Kommunikation in einer nicht von dir
selbst vollständig kontrollierten Umgebung erfolgt (und eigentlich
nichtmal dann). Es kann vorkommen, dass Pakete bei der Übertragung
verdoppelt werden, und das ist durchaus keine rein theoretische
Möglichkeit, sondern das habe ich schon einige Male auch selbst real
gesehen.
Mit Sequenznummern braucht sich der Empfänger keine Liste der letzten
Pakete merken, sondern er muss nur die aktuelle Sequenznummer
speichern. Er muss dann bei einem empfangenen Paket nur die Nummer des
Paketes mit seiner eigenen aktuellen Nummer vergleichen:
- Ist sie kleiner oder gleich der eigenen, dann darf das Paket
nicht erneut bearbeitet werden, es muss aber trotzdem ein ACK
gesendet werden.
- Ist sie exakt eins grösser als die eigene, wird das Paket
bearbeitet, ein ACK gesendet und die eigene Nummer um eins erhöht.
- Ist sie mehr als eins grösser als die eigene, wird das Paket
einfach verworfen, der Sender sendet das dann ja später erneut,
und bis dahin sind dann hoffentlich auch die fehlenden Pakete
"dazwischen" eingetroffen.
Das Verwerfen von Paketen im letzten Fall oben führt zu suboptimalem
Verhalten, wenn die Reihenfolge der Pakete unterwegs geändert wurde, da
Pakete verworfen werden, die eigentlich korrekt angekommen sind. In sehr
vielen Fällen tritt das in der Praxis aber nur selten auf, weshalb es
meist nicht lohnt, den Zusatzaufwand dafür zu betreiben. Der Aufwand
wäre im wesentlichen so hoch, dass man auch gleich TCP benutzen könnte.
Was du noch brauchst ist ein Synchronisationsmechanismus, mit dem am
Anfang der Kommunikation (oder dann wenn zuviele Pakete zwischendurch
verloren gehen, so dass der Sender irgendwann aufgibt) die
Sequenznummern von Sender und Empfänger auf den gleichen Stand gebracht
werden. Am besten schaust du dir dazu mal den Three-Way-Handshake von
TCP an.
Andreas
Ergänzung: auch für den Sender bringt dieses Sequenznummern-Verfahren in
einigen Fällen eine Verringerung des Aufwandes mit sich.
Angenommen, das letzte ACK hatte die Nummer 4, die Zähler bei Sender und
Empfänger stehen also beide auf 4. Jetzt werden die Pakete 5, 6, 7 und 8
gesendet, vom Empfänger bearbeitet und geACKt, es gehen jedoch die ACKs
5, 6 und 8 verloren.
Der Sender kann jetzt, wenn er das ACK 7 empfängt, nicht nur das Paket 7
aus seiner Retransmit-Liste entfernen, sondern auch die Pakete 5 und 6,
da er weiss, dass der Empfänger nur dann das ACK 7 senden würde, wenn er
5 und 6 schon erfolgreich empfangen hat. Der Sender muss also nur das
Paket 8 erneut senden.
Andreas
Danke für die Infos, ich werde mal versuchen, etwas draus zu machen.
Sequenznummern klingen vernünftig. Allerdings muss ich die wohl für
jeden Clienten einzeln führen, wenn es mehrere an einem Server sind.
Was mache ich bei Zähler-Überlauf (z.B. Integer mit 4 Byte)? Aber wenn
der Server nach der gleichen Methode zählt, geht es ihm genau so und
dann stimmts ja wieder ...
Frank
Frank Esselbach schrieb:> Allerdings muss ich die wohl für> jeden Clienten einzeln führen,
Sinnvollerweise behandelt ein Server jeden Client logisch getrennt in
einer eigenen Session.