Server mit ThreadPool beenden

tdc

Bekanntes Mitglied
Hi,
ich versuche grad ein kleines Server-Client-Programm zu schreiben. Hierfür verwende ich beim Server einen ThreadPool, damit evtl. mehrere Clients gleichzeitig mit dem Server interagieren können. Ist das okay, oder sollte man inzwischen lieber NIO verwenden?

Mit dem ThreadPool habe ich jedenfalls das Problem, dass irgendwas scheinbar nicht richtig beendet wird, bzw. im Hintergrund irgendein Thread weiterläuft. Wenn ich nur den Server starte und wieder beende ist alles okay. Wenn ich nur den Client starte bekomme ich "java.net.ConnectException: Connection refused", das ist aber auch richtig so und wenn ich ihn wieder beende scheint auch alles zu funktionieren. Wenn ich alledings erst den Server starte, dann den Client starte und wieder beende und dann auch den Server stoppe, wird in Eclipse das "Terminate"-Symbol rot angezeigt, was ja eigentlich nur der Fall ist, wenn das Programm noch läuft, es scheint also noch irgendwas im Hintergrund zu laufen - oder irre ich mich da?

Mein Code besteht aus 3 Klassen, dem Client, dem Server und dem Handler. Client und Server sollten klar sein, der Handler wird vom Server aufgerufen, sobald ein Client connected und übernimmt in einem seperaten Thread die Kommunikation mit dem Client.
Da das Programm scheinbar nur nicht richtig beendet wird, wenn Server und Client gestartet werden vermute ich, dass das Problem beim Handler liegt. Laut der Ausgabe wird allerdings die while-Schleife beendet. Wo liegt also das Problem?

Server:
Java:
package net;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public abstract class Server implements Runnable {
	protected int serverPort;
	protected ServerSocket serverSocket;
	protected boolean isStopped = false;
	protected Thread runningThread;
	protected ExecutorService threadPool = Executors.newCachedThreadPool();

	public Server(int port) {
		serverPort = port;
		new Thread(this).start();
	}

	public void run() {
		synchronized (this) {
			runningThread = Thread.currentThread();
		}
		openServerSocket();
		while (!isStopped()) {
			Socket clientSocket = null;
			try {
				clientSocket = serverSocket.accept();
			} catch (IOException e) {
				if (isStopped()) {
					System.out.println("Server Stopped.");
					return;
				}
				throw new RuntimeException("Error accepting client connection",
						e);
			}
			onClientConnect(clientSocket);
		}
		threadPool.shutdown();
		System.out.println("Server Stopped.");
	}

	protected abstract void onClientConnect(Socket clientSocket);

	private synchronized boolean isStopped() {
		return isStopped;
	}

	public synchronized void stop() {
		isStopped = true;
		try {
			serverSocket.close();
		} catch (IOException e) {
			throw new RuntimeException("Error closing server", e);
		}
		System.out.println("Socket closed.");
	}

	private void openServerSocket() {
		try {
			serverSocket = new ServerSocket(serverPort);
		} catch (IOException e) {
			throw new RuntimeException("Cannot open port " + serverPort, e);
		}
	}
}

Client:
Java:
package net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

public abstract class Client implements Runnable {
	Socket server;
	boolean connected = false;
	protected BufferedReader in;
	protected PrintWriter out;

	public void connect(String host, int port) {
		server = null;
		try {
			if (connected) {
				disconnect();
			}
			server = new Socket(host, port);
			in = new BufferedReader(new InputStreamReader(
					server.getInputStream()));
			out = new PrintWriter(new OutputStreamWriter(
					server.getOutputStream()));
			connected = true;
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Connected.");
		new Thread(this).start();
	}

	public void run() {
		System.out.println("Connection created.");
		while (connected) {
			try {
				reciveMessage(in.readLine());
			}
			catch (SocketException e) {
				System.out.println("Connection interrupted");
				disconnect();
			}
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("Connection closed.");
	}

	protected abstract void reciveMessage(String message);

	public void disconnect() {
		try {
			if(connected) {
				out.close();
				in.close();
				server.close();
			}
			connected = false;
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Disconnected.");
	}

	public boolean isConnected() {
		return connected;
	}

	public void sendMessage(String message) {
		if (connected) {
			out.print(message);
		} else {
			System.err.println("Client is not connected to a Server!");
		}
	}
}

Handler:
Java:
package net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

public abstract class Handler implements Runnable {
	protected Socket clientSocket = null;

	public Handler(Socket clientSocket) {
		this.clientSocket = clientSocket;
	}

	public void run() {
		try {
			BufferedReader in = new BufferedReader(new InputStreamReader(
					clientSocket.getInputStream()));
			PrintWriter out = new PrintWriter(new OutputStreamWriter(
					clientSocket.getOutputStream()));

			boolean connected = true;
			while (connected) {
				String message = in.readLine();
				System.out.println("t: " + message);
				if(message != null) {
					reciveMessage(message);
				}
				else {
					connected = false;
				}
			}

			out.close();
			in.close();
			clientSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Handler stopped.");
	}

	protected abstract void reciveMessage(String message);
}


Ausgabe:
Server:
Code:
t: clientmessage
t: null
Handler stopped.
Socket closed.
Server Stopped.
Client:
Code:
Connected.
Connection created.
Connection interrupted
Disconnected.
Disconnected.
Connection closed.
 
Das shutdown auf den Threadpool bewirkt nicht, dass aktive Tasks abgebrochen werden. Es werden nur keine neuen mehr akzeptiert. Du musst selbstständig alle Verbindungen closen, sodass du aus der in.readLine() rausfliegst und die Threads sich beenden.
 
Ja, das habe ich mir schon gedacht, aber wenn ich clientseitig die Verbindung close, dann kommt beim Server scheinbar dauernd "null" an, weshalb ich den boolean "connected" dann auf false setze. Die Testausgabe "Handler stopped." zeigt, dass das wohl auch funktioniert. Läuft da überhaupt noch was? Scheinbar wird ja alles beendet...
 
Wenn du ThreadPoolExecutor.html#shutdownNow() verwendest wird versucht alle Threads aus dem Pool via Thread#interrupt() zu beenden. Das kannst du dann via Thread.interrupted() in deine Schleifen als Abbruchbedingung abfragen.
 
Auch wenn ich .shutdownNow() statt .shutdown() (oder beides) verwende scheint immer noch etwas im Hintergrund zu laufen. In der Dokumentation steht auch zu shutdownNow():
There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.
Mache ich vielleicht einfach bei den Threads etwas falsch? (so oft habe ich noch nicht mit Threads programmiert....)
 

Zurück
Oben