Optimaler Aufbau von Netzwerkpaketen?
-
Hallo zusammen.
Für jedes Spiel mit Multiplayerfunktion ist der Netzwerkcode von enormer Bedeutung für die Spielbarkeit. Ich überlege derzeit, wie ich Netzwerkpakete selbst möglichst effizient aufbaue und würde mich über weitere Ideen und Tipps eurerseits freuen.
Folgendermaßen stelle ich mir den grundlegenden Aufbau eines Netzwerkpaketes (des Spiels) vor (UDP-Protokoll):
16 Bit | CRC16-Checksumme:
Jedes Paket benötigt, so denke ich, eine Prüfsumme, falls bei der Übertragung ein Fehler passiert ist. Von einem Spiel kenne ich, dass jedem Paket eine 4 Byte lange CRC32-Checksumme des Paketinhaltes vorangestellt wird. Was mich daran stört ist, dass nunmal jedes Paket diese 4 Byte enthält. Je nach Anzahl der Spieler und der Häufigkeit der Updates (vor allem bei Bewegungen) können das sehr schnell sehr viele Pakete werden. Wäre da nicht CRC16 ausreichend? Könnte man nicht eventuell einen Algorithmus nehmen, der noch weniger Bits benötigt? Ich denke solche Checksummenfehler passieren nicht all zu oft. Vielleicht gibt es ja noch andere Wege so etwas zu verhindern?
*EDIT*
Da laut UDP-RFC bereits eine Checksumme im UDP-Header angegeben ist, kann ich diese 16 Bit auslassen *freu*4 Bit | Packettyp:
Normalerweise müssen Pakete strikt in Reihenfolge gesendet werden. Angenommen ein paar zeitkritische Bewegungspakete müssen raus, aber mehrere andere Pakete blockieren etwas die Leitung oder müssen wegen eines Fehlers erneut gesendet werden, dann muss das Bewegungspaket warten. Über den Pakettyp möchte ich parallele Warteschlangen für Pakete machen, wobei z.B. die Bewegungspakete eine wesentlich höhere Priorität haben. Hinzu kommt, dass man über den Pakettyp z.B. einen Typ hinzufügen kann, der die Kommunikation für Clients außerhalb des Servers ermöglicht (z.B. für ein Remote Control oder Abfrage der Spielerzahl etc.).
Beispiel für Pakettypen:
0: Paket von außerhalb - Spieler möchte nicht beitreten, sondern z.B. Konsolenbefehle ausführen o.ä.
1: Verbindungsverwaltung: Pakete rund um die eigentliche Verbindung. Verbindungsherstellung, -verweigerung, -beendigung, etc.
2: normale, geordnete Pakete (alle Pakete müssen in Reihenfolge abgearbeitet werden)
3: normale, ungeordnete Pakete (Reihenfolge ist egal, Paket direkt auswerten)
4: zeitkritische Pakete (Positionsangaben etc.)
und je nach Bedarf auch neue.13 Bit | PacketID:
Eine pro Packettyp eindeutige (fortlaufende) Packetnummer, die zum Sortieren der Pakete verwendet wird. 13 Bit reichen für 8.192 Pakete, wobei bei einem Überlauf einfach wieder bei 0 begonnen wird. Tritt plötzlich ein Paket mit einer Nummer ein, die nicht erwartet wurde, so gelten alle dazwischen liegenden Pakete als vermisst und es muss gewartet werden. Sollte das Verbindungstimeout auf 10 Sekunden sein, sind so max. 819 Pakete pro Sekunde möglich, was hoffentlich reichen sollte.14 Bit | Zeitstempel (Millisekunden)
(nur für manche Pakettypen)
Ich habe mir auch überlegt, wie man Bewegungen möglichst flüssig übertragen kann. Ich denke, dass der Spieler jeweils seine Position sendet und dem Server durchgibt und dieser lediglich prüft, ob die Position möglich ist (wobei geringe Abweichungen erlaubt sind). Damit der Server das besser prüfen kann, und um das Cheaten zu erschweren, habe ich mir gedacht, einen Zeitstempel mitzusenden. So kann der Server aus der vergangenen Zeit berechnen, wie weit sich der Spieler max. bewegen konnte. Eventuell könnte man dadurch auch stärkere Lags erkennen und entsprechend handeln. Der Zeitstempel wird nun bei vielen Paketen mitgesendet, wodurch eine Änderung (z.B. durch einen Cheatversuch) schneller auffallen würde. Ob ich die 14 Bit allerdings dafür hergebe weiß ich noch nicht, das muss ich erstmal in der Praxis testen.Abhängig vom Pakettyp folgt dann der weitere Aufbau der Pakete.
Ich hoffe ich habe das einigermaßen verständlich rübergebracht und ihr habt noch ein paar Tipps für mich, wie ich das ganze weiter optimieren kann.
Eine Frage noch am Rande: Wie genau funktionieren Deltapakete?
Grüße,
Marc
-
Moin,
waren bei dem UDP nicht schon Checksummen mit dabei?
-
SeppSchrot schrieb:
Moin,
waren bei dem UDP nicht schon Checksummen mit dabei?
Ja, aber leider nur für den UDP-Header. Finde ich eine Verschwendung, vielleicht ein Designfehler?
-
Aus der RFC 768:
Checksum is the 16-bit one's complement of the one's complement sum of a
pseudo header of information from the IP header, the UDP header, and the
data,...
-
...und mach' nicht so' bitgefummle (4,13,14 bits). das musste hinterher mühsam auseinanderfrickeln. mach besser immer vielfache von 8bits (8, 16, 16 bits). die pakete werden dadurch nur minimal länger, aber können schneller verarbeitet werden.
-
net schrieb:
...und mach' nicht so' bitgefummle (4,13,14 bits). das musste hinterher mühsam auseinanderfrickeln. mach besser immer vielfache von 8bits (8, 16, 16 bits). die pakete werden dadurch nur minimal länger, aber können schneller verarbeitet werden.
Die Pakete werden sehr viel länger dadurch, und das zusammenpacken und auseinandernehmen geht mit einer eigenen Klasse sehr einfach.
@SeppSchrot: aah, vielen Dank für den Hinweis! Habe ich doch gestern glatt überlesen
Kann man per C++ irgendwie beeinflussen, dass die Checksumme auch verwendet wird? Vielleicht wird sie das auch immer - ich versuche mal das irgendwie rauszufinden.
-
Neku schrieb:
Die Pakete werden sehr viel länger dadurch, und das zusammenpacken und auseinandernehmen geht mit einer eigenen Klasse sehr einfach.
also wenn ich mich jetzt nicht verzählt hab wirds dadurch nur insgesamt um 9bit länger, das sollte man doch verkraften können - finds so mit (14bit, 4bit) bissel unsauber
mfg blan
-
Neku schrieb:
net schrieb:
...und mach' nicht so' bitgefummle (4,13,14 bits). das musste hinterher mühsam auseinanderfrickeln. mach besser immer vielfache von 8bits (8, 16, 16 bits). die pakete werden dadurch nur minimal länger, aber können schneller verarbeitet werden.
Die Pakete werden sehr viel länger dadurch, und das zusammenpacken und auseinandernehmen geht mit einer eigenen Klasse sehr einfach.
@SeppSchrot: aah, vielen Dank für den Hinweis! Habe ich doch gestern glatt überlesen
Kann man per C++ irgendwie beeinflussen, dass die Checksumme auch verwendet wird? Vielleicht wird sie das auch immer - ich versuche mal das irgendwie rauszufinden.Bei UDP wird die checksumme IMMER verwendet. Beschädigte Pakete werden erst garnicht zugestellt.
Es sei denn du machst alles Per "Hand". Aber warum sollte man das Rad neu erfinden sollen?
-
blan schrieb:
Neku schrieb:
Die Pakete werden sehr viel länger dadurch, und das zusammenpacken und auseinandernehmen geht mit einer eigenen Klasse sehr einfach.
also wenn ich mich jetzt nicht verzählt hab wirds dadurch nur insgesamt um 9bit länger, das sollte man doch verkraften können - finds so mit (14bit, 4bit) bissel unsauber
mfg blan
Oben geht es ja nur um den Header. Die Paketdaten selbst benötigen ja wesentlich mehr Speicher.
Andreas XXL schrieb:
Neku schrieb:
@SeppSchrot: aah, vielen Dank für den Hinweis! Habe ich doch gestern glatt überlesen
Kann man per C++ irgendwie beeinflussen, dass die Checksumme auch verwendet wird? Vielleicht wird sie das auch immer - ich versuche mal das irgendwie rauszufinden.Bei UDP wird die checksumme IMMER verwendet. Beschädigte Pakete werden erst garnicht zugestellt.
Es sei denn du machst alles Per "Hand". Aber warum sollte man das Rad neu erfinden sollen?"An all zero transmitted checksum value means that the transmitter generated no checksum (for debugging or for higher level protocols that don't care)."
Wenn die Überprüfung aber standardmäßig aktiviert ist, dann bin ich zufrieden
-
Neku schrieb:
blan schrieb:
Neku schrieb:
Die Pakete werden sehr viel länger dadurch, und das zusammenpacken und auseinandernehmen geht mit einer eigenen Klasse sehr einfach.
also wenn ich mich jetzt nicht verzählt hab wirds dadurch nur insgesamt um 9bit länger, das sollte man doch verkraften können - finds so mit (14bit, 4bit) bissel unsauber
mfg blan
Oben geht es ja nur um den Header. Die Paketdaten selbst benötigen ja wesentlich mehr Speicher.
schau dir halt ma andere opensource games an, ich bin mir fast sicher das die meisten des mit 8bit, 16bit, ... gemacht haben..
mfg blan
-
Natürlich füllt man so einen Header auf 32bit aliasing auf ! Aber trotzdem sollte man einfache flags noch bitweise kodieren anstatt jedem bool gleich nen byte zu geben.
-
Man sollte sich aber im Klaren sein, dass so eine Datenstruktur dann nicht einmal mehr auf bitebene aligned ist und der Zugriff dadurch alles andere als schnell ist.
Wenn die paar Bits wirklich sein müssen, dann würde ich die Daten in dieser Struktur senden/empfangen, aber davor und danach die Daten in Strukturen speichern die richtig aligned sind.
-
User-- schrieb:
Wenn die paar Bits wirklich sein müssen, dann würde ich die Daten in dieser Struktur senden/empfangen, aber davor und danach die Daten in Strukturen speichern die richtig aligned sind.
Logisch, so ist es ja auch. Alles andere wäre mehr als langsam. Es geht hier lediglich um die Netzwerkpakete und um nichts anderes. Diese müssen eben möglichst komprimiert sein.
-
Neku schrieb:
Es geht hier lediglich um die Netzwerkpakete und um nichts anderes. Diese müssen eben möglichst komprimiert sein.
soll's über modem mit 9600 bps oder weniger laufen?
-
net schrieb:
Neku schrieb:
Es geht hier lediglich um die Netzwerkpakete und um nichts anderes. Diese müssen eben möglichst komprimiert sein.
soll's über modem mit 9600 bps oder weniger laufen?
Soll's nur ein einziges Paket werden?
-
Neku schrieb:
Soll's nur ein einziges Paket werden?
das wissen wir nicht. aber wenn's so ist dann liegste mit bit fiddling natürlich richtig...