diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/ssh/websocket/AbstractSSHWebSocketHandler.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/ssh/websocket/AbstractSSHWebSocketHandler.java index ff351654..10990d46 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/ssh/websocket/AbstractSSHWebSocketHandler.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/ssh/websocket/AbstractSSHWebSocketHandler.java @@ -713,6 +713,9 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler { /** * 发送输出消息到前端(使用强类型Response) * + *

⚠️ 线程安全:使用synchronized同步,防止stdout和stderr线程并发写入导致 + * {@code IllegalStateException: TEXT_PARTIAL_WRITING} 错误 + * * @param session WebSocket会话 * @param output 输出内容 */ @@ -734,7 +737,13 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler { // 创建消息 SSHWebSocketMessage msg = new SSHWebSocketMessage(SSHMessageType.OUTPUT, data); - session.sendMessage(new TextMessage(JsonUtils.toJson(msg))); + + // ⚠️ 关键:同步发送,防止并发写入导致TEXT_PARTIAL_WRITING错误 + synchronized (session) { + if (session.isOpen()) { + session.sendMessage(new TextMessage(JsonUtils.toJson(msg))); + } + } } catch (IOException e) { log.error("发送输出消息失败(IOException): sessionId={}", sessionId, e); } catch (Exception e) { @@ -744,6 +753,8 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler { /** * 发送状态消息到前端(使用强类型Response) + * + *

⚠️ 线程安全:使用synchronized同步,防止并发写入 */ protected void sendStatus(WebSocketSession session, SSHStatusEnum status) { try { @@ -761,7 +772,13 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler { // 创建消息 SSHWebSocketMessage msg = new SSHWebSocketMessage(SSHMessageType.STATUS, data); - session.sendMessage(new TextMessage(JsonUtils.toJson(msg))); + + // ⚠️ 同步发送,防止并发写入 + synchronized (session) { + if (session.isOpen()) { + session.sendMessage(new TextMessage(JsonUtils.toJson(msg))); + } + } } catch (IOException e) { log.error("发送状态消息失败: sessionId={}", session.getId(), e); } @@ -769,6 +786,8 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler { /** * 发送错误消息到前端(使用强类型Response) + * + *

⚠️ 线程安全:使用synchronized同步,防止并发写入 */ protected void sendError(WebSocketSession session, String error) { try { @@ -786,7 +805,13 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler { // 创建消息 SSHWebSocketMessage msg = new SSHWebSocketMessage(SSHMessageType.ERROR, data); - session.sendMessage(new TextMessage(JsonUtils.toJson(msg))); + + // ⚠️ 同步发送,防止并发写入 + synchronized (session) { + if (session.isOpen()) { + session.sendMessage(new TextMessage(JsonUtils.toJson(msg))); + } + } } catch (IOException e) { if (session.isOpen()) { log.error("发送错误消息失败: sessionId={}", session.getId(), e);