GUI "friert ein"

Baerdoc

Mitglied
Ich benötige Hilfe zu meinem GUI.
Der Server empfängt eine ID von (verschiedenen) Clienten und sendet ein Objekt zurück (Den Quellcode habe ich hier reduziert). Wird vom Clienten die ID "0000" geschickt, wird der Server beendet. In der Konsole funktioniert das ganze problemlos. Jetzt wollte ich ein GUI hinzufügen, bei dem man u.a. den Server stoppen und wieder starten kann. Beim Starten des Programms läuft es wie gewünscht. Ich kann den Server auch stoppen. Beim erneuten Starten des Servers läuft das Programm wie gewünscht, das GUI friert jedoch ein. Wenn ich den Server über einen Clienten wieder stoppe, kann ich das GUI wieder benutzen. Das Problem entsteht meiner Meinung nach in der Klasse Demoserver, Methode: startListening() mit while (endConnection). Ich habe bisher leider keine Lösung gefunden und bin für jeden Hinweis dankbar.

Java:
package main.java;

import java.awt.BorderLayout;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JToolBar;

public class GUIServer extends JFrame{
    
    private static final long serialVersionUID = 1L;
    private MeineAktionen startServerAktion, stopServerAktion, programmEndeAktion;
    private DemoServer server = new DemoServer();
    private JMenu menue;
    private JToolBar leiste;
    
    public GUIServer(String titel) {
        super(titel);
        
        Image serverAnImage = null;
        Image serverAusImage = null;
        Image programmEndeImage = null;   
        try {
            serverAnImage = ImageIO.read(getClass().getResource("/main/resources/startserver.png"));
            serverAusImage = ImageIO.read(getClass().getResource("/main/resources/stopserver.png"));
            programmEndeImage = ImageIO.read(getClass().getResource("/main/resources/ende.png"));           
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        startServerAktion = new MeineAktionen("Start Server", new ImageIcon(serverAnImage), "Startet den Server", "startServer");
        stopServerAktion = new MeineAktionen("Stopp Server", new ImageIcon(serverAusImage), "Stoppt den Server", "stoppServer");
        programmEndeAktion = new MeineAktionen("Programm beenden", new ImageIcon(programmEndeImage), "Beendet das Programm", "programmEnde");
        
        setLayout(new BorderLayout());
        menu();
        add(symbbolleiste(),BorderLayout.NORTH);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(600, 300);
        setLocation((int) (Toolkit.getDefaultToolkit().getScreenSize().width-600)/2,
                (int) (Toolkit.getDefaultToolkit().getScreenSize().height-300)/2);
        setVisible(true);
        startServer();
    }

    private void menu() {
        menue = new JMenu("Server");
        JMenuBar menuBar = new JMenuBar();
        menue.add(startServerAktion);
        menue.add(stopServerAktion);
        menue.addSeparator();
        menue.add(programmEndeAktion);
        menuBar.add(menue);
        this.setJMenuBar(menuBar);
    }
    
    private JToolBar symbbolleiste() {
        leiste = new JToolBar();
        leiste.add(startServerAktion);
        leiste.add(stopServerAktion);
        leiste.addSeparator();
        leiste.add(programmEndeAktion);       
        return leiste;
    }
    
    private void stoppProgramm() {
        System.exit(0);
    }
    
    private void startServer() {
        server.starteVerbinung();
        menue.getItem(0).setEnabled(false);
        menue.getItem(1).setEnabled(true);
        leiste.getComponentAtIndex(0).setEnabled(false);
        leiste.getComponentAtIndex(1).setEnabled(true);
        server.startListening();
    }
    
    private void stoppServer() {
        server.beendeVerbindung();
        menue.getItem(0).setEnabled(true);
        menue.getItem(1).setEnabled(false);
        leiste.getComponentAtIndex(0).setEnabled(true);
        leiste.getComponentAtIndex(1).setEnabled(false);
    }
    
    class MeineAktionen extends AbstractAction {
        private static final long serialVersionUID = 1L;
        public MeineAktionen(String menuetext, ImageIcon icon, String tooltip, String actionText) {
            super(menuetext,icon);
            putValue(SHORT_DESCRIPTION, tooltip);
            putValue(ACTION_COMMAND_KEY, actionText);
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getActionCommand().equals("programmEnde")) {
                stoppProgramm();
            }
            if (e.getActionCommand().equals("startServer")) {
                startServer();
            }
            if (e.getActionCommand().equals("stoppServer")) {
                stoppServer();
            }
        }
    }
}

Java:
package main.java;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;


public class DemoServer {

    private ServerSocket serverSocket;
    private Socket clientSocket;
    static boolean endConnection;
    int port = 7777;
    
    public void starteVerbinung() {
        try {
            serverSocket = new ServerSocket(port);
            endConnection = true;
            System.out.println("Starte ServerSocket");
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public void startListening() {
        while (endConnection) {
            try {
                System.out.println("Server wartet auf Verbindung");
                clientSocket = serverSocket.accept();
                Thread threadhandler = new Thread(new DataServerHandler(clientSocket, serverSocket));
                threadhandler.start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        beendeVerbindung();
    }
    
    
    public void beendeVerbindung() {
        try {
            serverSocket.close();
            if (clientSocket != null) {
                clientSocket.close();
            }   
            endConnection = false;
            System.out.println("Beende ServerSocket");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Java:
package main.java;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class DataServerHandler implements Runnable {

    private Socket clientSocket;
    private BufferedReader in;
    private ObjectOutputStream out;
    private String personID;
    private ServerSocket serverSocket;
    
    public DataServerHandler(Socket clientSocket, ServerSocket serverSocket) {
        this.clientSocket = clientSocket;
        this.serverSocket = serverSocket;
    }
    
    @Override
    public void run() {
        try {
            System.out.println("Thread " + Thread.currentThread().getId() + " for client request started.");
            Person person = new Person(null,null);
            in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            personID = in.readLine();
            if (personID.equals("0000")) {
                System.out.println("Beende server durch Client");
                DemoServer.endConnection = false;
                out = new ObjectOutputStream(clientSocket.getOutputStream());
                out.writeObject(person);
                out.flush();
                out.close();
                in.close();
                clientSocket.close();
                serverSocket.close();
            } else {
                person.setName("Mustermann");
                person.setVorname("Max");
                System.out.println("Sende Daten");
                out = new ObjectOutputStream(clientSocket.getOutputStream());
                out.writeObject(person);
                out.flush();
                in.close();
                clientSocket.close();
            }
                
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Java:
package main.java;

import java.io.Serializable;

public class Person implements Serializable {

    private static final long serialVersionUID = 3L;
    private String name;
    private String vorname;

    public Person(String name, String vorname) {
        this.name = name;
        this.vorname = vorname;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setVorname(String vorname) {
        this.vorname = vorname;
    }
}

Java:
package main.java;

public class DemoServerTest {

    public static void main(String[] args) {
        new GUIServer("Server Monitor");
    }

}
 

mihe7

Top Contributor
Deine Vermutung ist völlig richtig: Du rufst aus dem Event Dispatch Thread (EDT) des UIs eine Methode auf, die blockiert, d. h. ggf. erst sehr viel später zurückkehrt. In der Zwischenzeit kann der EDT die folgenden Events nicht verarbeiten und damit friert das UI ein.

Die Lösung besteht darin, die lang dauernde Methode in einen eigenen Thread zu packen.
 

Baerdoc

Mitglied
Deine Vermutung ist völlig richtig: Du rufst aus dem Event Dispatch Thread (EDT) des UIs eine Methode auf, die blockiert, d. h. ggf. erst sehr viel später zurückkehrt. In der Zwischenzeit kann der EDT die folgenden Events nicht verarbeiten und damit friert das UI ein.

Die Lösung besteht darin, die lang dauernde Methode in einen eigenen Thread zu packen.
Warum passiert das dann nicht schon beim ersten Aufruf?
 

KonradN

Super-Moderator
Mitarbeiter
Ich habe es nicht im Detail angesehen, aber ich denke, dass Du beim ersten Lauf in dem Thread vom Start der Anwendung bist und nicht im EDT. Beim nächsten Start bist du im EDT.

Somit blockst du beim ersten Lauf lediglich den Hauptthread der Anwendung und nicht den EDT.

Das ist auch der Grund, wieso man bei Swing Anwendungen schon die Erstellung des Fensters per SwingUtilities.invokeLater macht, denn Swing UI ist nicht thread-sicher.

Edit: Zur Verdeutlichung einfach folgender Code:
Java:
    public static void main(String[] args) {
        System.out.println(Thread.currentThread());
        SwingUtilities.invokeLater(() -> {
            System.out.println(Thread.currentThread());
        });
    }

Das erzeugt die Ausgabe:
Code:
Thread[main,5,main]
Thread[AWT-EventQueue-0,6,main]
Das erste ist also auf dem main Thread. Das zweite auf dem EDT, der offiziell heisst: AWT-EventQueue-0
 

Ähnliche Java Themen

Neue Themen


Oben