You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 08:05:24