Mal ein paar Fragen zu UDP (inzwischen auch TCP)
Übersicht

MauBetreff: Mal ein paar Fragen zu UDP (inzwischen auch TCP) |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Ich lese mich jetzt schon seit einigen Tagen in das Thema UDP ein, aber da sind immer noch einige Fragen offen, die mir nicht so ganz klar sind, wenn ich mal versuche, mir das alles bildlich vorzustellen:
1. Wie vermeidet man am besten Probleme mit Router, Firewall etc.- Lässt man da am besten den Host alle Ports festlegen? Oder sollte jeder Teilnehmer seinen eigenen Empfangsport aussuchen können und diesen an den Host schicken? 2. Was passiert, wenn man eine Nachricht in einen Datenstream schreibt, aber in diesem noch eine "ungelesene" Nachricht wartet? Wird die neue Nachricht einfach drangehängt, oder wird alles vorherige immer mit der zuletzt gesendeten Nachricht überschrieben, also gelöscht? 3. Und was passiert mit der Absender-IP, die man mittels RecvUDPMsg() ausliest, wenn zwei Rechner kurz hintereinander (oder gleichzeitig) auf dem selben Stream zum Host eine Nachricht schicken? Ist die IP dann die des Rechners, dessen Nachricht zuletzt ankam? 4. Was passiert, wenn zwei miteinander verbundene Rechner gleichzeitig ein Paket zum jeweils anderen über den selben Stream schicken, also gleichzeitig in beide Richtungen? Kommen trotzdem beide Pakete an, nur eines davon oder gar keins? 5. Ist es nicht ratsam oder einfacher, KEINE bestimmten Ports zu benutzen und das alles von Blitz verwalten zu lassen, also z.B. bei CreateUDPStream() keinen speziellen Port anzugeben? Ist ein bisschen verwirrend, finde ich... da wird auch nirgendwo so richtig ins Detail gegangen ![]() |
||
Alles muss, nichts kann! |
- Zuletzt bearbeitet von Mau am Mo, Sep 19, 2005 15:42, insgesamt einmal bearbeitet
BIG BUG |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
1. Auf jeden Fall solltest Du die verwendeten Ports dokumentieren, so dass die Benutzer diese dann bei Bedarf auch freischalten können
2. Die Nachricht wird angehängt. First In, First Out. Also so oft RecvUDPMsg aufrufen, bis nix mehr da ist. 3. Du rufst RecvUDPMsg für jedes angekommene Paket auf. Folglich wird auch jedesmal die korrekte QuellIP angegeben. "Gleichzeitig" ankommende Pakete werden nacheinander bereitgestellt 4.Beides kommt an 5.Zumindest für den Server würde ich einen vorgegeben Port verwenden Ansonsten hast Du bei UDP keine Garantie, dass auch alle Pakete ankommen. Bei einem kleinen Praxistest im LAN mit 8 Spielern gab es aber keine Paketverluste. |
||
B3D-Exporter für Cinema4D!(V1.4)
MD2-Exporter für Cinema4D!(final) |
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Das hilft mir ein riesiges Stück weiter, danke dir!
Noch eine Frage: wie könnte man es realisieren, daß der Host "merkt", ob Pakete verloren gingen und diese dann noch mal anfordert? Ich grüble da schon länger drüber nach, komme aber auf kein gutes System. |
||
Alles muss, nichts kann! |
furbolg |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Jede Paket bekommt ne ID (1..2..3.....) sollte da welche Fehlen (1..2.....5.....9..10) so kannst du sie wieder anfordern lassen. | ||
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Vom groben her leuchtet das ja ein -aber das würde dann doch erfordern, daß sowohl Host als auch Client eine genaue "Liste" von allen gesendeten bzw. angeforderten Nachrichten anlegen, damit beide dann auch später noch einer jeweiligen ID eine bestimmte Nachricht zuordnen können, oder?
Man müsste also jede einzelne Nachricht zum eventuellen späteren Nachschlagen abspeichern. Zumindest die letzten fünfzig oder so. Oder sehe ich den Wald vor lauter Bäumen nicht? Okay, man könnte ja vereinbaren, daß ID = 4 immer für "Positionsangabe" steht -aber in meinem Spiel ginge das nicht, es werden nur einmal pro Runde Daten übertragen, dafür aber ein ganzer Chunk auf einmal -und die müssen auf jeden Fall komplett und richtig sein, sonst muss das Spiel abgebrochen werden. |
||
Alles muss, nichts kann! |
BIG BUG |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Wenn Dein Spiel rundenbasiert ist, kannste doch auch gleich TCP verwenden? Da wird protokollseitig sichergestellt, dass alle Pakete ankommen. | ||
B3D-Exporter für Cinema4D!(V1.4)
MD2-Exporter für Cinema4D!(final) |
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Schon -aber das gibt doch immer wieder Ruckler, weil das ganze Programm angehalten wird. Das sieht grottig aus.
Aber vielleicht kann man das doch ganz einfach mit UDP lösen: man schickt einfach eine Anfrage an einen anderen Rechner und das Programm merkt sich, ob darauf bereits eine Antwort kam. Wenn nicht, wird nur die letzte Anfrage immer wieder gesendet. Und um zu testen, ob es sich auch um die richtige Antwort handelt, schickt man halt einen MilliSecs()-Wert mit, den die Antwort mit zurücksenden muß. Das Problem ist ja nicht, ob ein UDP-Paket verstümmelt ankommt, sondern ob es ÜBERHAUPT ankommt. Da ich aber dann pro Runde alle wichtigen Daten in einem einzigen Paket verschicke -wie groß dürfen die maximal sein? Habe mal was von 1024 Bytes oder so gelesen... |
||
Alles muss, nichts kann! |
Nox |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Pakete, die größer als die maximal festgelegte Größe deines Betriebssystems sind, werden abgeschnitten und getrennt gesendet. Beim Zielrechner tauchen sie dann als 2 Pakete auf, die dann beide ausgelesen werden müssen. Da man i.d.R. aber eh solange empfängt, bist der Empfangsbuffer leer ist, stellt das kein Problem dar. Allerdings gibt's Probleme, wenn du z.B. ein Paket hast, welches geteilt wird, und ein 2., welches direkt danach folgt. Dies sähe dann so aus:
Paket0 Teil0 | Paket0 Teil1 | Paket1 Angenommen, Paket0, Teil1 geht nun verloren. Du empfängst also nur Paket 0, Teil0 und Paket1. Beim "Parsen" deines Buffers wirst du also irgendetwas Ungültiges erhalten, da die Daten, die in Paket1 stecken, sicherlich nicht zum Paket0 gehören sollten. ![]() Und wie BIG BUG schon sagte: Für Spiele und Programme, die nicht permanent Daten austauschen, ist TCP die definitiv bessere Wahl. Ich weiß nicht, wieso es bei dir ruckelt, denn normal wird beim Aufruf einer Paketsendung dieses nur in eine lokale Sendequeue deines Betriebssystems verschoben. |
||
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Langsam verstehe ich, warum alle sagen, UDP sei kompliziert. Es sind nicht die paar Befehle, es ist die ganze METHODE, die das so schwer macht.
![]() "START,1,2,3,END" Die Werte werden durch Komma getrennt. Und wenn das Programm am Ende des Strings kein END findet, oder am Anfang kein START, weiss man, daß das Paket zerschnitten wurde. Könnte das funktionieren? Einzelne Strings (WriteString) werden ja sicher nicht zerschnitten, oder? Und wie ist das mit ReadLine / WriteLine? Werden Lines zerschnitten? EDIT: Und noch eine Frage -hat man mit TCP nicht mehr Ärger in Bezug auf Router/Firewalls? |
||
Alles muss, nichts kann! |
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Ich hab's gerade mal mit TCP versucht:
Wenn ich z.B. mit dem Server verbinden will (und die IP-Adresse nicht korrekt eingegeben wird), stoppt das ganze Game komplett für zehn Sekunden und hängt völlig. Welcher User soll das noch nachvollziehen können? TCPTimeOuts ändert an diesem Freezen auch nichts. |
||
Alles muss, nichts kann! |
BIG BUG |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Das ist richtig, hier muss halt dann eine "Versuche zu verbinden"-Meldung den User solange vertrösten. Der Verbindungsaufbau passiert ja auch nur einmalig und auch bei anderen professionellen Onlinespielen gibt es teilweise dieses Problem.
Dass zerteilte Pakete der Anwendungsschicht(dem BB-Programm) dann auch wirklich zerteilt bereitgestellt werden glaube ich jetzt nicht, aber das kann man ja ausprobieren. |
||
B3D-Exporter für Cinema4D!(V1.4)
MD2-Exporter für Cinema4D!(final) |
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
@BIG BUG: Ja, habe jetzt einen Wartescreen eingebaut. Dauert ja auch nur so lange, wenn man eine falsche IP eingibt.
Da gibt es aber noch ein gewaltiges TCP-Problem, das mir gerade aufgefallen ist: Wenn man einen Server eröffnet, sich ein Client einloggt und dieser sein Blitzfenster dann einfach schliesst (sich also nicht vorher beim Server ausloggt), spuckt Blitz später einen MAV-Error aus, wenn man den Server mit CloseTCPServer() schliessen will. Hammer, oder?? ![]() Die einzige Lösung: den Server einfach offen lassen -oder? Hat es negative Effekte, wenn man z.B. gleich mehrere Server nacheinander geöffnet hat und keinen davon wieder schliesst? |
||
Alles muss, nichts kann! |
Nox |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Mau hat Folgendes geschrieben: Da gibt es aber noch ein gewaltiges TCP-Problem, das mir gerade aufgefallen ist:
Ich finde, dass es kein Problem ist, sondern sich genauso verhält, wie es sollte. Wenn die Funktion nicht wartet, bis die Verbindung als gescheitert deklariert wird (Timeout) bzw. erfolgreich war, was sollte er da sonst tun? Eine Lösung für solche Dinge wären entweder asynchrone Sockets oder - weitaus eleganter - Multithreading. Ich weiß nicht, welche beider Methoden in Blitz verwendet werden kann - wenn überhaupt eine. Allerdings könnte man dem "Problem" so aus dem Wege gehen. :> |
||
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Geht nicht. Wie soll ich aus den wenigen Blitz TCP-Befehlen eine Lösung basteln, damit der Server beim Schliessen keinen MAV ausspuckt, wenn sich ein Client durch Schliessen seines Fensters aus dem Staub gemacht hat? Geht ja nicht.
Es gibt wohl nur zwei Lösungen: 1. Den Server einmal aufmachen, offen halten und immer wieder verwenden. Nachteile: er schleppt immer mehr "Altlasten" mit rum, also Verbindungen, die ja gar nicht mehr aktuell sind. 2. Immer wieder einen neuen Server aufmachen und den alten einfach offen lassen und "vergessen". Nachteile: ich weiss nicht, ob man überhaupt beliebig viele Server erstellen kann oder ob die sich gegenseitig verlangsamen oder beeinträchtigen. Das kann's doch nicht sein! ![]() |
||
Alles muss, nichts kann! |
Klaas |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
ich würde bei UDP bleiben ... die Zuverlässigkeit der Übermittlung von daten kann man wie folgt gewährleisten.
Der Client sendet seine Daten mit einer ID gefolgt von der länge der Daten gefolgt von einem Hashwert. Wenn der Server ein Packet empfängt, merkt er sich die ID und liest dann die Längenangabe und den Hashwert. Nun liest er so lange bis er die Länge der Daten bekommen hat (Dies kann natürlich segmentiert sein, am besten man speichert erstmal alles in eine Bank). Nachdem alle Daten gelesen wurden errechnet der Server den Hashwert der Daten und vergleicht den mit dem empfagenen Hashwert. Ist dieser gleich so sendet er dem Client die ID des Paketes mit einem "alles Okay" commando zurück. Der Client wartet einige Zeit, bekommt er kein "alles Okay" Paket sendet er die Daten nocheinmal, bis es korekt angekommen ist. Das ganze lässt sich relativ einfach mit einem Netzwerk-Stack (sozusagen eine Auftragsliste) umsetzen. Beim Server ist das ganze ein wenig komplizierter, da dieser die Daten teilweise in mehreren Zyklen empfangen muß. Dazu würde ich halt auch Types benutzen und einer Cache-Bank die die Daten zwischenspeichert. Ist ein Paket vollständig so wird dieses an die Verarbeitungs-Funktion weitergegeben. Zu den Ports gibt es noch zu sagen das man immer zu dem Port schickt von dem man es erhalten hat. Da sich die Router mittels UDP-Sessions merken wer welches Paket erhalten soll. Dazu sucht der Router sich einen Port aus und mappt das Paket entsprechend um, diese Route bleibt dann bestehen. Client sendet von Port 20 zu Port 20 über den Router zum Server Router sendet dann von Port 6574 zu Port 20 des Servers Der Server erhält das Paket auf Port 20 von Port 6574 Der Server sendet die Antwort von Port 20 zu Port 6574 ... verstanden? ... oder zu ungenau beschrieben? |
||
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Herrje... das würde in meinem Fall alles viel komplizierter machen, als mit TCP. Ich habe jetzt alles auf TCP umgestellt -was eigentlich auch kein Problem ist, ausser eben diesem CloseTCPServer-Blitz-Bug.
Ich habe folgendes herausgefunden: - Wenn der Host nach einem Spiel den Server NICHT schliesst und einfach einen neuen öffnet, geht nichts mehr -es werden keine Clients mehr erkannt. Man MUSS ihn also schliessen, bevor man einen neuen öffnet. - Wenn ich aber einen Server schliesse, gibt es einen MAV-Error -und zwar nicht sofort, sondern erst am PROGRAMMENDE, direkt beim END!!!! Dieser MAV ist so hartnäckig, daß er sogar noch direkt hinter einem davor plazierten RuntimeError auftaucht. Das habe ich noch nie erlebt! Das muß ein Blitz-Bug sein, ich habe keine Ahnung was ich dagegen tun kann. Hatte schon mal jemand dieses Problem? |
||
Alles muss, nichts kann! |
furbolg |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Das ist kein Bug in dem Sinne, das hängt mit dem WinSock zusammen, bei C++, VB und andere Sprachen ist es das selbe.
Die Lösung ? Beim Beenden ein Disconned vom Client senden lassen. Das Problem bei BB (Bei Max könnte das folgende funtzen) ist das es bei einer internen Exception sich beendet und der BB Coder darauf nicht reagieren kann. Bei C++ (BMax denk ich auch) kann man dies per Try/Catch/Throw sprich Exceptionhandling abfangen und trotz des kritischen Error noch seinen Code ausführen lassen). Sollte im normalfall bei BB aber nicht vorkommen, von daher mach einfach vor deinem End oder der Abbruchbediengung folgendes: Code: [AUSKLAPPEN] Graphics..... SetBuffer BackBuffer() While 'Hauptschleife Wend SendUDPMsg("Client XXX meldet sich ab") End |
||
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
@furbolg: Ich verwende momentan TCP, nicht UDP. Ich mache das im Moment so:
- Wenn sich ein Client abmeldet, sendet er ein Tschüss-Byte und schliesst seinen eigenen Stream zum Server (CloseTCPStream ). Der Server selbst nimmt das zur Kenntnis, macht aber sonst nix, schliesst also keinen Stream (das muß ja der Client selbst machen, oder?) - Wenn aber der Host das Spiel beendet oder abbricht, sendet er ein Tschüss-Byte an alle Teilnehmer und schliesst sofort darauf den Server (CloseTCPServer ). Sobald ein Client das Tschüss-Byte empfängt, schliesst er seinen eigenen Stream zum Server (CloseTCPStream). Ist das korrekt so? Und es bleibt das Problem: was tun, wenn ein Client (oder der Host) ihre Blitzfenster einfach schliessen? Dagegen ist man machtlos. |
||
Alles muss, nichts kann! |
![]() |
Jolinah |
![]() Antworten mit Zitat ![]() |
---|---|---|
Wenn der Server die Nachricht bekommt dass ein Client weggeht so musst du den Stream auch vom Server aus schliessen, da TCP nicht ein verbindungsloses Protokoll ist.
Du hast ja irgendwie mit Accept den Stream angenommen. Am Ende musst du diesen auch wieder mit CloseTCPStream() schliessen. Somit ist die Verbindung von beiden Seiten aus beendet. Das könnte den MAV verursachen bei CloseTCPServer(). PS: Am besten wärs das direkt nach der Exit-Nachricht vom Client zu machen. Alternativ kannst du aber auch vor CloseTCPServer() alle Client-Streams durchgehen und CloseTCPStream() aufrufen. |
||
Mau |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Hab' das grad mal probiert (sch... Umstöpselei!). Gibt immer noch einen MAV!
Ich habe bis jetzt nur eine Lösung gefunden, wie ich diesen MAV vermeiden kann: Der Host öffnet nur EIN EINZIGES mal einen TCP-Server. Dieser wird das ganze Spiel über beibehalten und niemals, niemals wieder geschlossen. Dann klappt alles. Sobald ich aber ein CloseTCPServer benutze, gibt es einen MAV -und wie gesagt, nicht sofort, sondern erst am Programm-Ende. Ich habe also keine andere Wahl, als nur EINMAL einen Server aufzumachen und den dann offen zu halten. |
||
Alles muss, nichts kann! |
Übersicht


Powered by phpBB © 2001 - 2006, phpBB Group