Telematik 1 - Labor SS2002

LB 6 TCP-Server am Beispiel von Chat

Server-Sockets

Um serverseitig Socket-Verbindung zu verwalten steht in Java die Klasse java.net.ServerSocket zur Verfügung. Diese Klasse verwaltet die Zuteilung von serverseitigen Ports auf Client-Anfragen.

Ein ServerSocket wird mit dem Port angelegt, auf den sich Clients verbinden können um eine Kommunikation mit dem Server aufzubauen. Das nachfolgende Beispiel erstellt einen ServerSocket, welcher Verbindungen auf Port 4444 akzeptiert:

try { 
    serverSocket = new ServerSocket(4444);
} catch (IOException e) { 
    System.out.println("Could not listen on port: 4444");
}

Um nun einen Kommunikationskanal zum Client aufbauen zu können stellt die Klasse ServerSocket die Methode Socket accept() zur Verfügung. Diese Methode liefert ein Socket-Objekt zurück auf welches ein InputStream bzw. OutputStream geöffnet werden kann (siehe auch LB5). Über diese Streams kann die Kommunuikation mit dem Client erfolgen:

Socket clientSocket = null;
try { 
    clientSocket = serverSocket.accept();
    InputStream in = clientSocket.getInputStream();
    OutputStream out = clientSocket.getOutputStream();
    // process data coming from client socket
} catch (IOException e) {
    System.out.println("Accept failed: 4444");
} 
Diese Implementierung eines Server-Sockets erlaubt jedoch nur die sequentielle Verarbeitung von Client-Anfragen. Mehrere gleichzeitige Anfragen können mit dieser Implementierung nicht verarbeitet werden, dazu sind Threads notwendig.

Threads

Threads erlauben die gleichzeitige Ausführung von mehreren Programmabläufen in Java. Die Verwaltung der Threads erfolgt dabei vom Betriebssystem selbst, d.h. die Zuteilung von Prozessorzeit auf einen Thread wird vom Betriebssystem bestimmt. Threads werden in Java über die Klasse java.lang.Thread gekapselt.

Die Logik eines Threads wird in der Methode void run() gekapselt. Diese Funktionalität wird auch vom Interface java.lang.Runnable bereitgestellt. Dieses Interface ermöglicht es, Ablauflogik in Klassen zu definieren welche nicht von java.lang.Thread erben. Um ein Runnable Objekt nun in einem Thread ablaufen zu lassen stellt die Klasse java.lang.Thread einen Konstrukor mit einem Runnable Parameter bereit. Ein Thread wird über die Methode void start() gestartet.

public class ThreadTest {
    // inner class for printing test within a thread

    class ThreadPrinter implements Runnable {

        String name = "ThreadPrinter";

        public ThreadPrinter(String name) {
            this.name = name;
        }

        // Method from interface Runnable
        // The thread this runnable is in will
        // terminate as soon as this method ends.
        public void run() {
            for (int i = 0; i < 10000; i++) {
                System.out.println("Thread " + name + " message no. " + i);
            }
        }
    }

    public static void main(String[] args) {
        Runnable printer1 = new ThreadPrinter("Printer 1");
        Runnable printer2 = new ThreadPrinter("Printer 2");
        Runnable printer3 = new ThreadPrinter("Printer 3");
 
        Thread thread1 = new Thread(printer1);
        Thread thread2 = new Thread(printer2);
        Thread thread3 = new Thread(printer3);
 
        // start all threads - they will terminate as soon as
        // the runnables quit executing their run method...
        thread1.start();
        thread2.start();
        thread3.start();
    }
} 

Je nach Implementierung des Scheduling-Algorithmus des Betriebssystems werden die Ausgaben der drei Threads in einer Sequenz abgearbeitet oder durch time-slicing in unterschiedlicher Reihenfolge aufgerufen.

Bei der Implementierung von Server-Sockets können Threads vor allem dazu verwendet werden, mehrere Client-Anfragen gleichzeitig zu bearbeiten und somit die Rechenleistung des Server-Systems auf mehrere Clients aufzuteilen. Die Verwendung einers ServerSockets könnte daher wie folgt aussehen:

// this functionality might itself be implemented in a thread
try { 
    Socket clientSocket = null;
    ServerSocket serverSocket = new ServerSocket(4444);
    Thread thread = null;
    // instead of "true" there should be some abort condition set
    while (true) {
        clientSocket = serverSocket.accept();
        // spin off a separate Thread - implementation 
        // is not shown here
        thread = new CustomSocketThread(clientSocket);
        thread.start();
    }
} catch (IOException e) { 
    System.out.println(e.printStackTrace());
}

Aufgabenstellung

Implementieren Sie zu dem Chat-Client aus Übung 5 ein entsprechendes Server-Pendant.