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
相关产品推荐
相关产品推荐

