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

Java Socket聊天客户端输入'exit'无法正常关闭问题求助

Java Socket聊天应用exit无法立即退出问题解决

我用Java的Socket和ServerSocket开发简易聊天应用,客户端分两个线程分别处理消息接收和发送。但输入'exit'时,应用不会立即关闭,必须等收到其他客户端消息后才退出,求解决。


客户端代码(Client.java)

import java.io.*;
import java.net.Socket;
import java.net.ConnectException;
import java.util.Scanner;

public class Client {

    private Socket clientSocket;
    private BufferedReader serverReader;
    private boolean stopReceiving;
    private boolean stopSending;

    public static void main(String[] args) {
        Client client = new Client();
    }

    public Client() {
        try {
            this.clientSocket = new Socket("localhost", 8080);
            System.out.println("Spuštění klienta proběhlo úspěšně.");

            this.serverReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            this.stopReceiving = false;
            this.stopSending = false;


            Thread receiveThread = new Thread(new Runnable() {
                public void run() {
                    try {
                        while (!stopReceiving) {
                            String message = serverReader.readLine();
                            if (message == null) {
                                // Vypnutí připojení k serveru
                                stopReceiving = true;
                                closeClient();
                                System.exit(0); // Vypnutí aplikace
                            }
                            if (message.equals("Připojení odmítnuto - dosaženo maximálního počtu klientů. Zkuste se připojit později.")) {
                                closeClient();
                                System.exit(0); // Vypnutí aplikace
                            }
                            System.out.println("Zpráva od serveru: " + message);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            receiveThread.start();

            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(this.clientSocket.getOutputStream())); // vytvoření BufferedWriter
            Scanner in = new Scanner(System.in);
            while (!stopSending) {
                String message = in.nextLine();
                if (message.equals("exit")) {
                    System.out.println("REACHED EXIT");
                        serverReader.close();
                        clientSocket.close();
                        in.close();
                        out.close();
                        System.out.println("EXITED");

                        System.exit(0);
                        break;

                }else{
                    out.write(message + "\r\n");
                    out.flush();
                    System.out.println("Zpráva \"" + message + "\" byla odeslána.");
                }
            }

        } catch (ConnectException e) {
            System.out.println("Nepodařilo se připojit k serveru. Klient se ukončuje.");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeClient();
        }
    }

    private void closeClient() {
        try {
            if (serverReader != null)
                serverReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (clientSocket != null)
                clientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端代码(Server.java)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;

public class Server {

    private ServerSocket serverSocket;
    private ArrayList<ClientHandler> clients;
    private static final int MAX_CLIENTS = 2; // Maximální počet klientů

    public static void main(String[] args) {
        Server server = new Server();
    }

    public Server() {
        try {
            this.serverSocket = new ServerSocket(8080);
            System.out.println("Spuštění serveru proběhlo úspěšně.\nČekám na připojení klienta...\n");
            this.clients = new ArrayList<>();

            clients();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void clients() {
        Thread acceptThread = new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        Socket clientSocket = serverSocket.accept();
                        PrintWriter writer = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream()), true);
                        if (clients.size() < MAX_CLIENTS) { // Check if the maximum number of clients is reached
                            ClientHandler clientHandler = new ClientHandler(clientSocket, writer);
                            synchronized (clients) {
                                clients.add(clientHandler);
                            }
                            System.out.println("Klient " + clientHandler.getAddress() + " se připojil.");
                        } else {
                            writer.println("\"Připojení odmítnuto - dosaženo maximálního počtu klientů. Zkuste se připojit později.\"");
                            writer.flush();
                            clientSocket.close();
                            System.out.println("Připojení odmítnuto - dosaženo maximálního počtu klientů.");
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        acceptThread.start();

        while (true) {
            synchronized (clients) {
                Iterator<ClientHandler> iterator = clients.iterator();
                while (iterator.hasNext()) {
                    ClientHandler clientHandler = iterator.next();
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(clientHandler.getSocket().getInputStream()));
                        if (reader.ready()) {
                            String message = reader.readLine();
                            System.out.println("Přijata zpráva od klienta " + clientHandler.getAddress() + " : " + message);
                            broadcastMessage(message);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        iterator.remove(); // Bezpečně odstraní klienta z iterátoru
                    }
                }
            }
        }
    }

    private void broadcastMessage(String message) {
        synchronized (clients) {
            for (ClientHandler clientHandler : clients) {
                clientHandler.getWriter().println(message);
                clientHandler.getWriter().flush();
            }
        }
    }

    private static class ClientHandler {
        private final Socket socket;
        private final PrintWriter writer;

        public ClientHandler(Socket socket, PrintWriter writer) {
            this.socket = socket;
            this.writer = writer;
        }

        public Socket getSocket() {
            return this.socket;
        }

        public PrintWriter getWriter() {
            return this.writer;
        }

        public String getAddress() {
            return this.socket.getInetAddress().toString();
        }
    }
}

问题原因分析

  1. 客户端接收线程阻塞:serverReader.readLine()是阻塞方法,客户端输入exit关闭socket后,接收线程仍卡在读取操作上,只有当服务器向该socket写入数据(如其他客户端消息广播)时,才会触发IO异常或返回null,进而退出线程。
  2. 资源关闭顺序错误:客户端先关闭serverReader再关闭socket,可能导致socket关闭不彻底;且未设置标志通知接收线程主动退出。
  3. 服务端读取机制低效:服务端用单线程循环检查所有客户端的reader.ready(),无法及时检测到客户端断开,导致客户端退出后服务器未及时清理连接。

解决方案

修改后的客户端代码(Client.java)

import java.io.*;
import java.net.Socket;
import java.net.ConnectException;
import java.util.Scanner;

public class Client {

    private Socket clientSocket;
    private BufferedReader serverReader;
    private BufferedWriter serverWriter;
    private volatile boolean stopReceiving;
    private volatile boolean stopSending;
    private Thread receiveThread;

    public static void main(String[] args) {
        new Client();
    }

    public Client() {
        try {
            this.clientSocket = new Socket("localhost", 8080);
            System.out.println("客户端启动成功。");

            this.serverReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            this.serverWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
            this.stopReceiving = false;
            this.stopSending = false;

            receiveThread = new Thread(() -> {
                try {
                    while (!stopReceiving) {
                        String message = serverReader.readLine();
                        if (message == null) {
                            break;
                        }
                        if (message.equals("Připojení odmítnuto - dosaženo maximálního počtu klientů. Zkuste se připojit později.")) {
                            closeClient();
                            System.exit(0);
                        }
                        System.out.println("服务器消息: " + message);
                    }
                } catch (IOException e) {
                    // 客户端主动关闭socket时触发的异常,无需打印
                } finally {
                    closeClient();
                    System.exit(0);
                }
            });
            receiveThread.start();

            Scanner in = new Scanner(System.in);
            while (!stopSending) {
                String message = in.nextLine();
                if (message.equals("exit")) {
                    System.out.println("准备退出...");
                    stopReceiving = true;
                    stopSending = true;

                    // 中断接收线程的阻塞读取
                    receiveThread.interrupt();
                    // 先关闭输出流告知服务器,再关闭其他资源
                    serverWriter.close();
                    serverReader.close();
                    clientSocket.close();
                    in.close();

                    System.out.println("已退出");
                    break;
                } else {
                    serverWriter.write(message + "\r\n");
                    serverWriter.flush();
                    System.out.println("消息\"" + message + "\"已发送。");
                }
            }

        } catch (ConnectException e) {
            System.out.println("无法连接到服务器,客户端即将退出。");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeClient();
        }
    }

    private void closeClient() {
        try {
            if (serverWriter != null)
                serverWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (serverReader != null)
                serverReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (clientSocket != null && !clientSocket.isClosed())
                clientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

修改后的服务端代码(Server.java)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;

public class Server {

    private ServerSocket serverSocket;
    private CopyOnWriteArrayList<ClientHandler> clients;
    private static final int MAX_CLIENTS = 2; // 最大客户端数量

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

    public Server() {
        try {
            this.serverSocket = new ServerSocket(8080);
            System.out.println("服务器启动成功。\n等待客户端连接...\n");
            this.clients = new CopyOnWriteArrayList<>();

            acceptClients();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void acceptClients() {
        new Thread(() -> {
            while (true) {
                try {
                    Socket clientSocket = serverSocket.accept();
                    PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
                    if (clients.size() < MAX_CLIENTS) {
                        ClientHandler clientHandler = new ClientHandler(clientSocket, writer);
                        clients.add(clientHandler);
                        // 为每个客户端启动独立的读取线程
                        new Thread(clientHandler).start();
                        System.out.println("客户端 " + clientHandler.getAddress() + " 已连接。");
                    } else {
                        writer.println("Připojení odmítnuto - dosaženo maximálního počtu klientů. Zkuste se připojit později.");
                        clientSocket.close();
                        System.out.println("连接被拒绝 - 已达最大客户端数量。");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void broadcastMessage(String message) {
        for (ClientHandler clientHandler : clients) {
            clientHandler.getWriter().println(message);
        }
    }

    private class ClientHandler implements Runnable {
        private final Socket socket;
        private final PrintWriter writer;
        private BufferedReader reader;

        public ClientHandler(Socket socket, PrintWriter writer) {
            this.socket = socket;
            this.writer = writer;
            try {
                this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            String message;
            try {
                while ((message = reader.readLine()) != null) {
                    System.out.println("收到客户端 " + getAddress() + " 的消息: " + message);
                    broadcastMessage(message);
                }
            } catch (IOException e) {
                // 客户端断开连接时触发的异常
            } finally {
                // 清理客户端连接资源
                try {
                    reader.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                clients.remove(this);
                System.out.println("客户端 " + getAddress() + " 已断开连接。");
            }
        }

        public Socket getSocket() {
            return this.socket;
        }

        public PrintWriter getWriter() {
            return this.writer;
        }

        public String getAddress() {
            return this.socket.getInetAddress().toString();
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:57:00