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

Java Socket多客户端聊天无法收发消息问题求助

多客户端聊天应用Socket编程问题排查

问题描述

我正在使用Java Socket开发多客户端聊天应用,参考网上代码修改后,客户端仅能收到其他用户加入/离开的通知,无法互相收发消息,恳请指出代码错误。


相关代码

Server.java

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

public class Server {
    
    private final ServerSocket serversocket;
    
    public Server (ServerSocket serversocket)
    {
        this.serversocket = serversocket;
    }
    
    public void startserver ()
    {
        try
        {
            while (!serversocket.isClosed())
            {
                Socket socket = serversocket.accept();
                System.out.println("A client is connected");
                
                ClientHandler clienthandler = new ClientHandler(socket);
                Thread t = new Thread(clienthandler);
                t.start();
            }
        }catch (IOException e)
        {
            
        }
    }
    
    public static void main(String[] args) throws IOException 
    {
       ServerSocket serversocket = new ServerSocket(5000);
       Server server = new Server(serversocket);
       server.startserver();
    }
}

ClientHandler.java

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

public class ClientHandler  implements Runnable
{
    public static ArrayList <ClientHandler>clienthandlers = new ArrayList<>() ;
    private final Socket socket;
    private final BufferedReader bufferedreader;
    private final PrintWriter writer; 
    private final String clientusername;
    
    public ClientHandler(Socket socket) throws IOException
    {
        this.socket = socket;
        bufferedreader =  new BufferedReader(new InputStreamReader(socket.getInputStream()));
        writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
        clientusername = bufferedreader.readLine().trim();
    }
    
    @Override
    public void run ()
    {
        try {
            System.out.println(clientusername+ " has joined the achat");
            clienthandlers.add(this);
            broadcastMessage ("SERVER :"+ clientusername+ " has joined the chat");
            
            while (socket.isConnected())
            {
                String messageFromClient; 
                messageFromClient= bufferedreader.readLine();
                broadcastMessage(messageFromClient);
            }
        }catch (IOException e)
        {
            closeEverything(socket,bufferedreader,writer) ;
        }
    }
    
    public void broadcastMessage (String messageToSend)
    {
        for (ClientHandler ch: clienthandlers)
        {
            if (!ch.clientusername.equals(clientusername)) 
            {
                ch.writer.println(messageToSend);
            }
        }
    }
    
    public void removeClientHandler ()
    {
        clienthandlers.remove(this);
        System.out.println(clientusername+ " has left the achat");
        broadcastMessage ("SERVER :"+ clientusername+ " has left the achat");
    }

    private void closeEverything(Socket socket, BufferedReader bufferedreader, PrintWriter writeer)
    {
        removeClientHandler();
        try 
        {
            if (bufferedreader!=null)
            {
                bufferedreader.close();
            }
        
            if (writeer!=null)
            {
                writeer.close();
            }
        
            if (socket!=null)
            {
                socket.close();
            }
        }catch (IOException e)
        {
        }
    }
}

Client.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    
    private Socket socket;
    private BufferedReader bufferedreader;
    private PrintWriter writer; 
    private String username; 
    
    public Client (Socket socket, String username)
    {
        try
        {
            this.socket = socket;
            this.bufferedreader =  new BufferedReader(new InputStreamReader(socket.getInputStream()));
            this.writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
            this.username = username;
            writer.println(username);
        }catch (IOException e)
        {
            closeEverything(socket, bufferedreader, writer);
        }
    }
    
    private void sendMessage ()
    {
        Scanner sc = new Scanner(System.in);
        while (socket.isConnected())
        {
            String messageToSend = sc.nextLine();
            writer.write(username+ ": " + messageToSend);
        }
    }
    
    public void listenForMessage ()
    {
        new Thread(() -> {
            String messageFromGroupchat;
            
            while (socket.isConnected()) 
            {
                try
                {
                    messageFromGroupchat = bufferedreader.readLine();
                    System.out.println(messageFromGroupchat);
                }catch (IOException e)
                {
                    closeEverything(socket, bufferedreader, writer);
                }
            }
        }).start();
    }
    
    private void closeEverything(Socket socket, BufferedReader bufferedreader, PrintWriter writeer)
    {
        try 
        {
            if (bufferedreader!=null)
            {
                bufferedreader.close();
            }
        
            if (writeer!=null)
            {
                writeer.close();
            }
        
            if (socket!=null)
            {
                socket.close();
            }
        }catch (IOException e)
        {
        }
    } 
    
    public static void main(String[] args) throws IOException 
    {
        Scanner sc = new Scanner(System.in);
        System.out.println("Enter your name");
        String username = sc.nextLine();
        
        Socket socket = new Socket("localhost", 5000);
        
        Client client = new Client(socket, username);
       
        client.listenForMessage();
        client.sendMessage();
    }
}

错误分析与修复方案

1. 客户端消息发送阻塞问题

错误点:Client类的sendMessage方法使用writer.write()发送消息,该方法不会自动添加换行符,也不会触发缓冲区刷新。服务器端的bufferedreader.readLine()会一直等待换行符,导致无法读取到消息。

修复:将writer.write()替换为writer.println(),该方法会自动添加换行符并触发缓冲区刷新(初始化PrintWriter时已设置autoFlush=true):

private void sendMessage ()
{
    Scanner sc = new Scanner(System.in);
    while (socket.isConnected())
    {
        String messageToSend = sc.nextLine();
        writer.println(username+ ": " + messageToSend);
    }
}

2. 多线程环境下的集合线程安全问题

错误点:ClientHandler中的clienthandlers使用ArrayList,多线程添加/移除元素时会引发并发修改异常,导致广播逻辑异常。

修复:替换为CopyOnWriteArrayList(线程安全的集合类),需导入java.util.concurrent.CopyOnWriteArrayList:

public static CopyOnWriteArrayList<ClientHandler> clienthandlers = new CopyOnWriteArrayList<>();

3. 异常处理缺失问题

错误点:Server类的startserver方法捕获IOException后无任何处理,无法定位服务器启动或运行时的错误。

修复:添加异常打印,并新增服务器关闭方法:

public void startserver ()
{
    try
    {
        while (!serversocket.isClosed())
        {
            Socket socket = serversocket.accept();
            System.out.println("A client is connected");
            
            ClientHandler clienthandler = new ClientHandler(socket);
            Thread t = new Thread(clienthandler);
            t.start();
        }
    }catch (IOException e)
    {
        e.printStackTrace();
        closeServerSocket();
    }
}

public void closeServerSocket() {
    try {
        if (serversocket != null && !serversocket.isClosed()) {
            serversocket.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 04:34:54