Java Socket客户端与服务器无法连接:new ObjectInputStream阻塞问题
问题分析与解决
客户端执行objectInputStream = new ObjectInputStream(socket.getInputStream());时持续阻塞,服务器可识别连接但无法完成双向流初始化,核心问题及解决方法如下:
根本原因
ObjectInputStream的构造函数会主动读取对方ObjectOutputStream写入的序列化协议头部(魔数+版本号)。当前服务器端仅创建自身的ObjectInputStream,未先初始化ObjectOutputStream并发送头部数据,导致客户端输入流一直等待该数据,陷入阻塞。
解决方案
- 服务器端优先创建ObjectOutputStream并flush:在客户端输入流初始化前,服务器需先完成输出流的创建与刷新,确保序列化头部发送至客户端。
- Message类必须实现Serializable接口:这是对象序列化传输的强制要求,否则会触发序列化异常。
修正后的关键代码
服务器端ChatServerConnector类修正
class ChatServerConnector extends Thread { private ChatServer server; private Socket socket; private ObjectInputStream objectInputStream; private ObjectOutputStream objectOutputStream; private Message message; public ChatServerConnector(ChatServer server, Socket socket) { this.server = server; this.socket = socket; } public void run() { try { // 先创建输出流并刷新,发送序列化头部 objectOutputStream = new ObjectOutputStream(this.socket.getOutputStream()); objectOutputStream.flush(); // 再创建输入流 objectInputStream = new ObjectInputStream(this.socket.getInputStream()); System.out.println("[system] connected with " + this.socket.getInetAddress().getHostName() + ":" + this.socket.getPort()); } catch (IOException e) { System.err.println("[system] could not open streams!"); e.printStackTrace(); return; } while (true) { try { message = (Message)objectInputStream.readObject(); } catch (Exception e) { System.err.println("[system] failed to read message"); e.printStackTrace(); break; } switch(message.type) { case "public": try { this.server.sendToAllClients(message); } catch (Exception e) { e.printStackTrace(); } break; default: // 处理其他类型消息 break; } } // 资源清理 try { objectInputStream.close(); objectOutputStream.close(); socket.close(); server.removeClient(socket); } catch (IOException e) { e.printStackTrace(); } } }
Message类补充Serializable接口
import java.io.Serializable; class Message implements Serializable { private static final long serialVersionUID = 1L; // 显式声明序列化版本号避免兼容性问题 public String type; public String sender; public String reciever; public long time; public String message; // 构造函数、getter/setter等逻辑 }
额外注意事项
- 客户端代码中
sendMessage方法调用的tmp变量未定义,需补充终端输入读取逻辑,避免空指针异常。 - 服务器端
sendToAllClients方法需遍历客户端列表,为每个客户端复用或创建输出流发送消息,注意线程同步。
内容的提问来源于stack exchange,提问作者Janez Kranjski
相关产品推荐
相关产品推荐

