增加ping 超时检验
authorhcj <1224667075@qq.com>
Tue, 13 Jan 2026 11:17:40 +0000 (19:17 +0800)
committerhcj <1224667075@qq.com>
Tue, 13 Jan 2026 11:17:40 +0000 (19:17 +0800)
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c
code/sdk/zhihui/websocket/os_ws/haisi_websocket.c
code/sdk/zhihui/websocket/os_ws/haisi_websocket.h

index cd39da2..8b30e79 100644 (file)
@@ -118,6 +118,11 @@ static volatile bool g_vad_server_auth_success = false;
 /* 回调函数结构体 */
 static ws_call_backs_t g_ws_callbacks = {0};
 
+/* Ping 超时检测相关变量 */
+static uint32_t g_ping_send_tick = 0;  /* Ping 发送时间(tick) */
+static bool g_ping_timeout_active = false;  /* Ping 超时检测是否激活 */
+#define LN_WS_PING_TIMEOUT_MS 3000  /* Ping 超时时间(毫秒),默认3秒 */
+
 static void ln_ws_build_url(char *out, size_t out_size);
 int ln_ws_send_ping(void);
 static ws_command_t ln_ws_parse_command_string(const char *command_str);
@@ -272,6 +277,9 @@ static void ln_ws_on_open(int client)
     (void)client;
     LN_WS_DEBUG("WebSocketClientConstructor ln_ws_on_open\r\n");
     g_ws_connected = WS_STATE_CONNECTED;
+    // 重置 ping 超时状态
+    g_ping_timeout_active = false;
+    g_ping_send_tick = 0;
     LN_WS_DEBUG("连接状态: 连接成功\n");
 }
 
@@ -281,6 +289,12 @@ static void ln_ws_message_callback(const char *raw_data, uint32_t timestamp)
     (void)timestamp;
     if (raw_data == NULL) return;
     
+    // 收到任何消息时,清除 ping 超时检测
+    if (g_ping_timeout_active) {
+        g_ping_timeout_active = false;
+        LN_WS_DEBUG("Ping timeout detection cancelled due to received message\n");
+    }
+    
     size_t length = strlen(raw_data);
     char *buf = (char *)malloc(length + 1);
     if (!buf) return;
@@ -326,6 +340,9 @@ static void ln_ws_event_callback(ws_event_type_t event_type)
         case WS_EVENT_CONNECTED:
             LN_WS_INFO("Event: Connected\n");
             g_ws_connected = WS_STATE_CONNECTED;
+            // 重置 ping 超时状态
+            g_ping_timeout_active = false;
+            g_ping_send_tick = 0;
             
             // 触发连接成功事件
             if (g_ws_message_callback != NULL) {
@@ -335,6 +352,9 @@ static void ln_ws_event_callback(ws_event_type_t event_type)
         case WS_EVENT_DISCONNECTED:
             LN_WS_INFO("Event: Disconnected\n");
             g_ws_connected = WS_STATE_DISCONNECTED;
+            // 重置 ping 超时状态
+            g_ping_timeout_active = false;
+            g_ping_send_tick = 0;
             
             // 触发断连事件
             if (g_ws_message_callback != NULL) {
@@ -418,6 +438,29 @@ static int ln_ws_heartbeat_callback(void)
         check_playback_completion_by_timeout();
     }
     
+    // 检查检活 ping 超时(仅在检活接口调用后检查)
+    if (g_ping_timeout_active && g_ws_connected == WS_STATE_CONNECTED) {
+        uint32_t current_tick = osKernelGetTickCount();
+        uint32_t elapsed_ticks = current_tick - g_ping_send_tick;
+        uint32_t tick_freq = osKernelGetTickFreq(); // 获取 tick 频率(tick/秒)
+        uint32_t timeout_ticks = (LN_WS_PING_TIMEOUT_MS * tick_freq) / 1000; // 将毫秒转换为 tick 数
+        
+        if (elapsed_ticks >= timeout_ticks) {
+            uint32_t elapsed_ms = (elapsed_ticks * 1000) / tick_freq; // 转换为毫秒用于日志
+            LN_WS_ERROR("Keepalive ping timeout detected: elapsed %u ms (timeout: %u ms), triggering reconnection\n", 
+                       elapsed_ms, LN_WS_PING_TIMEOUT_MS);
+            
+            // 清除超时状态
+            g_ping_timeout_active = false;
+            g_ping_send_tick = 0;
+            
+            // 触发重连
+            if (ln_ws_reconnect() != 0) {
+                LN_WS_ERROR("Failed to reconnect after keepalive ping timeout\n");
+            }
+        }
+    }
+    
     if (heartbeat_counter >= LN_WS_HEARTBEAT_COUNTER_THRESHOLD) { // 30秒心跳间隔
         heartbeat_counter = 0;
         LN_WS_DEBUG("Sending heartbeat (interval: %u ms, threshold: %u)\n", 
@@ -429,6 +472,11 @@ static int ln_ws_heartbeat_callback(void)
 }
 
 
+/**
+ * @brief 发送ping并启动检活超时检测
+ * @return 0成功,-1失败
+ * @note 调用此接口后,如果超时(默认3秒)未收到任何消息,将触发重连
+ */
 int ln_ws_send_ping(void)
 {
     if (g_ws_connected != WS_STATE_CONNECTED) {
@@ -465,7 +513,15 @@ int ln_ws_send_ping(void)
     // 发送 JSON 字符串
     int ret = uapi_ws_send_text(json_string);
     
-    LN_WS_DEBUG("Sent ping: %s\n", json_string);
+    if (ret == 0) {
+        // 发送成功,记录发送时间并启动超时检测
+        g_ping_send_tick = osKernelGetTickCount();
+        g_ping_timeout_active = true;
+        LN_WS_DEBUG("Sent ping: %s, keepalive timeout detection activated (timeout: %u ms)\n", 
+                   json_string, LN_WS_PING_TIMEOUT_MS);
+    } else {
+        LN_WS_ERROR("Failed to send ping: %s\n", json_string);
+    }
     
     // 清理内存
     cJSON_free(json_string);
@@ -1054,6 +1110,12 @@ static void ai_binary_message_handler(const uint8_t *raw_data, uint32_t timestam
 {
     LN_WS_INFO("Received binary message at %u, length: %d bytes\n", timestamp, data_len);
     
+    // 收到二进制消息时,清除 ping 超时检测
+    if (g_ping_timeout_active) {
+        g_ping_timeout_active = false;
+        LN_WS_DEBUG("Ping timeout detection cancelled due to received binary message\n");
+    }
+    
     // 记录接收数据的时间
     g_last_binary_data_tick = osKernelGetTickCount();
     g_vad_cooldown_active = false;
@@ -1339,6 +1401,7 @@ void vad_status_callback_ln_ws(int status)
 
 void ln_ws_audio_init(bool is_vad_server_enabled)
 {
+    // is_vad_server_enabled=false;
     // 检查是否已经初始化
     if (g_audio_initialized) {
         LN_WS_WARN("Audio already initialized, skipping duplicate initialization\n");
@@ -3110,6 +3173,9 @@ static void ln_ws_reconnect_success_callback(void)
     LN_WS_INFO("WebSocket reconnection successful, triggering reconnect success event\n");
     
     g_ws_connected = WS_STATE_CONNECTED;
+    // 重置 ping 超时状态
+    g_ping_timeout_active = false;
+    g_ping_send_tick = 0;
 
     if(strlen(story_theme_cache)>3)
     {
index 5abe331..51cded0 100644 (file)
@@ -311,10 +311,16 @@ static void uapi_ws_on_error(int client, struct WebSocketErrorResult error)
         last_error.errorMessage=NULL;
     }
     last_error.errorCode=error.errorCode;
-    last_error.errorMessage=malloc(strlen(error.errorMessage));
-    memcpy(last_error.errorMessage,error.errorMessage,strlen(error.errorMessage));
-
+    last_error.errorMessage=malloc(strlen(error.errorMessage) + 1);
+    memcpy(last_error.errorMessage, error.errorMessage, strlen(error.errorMessage));
     HAISI_WS_ERROR("WebSocketClientConstructor error: code=%d msg=%s\n", error.errorCode, error.errorMessage);
+    
+    // 触发错误回调
+    if (g_ws_callbacks.error_callback != NULL) {
+        g_ws_callbacks.error_callback(error.errorCode, error.errorMessage);
+    } else {
+        HAISI_WS_DEBUG("No error callback registered\n");
+    }
 }
 
 /* 断开回调:标记为未连接。是否自动重连交由上层决定 */
@@ -571,6 +577,8 @@ int last_connection_state = WS_STATE_DISCONNECTED;  // 记录上次连接状态
             {
                 ret= WebSocketClientConnect(g_ws_client_fd, url, header_buf);
             }
+
+          
            
             // int ret = WebSocketClientConnect(g_ws_client_fd,"ws://118.145.158.207:22080/chat/ws?platform=huoshan");
            
@@ -592,6 +600,7 @@ int last_connection_state = WS_STATE_DISCONNECTED;  // 记录上次连接状态
             else
             {
                 g_ws_connected = WS_STATE_CONNECTING;
+                g_ws_close_status = WS_CLOSE_NOT_EXECUTED;
             }
             /* 连接请求已发送,等待握手完成 */
             HAISI_WS_DEBUG("连接请求已发送,等待握手完成...\n");
@@ -790,6 +799,12 @@ int uapi_ws_start(const ws_call_backs_t *callbacks)
         } else {
             HAISI_WS_WARN("No event callback registered\n");
         }
+        
+        if (g_ws_callbacks.error_callback != NULL) {
+            HAISI_WS_DEBUG("Error callback registered\n");
+        } else {
+            HAISI_WS_DEBUG("No error callback registered\n");
+        }
     } else {
         memset(&g_ws_callbacks, 0, sizeof(ws_call_backs_t));
         HAISI_WS_DEBUG("No callbacks provided, using defaults\n");
@@ -1394,6 +1409,20 @@ void uapi_ws_register_reconnect_success_callback(ws_reconnect_success_callback_t
     }
 }
 
+/**
+ * @brief 注册错误回调函数(公共接口)
+ * @param callback 回调函数
+ */
+void uapi_ws_register_error_callback(ws_error_callback_t callback)
+{
+    g_ws_callbacks.error_callback = callback;
+    if (callback != NULL) {
+        HAISI_WS_DEBUG("Error callback registered successfully\n");
+    } else {
+        HAISI_WS_DEBUG("Error callback unregistered\n");
+    }
+}
+
 /**
  * @brief 获取主线程循环间隔时长(公共接口)
  * @return 当前间隔时长(毫秒)
index 358d3ea..7011f59 100644 (file)
@@ -48,6 +48,9 @@ typedef void (*ws_event_callback_t)(ws_event_type_t event_type);
 /* 重连成功回调函数类型 */
 typedef void (*ws_reconnect_success_callback_t)(void);
 
+/* 错误回调函数类型 */
+typedef void (*ws_error_callback_t)(int errorCode, const char *errorMessage);
+
 /* WebSocket回调函数结构体 */
 typedef struct {
     ws_message_callback_t message_callback;         /* 文本消息处理回调函数 */
@@ -57,6 +60,7 @@ typedef struct {
     ws_heartbeat_callback_t heartbeat_callback;     /* 心跳处理回调函数 */
     ws_event_callback_t event_callback;            /* 连接事件回调函数 */
     ws_reconnect_success_callback_t reconnect_success_callback; /* 重连成功回调函数 */
+    ws_error_callback_t error_callback;            /* 错误回调函数 */
     uint32_t main_thread_interval_ms;              /* 主线程循环间隔时长(毫秒),默认1000ms */
 } ws_call_backs_t;
 
@@ -95,6 +99,7 @@ void uapi_ws_register_url_callback(ws_url_callback_t callback);
 void uapi_ws_register_heartbeat_callback(ws_heartbeat_callback_t callback);
 void uapi_ws_register_event_callback(ws_event_callback_t callback);
 void uapi_ws_register_reconnect_success_callback(ws_reconnect_success_callback_t callback);
+void uapi_ws_register_error_callback(ws_error_callback_t callback);
 
 #ifdef __cplusplus
 }