Socket传输字符串时遇java.io.StreamCorruptedException问题排查
解决
java.io.StreamCorruptedException: invalid type code: 00问题 嘿,我来帮你搞定这个烦人的流异常!这个错误本质上是对象流的读写状态被破坏了,结合你的代码和业务场景,咱们来一步步拆解问题根源和解决方案:
问题根源分析
你遇到的invalid type code: 00通常是以下几个原因导致的,对应你的代码来看:
- 静态流变量导致多实例冲突
你的inputStream和outputStream是static修饰的,这意味着所有Client实例会共享同一个流引用。如果有多个客户端连接,或者连接断开重连后,旧的流和新的流会互相干扰,导致读写的字节序列混乱。 - 多线程并发读写同一个流
你在dataListener(线程池线程)和updateLocalChain(单线程池线程)两个地方同时调用inputStream.readObject(),而ObjectInputStream本身不是线程安全的!两个线程抢着读流,会把流内部的字节指针搞乱,读到不完整或错误的类型标识(也就是报错里的00)。 - 流的初始化顺序可能有问题
没看到你初始化流的代码,但如果先创建ObjectInputStream再创建ObjectOutputStream,会因为ObjectOutputStream需要先写入一个头部字节,读流的时候找不到这个头部而抛出异常。
针对性解决方案
1. 把流变量改成实例变量
去掉inputStream和outputStream的static修饰符,让每个Client实例拥有自己独立的流:
// 去掉static修饰符 private ObjectInputStream inputStream = null; private ObjectOutputStream outputStream = null;
2. 确保流的初始化顺序正确
在建立Socket连接后,必须先创建并flushObjectOutputStream,再创建ObjectInputStream,这样能保证流的头部信息被正确发送:
// 假设在连接建立的方法里初始化流 private void initStreams(Socket socket) throws IOException { // 先创建输出流并flush头部 outputStream = new ObjectOutputStream(socket.getOutputStream()); outputStream.flush(); // 再创建输入流 inputStream = new ObjectInputStream(socket.getInputStream()); isConnected = true; }
3. 用单线程统一处理流读取,避免并发冲突
不要让多个线程直接操作输入流,而是用一个专门的线程负责读取所有输入,把读到的数据放到线程安全的队列里,其他线程从队列取数据处理:
// 新增一个线程安全的消息队列 private BlockingQueue<String> incomingChainQueue = new LinkedBlockingQueue<>(); // 启动专门的读线程(在Client构造或连接建立后调用) private void startSingleReaderThread() { cachedExecutor.submit(() -> { try { while (isConnected) { // 统一在这里读流 String chainJson = (String) inputStream.readObject(); incomingChainQueue.put(chainJson); } } catch (Exception e) { // 处理连接断开等异常 System.err.println("读取流异常: " + e.getMessage()); isConnected = false; } }); } // 修改updateLocalChain从队列取数据,而不是直接读流 private BloomChain updateLocalChain() throws InterruptedException { String newChainJson = incomingChainQueue.take(); return gson.fromJson(newChainJson, BloomChain.class); } // 修改dataListener也从队列取数据 public String dataListener() throws InterruptedException { String chainJson = incomingChainQueue.take(); BloomChain chain = gson.fromJson(chainJson, BloomChain.class); return mineMessage(chain); }
4. 清理业务逻辑中的重复读操作
你的sendMessage方法里调用updateLocalChain等待peer返回,同时dataListener又在持续监听,这会导致同一响应被两个线程争抢读取。用队列统一管理后,就能避免这种冲突——不管是主动请求的响应还是被动接收的消息,都会被放到队列里,按顺序处理。
额外检查点
- 确保Socket连接断开时,正确关闭
inputStream、outputStream和Socket,避免残留的流资源干扰后续连接。 - 验证peer端发送的确实是
String类型的JSON数据,和你这边readObject()读取的类型一致,不要出现一端发对象、一端读字符串的情况。
内容的提问来源于stack exchange,提问作者Adrian Coutsoftides
相关产品推荐
相关产品推荐

