Client nicht mehr angemeldet

Hallo zusammen,

ich hoffe ihr könnt mir helfen. Ich habe 3in Chatprogramm, welches mehrere Clients mit dem Server verbindet.

Meine Aufgabe ist es nun, aus der erstellten LinkedList (welche alle aktiven Clients verwaltet) die Clients zu löschen, welche NICHT MEHR MIT DEM Server verbunden sind.

Ich bekomm es einfach nicht hin, habe hier im Forum schon eine Methode gefunden, aber damit klappts nicht so recht! Wo soll ich die Methode aufrufen!?

Steh sowas von auf dem Schlauch, schon seit 4 Tagen bin ich an diesem Problem

Die Methode:

        this.toRemove = toRemove;
        //Schleife durch die Liste um den Namen herauszufiltern
        for (int i=0; i<threadList.size(); i++) {
            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
     */
    pu```

Meine Klasse:
``` package lektion5.copy;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.LinkedList;

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 removeDisconnectedClient(String toRemove) {
        this.toRemove = toRemove;
        //Schleife durch die Liste um den Namen herauszufiltern
        for (int i=0; i<threadList.size(); i++) {
            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 synchronized 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 "Kein User online :-(";
        }
        String result = "Gerade 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());
        }
    }
}

Strings werden mit String.equals(String) verglichen … nicht mit String==String

Moin,

mein Vorredner/-schreiber meinte diese Zeile:

if (name == toRemove) // !!!

Gruß
Klaus

4 Tage klingt übertrieben, wenn wahr und auch nur eine Stunde täglich, dann wirklich verschwendet,
früher Forum fragen oder überlegen, was genau du in der Zeit eigentlich getestet hast,

geprüft mit Debugger oder System.out.println() ob Methode drankommt, welche Clients in Schleife drankommen, ob das if erfüllt ist oder nicht?
konstruktiv kommt man gar nicht drumherum den Fehler alsbald zu finden,

nur durch Draufstarren kommt die Lösung freilich erst an Tag 799, da ist der 4. Tag zu früh ( sorry :wink: )


zum Erlernen des String-Vergleichs war die Aufgabe und die Zeit immer noch gut investiert,
allgemein eine fragwürdige Methode, wer ruft die auf?
wenn nur der Client sich selber melden und abmelden kann, wäre es etwas schlauer, dass bei der Verarbeitung der Nachricht
im hoffentlich vorhandenen ClientThread-Objekt ein boolean gesetzt wird oder ähnliches,

die Methode sollte dann gar keinen Parameter benötigen, die Liste der ClientThreads durchgehen und jedes Objekt für sich objektiv prüfen,
wenn kein boolean vorhanden dann geht auch schauen ob Socket geschlossen oder ähnliches,

wird auch darauf geachtet dass der ClientThread wirklich ausläuft, run-Methode beendet?
nur als Liste entfernen beendet ihn nicht

die Methode removeDisconnectedClients() könnte theoretisch auch unabhängig von Clients in regelmäßigen Abständen allgemein aufgerufen werden,
was aber nicht besser sein muss

da alles nebenläufig passiert muss man jedenfalls sowieso bei jeder Nachricht an Clients abfangen, ob deren Sockets schon geschlossen usw.,
in Liste drin oder nicht ist keine sichere Bedingung

Hi, die Methode hab ich im Forum gesehen und in meinem Code getestet > geht nicht. Dass man String mit equals vegleicht weis ich. Dennoch danke für den Input.

Die Nachricht von Slater muss ich mir jetzt mal in Ruhe ansehen.

Danke schon mal für hilfreiche Hilfe.

was geht daran nicht?

gilt nach wie vor,
wird die Methode überhaupt verwendet? am geposteten Code nich zu sehen,
welcher Parameter dann, welche Einträge sind in der List mit welchen Namen,

falls Namen augenscheinlich gleich, klappt dann das if?
einfache Schritte, so wie um drei Ecken zu laufen zum Kühlschrank,
es kann dabei (beim Prüfen des Fehlers) quasi keine (schwierigen) Probleme geben, nur machen, halb im Schlaf

wenn Code gepostet, in welchem es falsch steht, ob von dir oder kopiert und nicht gesehen/ korrigiert,
dann lieber Thema nicht weiter betonen, nur hinnehmen :wink:

Hab ich nicht geändert im geposteten Code :wink:

Die Methode wird nur durchlaufen, wenn ich sie aufrufe. Aber ich rufe sie garantiert in der falschen Ecke auf.

Ich komm einfach nicht weiter
hab jetzt einen boolean eingefügt in ClientInputThread, denn dort fliegt eine StreamClosed -Exception, aber dies funktioniert nicht, wenn ich mir die LinkedList ausgeben lasse, werden dort KEINE Einträge gelöscht. Ich verfluche die se Aufgabe langsam!

In der LinkedList werden clientThread-Objekte gespeichert, also die welche mit dem Server verbunden sind.

Wer mir helfen möchte, über den freu ich nicht, andernfalls lasst bitte eure Kommentare stecken, die vertrage ich glaube nicht noch zusätzlich^^.

vom boolean-Setzen oder Liste ausgeben werden keine Einträge gelöscht, dass musst du dir schon anhören

sofern es Code mit Schleife und remove gibt, in welcher Form auch immer, dann den auch posten oder analysieren


da du anscheinend schon mehr oder weniger unglücklicherweise auf meine Ideen hörst, dann als weiteres angemerkt:

wenn man das ClientThread-Objekt hat und removeDisconnectedClient() individuell aufruft,
dann ginge auch und ist empfehlenswert, dieses ClientThread-Objekt als Parameter zu übergeben,
in removeDisconnectedClient() ohne Schleife nur liste.remove(client); aufrufen

Überprüfung ob es funktioniert gehört natürlich temporär dazu, Rückgabewert des remove-Aufrufs anschauen,
evtl. vorher und nachher Liste ausgeben usw.

Hi Slater,

ich meine Schleife hab ich doch schon gepostet wo remove() aufgerufen wird :frowning:

    //    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.equals(toRemove)){
                //Eintrag entfernen
                threadList.remove(i);
                System.out.println("Thread \""+toRemove+"\" erfolgreich entfernt!");
            }
        }
    }  ```

und in der KLasse ClientThread, habe ich:

```if((fromClient = clientIn.readLine()) == null) {
					ChatServer.removeDisconnectedClient(this.getThreadName());
				}```

So falsch ist das doch nicht oderf?!?! :scheiterhaufen:

klang eben nach ganz anderen Code

wenn du noch beim Namen bist, gilt einmal mehr, ich kann es nur wiederholen:
„programmieren, laufen lassen, geht nicht, huch?“ reicht nicht

prüfte als erstes (nachdem viele andere Funktionalitäten (Verbindung, Übertragung) gehen),
ob removeDisconnectedClient() aufgerufen wird,

wo steht der ungewöhnliche Code, du vermutest ja selber ‚ich rufe sie garantiert in der falschen Ecke auf‘
bisher kann man es nicht erkennen

if((fromClient = clientIn.readLine()) == null) {
          ChatServer.removeDisconnectedClient(this.getThreadName());
}

die Bedingung sieht nach Socket-Ende auf,
wann, von wem auf welchen Anlass hin wird das if durchgeführt?

eingängiger wäre das Versenden und Prüfen einer „ENDE“-Nachricht,

jedenfalls muss in das if als erstes
System.out.println("if für geplantes removeDisconnectedClient betreten");
alles andere kann erstmal fehlen, ein System.out.println() zur Ablaufkontrolle ist der wichtigste Befehl an jeder Stelle jeden Programmes :wink:

was ist hier eigentlich ChatServer, hoffentlich kein Variablenname,
als Klassenname kann es aber kaum funktionieren, die Methode ist nicht static, oder doch?
gäbe dann schon Fehlermeldung,
hast du diese und verschweigst sie?..

ChatServer ist die obenstehende Klasse, du verwirrst mich noch mehr :frowning:

Ich soll einfach nur erreichen, dass aus der LinkedList wo die akticen Clients drin sind, die Clients gelöscht werden, welche die Verbindung zum Server unterbrochen haben…das kann doch nicht so schwer sein!? Ich habe keine Ahnung warum ich das nicht hinbekomme! Mein Tutor ist auch total witzig, antwortet mir einfach nicht.

Könnt grad alles hinschmeißen

ChatServer.removeDisconnectedClient(this.getThreadName());
ist nun mal ein statischer Aufruf, falls es nicht böserweise auch eine Variable gibt die wie die Klasse heißt

solche Details verdecken zunächst das große Ganze, aber es gibt kein Fingerschnippen so dass es geht,
sondern nur lauter kleine Schritte die alle einzeln zu gehen sind

wenn du alles ignorierst und nur ‘so, funktioniere jetzt!’ rufst, dann kommst du nicht voran

Ich hab doch schon sämtliches versucht, ich hab booleans eingefügt und und und, hab nicht umsonst schon nach der Lösung gegoogelt. Ich weis einfach nicht, was ich noch machen soll.

NOchmal zusammenfassend:
In die LinkedList kommen ClientThread-Objekte. Also muss ich auch prüfen ob die ClientThread-Objekte noch mit dem Server verbuden sind (wie?) um sie wieder aus der Liste zu entfernen. < Aufgabe ist entfernen Sie die nichtverbundenen Clients aus der LinkedList

Wie prüfe ich, ob ein Client noch verbunden ist?
schon versucht:
Einen Boolean setzen, wenn der Stream geschlossen ist, aber wie teile ich der removeDisconnectedClient() mit, welchen Client sie entfernen soll? Das kann ich doch nur machen, wenn ich zur Laufzeit in der ClientThread-Klasse ermittle, ob der Client noch verbunden ist, wenn nicht soll er eine Meldung an den Server geben und dieser löscht ihn dann?!

ich habe meine Zweifel ob die Methode überhaupt ausgeführt wird (inzwischen gar ob das Programm kompiliert, siehe static),
aber wenn du meinst dass dies kein Thema ist, dann zum Inhalt der Methode:

auch hier unvermeidlich der Rat, überall System.out.println() zu setzen,
schaue dir an welche Objekte in der Schleife geprüft werden, welche Namen, Zustand der Sockets, bestimmer Boolean usw.

abgesehen von selber gesetzten booleans wie gesagt z.B. Socket-Objekt holen, abfragen ob das schon geschlossen ist

[quote=NicoDeluxe;90076]schon versucht:
Einen Boolean setzen, wenn der Stream geschlossen ist, aber wie teile ich der removeDisconnectedClient() mit, welchen Client sie entfernen soll?[/quote]
gar nichts ist mitzuteilen (kein Parameter) falls die Methode alle Clients mit gesetzten boolean entfernt

aber das ist auch nur eine Idee, es kann ja auch beim Namen-Parameter bleiben,
entscheide dich für eine Lösung, sonst in der Tat nur mehr Verwirrung,

du kannst nicht über boolean reden, aber keinen Code zu boolean posten, sonsten weiter zum Namen (Posting #9)

[quote=NicoDeluxe;90076]Das kann ich doch nur machen, wenn ich zur Laufzeit in der ClientThread-Klasse ermittle, ob der Client noch verbunden ist,
[/quote]
merkwürdiger Satz,
die Feststellung, dass der Client nicht mehr verbunden ist, ist doch die Grundvoraussetzung für eine Aktion, erst dann Aufruf removeDisconnectedClient() usw.,
du kannst nicht gleichzeitig darüber, und über Details der Methode reden…

[quote=NicoDeluxe;90076]
wenn nicht soll er eine Meldung an den Server geben und dieser löscht ihn dann?![/quote]
‚wenn nicht soll er‘
→ wenn was nicht, und ‚er‘ ist das Client-Programm, um welches es bisher gar nicht ging?
meine Güte, alles durcheinander

Ablauf durchgehen täte wirklich gut:

  1. Client-Programm trifft aus freien Stücken (nach Benutzereingabe oder Timer im Code) Entscheidung, Verbindung zu beenden,
    erstmal im Client-Programm mit
    System.out.println("nun habe ich genug von dieser Verbindung");
    dokumentieren

  2. diese Entscheidung muss umgesetzt werden, ein einfaches close() auf dem Socket ist schon was,
    schön könnte es sein, vorher eine Text-Nachricht „Ende“ an den Server zu schicken

sowas musst du dir überlegen, entscheiden, umsetzen, lange lange vor der Methode removeDisconnectedClient()

  1. nun beim Server, dortige Klasse ClientThread,
    diese muss je nach Umsetzung von 2. reagieren, entweder die „Ende“-Nachricht bekommen,
    oder eine Exception bei close() des Sockets wenn darauf in Schleife dauerhaft gelesen wird (ähnlich Zeile 46 ChatServer)

es mag nicht leicht sein, es ist daran zu arbeiten (wiederum lange bevor über removeDisconnectedClient() nachzudenken),
jedenfalls muss es irgendeinen Zeitpunkt geben, zu dem sich der ClientThread im Server bewußt ist, dass die Verbindung ein Ende genommen hat
→ erst mal
System.out.println("Verbindungsende für Client "+...);
DANACH alles weitere

  1. nun kann man noch im Server aufräumen, im ClientThread-Objekt ggfs boolean setzen,
    removeDisconnectedClient() aufrufen welche nun wirklich interessant wird,

die Methode muss nicht auf Anhieb funktionieren, ablaufen lassen und sich anschauen ob die Schleife durchlaufen wird,
was bei if-Vergleichen herauskommt usw. ist auch schon gut,

eine Strategie finden (Namens-Vergleich, boolean-Attribut, schauen ob Socket geschlossen, …), programmieren, prüfen, usw.

Ok, danke schon mal. Ich denke ich brauch eine Pause! Bin einfach nur genervt von der Aufgabe.

In anderen Foren habe ich gelesen, dass zb socket.isClosed() überhaupt nicht zurück gibt ob die Verbindung wirklich geschlossen ist. Jeder erzählt was anderes -.- und mein Tutor garnix, auch toll!

ob isClosed() funktioniert ist eine Blackbox, ja, nein, vielleicht,
habe ich gerade nicht parat, Verdacht klingt aber bekannt, ja

deswegen die Strategie, sich auf das Wenigste zu verlassen was nötig ist:
Verbindung aufbauen und Nachrichten austauschen,
soweit wird es ja wohl problemlos gehen, sonst stellen sich ganz andere Fragen als Verbindungsende…

→ schicke daher „Ende“, dann weiß es der Server, der Client sowieso, beide können auf ihren Seiten aufräumen,
Zustand Socket egal, gar nicht erst anschauen, close() auf alle möglichen Streams und Sockets möglichst dennoch aufrufen,

für Ende der run-Methode des Threads sorgen, eigenen boolean setzen, aus Client-Liste entfernen :wink: usw.