ObjectOutputStream无报错停止工作——视频传输广播异常求助
视频传输服务器广播停止问题分析与修复方案
针对你的视频传输服务器接收正常但几次迭代后停止广播的问题,结合代码细节,核心问题集中在线程池资源耗尽、Socket输出阻塞未处理这两个关键点,以下是具体分析和修复方案:
1. 无界线程池导致资源耗尽
你使用的Executors.newCachedThreadPool()是无界线程池,每收到一帧就给每个客户端提交一个异步任务。按75ms一帧、单房间多客户端的频率计算,短时间内会创建大量线程,操作系统线程数上限被打满后,新任务无法执行,直接导致广播停止。
修复方案:
改用有界线程池,根据并发客户端数量设置合理的线程上限和任务队列:
// 替换原线程池初始化,设置核心线程数5、最大线程数20、任务队列容量100 private ExecutorService pool = new ThreadPoolExecutor( 5, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy() // 队列满时让调用线程执行,避免任务丢失 );
2. Socket输出阻塞未处理,线程挂起
clientObjectOutputStream.write(imageBytes)是阻塞操作:如果客户端网络卡顿、断开连接但服务器未清理,这个write()会一直阻塞,占用线程池中的线程。同时因为你用clientObjectOutputStream作为锁对象,其他针对该客户端的广播任务也会被挂起,最终线程池被耗尽,无法处理新的广播任务。
修复方案:
- 给Socket设置写入超时,避免无限阻塞:
在UserWithSocket构造方法中添加超时配置:
public UserWithSocket(int id, Socket userSocket) { super(); this.id = id; this.userSocket = userSocket; try { userSocket.setSoTimeout(5000); // 设置5秒写入超时 } catch (IOException e) { e.printStackTrace(); } }
- 增加失效客户端清理逻辑:
在广播任务的异常捕获块中,移除失效客户端并关闭资源:
pool.submit(() -> { try { ObjectOutputStream clientObjectOutputStream = client.getClientObjectOutputStream(); synchronized (clientObjectOutputStream) { clientObjectOutputStream.write(imageBytes); clientObjectOutputStream.flush(); } System.out.println("Frame Broadcasted"); } catch (Exception e) { e.printStackTrace(); // 移除失效客户端 videoChatRoomClients.get(roomId).remove(client); // 关闭关联资源 try { client.getClientObjectOutputStream().close(); client.getUserSocket().close(); } catch (IOException ex) { ex.printStackTrace(); } } });
3. 同步锁粒度优化
你用clientObjectOutputStream作为锁对象,虽然能保证单个客户端的写入顺序,但锁竞争可能随客户端数量增加而加剧。可以调整锁粒度到UserWithSocket对象本身,简化锁逻辑:
// 改为同步UserWithSocket实例 synchronized (client) { ObjectOutputStream oos = client.getClientObjectOutputStream(); oos.write(imageBytes); oos.flush(); }
4. 发送端流状态验证
确保发送端的objectOutputVideoStream是全局唯一且持续打开的,每次发送后的flush()操作正常执行,没有被意外关闭或重置。
内容的提问来源于stack exchange,提问作者Bottas
相关产品推荐
相关产品推荐

