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

客户端与服务器均创建ObjectInputStream时初始化阻塞问题排查

ObjectInputStream/ObjectOutputStream 结合分组密码流时的阻塞问题及解决办法

问题现象

当仅在客户端创建ObjectInputStream、服务器端创建ObjectOutputStream时,初始化可正常完成;但同时在服务器端创建ObjectInputStream时,程序会陷入无限阻塞。尝试提前初始化所有流也无法解决该问题。

复现代码

服务器端代码

package Server;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class SimpleServer {
    public static void main(String[] args) throws Exception {
        int port = 5000;
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("服务器已启动,等待连接...");
        Socket clientSocket = serverSocket.accept();
        System.out.println("客户端已连接。");

        // 硬编码的密钥和IV
        byte[] keyBytes = "1234567890123456".getBytes();
        byte[] ivBytes = "1234567890123456".getBytes();
        SecretKey key = new SecretKeySpec(keyBytes, "AES");
        IvParameterSpec iv = new IvParameterSpec(ivBytes);

        // 初始化密码器和流
        Cipher cipher_enc = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher_enc.init(Cipher.ENCRYPT_MODE, key, iv);
        Cipher cipher_dec = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher_dec.init(Cipher.DECRYPT_MODE, key, iv);
        CipherOutputStream cipher_encOut = new CipherOutputStream(clientSocket.getOutputStream(), cipher_enc);
        CipherInputStream cipher_decIn = new CipherInputStream(clientSocket.getInputStream(), cipher_dec);

        System.out.println("密码器已初始化。");
        ObjectOutputStream objOut = new ObjectOutputStream(cipher_encOut);
        System.out.println("ObjectOutputStream已初始化。");
        objOut.writeObject("来自服务器的问候!");
        objOut.flush();

        // 移除该代码块可使程序正常运行
        ObjectInputStream objIn = new ObjectInputStream(cipher_decIn);
        String message = (String) objIn.readObject();
        System.out.println("来自客户端的消息:" + message);

        objOut.close();
        objIn.close();
        clientSocket.close();
        serverSocket.close();
        System.out.println("服务器已结束运行。");
    }
}

客户端代码

package Server;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.Socket;

public class SimpleClient {
    public static void main(String[] args) throws Exception {
        String host = "localhost";
        int port = 5000;
        Socket socket = new Socket(host, port);
        System.out.println("已连接到服务器。");

        // 硬编码的密钥和IV
        byte[] keyBytes = "1234567890123456".getBytes();
        byte[] ivBytes = "1234567890123456".getBytes();
        SecretKey key = new SecretKeySpec(keyBytes, "AES");
        IvParameterSpec iv = new IvParameterSpec(ivBytes);

        // 初始化密码器和流
        Cipher cipher_enc = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher_enc.init(Cipher.ENCRYPT_MODE, key, iv);
        Cipher cipher_dec = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher_dec.init(Cipher.DECRYPT_MODE, key, iv);
        CipherInputStream cipher_decIn = new CipherInputStream(socket.getInputStream(), cipher_dec);
        CipherOutputStream cipher_encOut = new CipherOutputStream(socket.getOutputStream(), cipher_enc);

        System.out.println("密码器已初始化。");
        ObjectInputStream objIn = new ObjectInputStream(cipher_decIn);
        System.out.println("ObjectInputStream已初始化。");
        String message = (String) objIn.readObject();      
        System.out.println("来自服务器的消息:" + message);


        // 移除该代码块可使程序正常运行
        ObjectOutputStream objOut = new ObjectOutputStream(cipher_encOut);
        objOut.writeObject("来自客户端的问候!");
        objOut.flush();

        objIn.close();
        objOut.close();
        socket.close();
        System.out.println("客户端已断开连接。");
    }
}

问题原因

ObjectOutputStream初始化时会写入固定的序列化协议头部,而分组密码(如AES/CBC/PKCS5Padding)对应的CipherOutputStream会缓存数据,直到凑够一个完整的加密块才会发送数据。当头部长度不足一个加密块时,数据会被滞留在缓存中,导致对端的ObjectInputStream一直等待完整的头部,最终引发无限阻塞。此问题与常规的ObjectInputStream阻塞原因(如未发送数据、流未刷新)无关。

解决方案

将分组密码模式替换为流密码模式,例如使用AES/CTR/NoPadding。流密码逐字节处理数据,不会缓存数据等待凑块,能确保ObjectOutputStream的头部被及时发送,避免阻塞。

内容的提问来源于stack exchange,提问作者SmoothBren

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 21:24:55