[Hilfe] Kommunikation über die IP-Adresse

Übersicht BlitzBasic Beginners-Corner

Gehe zu Seite 1, 2  Weiter

Neue Antwort erstellen

 

Snowdragon

Betreff: [Hilfe] Kommunikation über die IP-Adresse

BeitragDi, Jul 08, 2014 13:32
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo,

wie einige von euch vielleicht wissen bastel ich an einem Kartenspiel und habe schon das ein oder andere nachgefragt. Nun steht das Spiel im Großen und Ganzen.

Ich würde das Spiel später z.B. gerne gegen Freunde etc. spielen. Für den Anfang natürlich gegen mich selbst um alles zu testen. Dazu würde ich gerne meinen Rechner als Host (Server) verwenden und mit meinem Laptop zu diesem eine Verbindung aufbauen, so dass ein Spiel möglich wird.

Allerdings habe ich einige Fragen, da ich mich mit TCP nur oberflächlich in der Schule beschäftigt habe und 0-Erfahrung in dieser Thematik habe. Zudem habe ich noch einige andere Fragen, was weniger mit der Kommunikation als eher mit dem Spiel zu tun hat. Deshalb glaube ich ist es wichtig, wenn ich die "Spielmechanik" erklären würde:

Es handelt sich dabei um ein Kartenspiel (1vs1). Es gibt drei Phasen (Ziehen, Kampf und eine End-Phase). Bisher habe ich es so programmiert, dass ich als Spieler sämtliche Phasen durchführen kann, den Gegner habe ich simuliert in dem ich einfach "statische Karten" für ihn eingefügt habe.
Vielleicht kennt der ein oder andere die Onlineversion von Yu-Gi-Oh. Dort ist es quasi so (wir haben Spieler A und Spieler B), dass Spieler A die untere Häfte des Bildschirms mit seinem Spielfeld ausfüllt und sein Gegner das obere Spielfeld. Für Spieler B verhält es sich auch so. Aus seiner Sicht gehört ihm das untere Spielfeld und Spieler A das obere (er sitzt ihm quasi) gegenüber.

So würde ich das auch ganz gerne auch handhaben, doch ich glaube dazu müsste ich erstmal wissen, wie die Kommunikation abläuft und wie es sich realisieren lässt.

Steht in der Online-Hilfe etwas brauchbares zur Realisierung einer solchen Kommunikation?

Vielleicht kann der ein oder andere mir einen Tipp geben, wie man so eine Kommunikation am besten umsetzen sollte, bzw. das Thema angehen sollte. Natürlich werde ich erstmal andere kleine Programme schreiben um zu testen wie ich das ganze am besten übernehmen kann.


Dann habe ich noch eine kleine Frage: Ist es möglich ein Bild zu laden, dieses um 180° zu drehen und dann zu zeichnen?

DAK

BeitragDi, Jul 08, 2014 17:04
Antworten mit Zitat
Benutzer-Profile anzeigen
Das Kurze zu erst: drehen geht per RotateImage, soweit ich mich erinner. Sollte nur beim Laden ausgeführt werden, nicht jedes Frame, da es langsam ist.

Zum Internet: Wie du geschrieben hast ist TCP besser als UDP, da dein Spiel in Richtung Rundenbasiert geht. In der Hilfe sollte recht viel dazu stehen. Entry Points sind für den Server AcceptTCPStream und für den Client OpenTCPStream.
Gewinner der 6. und der 68. BlitzCodeCompo
 

Bobo2040

BeitragDi, Jul 08, 2014 17:20
Antworten mit Zitat
Benutzer-Profile anzeigen
Wie DAK bereits sagte, RotateImage gibts auch nen Beispiel in der Hilfe
RotateImage Erklärung

Und zu TCP wie DAK auch bereits gesagt hatte, hier ein Link zur Hilfe
TCP Server/Client Erklärung
falls du Fragen oder Probleme dazu hast dann frag drauf los!Smile

Und noch ein Kleiner Tipp für die 3 Phasen sende die Phasen mit Bytes z.B. so:
BlitzBasic: [AUSKLAPPEN]

CMD = ReadByte(TCP_Stream)
Select CMD
Case Ziehen
Print "Gegner zieht gerade.."
;Hier kommt dann rein was er gerade macht :)
Case Kampf
Print "Gegner kämpft.."
;Hier kommt rein was er gerade macht
Case Ende
Print "Du bist dran!"
;was halt gemacht werden soll :D
Default ;Es muss einen Wert geben der falls eine Falsche Nachricht kommt
;Diese Trozdem "Handlet"
Print "Ups es ist ein kleiner Fehler ausgetreten"
Print "Byte-Wert:" + CMD
End Select


Bytes werden so gesendet:
Writebyte TCP_Stream,ByteWert (z.B. 1,2 o.ä.)
Wenn du einen Byte mit dem Wert "1" Schickst dann wird beim anderen auch ein "Byte" mit
dem Wert "1" ankommen Smile
Auslesen tut man diese so:
ZwischenVar = Readbyte TCP_Stream

Wobei "TCP_Stream" die Stream Variable ist die mit
TCP_Stream = OpenTcpStream(IP,Port)
erstellt wird natürlich kannst du Sie nennen wie du möchtest! :=)

Mfg
bitro.de

DAK

BeitragDi, Jul 08, 2014 18:05
Antworten mit Zitat
Benutzer-Profile anzeigen
Um Bobos Beispiel noch zu vervollständigen: Du baust dazu Pakete zusammen. Das erste Byte des Pakets enthält den Typ des Pakets und bestimmt somit, wie der Rest des Pakets ausgelesen und verarbeitet wird.

z.B.:
Code: [AUSKLAPPEN]

Paket-Typ Spieler:
  byte Typ = 1
  int x = X-Position
  int y = Y-Position
  int hp = Hitpoints

Paket-Typ Haus:
  byte Typ = 2
  String Adresse = Adresse des Hauses


Wenn du sendest, dann sendest du die einzelnen Elemente nacheinander mit WriteInt, WriteByte, WriteFloat, WriteLine oder WriteString. Beim Empfangen machst du zuerst eine If-Verzweigung wie Bobo sie geschrieben hat, und liest dann in der gleichen Reihenfolge wie beim Schreiben jedes einzelne Feld aus.
Gewinner der 6. und der 68. BlitzCodeCompo
 

Snowdragon

BeitragMi, Jul 09, 2014 21:57
Antworten mit Zitat
Benutzer-Profile anzeigen
WOW!

Vielen Dank! Ich werde mir das mal in Ruhe ansehen und mich Schritt für Schritt daran versuchen. Wie gesagt Ihr seid die besten - TOP Coummunity!

DAK

BeitragMi, Jul 09, 2014 22:18
Antworten mit Zitat
Benutzer-Profile anzeigen
Schau dir vielleicht mal das an, was ich hier rein gepostet hab. Das macht dir dieses Packen und Entpacken automatisch.
Gewinner der 6. und der 68. BlitzCodeCompo
 

Snowdragon

BeitragMo, Jul 14, 2014 16:16
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey,

ich hab da nochmal eine Frage zum TCP Server/Client-Prinzip (siehe Post von Bobo2040).
In der Kommentierung steht: "; Ausgelegt für lokalen Betrieb mit einem Client

Ich habe den Code des Clients auf meinem Laptop laufen lassen und den Server auf meinem Rechner, hat soweit auch alles funktioniert.

Im Client-Code
Code: [AUSKLAPPEN]
Local Stream = OpenTCPStream( "127.0.0.1", 12345 )

Habe ich die IP-Adresse ausgetauscht, dazu habe ich cmd ausgeführt und ipconfig eingegeben und dann die IP-v4-Adresse des Rechners (Server) eingetragen.

Anschließend habe ich die daraus eine Client.exe erstellt und diese einem Freund gesendet um zu sehen ob es auch außerhalb des lokalen Netzwerks funktioniert. Jedoch kam es zum RunTimeError. Ich dachte es liegt vlt. an IP Masquerading (NAT und PAT) und die IP-Adresse die ich ausgelesen habe über ipconfig entspricht nicht der, mit der ich eine Internetverbindung aufbaue. Über "Wie ist meine IP" habe ich im Internet tatsächlich eine andere "dynamische IP-Adresse" ausgemacht und die aktuelle IP-Adresse im Code mit dieser ausgetauscht, dann wieder eine exe erstellt und einem Freund gesendet, doch auch hier rührte sich nichts.

Liegt es am Quellcode? Müssen dort bestimmte Änderungen vorgenommen werden um auch eine Kommunikation außerhalb des Netzwerks herzustellen?
 

Kruemelator

BeitragMo, Jul 14, 2014 16:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Außerhalb eines Lokalen Netzwerkes gibt es Router und Firewall die erstmal alles abblocken. Es ist möglich bei einem Router Ports zu "Forwarden". Man sagt dem Router sozusagen, dass er alles was an diesen Port geschickt wird an einen bestimmten Rechner im Netzwerk weitergeleitet werden soll.

Da du TCP verwendest weiß ich nicht ob das folgende funktioniert, mit UDP ist es aufjedenfall möglich.
Wenn sich beide Clienten gleichzeitig über den selben Port Daten zuschicken (an die jeweilige Internet-IP) wird der Port, an beiden Seiten, sozusagen von innenheraus geöffnet und es kommen auch ankommende Daten durch.
Man braucht also keinen externen Server oder Port Forwarding sondern nur die IP des anderen.

DAK

BeitragMo, Jul 14, 2014 17:20
Antworten mit Zitat
Benutzer-Profile anzeigen
UDP Holepunching (das was Kruemelator im 2. Absatz beschreibt) funktioniert leider nur auf UDP, nicht auf TCP und ist auch etwas schwieriger hin zu kriegen als eine normale Verbindung. Für einen Anfänger ist das wohl nicht das Beste.

Du bist schon auf den Großteil draufgekommen: sitzt du hinter einem Router (und somit in einem NAT), dann gibt's da ein virtuelles Netzwerk (das NAT). Alle Computer, die sich im gleichen NAT befinden, haben von außen gesehen die gleiche IP. Verbindungsanfragen von außen gehen also nur an den Router, und wenn der sie nicht verarbeitet (=Standardfall), dann lässt er sie einfach fallen.

Um dich von Außen auf einen Computer im NAT verbinden zu können, brauchst du, wie Kruemelator schon geschrieben hat, Port Forwarding. Dabei stellst du am Router ein, dass Verbindungen, die auf einem (oder mehreren) bestimmten Port rein kommen standardmäßig auf einen bestimmten Computer weitergeleitet werden sollen. Dann kannst du deinen Computer von außen mit der IP des Routers ansprechen.

Es gibt dabei aber noch ein Problem: manche Internet-Provider geben einem keine eigene IP mehr, sondern setzen ihre Kunden wieder hinter ein NAT. Mobiles Internet rennt fast immer auf die Art, aber auch ein paar Festnetzanbieter machen das so (u.A. Kabel Deutschland, soviel ich weiß).
Ist das der Fall, dann kannst du es im Grunde vergessen. Eventuell kannst du den Kundendienst beknieen, dass sie dir eine Portweiterleitung für ein paar Ports einrichten, aber ich würde es nicht erwarten.
In dem Fall kannst du immer noch über Dienste wie Hamatchi (weiß nicht, ob es den noch gibt) hosten, die ein LAN über das Internet hinweg simulieren.
Gewinner der 6. und der 68. BlitzCodeCompo
 

Snowdragon

BeitragDi, Jul 15, 2014 11:07
Antworten mit Zitat
Benutzer-Profile anzeigen
Hat wunderbar geklappt am Router!
Danke nochmals! Ich muss noch meinen Code etwas neu strukturieren und dann setze ich mich weiter damit auseinander. War mir erstmal nur wichtig zu erfahren, ob ich wirklich mit meinen Freunden spielen kann.

Tankbuster

BeitragSa, Jul 19, 2014 14:10
Antworten mit Zitat
Benutzer-Profile anzeigen
Am einfachsten ist es, einen Server zu benutzen, zu dem alle Clients connecten. Das bedeutet, der Server muss Port-Forwarding betreiben. und die Clients müssen die IP des Servers kennen.

Um ersteres zu erreichen, reicht es die Routereinstellungen zu verändern. Zweiteres lässt sich mit einer dynamischen DNS regeln, z.b. www.GoIP.de.

Wenn der Client dem Server dann eine Nachricht sendet, sollte der Server in der Lage sein, dem Client ebenfalls Pakete zu schicken. Somit wäre dann eine Verbindung erstellt.

Um eine peer-to-peer verbindung aufzubauen, schickt der Server zwei Clients die gegenseitige IP, damit diese wiederrum eine Verbindung aufbauen können nach dem Muster:

Client 1 sendet an Client 2 -> Paket kommt nicht an, da kein Port-Forwarding
Client 2 sendet an Client 1 -> Paket kommt an (Router erwartet Antwort)
Client 1 sendet an Client 2 -> Paket kommt an, (Router erwartet antwort)
Verbindung hergestellt.
Twitter
Download Jewel Snake!
Windows|Android
 

Snowdragon

BeitragDo, Jul 24, 2014 18:32
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich versuche mich kurz zu fassen:
- Wie bereits gesagt kann ich eine Verbindung aufbauen
- Ich habs auch geschafft die Handkarten des Gegners zu übermitteln (ich denke aber eher umständlich)


Nun habe ich 2 Fragen:

1: ReadAvail
Kann mir jemand ReadAvail etwas genauer erklären?

2: Programmcode
Hat sich erledigt.
  • Zuletzt bearbeitet von Snowdragon am Sa, Jul 26, 2014 16:19, insgesamt 2-mal bearbeitet
 

Hangman

BeitragDo, Jul 24, 2014 18:41
Antworten mit Zitat
Benutzer-Profile anzeigen
Sende die Daten in einem Paket, nicht in 3.
Du könntest die Daten z.B. in einzelne Lines schreiben, oder auch die nativen Datentypen verwenden (String, Int usw.)
Entsprechend hast du keine Probleme beim Auslesen.
Erstell dir ein Modell wie eine Nachricht bei dir aufgebaut sein soll, als ersten Datensatz würde ich einen sogenannten OPCODE verwenden, der erstmal sagt, was da für Daten reinkommen, bzw. was der Empfänger tun muss. Nach diesem OPCODE könntest du dann dein Select-Statement aufbauen.
Ich habe Berthold gebrochen.
 

Snowdragon

BeitragDo, Jul 24, 2014 19:26
Antworten mit Zitat
Benutzer-Profile anzeigen
Oh man ich habe garnicht daran gedacht, dass ich ja einfach alle in einer Line senden kann.
Okay vielen dank tut mir Leid! Razz Embarassed

DAK

BeitragDo, Jul 24, 2014 22:50
Antworten mit Zitat
Benutzer-Profile anzeigen
@ReadAvail:
Diese Funktion gibt dir zurück, wie viele Bytes schon angekommen sind, du aber noch nicht verarbeitet hast. Das mit den Internet-Streams funktioniert intern so, dass Daten, die ankommen, sofort in einen Buffer geschrieben werden. Sobald die also über das Kabel an deinen Computer kommen, werden sie am Computer zwischengespeichert, nicht erst, wenn du sie mit einem Lesebefehl (z.B. ReadLine) ausliest. Der Lesebefehl holt die Daten nur aus dem Buffer raus, und löscht sie von dort.


@Programmcode:
Könnte so funktionieren. Verwende besser nicht ReadLine sondern ReadInt/String/Byte/Short/Float. Diese Befehle brauchen deutlich weniger Sendeleistung. Der einzige Nachteil ist, dass du hald auf die Lesereihenfolge etwas genauer achten musst.
Beispiel: mit ReadInt/WriteInt brauchst du immer 4 Byte, das ist äquivalent zu 4 Zeichen. Die höchste Zahl, die du in einem Int speichern kannst, ist 2147483647, was dich also wenn du es als Line schickst, 10 Byte kostet. Willst du kleinere Werte nur speichern, dann geht auch Short (Maximalwert 32767, 2 Byte) oder Byte (Maximalwert 255, 1 Byte).

Du kannst dabei immer noch mehrere Sachen in ein Paket fassen, da Blitz automatisch zeitnahe Sendungen zu einem Paket zusammenfasst. Ich hab gerade mal mit Wireshark nachgetestet, wie ich 1000 Mal WriteInt nach einander ausgeführt habe. Diese 4000 Bytes wurden in 21 Paketen geschickt. Um das musst du dir also keine Sorgen machen.
Gewinner der 6. und der 68. BlitzCodeCompo
 

Snowdragon

BeitragFr, Jul 25, 2014 10:03
Antworten mit Zitat
Benutzer-Profile anzeigen
Okay mache ich so! Wink
DAK ich kanns immer nur wieder sagen vielen vielen DANK!
Den anderen natürlich auch einfach klasse.
 

Snowdragon

BeitragSa, Jul 26, 2014 21:20
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey,

nun hab ich weiteres Problem und verharre schon seit einigen Stunden daran.

Der Spieler der nicht am Zug ist befindet sich in einer While-Schleife und überprüft, ob ein TCP-Paket gesendet wurde. Anschließend verlässt er die While-Schleife geht in eine Case-Auswahl und danach wieder in die While-Schleife.

In der Case-Auswahl wird z.B. das legen einer gegnerischen Karte realisiert.



Mein Problem:
Wird nicht innerhalb einer kurzen Zeit ca. 5 Sekunden eine Karte gelegt, merkt man das sich etwas beim Spieler der wartet tut. Das Spiel scheint eingefroren (keine Rückmeldung erscheint) zu sein und das Ladeicon der Windows 7 - Maus erscheint. Nach weiteren 3-4 Sekunden scheint das Spiel wieder "entfroren" zu sein.

Lege ich nun beim Spieler der dran ist eine Karte, so reagiert der wartende Spieler nicht. Die Karte wird beim ihm nicht angezeigt. Das Spiel stürzt auch nicht ab - reagiert aber auf keine TCP-Pakete.

Spiele ich aber direkt nach Spielbeginn schnell eine Karte, so funktioniert dies problemlos.

Ich habe vieles probiert und weiß ehrlich nicht mehr weiter. Vielleicht hängt es mit meiner Methode zur Überprüfung der Pakete zusammen, da nach einigen Sekunden ein Speicher vollausgelastet ist, oder die Verbindung abgebrochen wird oder was auch immer (ich kenne mich damit nicht wirklich aus^^ - habe wie gesagt nur leichte Grundkenntnisse was die TCP-Geschichte angeht). Nur um alle Möglichkeiten durchzugehen - kann es vielleicht auch daran liegen das ich mit include Arbeite und die Abfrage darin eingebaut habe, was Störungen verursachen kann?

Hier mal meine Methode der Überprüfung beim Spieler der nicht am Zug ist:
Code: [AUSKLAPPEN]
While ArrayStatus$(0) = "" Or ArrayStatus$(0) = "Warten"
       ArrayStatus$(0) = ReadLine( Stream )
Wend
  • Zuletzt bearbeitet von Snowdragon am Sa, Jul 26, 2014 21:35, insgesamt 3-mal bearbeitet
 

Kruemelator

BeitragSo, Jul 27, 2014 1:26
Antworten mit Zitat
Benutzer-Profile anzeigen
Du wartest mit der While-Schleife. Solange die Schleife läuft hängt alles (100% CPU-Auslastung). Packe in die Haupt-Schleife einen Timer und merke dir den Status des Spielers. Je nach Status werden entsprechende Dinge gemacht. Hier mal ein kleines Beispiel.
BlitzBasic: [AUSKLAPPEN]
spielerstatus = 0;status als zahl reicht vollkommen

timer = CreateTimer(60)
While True
WaitTimer(timer);nur hier wird gewartet!!!!

If spielerstatus = 0 Then
;hier ist der Spieler am Zug und darf etwas machen
;hier darf nicht gewartet werden!!!!
EndIf
If spielerstatus = 1 Then
;hier ist der Gegner am Zug und der Spieler darf nix machen
;hier darf auch nicht gewartet werden!!!!
;es wird EINMAL überprüft ob etwas angekommen ist, wenn nicht geht es einfach weiter

EndIf

;hier wird alles gerendert
Wend

DAK

BeitragSo, Jul 27, 2014 9:34
Antworten mit Zitat
Benutzer-Profile anzeigen
Das mit dem "Keine Rückmeldung" liegt daran, wie Windows einen Absturz definiert:
Ein Programm zählt als abgestürzt, wenn:
- Es zu lange auf Vollast fährt (kein Delay / Waittimer)
und
- Das Programm zu lange keine neuen Frames zeigt (Flip)

Windows nimmt dabei an, dass das Programm in einer ungewollten Endlosschleife steckt.

Um das zu verhindern, tu was Kruemelator sagt. Außerdem solltest du weiterhin jedes Frame zeichnen, auch wenn der Client wartet, da es sonst zu Grafikfehlern kommen kann (wenn du z.B. ein Fenster minimierst).
Gewinner der 6. und der 68. BlitzCodeCompo
 

Snowdragon

BeitragSo, Jul 27, 2014 11:34
Antworten mit Zitat
Benutzer-Profile anzeigen
Hm habe eure Ratschläge umgesetzt aber es kommt trotzdem zum "Absturtz". Die CPU-Auslastung hatte ich auch im Blick und lediglich eine Auslastung von max 15%.


Eine Frage hätte ich noch, es kommt ja wirklich nur 1x zu einem kleinen Absturtz. Müsste es nicht nach der Theorie mit der Vollast zu mehreren Abstürtzen kommen? Vlt. habe ich auch ein wichtiges Detail unterschlagen, ich habe so einiges probiert und was mir aufgefallen ist:
- Wird innerhalb von ca. 5 Sekunden keine Karte gelegt = 1x kurzer Absturtz - Das Spiel läuft anschließend weiter
- Wird zu lange keine Karte gelegt = 1x Absturtz - danach nie wieder und das Spiel läuft danach weiter

- Ich habe in der Abfrage des TCP-Streams einfach ein Print Befehl eingebaut und eine Variable hochzählen lassen und auch das Array ausgeben lassen
- Variable wurde normal hochgezählt und für für das Array wurde einfach eine leere Zeile ausgegeben
- Wird nun eine Karte gespielt wird immer noch eine leere Zeile ausgegeben, obwohl ich an den Client etwas schicke

Ich werde das Prinzip einfach in einer seperaten BB-Datei umsetzen und schauen ob das Problem immernoch auftaucht. Wenn ja kann ja zumindest davon ausgehen das nicht mein restlicher Code daran Schuld ist. Ich werde den Post updaten sobald ich fertig bin und neue Ergebnisse habe.

Gehe zu Seite 1, 2  Weiter

Neue Antwort erstellen


Übersicht BlitzBasic Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group