Java实现P2P聊天系统TCP协议偶发消息丢失、无法全量展示问题求助
偶发消息展示不全根因
numberOfConnections变量存在可见性问题:waitUntilEnds方法直接读取numberOfConnections时未加同步锁,虽然addNumberOfConnections是同步方法,能保证修改后刷新到主内存,但无同步的读操作可能读取到工作线程缓存的旧值,导致numberOfConnections实际未归零就提前退出等待,部分消息还未写入集合就被读取,出现展示不全。- 集合读写未保证一致性:仅对
addMessage写操作加了同步锁,执行show命令读取messages、senders集合时没有加锁,并发场景下可能读取到修改过程中的中间状态,出现计数不准、漏展示消息的问题。 - 连接处理逻辑阻塞accept线程:当前在
ServerSocket的accept主线程中直接执行startNewConnection处理消息,没有为每个连接启动独立线程,同一时间只能处理一个连接,多个连接同时到达时后续连接会排队等待,导致numberOfConnections计数滞后,提前触发等待结束。
端口重绑定后完全收不到消息根因
- 未开启端口复用选项:TCP连接断开后会存在2MSL的TIME_WAIT阶段,端口默认会被占用1~2分钟,创建
ServerSocket时未设置SO_REUSEADDR参数,频繁重绑端口时会绑定失败。代码中仅捕获绑定异常打印堆栈,未做失败提示,容易误以为端口监听已启动,实际服务未生效,自然收不到该端口的消息。 - 旧服务资源未清理干净:重绑定端口时,未正确关闭旧的
ServerSocket、中断旧的accept线程,可能出现旧线程仍占用端口,导致新服务绑定失败。
修复建议
- 调整
setServer中连接处理逻辑,为每个连接启动独立线程,避免阻塞accept流程:
private void setServer(int port) { try { ServerSocket serverSocket = new ServerSocket(); serverSocket.setReuseAddress(true); // 开启端口复用 serverSocket.bind(new InetSocketAddress(port)); new Thread(() -> { while (!serverSocket.isClosed()) { try { Socket socket = serverSocket.accept(); addNumberOfConnections(1); // 独立线程处理单连接逻辑 new Thread(() -> { try { startNewConnection(socket); } finally { addNumberOfConnections(-1); } }).start(); } catch (IOException ignored) {} } }).start(); } catch (IOException e) { e.printStackTrace(); // 补充绑定失败的错误提示,避免静默失败 System.err.println("端口" + port + "监听失败"); } }
- 修正
waitUntilEnds的变量读取逻辑,保证可见性:
private void waitUntilEnds() { while (true) { // 读操作也加同步锁,保证读取的是主内存最新值 synchronized (this) { if (numberOfConnections == 0) break; } try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } } }
- 所有读取
messages、senders集合的逻辑(比如show命令的实现),都要加和addMessage同一对象的同步锁,保证读写一致性。
内容的提问来源于stack exchange,提问作者Taha Akbari
相关产品推荐
相关产品推荐

