Chat-Programm

Nebenbei: Du kannst die Liste als

    protected LinkedList<ClientThread> threadList = new LinkedList<ClientThread>();

deklarieren. Dann braucht man nicht zu casten:

//ClientThread clientThread = (ClientThread)threadList.get(0); // Vorher
ClientThread clientThread = threadList.get(0); // Nachher

Ansonsten kannst du einen String erstellen, aus den Namen von allen, die sich bisher angemeldet haben

    private String createClientNamesString()
    {
        if (threadList.size() == 0)
        {
            return "Keine Verbindungen";
        }
        String result = "An Bord: ";
        for (int i=0; i<threadList.size(); i++)
        {
            ClientThread clientThread = threadList.get(i);
            String name = clientThread.getThreadName();
            result += name;
            if (i < threadList.size()-1)
            {
                result += ", ";
            }
        }
        return result;
    }

Und diesen dann (jedes mal, wenn sich ein neuer Client verbunden hat) mit generateOutput rausschicken.

Guten Morgen!

Mit dieser Code komme ich nicht durch, beschäftige mich damit aber
weiter, solange bis ich das hinkriege. Falls es klappt, melde ich mich dann.

Letzte oben genannte Code habe ich in der ChatServer eingegeben.
Und mit parent.createClientNamesString()
habe ich in der ClientThread eingefügt. Im Fenster (ChatClientFrame)
ist es nicht davon gekommen. In der Parameter muß irgendwas eingegeben
werden. Oder von output soll es angehangen werden, ne? Oder habe
ich da was falsch gemacht? :o

Und diesen dann (jedes mal, wenn sich ein neuer Client verbunden hat) mit generateOutput rausschicken.

Das sollte bedeuten, dass man, wenn die Methode im ChatServer steht, in


            while (true) **// <---------------------------- Diese Schleife**
            {
                clientSocket = serverSocket.accept();
                System.out.println("Verbindung zu Socket " +clientSocket.getRemoteSocketAddress()
                        +" aufgenommen");
                ClientThread clientThread = new ClientThread(clientSocket, this);
                Thread thread = new Thread(clientThread, clientThread.getThreadName());
                threadList.add(clientThread);
                thread.start();

                **// ... am Ende noch das hier schreiben kann:**
                generateOutput(createClientNamesString());

            }

Damit wird jedes mal, wenn sich ein Client verbunden hat, ein neuer „ClientNamesString“ erstellt, und an alle Clients geschickt.

Wow…das funktioniert! Super!

DANKE!

Was Du da angegeben hast, habe ich das langsam im Griff!

Hallo Yampi, Hallo ihr Lieben!

Ich bin zufällig via Google über Yampis Beitrag zu diesem Chat Programm gestolpert. So wie es ausschaut, sitzen wir gerade an dem Gleichen/Ähnlichen Fernstudium mit identischen Java-Lernheften. Deshalb würde ich die Gelegenheit gerne ergreifen und genau an dieser Stelle mit in die Diskussion einsteigen, falls Euch das Recht ist. Ich sitze ebenfalls gerade an den Aufgabenstellungen zu diesem kleinen Multi-Client-Chat und muss gestehen, dass ich als „Fossil“ der prozeduralen Programmierung (Basic und Pascal) noch in der Eingewöhnungszeit zu Java stecke, was sich aber dank netter Code-Symantik und dicker API-Doc recht angenehm antrainieren lässt. :slight_smile:
Jetzt habe ich zu diesem ChatServer eine grundlegende Frage. Zur Übersicht hier mal kurz meine - fast identische Version - der ChatServer.java:

import java.net.*;
import java.util.Iterator;
import java.util.LinkedList;
/**
 * Multi-Client Chat Programm; Modul: ChatServer; 
 * Aufruf über 'java ChatServer und Angabe des gewünschten Ports
 * 
 * @author Andy Rudolph
 *
 */
public class ChatServer extends Thread{

	private Socket clientSocket;
	private ServerSocket serverSocket;
	private static int port = 4444;
	protected LinkedList<ClientThread> threadList = new LinkedList<ClientThread>();//Client Liste
	private Iterator<ClientThread> iterator;
	private ClientThread clientThread;
	private Thread thread;
	private String toRemove;
	/**
	 * 
	 * @param args Portangabe über den User beim Start des Servers
	 */
	public static void main(String[] args) {
		if (args.length == 1) {
			try {
				port = Integer.parseInt(args[0]);
			} catch (NumberFormatException nfe) {
				System.out.println("Ungültige Port-Angabe! "+nfe.toString());
			}
		}
		ChatServer server = new ChatServer();
		server.start();
		StringBuffer sb;
		char c;
		String ausgabe;
		while(true) {
			sb = new StringBuffer();
			try {
				Reader in = new InputStreamReader(System.in);
				while((c=(char)in.read()) != '\r') {
					sb.append(c);
				}
			} catch (IOException ioe) {
				System.out.println(ioe.toString());
			}
			ausgabe = sb.toString();
			if (ausgabe.equals ("exit")) {
				server.closeAll();
			} else {
				System.out.println("\""+ausgabe+"\" wird nicht ausgewertet");
			}
		}//ENDE Main
	}
	/**
	 * @override run() durch main der Klasse ChatServer:
	 * 
	 * Aufruf der run()-Methode automatisch über server.start();
	 * Als erstes bekommt der Server einen Port zugewiesen, der sodann
	 * von ihm überwacht wird. Nach dem Anlegen einer Thread (Client) Liste
	 * Wartet der Server mit dem Einstieg in die while-Schleife auf
	 * Anmeldung eines neuen Clients und bestätigt diese Verbindung
	 * dann mit einem Promt und der Ausgabe der Teilnehmer im Chat.
	 */
	public void run() {
		try {
			serverSocket = new ServerSocket(port);
			System.out.println("SERVER online und überwacht Port: "+port);
		} catch (IOException ioe) {
			System.out.println("ServerSocket-Fehler: "+ioe.toString());
		}
		threadList = new LinkedList<ClientThread>();//Clientliste anlegen
		try {
			//Bedingung: Anmeldung eines Clients
			while ((clientSocket = serverSocket.accept()) != null) {				
				System.out.println("Verbindung zu Socket "
						+ clientSocket.getRemoteSocketAddress()
						+ " aufgenommen");
				clientThread = new ClientThread(clientSocket, this);
				thread = new Thread(clientThread, clientThread.getThreadName());
				System.out.println("Thread: "+thread);
				System.out.println("Name: "+clientThread.getThreadName());
				threadList.add(clientThread);//Client in die Liste zufügen				
				thread.start();
				//Liste aktiver Teilnehmer an alle versenden
				//siehe JAV06N, Einsendeaufgabe Nr.:6
				generateOutput(createClientNamesString());
			}
		} catch (IOException ioe) {
			System.out.println(ioe.toString());
		}
	}
	/**
	 * Einsendeaufgabe Nr.5: Sorgen Sie dafür, dass der Server einen Client 
	 * nicht mehr mit Meldungen versorgt, wenn sich dieser abgemeldet hat.
	 * 
	 * Idee: Die ThreadListe wird über eine for-Schleife nach dem passenden
	 * Eintrag durchsucht. Bei einer Übereinstimmung wird durch .remove(Object)
	 * der zu schließenden ClientThread aus der Liste entfernt.
	 * 
	 * @see java.util.LinkedList.remove(Object o)
	 */
	public void removeThreadFromList(String toRemove) {
		this.toRemove = toRemove;
		//Schleife durch die Liste um den Namen herauszufiltern
		for (int i=0; i<threadList.size(); i++) {
			ClientThread clientThread = threadList.get(i);
			String name = clientThread.getThreadName();
			//Prompt zum Test...
			System.out.println("Remove-Schleife: Durchgang "+i+": toRemove: "+toRemove+", Name in Liste: "+name);
			if (name == toRemove){
				//Eintrag entfernen
				threadList.remove(i);
				System.out.println("Thread \""+toRemove+"\" erfolgreich entfernt!");
			}
		}
	}	
	/**
	 * Diese Methode sorgt für eine Sendung des Streams an alle beteiligten
	 * Chatteilnehmer. Dazu wird die erstellte Linkliste befragt.
	 * 
	 * @param output
	 */
	public void generateOutput(String output) {
		iterator = threadList.iterator();
		while(iterator.hasNext()) {
			ClientThread thread = iterator.next();
			thread.output(output);
		}
	}
	/**
	 * Einsendeaufgabe Nr.6: Ergänzen Sie die Applikation, sodass jedem
	 * neuen angemeldetem Besucher alle bereits im Chat angemeldeten
	 * Teilnehmer in der Begrüßungsmitteilung des Servers aufgelistet
	 * werden
	 * 
	 * Idee: Threadliste per Schleife abfragen und im String speichern. 
	 * Bei der Abfrage der ThreadList.size() muss beachtet werden das
	 * der "abfragende" Teilnehmer bereits einen Eintrag in der Liste
	 * darstellt!
	 * 
	 * @return String aller Client-Threads (Chatteilnehmer) an Output
	 */
	private String createClientNamesString()
    {
        if (threadList.size() == 1)//Da ein "Abfrager" online
        {
            return "Keine weiteren Teilnehmer im Chat...";
        }
        String result = "Jetzt online: ";
        for (int i=0; i<threadList.size(); i++){
            ClientThread clientThread = threadList.get(i);
            String name = clientThread.getThreadName();
            result += name;
            if (i < threadList.size()-1){
                result += ", ";
            }
        }
        return result;
    }
	/**
	 * 
	 */
	private void closeAll() {
		//die Streams werden in den ClientThread-Objekten geschlossen
		try {
			serverSocket.close();
			System.exit(0);
		} catch (IOException e) {
			System.out.println(e.toString());
		}
	}
}```

Nach meinem bisher erworbenen Verständnis der objektorientierten Programmierung, startet mit dem Aufruf der kompilierten ChatServer.java via main der erste Thread und arbeitet sich durch bis 

```ChatServer server = new ChatServer();
server.start();```

um dann über die interne "Mechanik" via run() den gewünschten 2. Thread zu bearbeiten, der spätestens bei

```while ((clientSocket = serverSocket.accept()) != null) ```

in die gewünschte blockierende Wartestellung über .accept() geht.

Ist es nun so, dass die Main parallel dazu weiter bearbeitet wird und ich unabhängig davon tatsächlich eine 2. while (Endlos-)Schleife starten kann, die trotz der .accept()-Blockade Tastatureingaben entgegen nehmen kann?

a) Wenn ja, scheine ich einen Fehler bei der Umsetzung gemacht zu haben um paralell zur ".accep()-Blockade" Tastatureingeben in main entgegen zu nehmen zu können. Sieht die jemand? :(

b) Falls diese Parallelität (heisst das so? :o ) nicht so wie gedacht abläuft, wie kann ich das hoffentlich verständlich gemachte Problem lösen?

Wie hast Du es denn z.B. gelöst Yampi?

Gruß Gecko

Hallo und herzliches Willkommen im Forum.
Wie es scheint, ist ‘yampis’ Problem bereits geklärt. Vielleicht antwortet sie trotzdem nochmal auf deinen Post…

Das mit den Threads stimmt soweit: ChatServer ist ein thread, und mit dem Aufruf von “start” wird die “run”-Methode in diesem (neuen) Thread ausgeführt. Der Rest läuft unabhängig davon weiter.

Beschreib’ ggf. nochmal den Fehler genauer. Insbesondere solltest du beachten, dass das vermutlich von der Konsole aus gestartet werden muss (Eingabeuafforderung, dort
java ChatServer
eintippen). Aus einer Entwicklungsumgebung heraus könnte das nicht funktionieren.

Servus!

  1. Oki, also erst mal Danke für die Bestätigung, dass ich die main also quasi munter weiter nutzen kann um weitere Thread etc. zu starten. :slight_smile:

  2. Die Geschichte mit der Eingabeaufforderung ist natürlich richtig, das hatte ich vergessen zu erwähnen. Der ChatServer wird via Console mit <java ChatServer [port-Nr.] gestartet. Ebenso werden die bereits von Yampi geposteten ChatClientsFrame(s) über jeweils eigene Consolen gestartet um eigene Thread zu ermöglichen und beliebig viele ChatFenster angebunden werden können.

Das funktioniert auch alles so weit, nur bin ich nicht in der Lage in der Console des ChatServers eine Eingabe zu machen um ihm - wie im Programmcode als String hinterlegt - mit sein Beenden zu erzwingen.

Nach der Aussage von Marco13 weiß ich ja jetzt dass die main brav weiterarbeitet und demzufolge in die while-Schleife zur Tastatureingaben-Überwachung laufen sollte. Nur… reagiert der ChatServer leider nicht wie gewünscht auf die Eingabe(n).
(Genauer gesagt reagiert er nur auf , also gewaltsamer Abbruch.)

Fazit: Ich muss irgenwie einen Denkfehler in der Main haben, der meine Eingabe „zunichte“ macht… ?? :confused:

Hm. Vorhin hatte ich es getestet, und da ging’s. Vielleicht mal ein Testprogramm machen, wo in der main NUR diese while(true)-Schleife für die Eingabe steht, und schauen, ob man da dann was eingeben kann.
Vielleicht hat noch jemand eine Idee … ?!

Oki, hab deinen Rat mal befolgt und den Rest in main einfach mal auskommentiert, so dass er lediglich die Schleifenbearbeitung zur Auswahl hat. Aber… gleiches Ergebnis… :frowning:

Eingaben werden weiterhin munter ignoriert, egal was auch immer ich eintippe und mit Enter bestätige… Ist das unabhängig der JVM vielleicht eher ein Consolen-Problem unter Linux in diesem Fall? Wobei das jetzt weit hergeholt wäre…

(BS: Ubuntu Linux Version 10.04 (lucid), sun.java JDK installiert, keinerlei Probleme dieser Art bis jetzt…)

Nachtrag: Kurz mal zu Bill Gates gewechselt und dort getestet, und tatsächlich… es funktioniert einwandfrei.
Somit erübrigt sich eine Codebasierte Diskussion hier wohl leider und ich werde dieses Problemchen bei Gelegenheit eingehend mit meinem Linux diskutieren müssen… :wink:

Aber vielen Dank schon mal!

PS: Wer ne Idee hat kann natürlich gerne nen Tip rüberwerfen…

Vielleicht musst du einen Kezboard/Treiber installieren(

(SCNR :o) )

Oh, in Kürze melde ich mich noch, da ich mich mit einem anderen Thema, das mit Java zu tun hat, beschäftige.

Gecko, herzlich Willkommen! Wie gesagt, poste ich es noch.

Gecko, wie ich da gelesen habe, bist du damit soweit oder irgendwelche Probleme?
Wenn ja, welche sind es denn? Und wie sieht deine Code aus?

Wie ich bereits erklärt habe, steht es da auch da. Aber ein paar Stücke habe ich
dazu eingefügt, weil es sauber passt.