退出对话时保证资源销毁后重连websocket。减少死机风险。 3102/1 3102/head
author孙春阳 <snickersun@163.com>
Sat, 17 Jan 2026 04:10:52 +0000 (12:10 +0800)
committer孙春阳 <snickersun@163.com>
Sat, 17 Jan 2026 04:10:52 +0000 (12:10 +0800)
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c

index 1a98f6f..e9e9071 100644 (file)
@@ -1851,6 +1851,9 @@ int ln_ws_send_voice_chat_interrupt(const char *reason)
         return -1;
     }
     
+    // 检查是否是退出对话场景
+    bool is_exit_dialogue = (strcmp(reason, "退出对话") == 0);
+    
     // 检查是否需要在1秒内有数据时触发重连
     bool need_reconnect = ln_ws_check_binary_data_interrupt();
     
@@ -1899,24 +1902,64 @@ int ln_ws_send_voice_chat_interrupt(const char *reason)
     LN_WS_DEBUG("Sending VoiceChatInterrupt: %s\n", json_string);
     
     // 发送消息
+    int ret = uapi_ws_send_text(json_string);
+    if (ret == 0) {
+        LN_WS_DEBUG("VoiceChatInterrupt sent successfully\n");
+    }
     
     // 清理资源
-    int ret=0;
-    if (need_reconnect ||  (g_audio_chat_ctx.finish_state == LN_CHAT_SENT_END_NO_REPLY)) 
-    {
-        ret = uapi_ws_send_text(json_string);
-        if (ret == 0) {
-            LN_WS_DEBUG("VoiceChatInterrupt sent successfully\n");
-        }
-        // 如果检测到频繁数据接收,直接重连
-        LN_WS_INFO("Triggering reconnection due to frequent binary data reception\n");
-        if (ln_ws_reconnect() != 0) {
-            LN_WS_ERROR("Failed to reconnect after frequent data reception\n");
+    // 如果需要重连
+    if (need_reconnect || (g_audio_chat_ctx.finish_state == LN_CHAT_SENT_END_NO_REPLY)) {
+        if (is_exit_dialogue) {
+            // 只有退出对话场景,才等待资源清理完成后再重连
+            // 资源清理(AUDIO_DEINIT)通过消息队列异步执行,需要等待清理完成
+            LN_WS_INFO("Exit dialogue mode, waiting for resource cleanup before reconnection\n");
+            
+            // 轮询检查资源清理完成标志,最多等待500ms
+            const uint32_t max_wait_ms = 500;
+            const uint32_t check_interval_ms = 20; // 每20ms检查一次
+            uint32_t waited_ms = 0;
+            bool cleanup_complete = false;
+            
+            while (waited_ms < max_wait_ms) {
+                // 检查音频是否已反初始化(资源清理完成的标志)
+                if (!ln_ws_is_audio_initialized()) {
+                    cleanup_complete = true;
+                    LN_WS_INFO("Resource cleanup completed after %u ms\n", waited_ms);
+                    break;
+                }
+                LOS_Msleep(check_interval_ms);
+                waited_ms += check_interval_ms;
+            }
+            
+            if (!cleanup_complete) {
+                LN_WS_WARN("Resource cleanup timeout after %u ms, proceeding with reconnection anyway\n", max_wait_ms);
+            }
+            
+            // 再次检查连接状态,确保在等待期间连接状态未改变
+            if (g_ws_connected == WS_STATE_CONNECTED) {
+                LN_WS_INFO("Triggering reconnection after exit dialogue (resource cleanup %s)\n", 
+                          cleanup_complete ? "completed" : "timeout");
+                if (ln_ws_reconnect() != 0) {
+                    LN_WS_ERROR("Failed to reconnect after exit dialogue\n");
+                }
+            } else {
+                LN_WS_WARN("Connection state changed during wait, skip reconnection: %d\n", g_ws_connected);
+            }
+        } else {
+            // 非退出对话场景(如二进制数据间隔检查触发的重连),直接重连
+            // WebSocket重连是网络层操作,与音频资源无关
+            if (g_ws_connected == WS_STATE_CONNECTED) {
+                LN_WS_INFO("Triggering reconnection %s\n", 
+                          need_reconnect ? "due to binary data interrupt" : "due to chat finish state");
+                if (ln_ws_reconnect() != 0) {
+                    LN_WS_ERROR("Failed to reconnect\n");
+                }
+            } else {
+                LN_WS_WARN("Connection state changed, skip reconnection: %d\n", g_ws_connected);
+            }
         }
     }
-    else {
-        LN_WS_INFO("no to send VoiceChatInterrupt\n");
-    }
     cJSON_free(json_string);
     cJSON_Delete(json);
 
@@ -2409,6 +2452,16 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms
     if (data_msg->command == WS_CMD_RANDOM_STORY_RESPONSE) {
         cJSON *data_obj = cJSON_GetObjectItem(root, "data");
         if (data_obj && cJSON_IsObject(data_obj)) {
+            // 根据协议,先检查errorCode字段(在data对象中,不在data.data中)
+            cJSON *error_code = cJSON_GetObjectItem(data_obj, "errorCode");
+            if (error_code && !cJSON_IsNull(error_code)) {
+                // 如果errorCode不为null,表示发生了错误
+                // 根据协议,错误响应中会包含data字段,其中包含错误信息和音频URL
+                LN_WS_WARN("RandomStoryResponse error: errorCode=%s\n", 
+                       cJSON_IsString(error_code) ? error_code->valuestring : "unknown");
+                // 错误响应中仍然会包含data字段,继续解析以便播放错误音频
+            }
+            
             // 解析finished字段(先解析到data_msg,但不更新全局上下文)
             cJSON *finished = cJSON_GetObjectItem(data_obj, "finished");
             if (finished && cJSON_IsBool(finished)) {
@@ -2437,7 +2490,7 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms
                 }
             }
             
-            // 解析data.data字段
+            // 解析data.data字段(根据协议,即使有错误,data字段也可能存在,包含错误信息和音频URL)
             cJSON *data_data = cJSON_GetObjectItem(data_obj, "data");
             if (data_data && cJSON_IsObject(data_data)) {
                 // 解析storyTheme字段,存储到text_content中