none
VB.Net: Kommunikationslösung gesucht (für Networking Null-Niveau) RRS feed

  • Allgemeine Diskussion

  • Hallo,

    leider habe ich zu diesem Thema keine guten Tutorials, Links, Bücher, etc. gefunden, die MSDN Library habe ich auch schon erfolgslos durchforscht, deshalb frage ich hier jetzt mal nach etwas Beispielcode.

    ich möchte in ein Programm eine Kommunikationslösung integrieren, die folgendes macht:

    • Sendet ein Client eine Nachricht, wird mit der Nachricht ein ihm vorher (mit allen anderen Clients "abgesprochener") zugeordneter Name versendet.
    • Die Nachricht, soll Zeitgleich jeden Client (auch der, der die Nachricht gesendet hat) erreichen.
    • Der Client, der die Nachricht gesendet hat, brauch nicht wissen, dass er die Nachricht gesendet hat.

    Für die Benutzer soll das dann so ausschauen: Sendet er eine Nachricht, wird es sofort bei allen Benutzern die Nachricht angezeigt

    Ich habe mir schon ein paar Gedanken gemacht und mir folgendes Schema überlegt:

    Schema?

    Ich weiß, dass das ein bisschen viel ist, vielleicht gibt es auch einen anderen "Ort" wo man das besser machen sollte, ich weiß davon jedenfalls nichts, deshalb steht ja auch hier dieser Thread.

    Wenn ich für diesen Thread die falsche Kategorie gewählt habe, bitte melden/sagen.

    Vielen Dank schon im Voraus.

     



    Gruß, Bolzen


    • Bearbeitet Bolzen Samstag, 19. Januar 2013 19:22 Ergänzung
    • Typ geändert Bolzen Dienstag, 5. Februar 2013 15:42
    Samstag, 19. Januar 2013 19:19

Alle Antworten

  • Hi,

    das ist dann wohl ein Chat. Siehe dazu:

      http://social.msdn.microsoft.com/Forums/de/visualbasicde/thread/07294110-c731-4874-8caf-cc48ab29f37d

    Dort findest Du Hinweise und einige Links zu weiteren Erklärungen, auch zu einer Chatanwendung m,it VB.NET. Die sollte dir helfen, dich einarbeiten zu können. Bei spezifischen Fragen kannst Du dich natürlich einfach nochmal hier melden.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Samstag, 19. Januar 2013 20:04
    Moderator
  • Hallo Stefan,

    vielen Dank für Deine Antwort. So etwas in der Art habe ich mir auch schon angeguckt, aber erhalten die Clients auch alle gleichzeitig die Nachricht (deswegen ist mein Programm auch nur eine Art Chat.)? Ich habe mir die Links des Threads noch nicht angeschaut, aber ich werde es heute mal tun.

    Nochmal vielen Dank für die Antwort


    Gruß, Bolzen

    Sonntag, 20. Januar 2013 07:39
  • Hi,

    erreichen wird die Nachricht die Clients wahrscheinlich versetzt. Ob das nun 1 Milisekunde oder eher 5 Sekunden sind, kann dir vorher keiner sagen, das hängt von vielen Faktoren ab.

    Wenn Du es noch verrätst, warum es so immens wichtig ist, dass die Nachricht absolut zeitgleich ankommt, kann man dir evtl. auch einen Lösungsansatz geben.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Montag, 21. Januar 2013 09:50
    Moderator
  • Hi,

    um ehrlich zu sein, es ist eine Art Spiel, dass auch über das Internet funktionieren soll. Wenn also ein Spieler die Verbindung zum Server verloren hat (Kabel raus, etc.), kann er ja unmöglich weiterspielen.Ich bin noch nicht sehr weit mit diesem Projekt, aber wenn ich das TcpIP Problem gelöst habe, wäre ich schon so gut wie fertig.


    Gruß, Bolzen

    Montag, 21. Januar 2013 13:29
  • Hi Bolzen,

    für ein Spiel ist das UDP Protokoll besser geeignet.

    Zwar können im Gegensatz zum TCP Pakete verloren gehen. Aber dafür ist es schneller und was nützt dir die Position eine Spielfigur wenn sie nicht mehr dort steht.

    MFG

    Björn

    Montag, 21. Januar 2013 13:42
  • Hi Palin,

    ich habe mir das mit UDP auch schon angeschaut, aber es ist doch ziemlich unpraktisch, wenn plötzlich ein paar Daten fehlen, oder?


    Gruß, Bolzen

    Montag, 21. Januar 2013 13:44
  • Hi,

    ich glaube, da stellst Du dir ein paar Sachen etwas zu einfach vor.

    Wenn ein Client nicht mehr verbunden ist, erhält er keine Nachricht, egal, was auch immer Du dann machen wirst.

    Falls das Spiel also davon abhängig ist, dass alle angemeldeten Clients die Nachricht(en) erhalten, bleibt dir nichts anderes übrig, als von jedem Client eine Rückmeldung für jede Nachricht zu erhalten und falls ein Client keine Rückmeldung sendet, die anderen Clients über den Abbruch zu benachrichtigen.

    Und für das "TcpIp Problem" schau bitte in meine erste Antwort. Das Grundprinzip ist wie bei einem Chat. Einer sendet was, alle anderen erhalten diese Nachricht ebenfalls. Dass das absolut zeitgleich passiert, kann dir niemand garantieren. Das Problem kennst Du evtl. von anderen Onlinespielen und den dort oft vorhandenen "Lags". D.h. Spieler 1 hat dich evtl. schon vor 1 Sekunde gekillt, Du läufst aber trotzdem noch rum, weil dein Rechner es noch nicht mitbekommen hat :)


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Montag, 21. Januar 2013 13:47
    Moderator
  • Jetzt bin ich leicht verwirrt. UDP ist generell besser geeignet (was dieses Problem betrifft), aber TCP irgendwie auch?

    Ist es besser

    • per UDP die Daten zu versenden und auf eine Bestätigung zu warten
    • oder per TCP einfach die Daten "normal" zu schicken?

    Mein Grundprinzip funktioniert eigentlich so: Es werden nur Daten versendet, wenn ein Spieler eine Eingabe macht und nicht in Zeitintervallen (oder wäre es so besser?). Wenn also ein Spieler keine Eingabe mehr machen kann, weil er zum Beispiel die Verbindung verloren hat, bleibt seine Position einfach gleich.


    Gruß, Bolzen

    Montag, 21. Januar 2013 14:00
  • Hi,

    es kommt immer darauf an was man machen will.

    Z.B. beim Schachspiel muss jeder Zug übertragen werden also TCP.

    Bei einem Ego Shooter ist es OK wenn ich mal eine Positionsdatensatz verliere, ich bekomme ja dann die nächste Position (die Figur ruckelt). Wenn ein Spieler die Verbindung verliert, können die Anderen dann auch weiter Spielen. Du wirst hier aber die Position übertragen müssen und nicht die Änderung der Position.

    MFG

    Björn

    Montag, 21. Januar 2013 14:32
  • Gut, dann wäre das geklärt. Jetzt brauche ich aber noch ein bisschen Theorie.

    Es soll ein Gegenstand bewegt werden. Ich habe mir das so überlegt:

    1. Sobald das KeyDown Ereignis eintritt, wird ein Signal an den Server gesendet.
    2. Wird das KeyUp Ereignis ausgelöst, wird wieder ein Signal an den Server gesendet.

    Die anderen Clients erhalten diese Signale und mache daraus eine ruckelfreie Bewegung. Nun stellt sich für mich folgende Problem: TCP ist zu langsam und bei UDP kann der erste oder zweite Datensatz verloren gehen, was natürlich Folgen hat. Wie begebt man dieses Problem?


    Gruß, Bolzen

    Dienstag, 22. Januar 2013 16:24
  • Wenn Du TCP-Kommunikation machst, ist bereits ein automatisches Handshake inbegriffen. Einzelne Daten-Pakete, die verloren gehen, werden ohne Dein Zutun erneut gesendet.

    Bei UDP-Kommunikation musst Du selbst dafür sorgen, dass die Übertragung geklappt hat. Das kannst Du genauestens auf Deine Anforderungen anpassen und hast damit weniger Overhead als bei TCP. Dafür allerdings mehr Programmieraufwand. Berücksichtige auch, dass die Datenpakete nicht unbedingt in der gleichen Reihenfolge eintreffen, wie sie abgeschickt wurden.

    Gruß,

    Winfried

    Dienstag, 22. Januar 2013 17:03
  • Würde es sich also rentieren, das selber zu machen, oder TCP zu benutzen?

    Oder gibt es eine Methode, die Position "schrittweise", statt einmal Start und einmal Stopp zu senden?


    Gruß, Bolzen

    Dienstag, 22. Januar 2013 17:37
  • Hallo Bolzen,

    ich würde das erst mal mit TCP ausprobieren und, falls es zu langsam ist, dann mit UDP. Aber, da ich nicht wirklich weiss, was Du da vorhast, ist es schwer, Vorschläge für's UDP-Handshake zu geben; es gibt da viele Techniken. im LAN könntest Du es auch mal völlig ohne probieren; normalerweise kommt da jedes Paket an und auch in der richtigen Reihenfolge.

    Eine mögliche Technik, um Einzelhandshakes unter UDP zu umgehen, ist das Timer-gesteuerte Senden von Keep-Alive-Packets, die praktisch einen Sammel-Handshake beinhalten. Auch kann Deine Software berücksichtigen, dass vor jedem Key-Up ein Key-Down war, auch wenn das Paket nie eingetroffen ist.

    Gruß,

    Winfried

    Dienstag, 22. Januar 2013 18:57
  • Hi Bolzen,

    ich möchte mich hier einfach mal Winfrieds Meinung anschließen.

    Die Unterschiede kennst du ja jetzt.

    Und ohne genau zu wissen was du machen möchtest, ist es schwer zu sagen was besser ist.

    TCP würde ich jetzt auch erst mal vorschlagen.

    Ergänzend würde ich noch gerne hinzufügen, das du den zugriff zu TCP/UDP sauber kapseln solltest.

    So dass du bei bedarf den Zugriff ändern kannst, ohne dein ganzes Programm ändern zu müssen.

    MFG

    Björn   

    Dienstag, 22. Januar 2013 20:25
  • Hallo Bolzen,

    schau Dir mal die englischsprachige Artikel-Serie an:
    http://gafferongames.com/networking-for-game-programmers/

    Die Beispiele sind zwar nicht in (VB) .NET aber die Konzepte gelten universell.

    Gruß Elmar

    Dienstag, 22. Januar 2013 21:35
    Beantworter
  • Ich habe mir jetzt zwei Progrämmchen zusammeneschnipselt, die aus einem Server und einem Client bestehen. Das funktioniert auch ganz gut, nur weiß ich nicht wie ich mehr als nur einen Client einbeziehen kann. Der Client wird durch die Server.AcceptTcpClient() Methode beschrieben, aber wie macht man das dann mit den anderen? Ich habe mir überlegt, das so zu machen:

    Const ClientMax As Integer = 4
    Dim ClientList As New List(Of TcpClient)
    
    For i = 0 To ClientMax - 1
      ClientList.Add(Server.AcceptTcpClient())
    Next

    Dann könnte man, wenn der Server eine Nachricht erhält durch ein For-Next-Struktur die Nachricht an alle Clients zurücksenden. Aber ich bin mit dieser Lösung nicht so sehr zufrieden.

    Wie löst man das richtig?


    Gruß, Bolzen



    • Bearbeitet Bolzen Donnerstag, 24. Januar 2013 18:24
    Donnerstag, 24. Januar 2013 18:23
  • Hallo,

    die eigentliche Arbeit kommt erst nachdem man die Client-Verbindung aufgebaut hat.
    Eine Zusammenstellung möglicher Varianten (allerdings C#) findest Du u. a. bei:
    C# 4.0 in a Nutshell - Chapter 23: Asynchronous Methods
    (falls Du mit Tasks und asnchronen Methoden noch nichts gemacht hast, nimm die erste)

    Wobei die wesentliche Arbeit (mit ReverseEcho) nur angedeutet ist ...
    Und damit sollte man sich zuerst beschäftigen, als davon zu träumen wie viele man versorgen möchte, weiteres siehe Link von die Tage ;)

    Gruß Elmar

    Freitag, 25. Januar 2013 17:24
    Beantworter
  • Hallo,

    da ich ja jedem Client einen einzigartigen Namen zuordnen möchte, ergibt sich ein neues Problem. Es gibt (glaube ich) zwei Möglichkeiten, es zu lösen.

    • Entweder ich erstelle eine Klasse MyTcpClient , die alles von der Klasse TcpClient erbt plus die Eigenschaft Spielername in der jeder Client einen Namen zugeordnet bekommt,
    • oder ich erstelle eine Auflistung, in der jeder Clientname registriert ist.

    Ich sympatiere mit der ersten Lösung (sie ist in meinen Augen um einiges komfortabler), leider weiß ich aber nicht, wie ich dann über die Server.AcceptTcpClient() Funktion den Client beschreiben soll oder wie man einen TcpClient in den MyTcpClient konvertiert.


    Gruß, Bolzen






    • Bearbeitet Bolzen Montag, 28. Januar 2013 14:57
    Montag, 28. Januar 2013 13:58
  • Hi Bolzen,

    Spielername ist nichts was die TCPClient-Klasse kennen sollt. (Das gehört eher zu den Daten die Übertragen werden.)

    Für die Eindeutigkeit sollte die IP reichen.

    MFG

    Björn

    Montag, 28. Januar 2013 15:27
  • Hallo Björn/Palin,

    das klingt einleuchtend, danke.


    Gruß, Bolzen


    • Bearbeitet Bolzen Montag, 28. Januar 2013 19:46
    Montag, 28. Januar 2013 19:29
  • Hallo zusammen.

    Schön, dass hier gerade so intensiv über Netzwerk-Programmierung getalkt wird. Da schalte ich mich nur zu gerne ein, da ich in den letzten Tagen auch an so etwas rumgefummelt habe. Und ich bin auch mittlerweile zu einem Status gelangt, dass eine Kommunikation in beide Richtungen stattfindet. Das war allerdings ein nicht ganz unsteiniger Weg. Ich möchte, da das von Interesse sein könnte, einmal ein paar Punkte nennen, die mich erheblich weitergebracht haben. Eventuell hat auch zu meinem Ansatz ja noch jemand Ideen oder Verbesserungsvorschläge. Ich bin so vorgegangen:

    - Server und Client sind zunächst unterschiedliche Klassen in unterschiedlichen Anwendungen (theoretisch könnte aber ein Client auch ein Server darastellen)

    - Client wie auch Server bauen einen TCP-Listener auf, auf einem vorher definierten Port

    - Client loggt sich beim Server ein, indem er eine Verbindung aufbaut und der Server diese annimmt und speichert (Wichtig: jeder Client bekommt einen neuen Thread. Die Rückgabe "Client" von TCPAcceptClient beim Server wird also direkt an einen neuen Thread weitergegeben, der dann die eigentliche Verbindung behandelt)

    - Will der Client etwas schicken, wird es nun mit diesem Thread empfangen. Nach erfolgreichem Empfang, und einer gewissen Karenzzeit, wird der Thread verworfen und die Verbindung wieder abgebaut. Jede Nachricht erzeugt also einen neuen Thread.

    - Wenn der Server eine Nachricht empfängt und das erste Mal eine Verbindung aufbaut, merkt er sich die IP des Senders. Sobald ein "LOGON" kommt als Nachricht, speichert er zudem den Status, dass dieser User eingeloggt ist. Hier kann wahlweise natürlich auch weitere Daten geschickt werden, Username, Passwort, was auch immer.

    - Sollte der Server selbst entscheiden, dass eine Aktualisierung nötig ist, die z.b. von einem Client kommt, und an andere verteilt werden soll, so sind ja über das "LOGON" alle Clients registeriert. Dann wird eine Nachrichtenkette initialisiert (über die sowieso alles läuft bei mir), und in diese werden an jeden Spieler eine Nachricht gehängt. Damit diese verschickt werden kann muss der Client auch einen TcpListener haben. Hier gilt im Grunde das selbe Prinzip: ein Listener, der bei jeder einkommenden Nachricht/Verbindung einen neuen Thread öffnet, der diese Nachricht empfängt und abspeichert. Im Listener wird dann auch die weitere Verarbeitung intialisiert.

    - Sollte einmal eine Verbindung abbrechen, so stürzt ja entweder der Thread ganz ab (das ist aber wurscht), oder nur ein Stream wird unterbrochen. Dieser Streamabbruch wird mit Try/Catch behandelt und dann an den jeweiligen Host zurückgegeben per Event. Der kann dann entscheiden, ob die Verbindung erneut aufgebaut werden soll. In meinem Fall bekommt erst bei vollständigem Senden die Nachricht ein "Sent"-Flag und verbleibt bis zum erhalt dieses Flags in der Queue. Sie wird also im Zweifelsfall erneut geschickt. Auch beim Empfänger wird sie erst verarbeitet, wenn sie komplett ohne Verbindungsabbruch empfangen wurde. Hier wird auch ein Fehler erzeugt, und die empfangene Teilnachricht verworfen, wenn der Stream abreißt.

    - Zusätzlich sende ich in gewissen Intervallen eine Art Ping (im Moment der Performanz und Benutzerfreundlichkeit zuliebe alle 30 Sekunden), um die Verbindung zu prüfen, ob der Server überhaupt noch erreichbar ist, bzw. der Client. So kann der Client einen (um das hiesige Beispiel aufzugreifen) benachrichtigen, dass man vom Server "geflogen" ist, oder der Server ein TimeOut generieren, bei dem man automatisch ausgeloggt wird. So würde man nicht direkt aus dem Spiel geschmissen, nur weil eine Nachricht mal kurz nicht übermittelt werden kann.

    Insgesamt war es ziemlich viel gefummel, eine richtige - für beide Seiten "initiierbare" - Kommunikation aufzubauen. Man muss bedenken, dass man erstmal nur "Verbindungen" aufbaut, die einseitig initiiert werden. Außerdem ist man bei der Programmierung für das komplette Protokoll selbst zuständig.

    Also denn ... happy coding!

    LG, Dennis.

    Dienstag, 29. Januar 2013 10:51
  • Ach ja, noch als Addon: Falls du, Bolzen, eventuell Interesse hast, dich konkret zu deiner Spieleidee auszutauschen und das auch alles in VB.NET probierst, wäre ich interessiert an einem Kontakt auch außerhalb des Forums. Da kann man dann vielleicht etwas detaillierter noch über Dinge wie Spielmechanik oder andere konkrete Dinge zum Projekt fachsimpeln, mit denen man nicht jedes Mal das Entwicklerforum behelligen möchte (hier gehts ja dann eher um technische Belange als inhaltliche). Vielleicht über GoogleTalk oder Skype?

    LG, Dennis.

    Dienstag, 29. Januar 2013 15:06
  • Danke für die vielen Anregungen. So ähnlich habe ich mir das auch überlegt nur ist es bei meinem Projekt zum Glück etwas einfacher. :)

    Gruß, Bolzen

    • Bearbeitet Bolzen Dienstag, 29. Januar 2013 19:30
    Dienstag, 29. Januar 2013 19:29
  • Ich habe es mir gemerkt.

    Gruß, Bolzen

    • Bearbeitet Bolzen Dienstag, 29. Januar 2013 19:30
    Dienstag, 29. Januar 2013 19:30
  • Da ich leider wenig Erfahrung im Bereich Threading habe (keine Sorge ich werde mich noch mal informieren), habe ich eine Frage: Wie löst man das Problem, wenn ein Thread einer ListBox etwas hinzufügen soll, aber keinen Zugriff darauf hat?

    Gruß, Bolzen

    • Bearbeitet Bolzen Mittwoch, 30. Januar 2013 18:29
    Mittwoch, 30. Januar 2013 18:29
  • Hallo Bolzen,

    aus einem anderen Thread musst Du über Control.Invoke zugreifen,
    mehr siehe u. a.: Von anderem Thread auf Control zugreifen

    Und mit etwas mehr Bezug auf das Netzwerkproblem hatte ich mal eine längere Diskussion:
    Meldung in Form schreiben aus Thread anderen Klasse // mehrere TCP-Clients mit Threads

    wo es auch darum ging, die Abhängigkeiten klein zu halten und in einem Beispiel gezeigt wird.

    Gruß Elmar

    P. S.: Bitte fange für weitere Fragen neue Diskussionen an, hier wird das etwas unübersichtlich.

    Mittwoch, 30. Januar 2013 18:55
    Beantworter
  • Gut, das werde ich machen. Meinst du eine Frage oder eine Diskussion, ich weiß nämlich nicht, wie hier im Forum "Diskussion" definiert ist.

    Gruß, Bolzen

    Mittwoch, 30. Januar 2013 19:12
  • Hallo Bolzen,

    drücke oben auf die große (hier violette) Schaltfläche Stellen Sie eine Frage

    Der Standardtyp Frage ist allgemein richtig, wenn man die Antwort nicht weiß;
    Diskussionen dann wenn Du bereits eine Lösung / Meinung hast, und sie weiter diskutieren möchtest.

    Gruß Elmar

    Mittwoch, 30. Januar 2013 19:38
    Beantworter
    • Bearbeitet Bolzen Sonntag, 17. Februar 2013 16:30 neuer Thread dazu
    Dienstag, 5. Februar 2013 15:39