Java中多ObjectInputStream/ObjectOutputStream初始化阻塞问题求助
问题:ObjectInputStream初始化导致Socket通信阻塞
我正在开发一个GameServer与三个GameClient之间的I/O通信程序。服务器接受三个客户端连接后,启动单独的HandlePlayer线程,该线程负责与三个客户端建立ObjectInputStream和ObjectOutputStream,但程序在此处出现阻塞(未终止,但无法执行后续代码)。调试发现,服务器能正常接受三个Socket连接,但调用HandlePlayer后,程序卡在fromPlayer1 = new ObjectInputStream(player1.getInputStream());行无法继续。将所有Object流替换为DataInputStream和DataOutputStream后,程序可正常运行,想了解其中原因。
相关核心代码
GameServer
public class GameServer implements Runnable, ShareData { public void run() { try { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("server start at port " + PORT); // wait for players to connect while (true) { Socket player1 = serverSocket.accept(); System.out.println("player 1: " + player1.getInetAddress().getHostAddress()); Socket player2 = serverSocket.accept(); // connect to player 2 System.out.println("player 2: " + player2.getInetAddress().getHostAddress()); Socket player3 = serverSocket.accept(); // connect to player 3 System.out.println("player 3: " + player3.getInetAddress().getHostAddress()); // start the game new Thread(new HandlePlayer(player1, player2, player3)).start(); } } catch (IOException ex) { ex.printStackTrace(); } } public static void main(String[] args) { Thread server = new Thread(new GameServer()); server.start(); } }
HandlePlayer
public class HandlePlayer implements Runnable, ShareData { // three players private Socket player1; private Socket player2; private Socket player3; // communication with players ObjectInputStream fromPlayer1; ObjectOutputStream toPlayer1; ObjectInputStream fromPlayer2; ObjectOutputStream toPlayer2; ObjectInputStream fromPlayer3; ObjectOutputStream toPlayer3; public HandlePlayer(Socket player1, Socket player2, Socket player3) { this.player1 = player1; this.player2 = player2; this.player3 = player3; } // start the game public void run() { try { // initialize communication connections fromPlayer1 = new ObjectInputStream(player1.getInputStream()); toPlayer1 = new ObjectOutputStream(player1.getOutputStream()); fromPlayer2 = new ObjectInputStream(player2.getInputStream()); toPlayer2 = new ObjectOutputStream(player2.getOutputStream()); fromPlayer3 = new ObjectInputStream(player3.getInputStream()); toPlayer3 = new ObjectOutputStream(player3.getOutputStream()); // receive robot selections char player1Robot = fromPlayer1.readChar(); char player2Robot = fromPlayer2.readChar(); char player3Robot = fromPlayer3.readChar(); // the game loop } catch (IOException e) { e.printStackTrace(); } } }
GameClient
public class GameClient implements Runnable, ShareData { private ObjectInputStream fromServer; private ObjectOutputStream toServer; private Scanner input = new Scanner(System.in); private String host = "localhost"; // connect to server private void connectToServer() { try { Socket socket = new Socket(host, PORT); fromServer = new ObjectInputStream(socket.getInputStream()); toServer = new ObjectOutputStream(socket.getOutputStream()); } catch (Exception ex) { ex.printStackTrace(); } } public void run() { try { connectToServer(); } catch (Exception e) { e.printStackTrace(); } } }
原因分析
1. ObjectInputStream的初始化阻塞特性
创建ObjectInputStream实例时,它会立即尝试从底层输入流读取序列化协议的魔术头(固定为0xACED),这个操作是阻塞的,必须等到另一端的ObjectOutputStream完成初始化并写入了这个魔术头,ObjectInputStream的构造才会成功返回。
你的代码中存在双向阻塞:
- 服务器端先创建
fromPlayer1 = new ObjectInputStream(...),等待客户端写入魔术头 - 客户端先创建
fromServer = new ObjectInputStream(socket.getInputStream());,等待服务器写入魔术头
双方都卡在初始化步骤,形成死锁。
2. DataInputStream的无初始化读取特性
DataInputStream的构造仅做流包装,不会触发任何读取操作,只有当调用readChar()、readInt()等具体读取方法时,才会从底层流获取数据。因此初始化阶段不会产生阻塞,只要两端读写逻辑对应,就能正常运行。
3. 解决方法:调整Object流初始化顺序
通信两端必须遵循先创建ObjectOutputStream并刷新,再创建ObjectInputStream的顺序:
- 客户端修改:
private void connectToServer() { try { Socket socket = new Socket(host, PORT); // 先创建输出流并刷新 toServer = new ObjectOutputStream(socket.getOutputStream()); toServer.flush(); // 再创建输入流 fromServer = new ObjectInputStream(socket.getInputStream()); } catch (Exception ex) { ex.printStackTrace(); } }
- 服务器端HandlePlayer修改:
// initialize communication connections toPlayer1 = new ObjectOutputStream(player1.getOutputStream()); toPlayer1.flush(); fromPlayer1 = new ObjectInputStream(player1.getInputStream()); toPlayer2 = new ObjectOutputStream(player2.getOutputStream()); toPlayer2.flush(); fromPlayer2 = new ObjectInputStream(player2.getInputStream()); toPlayer3 = new ObjectOutputStream(player3.getOutputStream()); toPlayer3.flush(); fromPlayer3 = new ObjectInputStream(player3.getInputStream());
内容的提问来源于stack exchange,提问作者Raine-Yang
相关产品推荐
相关产品推荐

