Chat-Programm (Client-Server)
Das hier vorgestellte „Chat-Programm“ basiert in seinem Quellcode auf dem Echo-Programm des vorigen Abschnitts. Es handelt sich um eine verteilte Anwendung nach dem Client-Server-Modell, das den Austausch textbasierter Nachrichten (Messages) zwischen angeschlossenen Clients ermöglicht. Hier ist es Aufgabe des Servers, die von einem Client gesandteten Nachrichten über das Netzwerk an alle angeschlossenen Clients weiterzuleiten (engl. Broadcasting = Weiter-Verteilung, Rundfunk, wörtlich „Breitstreuung“).
Dieses Chat-Programm soll keine Implementierung des ausgefeilten Chat-Protokolls (IRC) sein, wie es zum Beispiel von ICQ oder anderen Instant Messengers verwendet wird (RFC 1459). Anstelle des recht komplizierten IRC-Protokolls wird hier eine simple textbasierte Nachrichtenübertragung verwendet. Insgesamt handelt es sich um eine einfache und kompakte Client-Server-Anwendung, die aber entwicklungsfähig ist und daher für Unterrichtszwecke gut geeignet ist. Die grundlegende Struktur einer verteilten Client-Server-Anwendung wird hier trotz (oder gerade wegen) der Einfachheit des Java-Codes recht deutlich und veranschaulicht die wichtigsten Problemkreise, mit denen ein Programmentwickler bei verteilten Anwendungen meistens zu tun haben wird.
Bei der ersten Version des Chat-Programm handelt es sich um ist eine konsolenbasierte Java-Anwendung, die Nachrichten an alle Clients weiterleitet, aber noch nicht in der Lage ist, Nachrichten an einen bestimmten Client zu schicken. Im nächsten Kapitel wird der Chat-Client in Form einer SWING-Anwendung weiterentwickelt. Eine weitere Entwicklung ist ein Chat-Programm, das es ermöglicht, Nachrichten an bestimmte Clients zu übertragen, wie es bei professionellen Chat-Programmen üblich ist.
Beschreibung des Servers
Der Server besteht aus zwei Java-Klassen: 1.) dem eigentlichen Serverprogramm mit main( ) und 2.) einer Threadklasse mit dem Arbeitssocket zur Kommunikation mit dem Client. In der main-Methode der Serverklasse wird das Server-Socket erzeugt und an einen Port gebunden. In einer Endlosschleife wartet der Programmablauf auf eintreffende Client-Verbindungen. Sobald ein Client anfragt, liefert die Accept-Methode ein Arbeitssocket-Objekt zurück. Wie im vorigen Beispiel erläutert, wird dieses Socket-Objekt an einen Thread übergeben, der die weitere Kommunikation mit dem Client übernimmt. Der Ablauf kehrt dann zur Accept-Methode zurück und wartet auf den nächsten Client. Insoweit unterscheidet sich der Chat-Server nur unwesentlich vom Echo-Server des vorigen Kapitels.
Die wesentliche Weiterentwicklung besteht darin, dass der Chat-Server alle Clients „kennen“ muss, die momentan verbunden sind, damit eine eintreffende Nachricht korrekt an alle anderen Clients weitergegeben werden kann (Broadcast). Dies geschieht mittels eines im Server-Programm zentral verwalteten Array, das die Referenzen aller erzeugten Thread-Objekte und damit auch aller verbundenen Client-Sockets enthält. Weiterhin enthält die Server-Klasse eine Methode sendToAll( String msg ), deren Aufgabe es ist, die Nachricht, die vom Client (genauer vom Socket-Thread) übergeben wird, an alle anderen Clients weiterzugeben. Dies geschieht in einer Schleife, die das Array durchläuft und dabei die Nachricht an alle Clients sendet.
Der Socket-Thread besitzt die für die Kommunikation mit dem Client erforderlichen Datenströme (Input- und Output-Stream) und in seiner run-Methode wird die eingehende Nachricht an die erwähnte sendToAll-Methode übergeben.
/////////////////////////////////
// ChatServerThread2.java
// Multi-Clientfähiger Chatserver
// Arbeitssocket in Thread verpackt
// NEUE VERSION: 2005
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.ServerSocket;
public class ChatServerThread2 {
static final int MAX=100;
static int i=0, aktAnzahl=0;
static SocketThread [] ca = new SocketThread [MAX];
////////////////////////////////////////////////////////////
public static void main (String args[]) {
System.out.println ("Multi-Client Chat-Server gestartet ---- ");
try {
ServerSocket ss = new ServerSocket ( 9999 );
while(i < MAX) {
Socket s = ss.accept( );
ca[i] = new SocketThread( s ); // SocketThread in Array legen
ca[i].start();
aktAnzahl=++i;
}
} catch(Exception e) { System.out.println(e); }
}
//////////////////////////////////////////////
// Broadcast-Methode durchläuft Array
public static void sendToAll( String msg ) {
for (int j=0; j < aktAnzahl; j++) {
ChatServerThread2.ca[j].outp.println ( msg );
ChatServerThread2.ca[j].outp.flush();
}
}
}
/////////////////////////////////////////////////////////////
// SocketThread
class SocketThread extends Thread {
Socket s;
BufferedReader inp;
PrintWriter outp;
///////////////////////////////////////////////
// Konstruktor
SocketThread ( Socket t ) {
System.out.println("\nNeuer SocketThread erzeugt: " +this);
s = t;
try {
inp = new BufferedReader (
new InputStreamReader (
s.getInputStream()));
outp = new PrintWriter ( s.getOutputStream() );
} catch (Exception e) {}
}
////////////////////////////////////////////////
public void run() {
String msg;
try {
System.out.println ("Client-IP: " + s.getInetAddress() +
"\nClient-Port: " + s.getPort() +
"\nLocal-IP: " + s.getLocalAddress()+
"\nLocalPort: " + s.getLocalPort());
do {
msg = "Client "+s.getPort()+" ==> " + inp.readLine();
System.out.println (msg);
ChatServerThread2.sendToAll( msg ); // Broadcast-Methode aufrufen
} while (!(msg.equals("stop")));
s.close();
} catch(Exception e) {}
}
}
Beschreibung Chat-Client
///////////////////////////////////////////////////////////
// ChatClientThread1.java
// ChatClient mit Thread, der auf eingehende
// asynchrone Nachrichten lauscht
// Neue Version: 2005
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class ChatClientThread1 {
public static void main (String args[]) {
try {
Socket server = new Socket ( "localhost", 9999 );
System.out.println("Client gestartet..." +
"\nChat-Server kontaktiert: " + server.getInetAddress()+
"\nClient Port: " + server.getLocalPort());
PrintWriter outp = new PrintWriter ( server.getOutputStream() );
BufferedReader inp = new BufferedReader (
new InputStreamReader ( server.getInputStream()));
ClientHorcher ch = new ClientHorcher( inp ); // Horcher-Thread starten
ch.start();
String msg1=null;
String msg2=null;
System.out.println("Geben Sie eine Nachricht ein, stop beendet > ");
do {
msg1 = In.readLine();
outp.println ( msg1 );
outp.flush();
} while(!(msg1.equals("stop")));
} catch (Exception e) {
System.out.println ( "Keine Verbindung...\nFehler: " + e );
}
In.stop();
}
}
/////////////////////////////////////////////////////////////
class ClientHorcher extends Thread {
BufferedReader inp;
ClientHorcher( BufferedReader temp) {
inp = temp;
}
///////////////////////////////////////////
public void run() {
String msg;
try {
while(true) {
msg = inp.readLine();
System.out.println ( msg);
}
} catch(Exception e) {
System.out.println("Thread Exception: "+e);
}
}
}
Seite
|