# Client-Server-Kommunikation



## HerrJemineh (2. Apr 2014)

Hallo liebes Java-Forum!

Da wir im nächsten Semester ein Client-Server basiertes Spiel programmieren müssen, wollte ich zum Einstieg versuchen ein Programm zu schreiben, sodass sich bis zu 5 Clients am Server verbinden und ein Viereck in einem Feld bewegen können. (Ohne Kollision oder andere Features)
Da ich in diesem Gebiet ein totaler Anfänger bin, habe ich mich an einem Tutorial orientiert, dies jedoch nicht 1:1 kopiert. Mein erstes Problem ist folgendes: Ich starte den Server (mit Port 8888) und verbinde dann einen Client mit gleichem Port, bekomme jedoch die Exception:"Address already in use: JVM_Bind"
Wenn ich unter der Eingabeaufforderung von Windows netstat -a eingebe, sehe ich jedoch dass dieser Port vorher nicht benutzt wurde, also muss der Fehler im Programm liegen (denke ich).
Es wäre sehr nett, wenn mir jemand meinen Fehler erklären würde, hier sind die Klassen:


```
import java.net.*;
import java.io.*;

public class Server {
	
	static DataInputStream in;
	static DataOutputStream out;
	static ServerSocket serverSocket;
	static Socket socket;
	static Player[] players;
	
	public static void main(String[] args) throws Exception {
		// starte den server
		System.out.println("Starte Server...");
		serverSocket = new ServerSocket(8888);
		System.out.println("Server gestartet.");
		// nehme solange neue clients an, bis 5 verbunden sind
		while(true){
			Socket socket = serverSocket.accept();
			for(int i = 0; i < 5; i++){
				if(players[i] == null){
					System.out.println("Verbinde mit: " + socket.getInetAddress());
					out = new DataOutputStream(socket.getOutputStream());
					in = new DataInputStream(socket.getInputStream());
					players[i] = new Player(out, in, players, i);
					Thread th = new Thread(players[i]);
					th.start();
					break;
				}
			}
		}
	}


}

class Player implements Runnable{
	
	DataInputStream in;
	DataOutputStream out;
	Player[] players = new Player[5];
	int playerid;
	int playeridin;
	int xin;
	int yin;
	
	public Player(DataOutputStream out, DataInputStream in, Player[] players, int pid){
		this.out = out;
		this.in = in;
		this.players = players;
		this.playerid = pid;
	}

	public void run() {
		// gebe zuerst immer die playerID aus
		try{
			out.writeInt(playerid);
		} catch (IOException e1){
			System.out.println("Failed to send PlayerID");
		}
		// lese koordinaten des jeweiligen players ein und sende sie an alle anderen clients
		// damit diese sie zeichnen können
		while(true){
			try{
				playeridin = in.readInt();
				xin = in.readInt();
				yin = in.readInt();
				for(int i = 0; i < 5; i++){
					if(players[i] != null){
						players[i].out.writeInt(playeridin);
						players[i].out.writeInt(xin);
						players[i].out.writeInt(yin);
					}
				}
			} catch (IOException e){
				players[playerid] = null;
			}
		}

	}
	
	
}
```

und hier die Client-Klasse:


```
import java.net.*;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.*;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Client extends MyFrame implements Runnable, KeyListener{
	
	static Socket socket;
	static DataInputStream in;
	static DataOutputStream out;
	int playerid;
	int x[] = new int[5];
	int y[] = new int[5];
	boolean up,down,right,left;
	int playerx, playery;
	MyFrame f;
	
	public void init(){
		// erstelle frame
		f = new MyFrame();
		f.setSize(600, 300);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setResizable(false);
		f.setVisible(true);
		// füge keylistener hinzu, um tastatureingaben zu erkennen
		addKeyListener(this);
		// initialisierung des clients
		try{
		System.out.println("Verbinde...");
		socket = new Socket("localhost",8888);
		System.out.println("Verbunden.");
		in = new DataInputStream(socket.getInputStream());
		playerid = in.readInt();
		out = new DataOutputStream(socket.getOutputStream());
		Input input = new Input(in, this);
		Thread thread = new Thread(input);
		thread.start();
		Thread thread2 = new Thread(this);
		thread2.start();
		} catch(Exception e){
			System.out.println("Unable to start Client.");
		}
	}
	
	public void updateCoordinates(int pid, int x2, int y2){
		this.x[pid] = x2;
		this.y[pid] = y2;
	}

	public void run() {
		while(true){
			if(right) playerx += 10;
			if(up) playery -= 10;
			if(down) playery += 10;
			if(left) playerx -= 10;
			if(up||down||left||right){
				try {
					out.writeInt(playerid);
					out.writeInt(playerx);
					out.writeInt(playery);
				} catch (IOException e) {
					System.out.println("Error sending Coordinates.");
				}
			}
			f.repaintScreen();
			try {
				Thread.sleep(400);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public void keyPressed(KeyEvent e) {
		if(e.getKeyCode() == KeyEvent.VK_UP) up = true;
		if(e.getKeyCode() == KeyEvent.VK_RIGHT) right = true;
		if(e.getKeyCode() == KeyEvent.VK_DOWN) down = true;
		if(e.getKeyCode() == KeyEvent.VK_LEFT) left = true;
	}

	public void keyReleased(KeyEvent e) {
		if(e.getKeyCode() == KeyEvent.VK_UP) up = false;
		if(e.getKeyCode() == KeyEvent.VK_RIGHT) right = false;
		if(e.getKeyCode() == KeyEvent.VK_DOWN) down = false;
		if(e.getKeyCode() == KeyEvent.VK_LEFT) left = false;
	}

	@Override
	public void keyTyped(KeyEvent arg0) {}
}

class Input implements Runnable{
	
	DataInputStream in;
	Client client;
	
	public Input(DataInputStream in, Client c){
		this.in = in;
		this.client = c;
	}

	public void run() {
		while(true){
			try {
				int playerid = in.readInt();
				int x = in.readInt();
				int y = in.readInt();
				client.updateCoordinates(playerid, x, y);
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}
	
}
```

Für eure Hilfe wäre ich sehr dankbar 

lieben Gruß!


----------



## Highchiller (2. Apr 2014)

Kannst du mal den Stacktrace posten?

Und du sagst den Server kannst du normal starten aber sobald du den 1.(!!!) Client startest fliegt die Meldung?


----------



## HerrJemineh (3. Apr 2014)

Starte Server...
Exception in thread "main" java.net.BindException: Address already in use: JVM_Bind
	at java.net.DualStackPlainSocketImpl.bind0(Native Method)
	at java.net.DualStackPlainSocketImpl.socketBind(Unknown Source)
	at java.net.AbstractPlainSocketImpl.bind(Unknown Source)
	at java.net.PlainSocketImpl.bind(Unknown Source)
	at java.net.ServerSocket.bind(Unknown Source)
	at java.net.ServerSocket.<init>(Unknown Source)
	at java.net.ServerSocket.<init>(Unknown Source)
	at Server.main(Server.java:15)

Das ist die Ausgabe, die ich nach dem Starten des 1. Clients bekomme.
Mir ist gerade aufgefallen, dass wenn ich den Server starte, das "rote Stop-Kästchen" auch in der Client-Klasse aufleuchtet. Das darf doch eigentlich nicht sein, oder?

EDIT: "Starte Server..." sollte ja auch gar nicht ausgegeben werden. Vielleicht führe ich den Client gar nicht korrekt aus, aber wenn ich erst den Server mit Run as starte und dann bei Client auf Run as klicke, kommt nur Run Configurations


----------



## Tobse (3. Apr 2014)

Port 8888 ist ein Standard-Port. Und wie die Fehlermeldung (selbstredend...) sagt, ist der Port schon in benutzung: 
	
	
	
	





```
"Already in use"
```

Sprich: Benutze einen anderen Port. Hier findest du eine Liste aller standardisierten Ports, die du besser nicht verwenden solltest weil dir sonst der gleiche Fehler wieder und wieder passiert.


----------



## HerrJemineh (3. Apr 2014)

Ich habe es nun mal mit dem Port 8886 versucht, welcher auf der Liste nicht auftaucht, und habe trotzdem das gleiche Problem.
Den Server zu starten stellt ja auch kein Problem dar, d.h. der Port muss ja frei sein.
Woran kann es denn liegen, dass ich einfach den Client nicht starten kann?
Wie gesagt, wenn ich in der ClientKlasse den Cursor auf das grüne PlayZeichen bewege, kommt die Meldung "Run Server (already running)". Braucht der Client eine main, damit ich ihn ausführen kann?

EDIT: deswegen wahrscheinlich auch "already in use" weil ich den server 2 mal hintereinander starte


----------



## VfL_Freak (3. Apr 2014)

Moin,


HerrJemineh hat gesagt.:


> EDIT: deswegen wahrscheinlich auch "already in use" weil ich den server 2 mal hintereinander starte


ist zwar schon lange her, aber ich erinnere mich dunkel dass Du eine ServerSocket zu EINEM Port auf einem Rechner auch nur EINMAL öffnen kannst!

Gruß
Klaus


----------



## HerrJemineh (3. Apr 2014)

Ja, also ich bin mir mittlerweile auch sicher, dass der Fehler in der fehlenden main-Methode liegt. Wegen dieser habe ich fälschlicher Weise immer gedacht, ich würde durch "Play" drücken den Client starten, habe aber den Server ein zweites mal versucht zu starten und dann kam die Exception. Ich bräuchte dennoch eure Hilfe:

Wenn ich jetzt versuche eine main einzubauen, bekomme ich Probleme in Zeile 38 und 41, da ich "this" in einer statischen Methode nicht verwenden kann.

In dem Tutorial hat Client von Applet geerbt und man konnte Client auch ohne main runnen. Ich wollte es jedoch mit JFrames (MyFrame) implementieren und nicht mit Applet.

Wie gehe ich am besten vor?


----------



## Highchiller (3. Apr 2014)

Klassischer Fehler  Eclipse halt, nur weil mans anwählt heißt das nicht das ers auch startet. Naja gut das wir das klären konnten.

Was das andere Problem angeht bin ich mir nicht sicher ob ich dich verstehe. Mach doch einfach:

```
public static void main(String[] args){
    Client client = new Client();
    client.init();
}
```

So startet man eigentlich immer Programme wenn man eine Klasse startet die eigene Main-Methoden besitzen. Außer man zielt darauf ab sowieso alles statisch zumachen was wohl nur zu Testzwecken Sinn macht 

Aber irgendwie hab ich den verdacht darum gehts dir irgendwie nicht. Deine Zeilennummern sind übrigens anscheinend nicht mehr konsistent. Welche klasse willst du mit einer Main ausstatten?


----------



## HerrJemineh (3. Apr 2014)

Doch, genau das meinte ich. Peinlich, dass ich darauf nicht gekommen bin :bloed:
Naja, funktionieren tut das ganze zwar noch nicht, aber ich werde noch was basteln und mich morgen nochmal melden, wenn es nicht klappt.

VIELEN DANK FÜR DIE HILFE BIS HIER HIN!!!


----------



## HerrJemineh (6. Apr 2014)

Hallo nochmal! Also es funktioniert mittlerweile so wie ich es wollte. Ich habe nur eine kurze Frage:
Wenn ich jetzt möchte, dass ein Freund von mir sich mit meinem Server verbindet muss er ja die Klasse Client haben und anstatt localhost meine IP-Adresse eingeben, oder muss man noch mehr ändern?
Es klappt nämlich irgendwie nicht. Kann das auch an meiner Firewall liegen oder ähnlichem?

Vielen Dank!


----------

