Java TCP Socket同机通信正常,局域网运行JAR包仅首条消息可达如何解决
根本原因
- 服务端接收逻辑设计错误
available()方法仅返回当前操作系统套接字缓冲区中可无阻塞读取的字节数,不代表对端已发送的总数据量。网络传输存在延迟时,首条消息读取完成后,下一条消息还未到达本地缓冲区,available()会返回0,直接终止读取循环,后续消息永远不会被读取。同时你用主线程统一读取所有客户端Socket,只要一个Socket读操作阻塞就会卡住所有客户端的消息处理。 - 客户端套接字非优雅关闭
客户端单次发送逻辑执行完成后没有保持进程运行,也没有执行套接字关闭流程,直接退出进程时操作系统会发送RST包强制断开连接,而非正常的FIN四次挥手,属于非优雅关闭。此时服务端还在等待读取数据就会触发Connection reset异常。IntelliJ IDEA运行时正常是因为IDE对启动的进程默认添加了套接字回收延迟机制,Eclipse和直接运行JAR包没有该机制,所以会暴露问题。
解决方案
服务端修改
- 废弃
available()作为循环判断条件,改用阻塞读模式,且每个客户端连接分配独立的读取线程,避免互相影响。示例代码如下:
// 每个客户端连接对应独立线程的读取逻辑 DataInputStream dIn = new DataInputStream(client.getInputStream()); try { while (true) { // 阻塞读类型字段,读到EOFException说明客户端正常断开连接 byte type = dIn.readByte(); switch(type) { case 0: System.out.println(dIn.readUTF()); break; case 1: System.out.println(dIn.readInt()); break; case 2: System.out.println(dIn.readByte()); break; default: throw new IllegalStateException("Unexpected value: " + type); } } } catch (EOFException e) { // 客户端正常断开,清理资源 System.out.println("客户端正常断开连接"); } catch (SocketException e) { if ("Connection reset".equals(e.getMessage())) { // 客户端异常断开,清理对应Socket资源即可 System.out.println("客户端异常断开连接"); } } finally { dIn.close(); client.close(); // 从Socket列表中移除当前连接 }
- 移除主线程统一读取所有客户端的逻辑,接入连接时直接启动独立线程处理该客户端的读写。
客户端修改
- 若需要持续发送消息,将发送逻辑放入循环保持进程运行,不要单次发送后直接结束进程,比如可以监听控制台输入触发消息发送。
- 若需要结束通信,执行优雅关闭流程:
// 告知服务端自身已无数据发送 clientSocket.shutdownOutput(); // 等待服务端返回确认后再关闭资源 dOut.close(); dIn.close(); clientSocket.close();
内容的提问来源于stack exchange,提问作者Dubstepzedd
相关产品推荐
相关产品推荐

