# Server auf eigenem Rechner



## April (11. Dez 2012)

Hallo,
Ich habe mich mit Netzwerkprogrammierung noch nicht sooo sehr auseinandergesetzt, aber ich programmiere gerade ein Spiel, das man auch mit mehreren Online spielen können soll.
Nun habe ich zum programmieren beim Clienten bei der IP "localhost" eigegeben und es hat soweit auch funktioniert... nur 2 Probleme...
1. Schon bei meiner Ansicht nach kleinen Datenmengen fängt die Übertragung an zu laggen...
z.b.  ich übertrage u.a. einen String. Wenn der null ist geht alles noch, wenn der String dann allerdings so etwa 15 zeichen hat laggt es schon ziemlich. (60 mal pro Sekunde wären ideal)
2. Wenn ich den Server starte und bei dem Clienten meine IPv4 Adresse eingebe, kommt "NoRoutetoHost". Auch wenn ich mit dem Internet verbunden bin.



Schon einmal Danke im Vorraus...


----------



## dayaftereh (11. Dez 2012)

Wie überträgst du den die Daten TCP oder UDP. Nutzt du bei TCP Streams? Welche Streams nutzt du den? Schau mal bei TCP nach setTCPNoDelay? Du nutzt kein NIO? Ruf mal nach dem verschicken des Strings flush() auf?


----------



## April (11. Dez 2012)

Ich glaube ich nutze TCP.
Von TCPnoDelay habe ich noch nie was gehört.
Ich nutze kein flush().
Und NIO kenne ich leider nicht.
(Ich merke gerade es gibt noch viel zu lernen )

Aber vielen Dank für die schnelle Antwort. Ich werde es ausprobieren. 


Ich hoffe mal das man dem hier etwas entnehmen kann.
Mein Aufbau ist ne Katastrophe glaube ich.



Ich habe 3 Klassen die damit etwas zu tun haben.
Connect -> im Package Server
Connection -> im Package Server
Connect -> im Package Client.


Server.Connect:

```
package Server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.*;
import org.lwjgl.util.vector.Vector3f;

public class Connect extends Thread{
private	Server server;
private ServerSocket ss;
public StatusFenster status;
private Player[] players;
	public Connect(Server server, Player[] players){
		status = new StatusFenster();
		this.players = players;
		status.println("Server wurde Gestartet...");
		this.server = server;
		status.setLocation(800, 300);
		try {
			ss = new ServerSocket(9999);
			status.println("Server wartet  auf Verbindungsanfrage...");
		this.start();
		} catch (IOException e) {status.println("ERROR");} catch (Throwable e) {status.println("ERROR");}
		
	}
	public void run(){
		for(int i = 10-1 ; i>=0;i--){
			Socket s;
			try {
				s = ss.accept();
				new Connection(s, players[i],server);
			} catch (IOException e) {}
			
		}

	}
}
```




Server.Connection:

```
package Server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.io.*;
import org.lwjgl.util.vector.Vector3f;

public class Connection extends Thread{
private Socket s1;
private Socket s2;
private StatusFenster status;
private Player p;
private ObjectInputStream in1;
private ObjectOutputStream out1;
private ObjectInputStream in2;
private ObjectOutputStream out2;
private Server server;
private boolean isExceptioned = false;

	public Connection(Socket s1, Player p, Server server) {
		this.p = p;
		this.s1 = s1;
		this.s2 = s2;
		this.server = server;
		this.status = server.connect.status;
		status.print("Ein neuer Client mit der IP " + s1.getInetAddress() + " hat connected!");
		try {
			in1 = new ObjectInputStream(s1.getInputStream());
			out1 = new ObjectOutputStream(s1.getOutputStream());
//			in2 = new ObjectInputStream(s2.getInputStream());
//			out2 = new ObjectOutputStream(s2.getOutputStream());
		} catch (IOException e) {
			status.println("IOException");
		}

		this.start();

	}
	
	public void run(){
		
		while(!this.isExceptioned){
			boolean[] b;
			
			try {		
				b = (boolean[]) in1.readObject();
				float[] f = (float[]) in1.readObject();
				Vector3f rot = new Vector3f(f[0], f[1], f[2]);
				p.keys = b;
				p.rotation = rot;
				float[] pos ={p.position.x,p.position.y,p.position.z};
				List<lib.Picture> pics = server.pics;
				
				out1.writeObject(pos);
				
//				Pictures Senden...
				
				String[] datas = new String[500];
				String[] sources = new String[500];
				boolean[] visible360s = new boolean[500];
				List<Object>  liste = new ArrayList<Object>();
				for(int i = pics.size()-1;i>=0;i--){
						datas[i] = String.valueOf(pics.get(i).p1[0]) +":"+ String.valueOf(pics.get(i).p1[1]) +":"+    String.valueOf(pics.get(i).p1[2])
						+":"+ String.valueOf(pics.get(i).p2[0]) +":"+ String.valueOf(pics.get(i).p2[1]) +":"+ String.valueOf(pics.get(i).p2[2])
						+":"+ String.valueOf(pics.get(i).p3[0]) +":"+ String.valueOf(pics.get(i).p3[1]) +":"+ String.valueOf(pics.get(i).p3[2])
						+":"+ String.valueOf(pics.get(i).p4[0]) +":"+ String.valueOf(pics.get(i).p4[1]) +":"+ String.valueOf(pics.get(i).p4[2]);
						sources[i] = pics.get(i).source;
						visible360s[i] = pics.get(i).visible360;
				}
				liste.add(datas);
				liste.add(sources);
				liste.add(visible360s);
				out1.writeObject(liste); 
				
				


				p.Loop();
			
			} catch (IOException e) {
				status.println("IOException");
				e.printStackTrace();
				this.isExceptioned = true;
			} catch (ClassNotFoundException e) {
				status.println("ClassNotFoundException");
			}
		}
	}
}
```


Client.Connect:

```
package Client;
import ...;

public class Connect {
private Socket socket;
private String host = "79.213.48.111";
private Client client;
private StatusFenster status;
private int port = 9999;
private ObjectInputStream in;
private ObjectOutputStream out;
boolean b1 = true;
boolean b2 = false;
private ClientListener lis;
private float[][] fl;

List<lib.Picture> pics = new ArrayList<lib.Picture>();
float[][] points1 = new float[500][0];
float[][] points2 = new float[500][0];
float[][] points3 = new float[500][0];
float[][] points4 = new float[50][0];
String[] sources = new String[500];
boolean[] visible360s = new boolean[500];

	public Connect(Client client) {
		this.client = client;
		status = new StatusFenster();
		status.println("Client wurde gestartet...");
		
		try {
			socket = new Socket(host, port);
			out = new ObjectOutputStream( socket.getOutputStream());
			in = new ObjectInputStream(socket.getInputStream());
			status.println("...Verbindung zum Server wurde hergestellt.");

			
		
		} catch (UnknownHostException e) {
			status.println("UnknownHostException");
			e.printStackTrace();
			try {Thread.sleep(3000);} catch (InterruptedException e1) {}
			System.exit(0);
		} catch (IOException e) {
			status.println("IOException");e.printStackTrace();
			try {Thread.sleep(3000);} catch (InterruptedException e1) {}
			System.exit(0);
		}

	}
	
	public void Loop(){
		lis = null;
		lis = new ClientListener();
		try {
			Vector3f rot = client.window.rotation;
			float[] f = {rot.x,rot.y,rot.z};
			out.writeObject(lis.areKeyPressed());
			out.writeObject(f);
			float[] pos = (float[])in.readObject();
			client.window.position = new Vector3f(pos[0],pos[1],pos[2]);
			
//			Flächen einlesen...
			

			client.window.pics.clear();
			
			List<Object> liste = (ArrayList)in.readObject();

			for(int i = ((boolean[])liste.get(2)).length-1;i>=0;i--){
				if(((String[])liste.get(0))[i] !=null){
					String s = (((String[])liste.get(0))[i] + "   ");
					String[] s1 = s.split(":");
					float[][] fl =  {{Float.valueOf(s1[0]),Float.valueOf(s1[1]),Float.valueOf(s1[2])},{Float.valueOf(s1[3]),Float.valueOf(s1[4]),Float.valueOf(s1[5])},{Float.valueOf(s1[6]),Float.valueOf(s1[7]),Float.valueOf(s1[8])},{Float.valueOf(s1[9]),Float.valueOf(s1[10]),Float.valueOf(s1[11])}};
					this.fl = fl;
					client.window.pics.add(new Picture(fl[0],fl[1],fl[2],fl[3],((String[])liste.get(1))[i], ((boolean[])liste.get(2))[i] ));
				}				
			}
			
			liste.clear();
			
			
//			...Flächen einlesen
			
			
		} catch (IOException e) {			status.println("IOException");e.printStackTrace();} catch (ClassNotFoundException e) {
			status.println("ClassNotFoundException");
		}
		
	}
}
```


----------



## dayaftereh (11. Dez 2012)

Also TCPNoDelay kann man auf eine [JAPI]Socket[/JAPI] anwenden.

Wo gibst du den die Frequenz für die Updates an? Warum verschickst du nicht deine eigenen Klassen? Dafür ist doch der ObjectOut/In-putStream? Da könntest du dan einmal eine Message für das Updaten der Welt machen , welche vom Server zu den Client 60 mal pro Sekunde geschickt wird und eine Message vom Client zum Server um dem Server mitzuteilen, das der Spieler sich gedreht oder die Geschwindigkeit verändert hat.


----------



## April (11. Dez 2012)

Die Frequenz wird im "window" also im Fenster des Spieles angegeben. Sollte im Idealfall 60/sec sein.
Immer wenn ich meine eigene Klasse verschicken will, gibt es eine "Unserialized irgentwas exception" .
Und der Client schickt die ganze Zeit die Spielerrotation und die tasteneingaben  an den Server, und der Server schickt daraufhin die Spielerposition und die "Sichtbaren Flächen" wieder an den Clienten. Alles was mit den Flächen zu tun hat ist im String. ich würde allerdings lieber ein Float[] senden(Muss ich einfach wieder umprogrammieren).


----------



## dayaftereh (11. Dez 2012)

Also um eine Klasse zu verschicken muss diese serializable sein, um eine Klasse serializable zu machen musst du einfach das Interface [JAPI]Serializable[/JAPI] implementieren, zu dem müssen alle Klassen Attribute auch das interface [JAPI]serializable[/JAPI] implementiert haben. die Primitiven Datentypen von java haben Standard mässig das Interface [JAPI]serializable[/JAPI] implementiert.

Für die Saubere Lösung würde ich meine eigenen Klassen als Message machen und dann einfach mit Hilfe einer Integer zählen, wie viele Message über den Sockel verschickt oder Empfangen werden.


----------



## April (11. Dez 2012)

Also ich habe in Server.Connection und in Client.Connect mal tcpnodelay angegeben. Echt cool, es funktioniert viel besser; kein Laggen mehr. 
Erst wenn ich dann wieder etwa 50 "Sichtbare Flächen" mache, fängt es wieder an. Doch ich glaube das liegt an der Leistung meines Rechners, denn wenn ich dabei noch Schiesse, laggt es noch viel schlimmer.(Warscheinlich wegen der Kollisionserkennung). Ich hoffe das lässt sich etwas Optimieren durch meine eigene Klasse. Das Problem wäre dann ersteinmal gelöst.


Ich habe auch lasse jetzt auch out.flush() nach dem Senden des String durchführen. Spüre aber keine Veränderung.

Nur jetzt kommt noch das 2. Problem. Ich kann den Klienten leider nur über "localhost" mit dem Server verbinden.
Wenn ich meine IPv4 adresse eingebe, bekomme ich "NoRoutetoHost". Ich habe auch eine Verbindung zum Internet.
Woher kommt das? Hat das etwas mit der Firewall zu tun? oder brauche ich viell. eine andere Adresse als IPv4?


PS:
Ich habe einen alten Mac(PowerBook G4) mit Mac os X 10.4.11(Tiger)


----------



## dayaftereh (11. Dez 2012)

Hey, schau dir mal die Klasse [JAPI]InetAddress[/JAPI] an, da gibt es eine Methode getByName(String). Das dir eine [JAPI]InetAddress[/JAPI] Objekt zurück gibt, versuche das mal an deinen [JAPI]Socket[/JAPI] weiterzugeben.


----------



## tröööt (11. Dez 2012)

also wenns 60updates/1sec sein soll würde ich hier definitiv mit UDP arbeiten ... TCP ist dafür einfach zu langsam ... und serialisierung erst recht (@dayaftereh de fugg kommst du hier überhaupt darauf ?) ... das erzeugt dafür viel zu viel overhead ...

ich bin jetzt bloß mal grob übers thema gegangen ... aber deine exception könnte daher kommen das du versuchst deine public-ip anzugeben ... aber in deinem router kein port-forwarding aktiviert hast ... dann bleibt der call nämlich am NAT hängen ... (gibt noch n paar mehr fehlerquellen in die richtung) ...


----------



## April (11. Dez 2012)

Also ich hab's geschafft.
Ich habe die Richtige IP rausbekommen durch [c]socket.getInetAdress().getLocalHost[/c]
Diese unterscheidet sich allerdings Gänzlich von der IPv4 adresse. Naja. Nur leider weis ich nicht was das alles zu bedeuten hat 


Trotzdem vielen, vielen Dank


----------



## tröööt (11. Dez 2012)

grundsätzlich gibt es genau drei mögliche adressen die einen interessieren könnten ...

1) MAC - adresse ... auch "hardware"-adresse genannt ... wird auf sehr tiefen schichten verwendet um das gerät selbst auf der physischen ebene anzusprechen
2) IPv4 - adresse ... standardmäßig ip-adresse der version 4 ... die gebräuchlichste adresse von allen und im format XXX.XXX.XXX.XXX aufgebaut ...
3) IPv6 - adresse ... neue, längere, eindeutige ip-adresse der version 6 .. hierbei wird jedem NIC in abhängigkeit der MAC eine IPv6 zugewiesen ... damit soll die adress-knappheit von IPv4 gelöst werden ...
eine IPv6 wird in HEX angegeben und hat das format hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh
"leere blöcke", also blöcke die nur aus "0000" bestehen können gänzlich entfallen ... so wird z.b. aus FE07:0000:0000:0000:0000:ABCD:EF1234 schlicht FE07::ABCD:EF1234


du dürftest vermutlich eine IPv4 haben ...
und da gibt es in der regel auch 3 verschiedene

1) localhost : 127.0.0.1
2) local : 192.168.0.0/16 , 10.0.0.0/8 und andere nicht-routbare adress-räume
3) public-WAN : die ip die man sieht wenn man z.b. auf wieistmeineip.de geht ...

um mit dir selbst zu kommunizieren verwendest du normalerweise localhost ... innerhalb des LAN deine local-ip (meist beginnend mit 192.168.xxx.xxx) ... und vom internet aus deine public-WAN ...
damit letzteres allerdings funktioniert musst du in deinem NAT den entsprechenden port an die richtig ip weiterleiten ... wie das geht verrät google


----------



## Bernd Hohmann (11. Dez 2012)

April hat gesagt.:


> Also ich habe in Server.Connection und in Client.Connect mal tcpnodelay angegeben. Echt cool, es funktioniert viel besser; kein Laggen mehr. [...]Ich habe auch lasse jetzt auch out.flush() nach dem Senden des String durchführen. Spüre aber keine Veränderung.



Weil es durch die Hintertür doppelt gemoppelt ist.

Ein TCP-Paket ist normalerweise 1500 bytes gross und hat eine Nutzlast in der Kante von 1450 bytes. Wenn weniger als die 1450 bytes in das Paket gesteckt werden, wartet der Nagle-Algorithmus nochmal kurz ob da nicht doch noch Daten kommen. Der Schalter "TCPNoDelay=yes" verhindert dieses Warten und schickt die Pakete sofort auf die Reise.

Das Gleiche macht letztendlich ein .flush().

Du kannst also auch mehr als 15bytes auf die Reise schicken (also bis ca. 1450bytes) - es wird dadurch nicht langsamer.

Für Spiele ist TCP/IP nicht so die erste Wahl: zwar stellt das Protokoll sicher, dass die Daten in der gesendeten Reihenfolge und natürlich auch alle Daten ankommen, diesen Komfort erkauft man sich aber unter Umständen mit derben Verzögerungen. Wenn Du TCP einigermassen im Griff hast, schau mal nach UDP.



April hat gesagt.:


> Nur jetzt kommt noch das 2. Problem. Ich kann den Klienten leider nur über "localhost" mit dem Server verbinden. Wenn ich meine IPv4 adresse eingebe, bekomme ich "NoRoutetoHost". Ich habe auch eine Verbindung zum Internet. Woher kommt das? Hat das etwas mit der Firewall zu tun? oder brauche ich viell. eine andere Adresse als IPv4?



Was ist "Deine" IPv4 Adresse? Das was der Router oder "http://www.wieistmeineip.de/" als Deine Internetadresse ausspuckt?

Bernd


----------



## April (12. Dez 2012)

die gleiche finde ich auch bei "www.wieistmeineip.de"


----------



## Bernd Hohmann (12. Dez 2012)

Dann lies Dich mal hier ein: https://de.wikipedia.org/wiki/Portweiterleitung

Bernd


----------



## trääät (13. Dez 2012)

@TO
wie ich bereits sagte : wenn du pakete an deine public-WAN adressierst werden diese vom NAT im router abgefangen ...
also musst du dem NAT sagen das er einen bestimmten port eines bestimmten protkolls an ein bestimmtes ziel weiterleitet ... ansonsten wird hinterm NAT nichts ankommen ...


----------

