解决两个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
相关产品推荐
相关产品推荐

