Bestätigungszuverlässigkeit mit UDP

16

Ich habe eine Frage zu UDP. Für den Kontext arbeite ich an einem Echtzeit-Actionspiel.

Ich habe einiges über die Unterschiede zwischen UDP und TCP gelesen und ich habe das Gefühl, dass ich sie recht gut verstehe, aber es gibt ein Stück, das sich nie richtig angefühlt hat, und das ist Zuverlässigkeit und besondere Anerkennung . Ich verstehe, dass UDP standardmäßig keine Zuverlässigkeit bietet (dh Pakete können verworfen werden oder unordentlich ankommen). Wenn eine gewisse Zuverlässigkeit erforderlich ist, sind Bestätigungen die Lösung, die ich gesehen habe (was konzeptionell sinnvoll ist) (dh der Server sendet ein Paket an den Client, und wenn der Client diese Nachricht empfängt, sendet er eine Bestätigung an den Server zurück). .

Was passiert, wenn die Bestätigung verworfen wird?

Im obigen Beispiel (ein Server sendet ein Paket an einen Client) behandelt der Server den potenziellen Paketverlust, indem er Pakete in jedem Frame erneut sendet, bis für diese Pakete Bestätigungen eingehen. Sie könnten immer noch auf Probleme mit der Bandbreite oder Nachrichten in der falschen Reihenfolge stoßen, aber rein aus der Perspektive des Paketverlusts wird der Server abgedeckt.

Wenn der Client jedoch eine Bestätigung sendet, die niemals eintrifft, hat der Server keine andere Wahl, als das Senden dieser Nachricht zu beenden, was das Spiel unterbrechen könnte, wenn die in diesem Paket enthaltenen Informationen erforderlich wären. Sie könnten einen ähnlichen Ansatz für den Server wählen (dh weiterhin Bestätigungen senden, bis Sie eine Bestätigung für die Bestätigung erhalten?), Aber bei diesem Ansatz müssten Sie für immer hin- und herschleifen (da Sie eine Bestätigung für die Bestätigung für die Bestätigung benötigen würden) und so weiter).

Ich habe das Gefühl, dass meine Grundlogik hier richtig ist, was mir zwei Möglichkeiten lässt.

  1. Senden Sie ein einziges Bestätigungspaket und hoffen Sie auf das Beste.
  2. Senden Sie eine Handvoll Bestätigungspakete (möglicherweise 3-4) und hoffen Sie auf das Beste, vorausgesetzt, nicht alle werden verworfen.

Gibt es eine Antwort auf dieses Problem? Verstehe ich etwas grundsätzlich falsch? Gibt es eine Garantie für die Verwendung von UDP, die mir nicht bekannt ist? Ich zögere, mit zu viel Netzwerkcode voranzukommen, bis ich mich sicher fühle, dass meine Logik solide ist.

Grimelios
quelle
11
Vielleicht fehlt Ihnen eine Vorstellung von "Zeitüberschreitungen" und "Wiederholungsversuchen".
Kromster sagt Unterstützung Monica
Ich könnte sicher sein. Sie meinen, meine Logik sei korrekt und nicht zu negativ, aber während der Netzwerkprogrammierung kann ich für praktisch alle vernetzten Informationen keine Garantie übernehmen? Während eines Echtzeitspiels sind das eine Menge potenziell verworfener Informationen, was in Ordnung ist, aber ich möchte nur sicherstellen, dass ich das Problem verstehe.
Grimelios
10
Keine Garantie. Richtig. Nehmen Sie niemals "Hoffnung" in Ihre Algorithmen auf. Sie müssen mit JEGLICHEN unglücklichen Kombinationen umgehen. PS Wir haben in unserem RTS einfach auf TCP umgestellt, wo für alles gesorgt ist, da wir eine zuverlässige Kommunikation benötigen (für die Lockstep-Simulation).
Kromster sagt Unterstützung Monica
5
Verwenden Sie TCP, wenn Zuverlässigkeit erforderlich ist, und UDP, wenn es keine Rolle spielt. Zum Beispiel werden die Koordinaten des Spielers in meinem Spiel über UDP gesendet. Ich verwende Interpolation und Glättung, um fehlende Pakete auszugleichen. klappt wunderbar. Dinge, die wirklich zuverlässig sein müssen, aber etwas langsamer sein können, werden über TCP gesendet. Wenn Sie einen Zustand haben, in dem ein neuerer Zustand den alten Zustand ungültig macht, ist UDP eine gute Wahl, da es keine Rolle spielt, wann etwas dazwischen abgelegt wurde 8e. Spielerposition).
Polygnome
Dies ist keine direkte Antwort auf Ihre Frage, aber ich empfehle dringend, eine Bestätigung in einem Echtzeitspiel nur dann zu fordern, wenn sie unbedingt erforderlich ist (z. B. bei der erstmaligen Verbindung). Es ist viel einfacher (und robuster), sowohl den Client als auch den Server so zu gestalten, dass sie "mit dem arbeiten, was sie haben", bis sie ein neues Paket in einem zustandslosen System erhalten, wenn Sie können. Quake 3 hat dies mit einem Snapshot-basierten System unglaublich gut gemacht . Auch Bibliotheken wie Enet können nur bestimmte Pakete zuverlässig senden, wenn Sie es wirklich brauchen
jrh

Antworten:

32

Dies ist eine Form des Zwei-Generäle-Problems , und Sie haben Recht - keine Anzahl von Wiederholungsversuchen ist ausreichend, um den Empfang perfekt zu garantieren.

In der Praxis in Spielen gibt es normalerweise einen Zeithorizont, über den hinaus die Informationen keine Rolle spielen, auch wenn sie technisch zuverlässig ankommen. Als ob Sie herausgefunden hätten, dass Sie vor zwei Sekunden einen perfekten Kopfschuss hatten - es ist zu spät für den Spieler, diese Informationen jetzt zu verwenden.

Wenn Ihr Paketverlust so hoch ist, dass Sie die benötigten Informationen nicht routinemäßig in einem engen Reaktionsfenster abrufen können, ist es für ein Echtzeitspiel möglicherweise besser, den Spieler auszuschalten und zu versuchen, eine bessere Übereinstimmung für ihn zu finden, als anderswo Versuchen Sie weiterhin, das Paket zu senden, um eine zuverlässige Verbindung herzustellen.

Aus diesem Grund überspringen einige Spiele-Replikationssysteme die Bestätigung und wiederholen sie insgesamt und wählen so oft wie möglich, nur das neueste Update als Spam zu versenden. Wenn jemand fallen gelassen wird oder zu spät ankommt, kann er es überspringen, den nächsten aufheben und weitermachen, wobei er sich auf die Vorhersage- und Interpolationssysteme verlässt, um die Lücke zu glätten und für den Spieler sichtbare Schluckaufe zu minimieren.

Ich möchte plötzlich anfangen, diese "Simba-Replikation" zu nennen, weil sie Probleme in der Vergangenheit ignoriert und versucht, im gegenwärtigen Moment zu leben. ;)

Rafiki legt eine gewisse Reduktion ad absurdum auf diese Lebensphilosophie

Eine hybride Lösung besteht darin, das neue Update UND vorauszusenden (da Updates des Spielstatus häufig recht klein / komprimierbar sein können) ) auch das letzte Update einzupacken, und möglicherweise das vorherige Sie müssen nicht auf eine vollständige Hin- und Rückfahrt warten, um dies herauszufinden und zu beheben. Meistens hat der Client dies bereits gesehen, sodass auf diese Weise redundante Daten vorhanden sind. Die Latenz für die Korrektur einer verpassten Nachricht ist jedoch geringer. Die Aktualisierungen des Clients können die Indexnummer der letzten aufeinanderfolgenden Aktualisierung enthalten, die sie gesehen haben. Sie können also mit der Anzahl der alten Aktualisierungen, die Sie in das nächste Aktualisierungspaket aufnehmen, minimal konservativ sein.

Sie können ein zweistufiges System auch als einen anderen Hybridtyp implementieren, bei dem der kurzlebige Zustand in unzuverlässiger Schnelligkeit repliziert wird und der langfristige Zustand zuverlässig synchronisiert wird, indem Sie TCP oder Ihre eigene Zuverlässigkeitsimplementierung mit einem hohen Wiederholungsversuch verwenden Anzahl. Die Verwaltung wird jedoch komplexer, da Sie zwei Messaging-Systeme warten müssen und die beiden Snapshots möglicherweise nicht miteinander synchronisiert sind, wodurch eine völlig neue Klasse von Edge-Cases hinzugefügt wird.

DMGregory
quelle
1
+1, gut geschrieben. Ich möchte nur betonen, dass dies für Action- / Echtzeitspiele relevanter ist. TBS- und RTS-Spiele (und einige Ereignisse in Actionspielen) sehen den "Zeithorizont, über den hinaus die Informationen eigentlich keine Rolle spielen" anders.
Kromster sagt Unterstützung Monica
3
Ja, für ein rundenbasiertes Spiel würde man sich vorstellen, TCP zu verwenden, anstatt zu versuchen, die eigene Zuverlässigkeitsschicht auf UDP zu setzen. ;) Ich würde das Mikro in einem RTS dennoch als die Art von Gameplay mit einem genauen Zeithorizont einstufen - dieser hybride Ansatz kann dort gut funktionieren, wo Sie sowohl Updates mit geringer Latenz für die Hitze des Augenblicks als auch ein Update haben Sicherheitsnetz zur rückwirkenden Behandlung von kritischen Ereignissen wie Ressourcenausgaben.
DMGregory
Das ist sehr hilfreich und bestätigt meine anfängliche Besorgnis. Vielen Dank.
Grimelios
2
Es kann auch nützlich sein, die Vorwärtsfehlerkorrektur zu erwähnen. Entwerfen Sie Ihr Protokoll so, dass der Empfänger unabhängig feststellen kann, dass ein Paket beim Empfang des nächsten Pakets verworfen wurde, und einige zusätzliche Daten hinzufügt, um die erforderliche Interpolation auszugleichen. Dies kann nützlich sein, da die UDP-Pakete oft ohnehin nicht voll sind und Sie nur häufig kleinere Pakete senden, um die Latenz gering zu halten. Das Hinzufügen einiger zusätzlicher Bytes wird die Latenz nicht beeinträchtigen, und die Bandbreite ist in diesen Fällen kein Problem.
MSalters
@MSalters Ich würde sagen, das ist es wert, in seiner eigenen Antwort näher darauf einzugehen, wenn Sie dazu bereit sind. Ich würde das positiv bewerten. :)
DMGregory
9

Der Ansatz, den TCP verwendet, besteht darin, dass der Absender das Paket erneut sendet, bis er eine Bestätigung erhält. Der Empfänger ignoriert doppelte Pakete, sendet jedoch weiterhin Bestätigungen für diese. Der Absender ignoriert doppelte Bestätigungen.

Wenn ein Paket verloren geht, sendet der Absender es erneut, wie Sie bereits wissen.
Wenn eine Bestätigung verloren geht, sendet der Absender das ursprüngliche Paket erneut, wodurch der Empfänger die Bestätigung erneut sendet.

Wenn innerhalb einer bestimmten Zeit (vielleicht 60 Sekunden oder 20 Wiederholungsversuche) keine Bestätigung eingeht, wird davon ausgegangen, dass der Spieler vom Spiel getrennt ist. Sie müssen eine Art Zeitlimitregel implementieren. Andernfalls bindet ein Player, der sein Netzwerkkabel abzieht, für immer Ressourcen auf Ihrem Server.

user253751
quelle
Ein wesentliches Merkmal von TCP ist, dass sich der Absender nicht darum kümmern muss, ob ein bestimmtes Paket bestätigt wurde, sondern sich hauptsächlich um die "High-Water-Mark" kümmern muss und wie lange Pakete ausstehen, ohne dass sich die High-Water-Mark bewegt.
Supercat
1
@supercat Ich würde nicht sagen, dass das wesentlich ist; eher wie eine Optimierung.
user253751
In Bezug auf die Sache in Klammern (Senden von ACK für bereits erhaltene Pakete) denke ich, dass Sie sie tatsächlich hervorheben sollten, anstatt sie in Klammern zu setzen. Es scheint im Verständnis des OP (oder zumindest seiner Beschreibung) zu fehlen.
Setzen Sie Monica am
@Angew jetzt fertig.
user253751
6

Wenn Sie TCP neu erfinden möchten, ist es sinnvoll, zuerst TCP zu betrachten , das sich mit dem genauen Problem befasst, das Sie beschreiben (ein Teil der Lösung besteht darin, benutzerdefinierte Werte für Wiederholungsversuche und Zeitüberschreitungen zu verwenden).

Lösungen, die 2 Kanäle verwenden, einen TCP-Kanal (für zuverlässige Kommunikation) sowie einen UDP-Kanal (für Kommunikation mit geringer Latenz), sind keine Seltenheit.

Einige Lösungen erkennen, wenn einem Client zu lange Informationen fehlen, und starten eine Neusynchronisierung, die UDP oder TCP verwenden kann.

Ein weiterer gängiger Ansatz besteht darin, die Kommunikation so zu gestalten, dass sie überhaupt nicht auf Bestätigungen beruht, dies liegt jedoch außerhalb des Rahmens der Frage.

Peter - Unban Robert Harvey
quelle
3

In einem RTS können Sie kein Protokoll wie TCP verwenden und UDP auch nicht zuverlässig machen. Wenn Sie dies versuchen, wird das Spiel eingefroren, sobald ein Netzwerkproblem auftritt.

Stattdessen entwerfen Sie das Protokoll so, dass verpasste Pakete keine große Rolle spielen.

Die kurze Version ist, dass es Ihnen egal ist, wo sich die anderen Spieler im letzten Frame befanden, solange Sie wissen, wo sie sich jetzt befinden . Die lange Version ist komplizierter.

Dann stellt sich die Frage, was Sie tun, wenn ein Paket verloren geht. Und die Antwort ist ... Sie raten. Der Spieler bewegt sich wahrscheinlich in einer geraden Linie, oder? Bewegen Sie sie einfach einen Schritt weiter entlang dieser Linie. ... es sei denn, kein RTS-Spieler bewegt sich jemals in einer geraden Linie. Und dann gibt es eine Kollisionserkennung.

Das ist schwer. Viele Spiele verstehen es falsch. Es kann argumentiert werden, dass es keine richtige Antwort darauf gibt, sondern nur verschiedene Fehler, die abgewickelt werden können.

Der Grund, warum diese Spiele ziemlich gut funktionieren, ist nicht nur, dass sie sich lange Gedanken über diese Probleme gemacht haben, sondern auch, dass das Internet ziemlich zuverlässig geworden ist. Fast alle UDP-Pakete erreichen ihr Ziel tatsächlich rechtzeitig. (Es sei denn, es gibt ein permanentes Problem wie eine Firewall)

Stig Hemmer
quelle
Warcraft 3 verwendet TCP.
fsp