You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决两个JFrame切换时Java Socket客户端程序崩溃问题

问题

我正在开发一个基于Socket的客户端/服务器应用,包含两个独立的JFrame类(CustomerGUI和AdminGUI)。用户初始打开其中一个窗口,通过按钮可切换到另一个窗口(关闭当前窗口并打开新窗口)。但目前每次切换时,新JFrame的构造函数都会创建新的Socket连接,导致程序崩溃,需要强制关闭。现寻求方法,实现两个JFrame正常来回切换且程序不崩溃。


相关代码

Server

public class Testserver {

    private ServerSocket serverSocket;
    private Socket client;
    private final int PORT = 5432;
    private ObjectOutputStream objectOutputStream;
    private ObjectInputStream objectInputStream;

    public Testserver() {
        try {
            serverSocket = new ServerSocket(PORT);
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
            System.out.println("error creating server socket: " + ex.getMessage());
        }
    }

    private void listenForClient() {

        try {
            System.out.println("Server is running and is waiting/listening for a connection to be established.");
            ////JOptionPane.showMessageDialog(null, "Server is running and is waiting/listening for a connection to be established." );
            client = serverSocket.accept();
            System.out.println("A client has connected");

            objectOutputStream = new ObjectOutputStream(client.getOutputStream());
            objectInputStream = new ObjectInputStream(client.getInputStream());
            processClient();
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
        }

    }
    
    
    private void processClient() {

        do {
            try {
                String messageFromClient = (String) objectInputStream.readObject();
                
                // check for clients requests and handle them (database etc)
                System.out.println("[CLIENT] " + messageFromClient);

            } catch (IOException ex) {
                Logger.getLogger(Testserver.class.getName()).log(Level.SEVERE, null, ex);
                break;
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(Testserver.class.getName()).log(Level.SEVERE, null, ex);
                break;
            }

        } while (true);
        
        closeConnections();

    }
    
    
    private void closeConnections() {
        try {
            objectInputStream.close();
            objectInputStream.close();
            client.close();
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
        }
    }


    public static void main(String[] args) {
        new Testserver().listenForClient();
    }
}

CustomerGUI

public class CustomerGUI extends JFrame implements ActionListener{
    
    private Socket server;
    private ObjectOutputStream objectOutputStream;
    private ObjectInputStream objectInputStream;
    private JPanel panel1;
    private JButton btnAdminGUI;
    private final int PORT = 5432;
    
    public CustomerGUI() {
        
        btnAdminGUI = new JButton("Go to Admin GUI");
               
        try {
            server = new Socket("localhost", PORT);
            System.out.println("Connected to server");
            
            objectOutputStream = new ObjectOutputStream(server.getOutputStream());
            objectInputStream = new ObjectInputStream(server.getInputStream());
            
            objectOutputStream.writeObject("from Customer");
            objectOutputStream.flush();

        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
        }
        
        btnAdminGUI.addActionListener(this);

    }
    
    
    public void setGUI() {
        
        
        add(btnAdminGUI);
        setSize(300, 400);
        setVisible(true);
    }

    public static void main(String[] args) {
        new CustomerGUI().setGUI();
        
    }

    @Override
    public void actionPerformed(ActionEvent e) {
       if(e.getSource() == btnAdminGUI) {
           new AdminGUI().setGUI();
           dispose();
       }
    }
}

AdminGUI

public class AdminGUI extends JFrame implements ActionListener{
    
    private Socket server;
    private ObjectOutputStream objectOutputStream;
    private ObjectInputStream objectInputStream;
    private JPanel panel1;
    private JButton btnCustomerGUI;
    private final int PORT = 5432;
    
    public AdminGUI() {
        
        btnCustomerGUI = new JButton("Go to Customer GUI");
               
        try {
            server = new Socket("localhost", PORT);
            System.out.println("Connected to server");
            
            objectOutputStream = new ObjectOutputStream(server.getOutputStream());
            objectInputStream = new ObjectInputStream(server.getInputStream());
            
            objectOutputStream.writeObject("from Admin");
            objectOutputStream.flush();

        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
        }
        
        btnCustomerGUI.addActionListener(this);

    }
    
    
    public void setGUI() {
        
        
        add(btnCustomerGUI);
        setSize(300, 400);
        setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
       if(e.getSource() == btnCustomerGUI) {
           new CustomerGUI().setGUI();
           dispose();
       }
    }
}

解决思路与实现方案

核心问题是重复创建Socket连接且旧连接未正确释放,同时当前服务器只能处理一个客户端连接。需要从客户端和服务器两端调整:

1. 客户端:共享Socket连接,避免重复创建

把Socket、输入输出流抽离成独立的单例类,让两个GUI共用同一个连接实例,切换窗口时不再新建连接。

步骤1:创建连接管理单例类

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import javax.swing.JOptionPane;

public class ClientConnection {
    private static ClientConnection instance;
    private Socket server;
    private ObjectOutputStream objectOutputStream;
    private ObjectInputStream objectInputStream;
    private final int PORT = 5432;
    private final String HOST = "localhost";

    private ClientConnection() {
        try {
            server = new Socket(HOST, PORT);
            System.out.println("Connected to server");
            objectOutputStream = new ObjectOutputStream(server.getOutputStream());
            objectInputStream = new ObjectInputStream(server.getInputStream());
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
            closeConnection();
        }
    }

    public static synchronized ClientConnection getInstance() {
        if (instance == null) {
            instance = new ClientConnection();
        }
        return instance;
    }

    public ObjectOutputStream getObjectOutputStream() {
        return objectOutputStream;
    }

    public ObjectInputStream getObjectInputStream() {
        return objectInputStream;
    }

    public void closeConnection() {
        try {
            if (objectInputStream != null) objectInputStream.close();
            if (objectOutputStream != null) objectOutputStream.close();
            if (server != null) server.close();
            instance = null;
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error closing connection: " + ex.getMessage());
        }
    }
}

步骤2:修改CustomerGUI使用共享连接

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class CustomerGUI extends JFrame implements ActionListener {
    private JButton btnAdminGUI;
    private ClientConnection connection;

    public CustomerGUI() {
        btnAdminGUI = new JButton("Go to Admin GUI");
        connection = ClientConnection.getInstance();

        try {
            connection.getObjectOutputStream().writeObject("from Customer");
            connection.getObjectOutputStream().flush();
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error sending message: " + ex.getMessage());
        }

        btnAdminGUI.addActionListener(this);
    }

    public void setGUI() {
        add(btnAdminGUI);
        setSize(300, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    public static void main(String[] args) {
        new CustomerGUI().setGUI();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == btnAdminGUI) {
            new AdminGUI().setGUI();
            dispose();
        }
    }
}

步骤3:修改AdminGUI使用共享连接

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class AdminGUI extends JFrame implements ActionListener {
    private JButton btnCustomerGUI;
    private ClientConnection connection;

    public AdminGUI() {
        btnCustomerGUI = new JButton("Go to Customer GUI");
        connection = ClientConnection.getInstance();

        try {
            connection.getObjectOutputStream().writeObject("from Admin");
            connection.getObjectOutputStream().flush();
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error sending message: " + ex.getMessage());
        }

        btnCustomerGUI.addActionListener(this);
    }

    public void setGUI() {
        add(btnCustomerGUI);
        setSize(300, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == btnCustomerGUI) {
            new CustomerGUI().setGUI();
            dispose();
        }
    }
}

2. 服务器:支持多客户端连接

当前服务器只能处理一个客户端,当旧连接未断开时新连接会被阻塞,修改为多线程模式,每个客户端连接分配独立线程:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

public class Testserver {
    private ServerSocket serverSocket;
    private final int PORT = 5432;

    public Testserver() {
        try {
            serverSocket = new ServerSocket(PORT);
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
            System.out.println("error creating server socket: " + ex.getMessage());
        }
    }

    private void listenForClients() {
        System.out.println("Server is running and is waiting/listening for connections.");
        while (true) {
            try {
                Socket client = serverSocket.accept();
                System.out.println("A client has connected");
                new ClientHandler(client).start();
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(null, "Error accepting client: " + ex.getMessage());
                break;
            }
        }
    }

    private class ClientHandler extends Thread {
        private Socket client;
        private ObjectOutputStream objectOutputStream;
        private ObjectInputStream objectInputStream;

        public ClientHandler(Socket client) {
            this.client = client;
        }

        @Override
        public void run() {
            try {
                objectOutputStream = new ObjectOutputStream(client.getOutputStream());
                objectInputStream = new ObjectInputStream(client.getInputStream());
                processClient();
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(null, "Error initializing client handler: " + ex.getMessage());
            } finally {
                closeConnections();
            }
        }

        private void processClient() {
            do {
                try {
                    String messageFromClient = (String) objectInputStream.readObject();
                    System.out.println("[CLIENT] " + messageFromClient);
                } catch (IOException ex) {
                    System.out.println("Client disconnected");
                    break;
                } catch (ClassNotFoundException ex) {
                    Logger.getLogger(Testserver.class.getName()).log(Level.SEVERE, null, ex);
                    break;
                }
            } while (true);
        }

        private void closeConnections() {
            try {
                if (objectInputStream != null) objectInputStream.close();
                if (objectOutputStream != null) objectOutputStream.close();
                if (client != null) client.close();
                System.out.println("Connection closed");
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(null, "Error closing connections: " + ex.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        new Testserver().listenForClients();
    }
}

3. 额外注意事项

  • 客户端关闭主窗口时,调用connection.closeConnection()释放Socket资源,避免服务器端出现异常。可通过窗口监听器实现:
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosing(WindowEvent e) {
        connection.closeConnection();
        System.exit(0);
    }
});

内容的提问来源于stack exchange,提问作者ServletException

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 15:40:41