# Computer über Internet verbinden



## _-`avaj´-_ (3. Mrz 2012)

Hallo zusammen,
ich möchte gerne ein Spiel programmieren, dass man auch im Multiplayer-Modus spielen kann...
Das Spiel steht soweit auch schon aber die Frage ist:
Wie bekomm ich den Multiplayer hin?
Im eigenem Netztwerk ist das ja kein Problem via server/socets, aber wie sieht das ganze aus wenn ich das Spiel von 2 veschiedenen Rechnern Spielen will, die nicht im selben Netztwerk sind?
Ich habe mir schon folgendes Thema angeguckt:
http://www.java-forum.org/netzwerkp...ng-hostrechner-ueber-internet-herstellen.html
bin aber nicht wirklich schlau daraus geworden...
Ich möchte folgendes:
Eine Verbindung der beiden Computer über die Informationen gesendet werden (im Prinzip nur 2stellige Zahlen), dabei sollte es minimale Verzögerungen haben (perfekt wären max. 5 millisekunden) und das wichtigste: Es sollen vom User keine Einstellungen am Router (z.B. port-forwarding) vorgenommen werden müssen.
So wie ich es bis jetzt gehört habe ist das ohne irgendwelche Voreinstellungen aber nicht machbar...
Da hat sich bei mir aber sofort die Frage gestellt: Wie machen es dann bitteschön die anderen Spieleentwickler?
z.B. Call of Duty nutzt doch auch ein server-client System für den Multiplayer oder Minecraft ist sogar in Java gecodet und kommt auch ohne Server aus...

ps:
Ich habe auch einen Server mit einer DYNS aber der unterstützt leider kein java sondern nur php...
Und bitte mach es nicht zu kompliziert, Netztwerkprogrammierung ist fast komplettes Neuland für mich... am besten wären konkrete Beispiele wenn möglich

Ich hoffe ihr könnt mir helfen!

Gruß
avaj


----------



## Gast2 (3. Mrz 2012)

Ob im selben Netzwerk oder nicht ist deinem Programm völlig egal. Du brauchst nur ne IP und nen Port um dich mit deinem gegenüber zu verbinden.

Der Port muss dabei aber zwangsläufig im Router per port-forwarding richtig eingestellt werden, sonst tut sich da nix. Das könnte man aber auch per udp hole punching umgehen, das sollten die modernen Router eigentlich alle unterstützen. Da würd ich einfach mal ner ner lib für Java googlen.



> dabei sollte es minimale Verzögerungen haben (perfekt wären max. 5 millisekunden)


Übers Internet... hm... utopisch 

EDIT:
Für ausgehende Verbindungen musst du in der Regel eh keine Ports freigeben, das betrifft meist nur Server. Und auch bei minecraft musst du Ports händisch freigeben wenn du nen Server aufmachen willst.


----------



## irgendjemand (3. Mrz 2012)

@TO
wie es andere spiele-entwickler machen ? ganz einfach : UPnP und UDP Hole Punching ... alles etablierte standards ...

ansonsten gilt das was bei jedem "normalen" host-service gilt : port muss im router manuell weitergeleitet werden ...

was du jetzt mit MC meinst wird mir leider nicht ganz klar ... denn wenn du hier einen server auf machen willst musst du im router auch den port weiterleiten ...
was COD angeht : könnte mir vorstellen das über STUN gearbeitet wird ...

bestes beispiel : Alien Swarm ... verwendet die steam-server als STUN server und baut dann mit UDP Hole Punching die verbindungen auf ... keine config von irgendwelchen routern und funktioniert auch bei deaktiviertem UPnP *was auf grund des sicherheitsrisikos eigentlich standard deaktiviert sein sollte*

aber deine 5ms ping übers netz kannst du getrost vergessen ...
in der regel brauchen datenpakete vom modem bis zum provider schon deutlich länger ...
wenn von da aus noch zu nem anderen provider geroutet werden muss und dann von dort zu einem weiteren kunden ... kannst du schon mal gut mit 40ms oder mehr rechnen ...

ansonsten : dem client kann es doch egal sein ob er mit dem server im selben netz ist ... ihn interessiert nur eine IPORT angabe ...


----------



## _-`avaj´-_ (3. Mrz 2012)

Wenn das so ist muss wohl doch mein Spiel über Hamachi laufen lassen...
schön wäre es nur gewesen ein kamplett eigenständiges Spiel hinzubekommen, das keine externen Programm oder Voreinstellungen benötigt - Userfreundlich eben 

Ach noch was vlt.:
Wenn ich meine 5 millisecs nicht hinbekomme (habs schon befürchtet) hat jemand ne Idee, wie ich das Spiel synchron hinkrieg?
Sprich es werden immer nur die Tastatureingaben übermittelt und jeder Computer rechnen dann für sich aber wenn diese Information erst sagen wir 1-0,5 sek später beim anderen Rechner ankommt, kann es ja passieren, das der eine Gegner bei PC1 schon game over ist aber bei PC2 ist er noch rechtzeitig ausgewichen...


----------



## irgendjemand (3. Mrz 2012)

vergiss das mit hamachi mal wieder ... du wirfst hier die flinte zu früh ins korn ...

schau dir mal UDP Hole Punching / STUN an ... das sollte mit jedem modernen gerät funktionieren ...
vielleicht auch noch UPnP ... wo aber nicht garantiert ist das dies von allen teilnehmern unterstützt oder aktiviert ist ... *mein router kann es zwar ... aber ich habs deaktiviert weil mit UPnP jedes programm willkürlich ports öffnen kann ... und das ist mir einfach ein zu großes sicherheitsrisiko*

oder du musst halt in den sauren apfel beißen und dem user sagen das manuelles port-forewarding verlangt wird ...

persönlich empfinde ich die installation einer VPN software als aufwändiger als mal eben im router n port weiter zu leiten ... sowas solltest du bei deiner entscheidung auch berücksichtigen ...

[EDIT]was das "keep-in-sync" angeht : genau desswegen sollte es bei einer client-server anwendung immer eine festgelegte verwaltungsinstanz geben *in der regel der server* der dann das rechnen leistet ... die clienten sind lediglich dazu da user-eingaben zum server zu senden und das ergebnis vom server darzustellen ...
eine selbstständige berechnung sollte man vermeiden da so deadlocks entstehen können *wenn eben beide rechner etwas anderes raushaben *pc1 : gegener tot / pc2 : ausgewichen**
überlege hier also noch mal dein konzept ansatz[/EDIT]


----------



## _-`avaj´-_ (3. Mrz 2012)

Wie gesagt ich bin Anfänger und Netztwerkprogrammierung ist komplett neu für mich...
Mit Links oder am besten Beispielen wäre mir sehr geholfen...
UDP Hole Punching hört sich interresant an aber ich hab bis jetzt keinen Code gefunden...
Außerdem braucht man dazu eine externe Bibliothek nehme ich an... das würde ich aber gerne vermeiden aber wenn nötig gehts auch so 

Und du hast Recht: eine Portfreigabe ist einfacher als ein prog zu installieren aber man muss ja immer vom DAU (Dümmster anzunehmender User) ausgehen und ich weiss nicht ob eine Port-Freigabe für jeden so einfach zu bewerkstelligen ist 

und zu keep-in-sync: Wie gesagt ich habe zwar einen server, aber der unterstützt kein java, also müssen die Rechner selber rechnen und auch wenn ich nur einen rechner als server festlege kommen die befehle vom pc auf dem der server läuft immer noch schneller an als die vom anderen pc -.-


----------



## irgendjemand (4. Mrz 2012)

@TO
nein ... für UDP Hole Punching braucht man doch keine extra lib ...
das kann man mit java eigenen mitteln und einem blick hierauf machen : UDP hole punching - Wikipedia, the free encyclopedia *leider nur in englisch verfügbar*

folgenden code habe auch ich genau so durch googlen gefunden

NAT.java

```
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;



public class NAT {

	public static void main(String[] args) throws Exception {
		
		if (args.length < 3) {
			System.out.println("USAGE: NAT [localport] [remoteIP] [remoteport]");
			return;
		}
		
		int localPort = Integer.parseInt(args[0]);
		String ip = args[1];
		int remotePort = Integer.parseInt(args[2]);
		
		System.out.println("Testing UDPTunneler");
		UDPTunneler tunnel = null;
		try {
			tunnel = new UDPTunneler(localPort,ip,remotePort);
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("Initialized, creating tunnel.");
		
		try {
			tunnel.createTunnel();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("Yes, tunnel successfully created.");
		
	}

}
```

UDPSender.java

```
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;


public class UDPSender extends Thread {

	String str,ip;
	int port;
	long interval;
	boolean run;
	DatagramSocket socket;
	
	public DatagramSocket getSocket() {
		return socket;
	}
	
	public void setStr(String str) {
		this.str = str;
	}
	
	public void send(String s) {
		byte[] data = s.getBytes();
		InetSocketAddress address = new InetSocketAddress(ip,port);
		
		DatagramPacket packet = null;
		try {
			packet = new DatagramPacket(data,data.length,address);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		try {
			socket.send(packet);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public UDPSender(DatagramSocket sock,String ip, int port,String s, long i) {
		super();
		str = s;
		interval = i;
		run = true;
		this.socket = sock; 
		this.ip = ip;
		this.port = port;
		this.start();
	}
	
	public void terminate() {
		run = false;
	}
	
	public void run() {
		
		while(run) {
			
			this.send(str);
			
			try {
				Thread.sleep(interval);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		
	}
	
}
```

UDPTunneler.java

```
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.InetSocketAddress;
import java.net.InetAddress;


public class UDPTunneler {
	
	int lport,rport;
	String ip;
	DatagramSocket socket;
	
	public UDPTunneler(int localPort, String hostIP, int remotePort) throws Exception {
		lport = localPort;
		rport = remotePort;
		ip = hostIP;
		
		socket = new DatagramSocket(localPort);
	}
	
	public void createTunnel() throws IOException {
		UDPSender sender = new UDPSender(socket,ip,rport,"CONN",1000);
		
		while(true) {
			
			DatagramPacket p = new DatagramPacket(new byte[10], 10);
			socket.receive(p);
			
			//analyse
			String data = new String(p.getData());
			if (data.indexOf("CONN") != -1) {
				sender.send("CONN");
				sender.terminate();
				break;
			}
			
		}
	}

}
```

selbst geschrieben ist daran von mir nichts ... und wie du siehst wird dort auch keinerlei lib benötigt ... das ist alles SE-api ...
das kannst du als grundlage nehmen ...

zum eigentlichen konzept

ich würde erstmal logik und user-interaktion trennen ... also einen "server" und einen "client" programmieren ...

aufgaben

server : beinhaltet "logik" und ist für die berechnung zuständig ...
client : nimmt user-eingaben an , leitet diese an den server ... und zeigt das zurückgeliefert ergebnis an ... KEINE eigenen berechnungen !

natürlich kannst du beides in einem "programm" unterbringen ... musst dann aber nur den start ändern

dann zu deinem problem das du keinen richtigen server sondern nur webspace mit php hast
hier bietet sich PHP in verbindung mit einer datenbank *meist MySQL* an ...

wenn jemand einen "server" startet wählt sich dieser erstmal selbstständig einen port für UDP Hole Punching *irgendwo zwischen 50'000 und 60'000 ist gut* ... dann übermittelt er diesen an ein php-script welches diesen port in verbindung mit der vom server ermittelten ip in eine datenbank einträgt ...

ein neuer client wählt jetzt ebenfalls erstmal seinen port fürs UDP Hole Punching ...
dann holt er beim server eine liste mit allen verfügbaren lobbies ...
wählt ein client eine bestimmte lobby aus wird ein weiterer eintag in der datenbank gemacht das sich client y zum server x verbinden will ...
dann beginnt er an die vom lobby-server erhaltene IP und PORT UDP pakete zu senden und öffnet damit sein NAT ...

der server *also der spiele-server ... nicht der lobby-server* pollt nun den lobby-server alle 5sec und holt sich die info ob bereits ein user für seine lobby eingetragen ist ...
wenn ja holt sich der server die infos welche IP und welchen PORT und beginnt ebenfalls das senden von UDP paketen ... dadurch wird sein NAT geöffnet ...

auf grund der in der RFC beschriebenen vorgehensweise sind nun beide router auf dem status das eine ausgehende UDP-verbindung von einer bestimmten quell-ip und einem bestimmten quell-port raus zu einer bestimmten ziel-ip und einem bestimmten ziel-port in die NAT-tabelle geschrieben wird ...
dies ermöglicht für einen kurzen zeitraum *miest was zwischen 30sec und 120sec* das ankommende pakete mit passendem absender und port an den rechner im lan weitergeleitet werden von dem aus diese verbindung initiiert wurde ...
das nennt sich dann UDP Hole Punching ... da beide seiten von innen in die auf dem router vorhandene firewall ein loch geschlagen haben um darüber zu kommunizieren ...
dadurch das zum verbindungsaufbau ein externe server verwendet wurde der den austausch dieser informationen zwischen beiden clienten geregelt hat wird das ganze *nur vom namen her* zu STUN ... und damit zu einer in einer RFC festgelegten art und weise eine verbindung durch ein NAT mit einem anderen user der ebenfalls hinter einem NAT sitzt aufzubauen ...

wenn jetzt der server feststellt das die verbindung erfolgreich aufgebaut wurde *in der regel in dem der server ein paket an den clienten schickt und erwartet das dieses mit dem selben inhalt , allerdings anderer quelle , an ihn zurück kommt* meldet er sich beim lobby-server und lässt durch ein weiteres php-script alle lobby-spezifischen daten aus der datenbank entfernen ...

die verbindung ist nun hergestellt ...

damit diese verbindung auch weiterhin offen bleibt müssen in kurzem abstand *persönlich halte ich 10sec für ausreichend* sog. PING-pakete gesendet werden ...
das sind lediglich pakete welche als inhalt "PING" haben ... und von der gegenstelle mit "PONG" beantwortet werden müssen ...
es ist empfehlenswert das beide seiten unabhängig von ein ander diese PING-requests senden und auf die antwort warten -> a-synchrone threads ...

*wenn man bei diesen "ping"-paketen zusätzlich noch die zeit-misst hat man die latenz : zeit die ein datenpaket zum ziel und zurück braucht*

um den verbindungs-abbau musst du dir keine sorgen machen ... so bald eine seite auf die PING-pakete nicht mehr reagiert gilt die verbindung als beendet und beide seiten hören selbstständig auf sich pakete zu senden ...
je nach dem welches timeout für UDP Hole Punching im router eingestellt ist hat man nach 30sec bis 120sec auch keine löcher mehr in seiner firewall da der router selbstständig die NAT einträge entfernt und somit das loch stopft ...


so ... viel theorie krams ... und kurzes beispiel ... einen link zu wikipedia *ich hoffe englisch ist kein problem* ...

da sollte man jetzt ansetzen können ...

ich werde heute nicht mehr zu kommen ... und morgen vormittag auch nicht mehr ... aber ab morgen nachmittag könnte ich dir bei der umsetzung helfen ...


[OT]Thema TimeOut
@admin : wenn ich angemeldet bin bin ich angemeldet ... und wenn ich eine antwort schreibe schreibe ich eine antwort ... und was macht das back-end ? es loggt mich aus ... und ich bekomme : "timeout abgelaufen" an den kopf ...
genau so LOL ist auch die aktion : edit ja ... aber nur für einen gewissen zeitraum ...

naja ... macht mal wie ihr denkt ... WITZfigurenkabinet ...[/OT]


----------



## Gast2 (4. Mrz 2012)

> nein ... für UDP Hole Punching braucht man doch keine extra lib ...


Klar, man kanns selber schreiben, aber man muss ja nicht jedesmal alles neu erfinden. Und du nutzt ja auch vorgefertigten Code den jemand anderes mal geschrieben hat. Das ist keine "lib"?

@Offtopic:
Schnapp dir mal nen bissl Baldrian, das soll beruhigend wirken


----------



## irgendjemand (4. Mrz 2012)

EikeB hat gesagt.:


> Klar, man kanns selber schreiben, aber man muss ja nicht jedesmal alles neu erfinden. Und du nutzt ja auch vorgefertigten Code den jemand anderes mal geschrieben hat. Das ist keine "lib"?



nein ... das ist in meinen augen keine lib ... sondern lediglich sample-code wie man UDP Hole Punching mit SE-api mitteln umsetzen kann ...


wenn jetzt etwas wie "Apache Commons" gekommen wäre ... ja ... sowas würde ich dann schon mal als lib bezeichnen ...

*ums vorweg zu  nehmen : einen JDBC-driver würde ich auch nicht dierekt LIB nennen ... sondern eben JDBC-driver*


was den baldrian angeht : mein post war weder aggressiv noch sonst wie gemeint ... sondern ich hab lediglich mal meinen gedanken freien lauf gelassen und es so aufgeschrieben wie ich es umsetzen würde ...
weder habe ich gesagt das TO es so machen MUSS ... noch war ich sonst wie aggressiv ... *zu mal ich kurz vorher grad mal aufgestanden bin ... da wäre ich nich mal fit gewesen mich über i-was aufzuregen geschweige denn mal wieder wahllos verbal um mich zu schlagen*


----------



## Gast2 (4. Mrz 2012)

Ich bin mir sicher dass die Java libs für UDP hole punching auch SE-Api mittel nutzen 
Aber nunja, liegt wohl im Auge des Betrachters.


----------



## irgendjemand (4. Mrz 2012)

hmm ... wie gesagt ... ich sehe hier nun mal keine lib ...
das einzige was hier gemacht wird ist ein DatagramSocket auf einem bestimmten localport zu binden und von diesem aus DatagramPacket zu einem ziel zu senden und auf antwort zu warten ...
ganz krass würde man das sogar in nem 10-20 zeiler unterkriegen *was dann aber schon echt schwer lesbar wäre*

und zum letzten satz : naja ... irgendwo greift alles auf SE-API zurück ... man muss den stack nur weitgenug runterbrechen ... egal wie viele libs man auf ein ander aufbauen lässt ...
einzige ausnahme wären hier jetzt NATIVE calls


----------



## _-`avaj´-_ (5. Mrz 2012)

@irgendjemand

Danke dein Code und deine Erklärung haben mir sehr geholfen... kann jetzt 2 Computer verbinden (habs mit einem Freund getestet) bzw. Datenpakete verschicken...
Der Delay ist auch annehmbar: ca. 80 millisec hin und wieder zurück.

Trotzdem: Das alte keep-in-sync Problem bleibt und ich habe immer noch keine Lösung 
Kannst du mir da auch weiterhelfen?

Und noch eine Frage:
Ist es möglich über UDP Hole Punching mit Streams zu arbeiten?
Also z.B. zum Versenden von Datein etc.


----------



## irgendjemand (6. Mrz 2012)

zum keep-in-sync

wie gesagt : grundlegendes konzept erstmal radikal ändern ...

heißt : verarbeitung der daten und deren anzeige trennen -> server und client ... nicht alles in einem ...
da ich dein projekt nicht kenne kann ich nur raten ... aber das ist erstmal der grundlegende ansatz nach dem du das ganze neu ausrichten solltest ...

um "lagg" zu verringern solltest du in den paketen noch einen timestamp einfügen


zum anderen

es gibt etwas was ähnliches mit TCP ermöglicht ... heißt dann TCP Hole Punching ...
das problem dabei ist nur : dadurch das TCP noch ein paar mehr daten im header als nur quelle und ziel hat und die meisten NAT auch intiligent genug sind den header so weit zu lesen ... ist das ganze schon nicht mehr so einfach ...
man müsste noch an die SYN kommen ... an andere flags ... status- und zustands infos ...
das ganze ist mit java leider nicht mehr möglich ... dafür brauchst du eine JNI-LIB *@EikeB : sowas würde ich jetzt LIB nennen : ein paket aus java-klassen , JNI-wrapern und OS-code *im allgemeinen ja auch nur "lib" genannt** um auf diese tiefe manipulations-ebene zu kommen ...

du kannst aber auf basis der stehenden UDP-verbindung etwas ähnliches wie TCP nachbauen ...
du müsstest dafür lediglich folgenden dinge implementieren

order-mark : ein eindeutiger identifier der jedes paket innerhalb des "datenstromes" eindeutig kennzeichnet und auf grund dessen eine genaue "positionsbestimmung" möglich ist
packet-ACK : bestätigung der gegenseite über den kompletten und vorallem fehlerfreien erhalt des paketes
checksum : einen hash über die nutz-infos um fehlererkennung- und behebung zu ermöglichen
timeout : der sender wartet ein definiertes timeout auf das packet-ACK vom empfänger ... kommt die antwort nicht / zu spät muss davon ausgegangen werden das das paket unterwegs verloren gegangen ist ... sollte der empfänger zwar das paket bekommen haben ... aber die bestätigung verloren gehen kann der empfänger auf grund des order-mark feststellen das dieses paket bereits empfangen wurde und verwirft es einfach ... *das sollte dem sender dann aber auch mitgeteilt werden ... oder halt einfach bei neu-erhalt einfach eine bestätigung schicken*

du siehst : TCP ist deutlich aufwändiger implementiert als UDP ... darum ist TCP Hole Punching auch schwieriger ... auch besteht das problem das es *zumindest meines wissens nach* nicht standardisiert ist und daher auch nicht von allen geräten unterstützt wird *mit der D-LINK 100er serie geht es z.b. nicht *eigene erfahrung**

es ist wie gesagt möglich auf basis von UDP etwas TCP-ähnliches nachzubauen ... aber es ist kompliziert und fehler-anfällig ...

sollte man also eine daten-übertragung tätigen wollen bei der TCP notwendig wird ... sollte man nach einer möglichkeit suchen TCP auch dierekt zu verwenden *entweder port-forewarding ... oder über einen server im netz* ...
UDP sollte man nicht für solche experimente "missbrauchen" ...


ps : 80ms latenz ... das ist schon heftig ... da würde ich an weiter entfernte orte wie UK , frankreich oder schweiz denken ... aber innerhalb deutschlands ... sollte man selbst von flensburg bis augsburg und zurück nicht mehr als gesamt 40ms-50ms brauchen ...


----------

