尝试使用AT指令替代 标准socket 目前为止很不稳定,URC数据解析问题还很突出。 2793/1 2793/head double_net_switch
author孙春阳 <snickersun@163.com>
Mon, 24 Nov 2025 03:06:05 +0000 (11:06 +0800)
committer孙春阳 <snickersun@163.com>
Mon, 24 Nov 2025 03:06:05 +0000 (11:06 +0800)
58 files changed:
code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdModel.cpp
code/sdk/build/script/parse_tool/config.py
code/sdk/middleware/chips/brandy/pm/include/pm_definition.h
code/sdk/middleware/chips/brandy/pm/pm.c
code/sdk/middleware/chips/brandy/pm/service/pm_sleep.c
code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_timeline_activities.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_token.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c
code/sdk/zhihui/leiniao/imsl_ln/ln_http_download.c [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/ln_http_download.h [new file with mode: 0644]
code/sdk/zhihui/leiniao/leiniao_python_test/at_socket.py [new file with mode: 0644]
code/sdk/zhihui/leiniao/leiniao_python_test/at_test_connect_tcp.py
code/sdk/zhihui/leiniao/leiniao_python_test/at_test_download_file.py
code/sdk/zhihui/leiniao/leiniao_python_test/at_test_get_device_token.py
code/sdk/zhihui/leiniao/leiniao_python_test/at_test_websocket.py [new file with mode: 0644]
code/sdk/zhihui/leiniao/leiniao_python_test/at_websocket_client.py [new file with mode: 0644]
code/sdk/zhihui/leiniao/leiniao_python_test/download_mp4_at.py [deleted file]
code/sdk/zhihui/leiniao/leiniao_python_test/get_device_token_at.py [deleted file]
code/sdk/zhihui/leiniao/leiniao_python_test/n706a_http_client.py
code/sdk/zhihui/leiniao/leiniao_python_test/n706a_tcp_client.py
code/sdk/zhihui/leiniao/leiniao_python_test/tcp_test_client.py [new file with mode: 0644]
code/sdk/zhihui/leiniao/leiniao_python_test/tcp_test_server.py
code/sdk/zhihui/n706a_net/AT_COMMAND_FIXES.md [new file with mode: 0644]
code/sdk/zhihui/n706a_net/CMakeLists.txt
code/sdk/zhihui/n706a_net/example/n706a_net_example.c
code/sdk/zhihui/n706a_net/include/n706a_at_handler.h
code/sdk/zhihui/n706a_net/include/n706a_http_client.h
code/sdk/zhihui/n706a_net/include/n706a_net_api.h
code/sdk/zhihui/n706a_net/include/n706a_tcp_client.h
code/sdk/zhihui/n706a_net/include/n706a_websocket.h
code/sdk/zhihui/n706a_net/include/ppp_config.h
code/sdk/zhihui/n706a_net/include/ppp_debug.h
code/sdk/zhihui/n706a_net/src/n706a_at_handler.c
code/sdk/zhihui/n706a_net/src/n706a_http_client.c
code/sdk/zhihui/n706a_net/src/n706a_net_api.c
code/sdk/zhihui/n706a_net/src/n706a_tcp_client.c
code/sdk/zhihui/n706a_net/src/n706a_websocket.c
code/sdk/zhihui/n706a_net/src/ppp_interrupt.c
code/sdk/zhihui/n706a_net/src/ppp_power_ctl.c
code/sdk/zhihui/n706a_net/src/ppp_serial.c
code/sdk/zhihui/ppp/doc/Neoway_功能模块TCP应用指南_V1.5.md [new file with mode: 0644]
code/sdk/zhihui/ppp/doc/Neoway_功能模块TCP应用指南_V1.5.pdf [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_config.h
code/sdk/zhihui/ppp/src/ppp_api.c
code/sdk/zhihui/ppp/src/ppp_at_handler.c
code/sdk/zhihui/utils/custom_utils.c
code/sdk/zhihui/utils/custom_utils.h
code/sdk/zhihui/websocket/websockets_py/src/websocket_console_server.py [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/include/zhihui_net.h
code/sdk/zhihui/zhihui_net/include/zhihui_net_http.h
code/sdk/zhihui/zhihui_net/src/zhihui_net_http.c
code/sdk/zhihui/zhihui_net/src/zhihui_net_websocket.c

index 8a5ef02..5a59235 100644 (file)
@@ -46,9 +46,9 @@
  #endif
 
  //最低视频帧率
- #define LOW_FREQUENCE 25
+ #define LOW_FREQUENCE 18
  //最高视频帧率
- #define HIGH_FREQUENCE 60
+ #define HIGH_FREQUENCE 30
  namespace OHOS
  {
      enum OTA_STATUS
index 70aaf5d..5936c12 100644 (file)
@@ -4,7 +4,7 @@
 \r
 LOSCFG_BASE_IPC_QUEUE_LIMIT = 50\r
 LOSCFG_BASE_CORE_SWTMR_LIMIT = 50\r
-LOSCFG_BASE_IPC_SEM_LIMIT = 30\r
+LOSCFG_BASE_IPC_SEM_LIMIT = 50\r
 LOSCFG_PLATFORM_HWI_LIMIT = 96\r
 configHWI_MAX_VECTOR_CNT = 152\r
 configQUEUE_REGISTRY_SIZE = 8\r
index dc8745e..6436793 100644 (file)
@@ -53,6 +53,7 @@ typedef enum {
     PM_ID_BP,//血压
     PM_ID_4G_AI,//4g AI
     PM_ID_GPS,//4g AI
+    PM_ID_NET_APP,//网络应用
 #ifdef SUPPORT_ALIPAY_SEC
     PM_ID_ALIPAY,//支付宝
 #endif
index 1ab7b5d..14c73c7 100644 (file)
@@ -243,13 +243,14 @@ const char* pm_id_name_arr[] ={
     "PM_ID_SPI_DMA",
     "PM_ID_I2C_DMA",
     "PM_ID_UART_DMA",
-    "PM_ID_HEART",//����������оƬ
-    "PM_ID_MOTOR",//����������
-    "PM_ID_GSENSOR",//���������ټ�
+    "PM_ID_HEART",//心率芯片
+    "PM_ID_MOTOR",//马达
+    "PM_ID_GSENSOR",//加速计
     "PM_ID_CAT1",//Cat1 communication
     "PM_ID_P_TEST",
-    "PM_ID_BUTTON_GPIO",//GPIO����
+    "PM_ID_BUTTON_GPIO",//GPIO按键
     "PM_ID_4G_AI",//AI投票
+    "PM_ID_NET_APP",//网络应用
 #ifdef SUPPORT_ALIPAY_SEC
     "PM_ID_ALIPAY",//支付宝
 #endif
index 0342888..e1abc2a 100644 (file)
@@ -237,12 +237,13 @@ void pm_enter_deep_sleep_only_ulp_wakeup(void)
 #endif
     if (status != 0) {
         pm_wkup_event_by_uart_callback();
+        #ifdef SUPPORT_CAT1_UART_WAKEUP
+        check_cat1_uart_wakeup();
+        #endif
     }
 #endif
 
-#ifdef SUPPORT_CAT1_UART_WAKEUP
-    check_cat1_uart_wakeup();
-#endif
+
     pm_wkup_prepare(PM_SLEEP_DS);
     pm_sleep_info_record(SLP_INFO_DEEPSLEEP);
     reg16_setbit(0x520003e4, 0x3);  // open mdam clock after deepsleep
@@ -298,9 +299,7 @@ static void pm_enter_deep_sleep(void)
     if (status != 0) {
         pm_wkup_event_by_uart_callback();
     }
-#ifdef SUPPORT_CAT1_UART_WAKEUP
-    check_cat1_uart_wakeup();
-#endif
+
     pm_wkup_prepare(PM_SLEEP_DS);
     reg16_setbit(0x520003e4, 0x3);  // open mdam clock after deepsleep
 }
index d4da6c3..08d4d13 100644 (file)
@@ -21,6 +21,7 @@ set(SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/ln_api_emoji.c
     ${CMAKE_CURRENT_SOURCE_DIR}/ln_api_timeline_activities.c
     ${CMAKE_CURRENT_SOURCE_DIR}/ln_api_web_ack.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/ln_http_download.c
 )
 
 # 公共头文件路径(供其他模块引用)
index 8a78fc1..b1ca89b 100644 (file)
@@ -1199,16 +1199,18 @@ static uint32_t ln_download_emoji_at(uint8_t *para, uint32_t paraLen, char *strC
     LN_UNUSED(paraLen);
     LN_UNUSED(strCommonCmd);
     printf("[LN][DOWNLOAD_EMOJI]\n");
-    char *json = (char*)malloc(1024);
-    if (json == NULL) {
-        printf("[LN][DOWNLOAD_EMOJI] OOM\n");
-        return ERRCODE_FAIL;
-    }
+
     const char *deviceId = uapi_ln_get_device_id();
     const char *id = (deviceId && deviceId[0]) ? deviceId : "DEVICE_LOCAL";
     const char *characterId = "char_001";
     const char *eyesColor = "blue";
     const char *eyesShape = "calm-eyes";
+    #if 0
+    char *json = (char*)malloc(1024);
+    if (json == NULL) {
+        printf("[LN][DOWNLOAD_EMOJI] OOM\n");
+        return ERRCODE_FAIL;
+    }
     int n = snprintf(json, 1024,
         "{\n"
         "  \"deviceId\": \"%s\",\n"
@@ -1221,12 +1223,12 @@ static uint32_t ln_download_emoji_at(uint8_t *para, uint32_t paraLen, char *strC
         "    \"eyesShape\": \"%s\",\n"
         "    \"emotionEmojis\": [\n"
         "      {\n"
-        "        \"emotion\": \"happy\",\n"
-        "        \"emojiUrl\": \"https://example.com/emoji/happy.mp4\"\n"
+        "        \"emotion\": \"dizzy\",\n"
+        "        \"emojiUrl\": \"http://snicker.com.cn:8089/LEINIAO/blue/dizzy.zip\"\n"
         "      },\n"
         "      {\n"
-        "        \"emotion\": \"sad\",\n"
-        "        \"emojiUrl\": \"https://example.com/emoji/sad.mp4\"\n"
+        "        \"emotion\": \"wronged\",\n"
+        "        \"emojiUrl\": \"http://snicker.com.cn:8089/LEINIAO/blue/wronged.zip\"\n"
         "      }\n"
         "    ]\n"
         "  }\n"
@@ -1237,9 +1239,54 @@ static uint32_t ln_download_emoji_at(uint8_t *para, uint32_t paraLen, char *strC
         printf("[LN][DOWNLOAD_EMOJI] build json failed\n");
         return ERRCODE_FAIL;
     }
+    #else
+    // 修正json_str的定义,格式整理、转义和换行,方便维护和后续修改
+    const char *json_str = "{"
+            "\"deviceId\":\"860132064629751\","
+            "\"timestamp\":1763690727,"
+            "\"messageId\":\"33d5ba5f6fe9499e\","
+            "\"command\":\"DownloadEmoji\","
+            "\"data\":{"
+                "\"characterId\":\"bf1b16908b33\","
+                "\"eyesColor\":\"yellow-green-gradient\","
+                "\"eyesShape\":\"calm-eyes\","
+                "\"emotionEmojis\":["
+                    "{\"emotion\":\"happy\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Smile.mp4\",\"size\":730359},"
+                    "{\"emotion\":\"laughing\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Laugh.mp4\",\"size\":777316},"
+                    "{\"emotion\":\"angry\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Angry.mp4\",\"size\":770986},"
+                    "{\"emotion\":\"wronged\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Wronged.mp4\",\"size\":1191639},"
+                    "{\"emotion\":\"heartbeat\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Heartbeat.mp4\",\"size\":952770},"
+                    "{\"emotion\":\"awkward\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Embarrassed.mp4\",\"size\":807173},"
+                    "{\"emotion\":\"confused\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Doubt.mp4\",\"size\":833790},"
+                    "{\"emotion\":\"shocked\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Shocked.mp4\",\"size\":844919},"
+                    "{\"emotion\":\"crying\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Tears.mp4\",\"size\":910162},"
+                    "{\"emotion\":\"blowing-kiss\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Kiss.mp4\",\"size\":544297},"
+                    "{\"emotion\":\"sleepy\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Sleepy.mp4\",\"size\":674698},"
+                    "{\"emotion\":\"mischievous\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Naughty.mp4\",\"size\":646516},"
+                    "{\"emotion\":\"speechless\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Speechless.mp4\",\"size\":899444},"
+                    "{\"emotion\":\"dizzy\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Dizzy.mp4\",\"size\":1135889},"
+                    "{\"emotion\":\"thinking\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Think.mp4\",\"size\":963503},"
+                    "{\"emotion\":\"working\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/WorkAndStudy.mp4\",\"size\":1052905},"
+                    "{\"emotion\":\"exercising\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Exercise.mp4\",\"size\":1077737},"
+                    "{\"emotion\":\"playing\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Play.mp4\",\"size\":1098020},"
+                    "{\"emotion\":\"daydreaming\",\"emojiUrl\":\"https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Daze.mp4\",\"size\":1057085}"
+                "],"
+                "\"size\":16969208"
+            "}"
+        "}";
+    int n = strlen(json_str);
+    char *json = (char*)malloc(n + 1);
+    if (json == NULL) {
+        printf("[LN][DOWNLOAD_EMOJI] OOM\n");
+        return ERRCODE_FAIL;
+    }
+    memcpy(json, json_str, n + 1);
+    json[n] = '\0';
+    #endif
     printf("[LN][DOWNLOAD_EMOJI] json = %s\n", json);
-    // ln_parse_download_emoji(json, n);
+    ln_parse_download_emoji(json, n);
     push_download_emoji_notification(characterId, eyesColor, eyesShape);
+    ln_start_emoji_download();
     free(json);
     return ERRCODE_SUCC;
 }
index 6b864e1..b9ea27e 100644 (file)
@@ -1,6 +1,8 @@
 #include "ln_api.h"
 /* 智慧网络统一接口(已包含 OpenHarmony 类型定义) */
 #include "zhihui_net.h"
+/* HTTP下载模块 */
+#include "ln_http_download.h"
 /* for HMAC-SHA256 */
 #include "mbedtls/md.h"
 /* for MD5 file verify */
@@ -244,15 +246,14 @@ static int websocket_retry_connect_count = 0;
 /* 获取设备token: 优先读本地缓存,判断过期,否则刷新 */
 char *uapi_ln_get_device_token(void)
 {
-    // return "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJXWVNaIiwiZGV2aWNlTW9kZWwiOiJhYm15Ym94XzEiLCJkZXZpY2VWZXJzaW9uIjoiMS4wLjAiLCJkZXZpY2VJZCI6IkRFVklDRV9QQ19URVNUXzAwMSIsImV4cCI6MTc2MTQ2MjQ5Mn0.Oeyu8BDopDTvtAcdNpzz46U2w1UwzEzqcvPwD3Dp08Y";
-    /* 若内存已有且非占位,直接返回 */
+    // 若内存已有且非占位,直接返回
     if (g_device_token && strcmp(g_device_token, "<null>") != 0)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "uapi_ln_get_device_token: cached token=%s", g_device_token);
         return g_device_token;
     }
 
-    /* 尝试从本地加载 */
+    // 尝试从本地加载
     char *cached = NULL;
     uint64_t exp_ts = 0;
     if (ln_token_load_json(&cached, &exp_ts) == ERRCODE_SUCC && cached && cached[0])
@@ -263,62 +264,112 @@ char *uapi_ln_get_device_token(void)
         {
             if (g_device_token)
                 free(g_device_token);
-            g_device_token = cached; /* 接管指针 */
+            g_device_token = cached; // 接管指针
             WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "uapi_ln_get_device_token: loaded from file, not expired(未过期)");
+            websocket_retry_connect_count = 0; /* 重置重试计数 */
             return g_device_token;
         }
-        /* 已过期:释放缓存并尝试刷新 */
+        // 已过期:释放缓存并尝试刷新
         free(cached);
+        
+        /* 检查网络状态,只有网络在线时才执行HTTPS刷新 */
+        if (!uapi_dev_runtime_get_cat1_networkstat())
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 本地token已过期,但网络离线,无法刷新 Token");
+            websocket_retry_connect_count++;
+            if (websocket_retry_connect_count >= 100)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络离线,无法刷新 Token,重试次数超过100次,进入休眠");
+                ln_ws_enter_sleep();
+            }
+            else
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络离线,无法刷新 Token,重试次数: %d", websocket_retry_connect_count);
+            }
+            return NULL;
+        }
+        
+        /* 网络在线,通过HTTPS刷新token */
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 本地token已过期,网络在线,通过HTTPS刷新 Token");
         if (uapi_ln_refresh_device_token() == ERRCODE_SUCC)
         {
             if (g_device_token && strcmp(g_device_token, "<null>") != 0)
             {
                 WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "uapi_ln_get_device_token: refreshed expired token");
+                websocket_retry_connect_count = 0; /* 重置重试计数 */
                 return g_device_token;
             }
         }
-        return NULL;
-    }
-
-    /* 没有,尝试获取 Token */
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token");
-    if (uapi_dev_runtime_get_cat1_networkstat())
-    {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络在线,刷新 Token");
-        if (uapi_ln_refresh_device_token() == ERRCODE_SUCC)
-        {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 成功");
-            return g_device_token;
-        }
         else
         {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 失败");
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] HTTPS刷新 Token 失败");
             websocket_retry_connect_count++;
             if (websocket_retry_connect_count >= 100)
             {
-                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络离线,无法刷新 Token,重试次数超过10次,进入休眠");
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 失败,重试次数超过100次,进入休眠");
                 ln_ws_enter_sleep();
             }
             else
             {
-                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络离线,无法刷新 Token,重试次数: %d", websocket_retry_connect_count);
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 失败,重试次数: %d", websocket_retry_connect_count);
             }
-            return NULL;
         }
+        return NULL;
     }
-    else
+
+    // 没有本地token,尝试获取 Token
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 没有本地token,尝试刷新 Token");
+    
+    /* 检查网络状态,只有网络在线时才执行HTTPS获取 */
+    if (!uapi_dev_runtime_get_cat1_networkstat())
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络离线,无法刷新 Token");
         websocket_retry_connect_count++;
         if (websocket_retry_connect_count >= 100)
         {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络离线,无法刷新 Token,重试次数超过10次,进入休眠");
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络离线,无法刷新 Token,重试次数超过100次,进入休眠");
             ln_ws_enter_sleep();
         }
         else
         {
             WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络离线,无法刷新 Token,重试次数: %d", websocket_retry_connect_count);
         }
+        return NULL;
+    }
+    
+    /* 网络在线,通过HTTPS获取token */
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络在线,通过HTTPS刷新 Token (当前重试计数: %d)", websocket_retry_connect_count);
+    if (uapi_ln_refresh_device_token() == ERRCODE_SUCC)
+    {
+        if (g_device_token && strcmp(g_device_token, "<null>") != 0)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 成功 (token长度: %zu)", strlen(g_device_token));
+            websocket_retry_connect_count = 0; /* 重置重试计数 */
+            return g_device_token;
+        }
+        else
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] HTTPS刷新 Token 返回空值 (g_device_token=%p)", g_device_token);
+        }
+    }
+    else
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 失败 (uapi_ln_refresh_device_token返回失败)");
+    }
+    
+    websocket_retry_connect_count++;
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 重试计数递增: %d (最大100次)", websocket_retry_connect_count);
+    if (websocket_retry_connect_count >= 100)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 失败,重试次数超过100次,进入休眠");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 注意:重试计数只有在uapi_ln_get_device_token被调用时才会递增");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 如果WebSocket连接失败后没有再次尝试连接,重试计数不会继续增加");
+        ln_ws_enter_sleep();
+    }
+    else
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 失败,重试次数: %d/100", websocket_retry_connect_count);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 下次WebSocket连接时会再次尝试获取token");
     }
     return NULL;
 }
@@ -434,12 +485,15 @@ void uapi_ln_set_device_bound(int bound)
     g_ln_is_device_bound = bound;
 }
 
-/* 下载文件
-@param url 文件URL
-@param save_path 保存路径
-@return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
-*/
+/* 下载文件 - 已迁移到 ln_http_download.c,使用统一的高级接口 */
 int uapi_ln_download_file(const char *url, const char *save_path)
+{
+    return ln_http_download_file(url, save_path);
+}
+
+#if 0
+/* 原始实现已迁移到 ln_http_download.c */
+int uapi_ln_download_file_old(const char *url, const char *save_path)
 {
     if (!url || !save_path)
     {
@@ -564,9 +618,17 @@ int uapi_ln_download_file(const char *url, const char *save_path)
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 下载过程结束,ret=%d", ret);
     return ret;
 }
+#endif
 
-/* 下载函数封装:带重试,不做UI通知与资源释放 */
+/* 下载函数封装:带重试,不做UI通知与资源释放 - 已迁移到 ln_http_download.c */
 int uapi_ln_download_with_retry(const char *url, const char *save_path, int max_retries)
+{
+    return ln_http_download_with_retry(url, save_path, max_retries);
+}
+
+#if 0
+/* 原始实现已迁移到 ln_http_download.c */
+int uapi_ln_download_with_retry_old(const char *url, const char *save_path, int max_retries)
 {
     if (url == NULL || save_path == NULL || (max_retries <= 0 || max_retries > 10 ))
     {
@@ -592,13 +654,17 @@ int uapi_ln_download_with_retry(const char *url, const char *save_path, int max_
     uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
     return ret;
 }
+#endif
 
-/* 下载文件(支持断点续传)
-@param url 文件URL
-@param save_path 保存路径(若存在则尝试从已下载大小处续传)
-@return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败(失败不删除部分文件)
-*/
+/* 下载文件(支持断点续传)- 已迁移到 ln_http_download.c */
 int uapi_ln_download_file_resume(const char *url, const char *save_path)
+{
+    return ln_http_download_file_resume(url, save_path);
+}
+
+#if 0
+/* 原始实现已迁移到 ln_http_download.c */
+int uapi_ln_download_file_resume_old(const char *url, const char *save_path)
 {
     if (!url || !save_path)
     {
@@ -790,8 +856,17 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path)
     fclose(fd);
     return ret;
 }
-/* 断点续传下载函数封装:带重试,不做UI通知与资源释放 */
+#endif
+
+/* 断点续传下载函数封装:带重试,不做UI通知与资源释放 - 已迁移到 ln_http_download.c */
 int uapi_ln_download_file_resume_with_retry(const char *url, const char *save_path, int max_retries)
+{
+    return ln_http_download_file_resume_with_retry(url, save_path, max_retries);
+}
+
+#if 0
+/* 原始实现已迁移到 ln_http_download.c */
+int uapi_ln_download_file_resume_with_retry_old(const char *url, const char *save_path, int max_retries)
 {
     if (url == NULL || save_path == NULL || (max_retries <= 0 || max_retries > 10 ))
     {
@@ -828,8 +903,14 @@ int uapi_ln_download_file_resume_with_retry(const char *url, const char *save_pa
     uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
     return ret;
 }
-/* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */
-int ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size)
+#endif
+
+/* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 - 已迁移到 ln_http_download.c */
+/* 注意:此函数在 ln_http_download.c 中实现,这里不再重复定义 */
+
+#if 0
+/* 原始实现已迁移到 ln_http_download.c */
+int ln_http_prepare_auto_old(HttpClientData *clientData, int header_size, int resp_size)
 {
     if (header_size < 2048)
         header_size = 2048;
@@ -850,6 +931,7 @@ int ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_s
     }
     return ret; 
 }
+#endif
 
 /* ============================ WebSocket 业务消息 上层处理 ============================ */
 void ln_ws_process_message(const ws_data_msg_t *data_msg, ws_command_t type)
index 4c7b505..7438942 100644 (file)
@@ -482,11 +482,6 @@ uint32_t ln_device_get_character_http(const char *device_id,
     LOS_Msleep(200);
 #endif
 
-    HttpClient client = (HttpClient){0};
-    HttpClientData clientData = (HttpClientData){0};
-    client.isHttp = false;
-    client.remotePort = 443;
-
     char *header_buf = (char *)malloc(512);
     if (!header_buf)
         return ERRCODE_FAIL;
@@ -500,28 +495,82 @@ uint32_t ln_device_get_character_http(const char *device_id,
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
     printf("device getCharacter header %s\n", header_buf);
 
-    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024);
-    if (prep != HTTP_SUCCESS)
+    /* 初始化HTTP客户端(使用通用接口) */
+    /* 注意:HTTPS 证书和 SSL 配置已自动在 n706a_http_client_setup_connection 中处理 */
+    ZhihuiHttpClientHandle http_client = NULL;
+    int init_ret = ZhihuiHttpClientInitAdvanced(&http_client);
+    if (init_ret != 0)
+    {
+        printf("device getCharacter: Failed to init HTTP client\n");
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    /* 设置URL */
+    int url_ret = ZhihuiHttpClientSetUrl(http_client, LN_DEVICE_GET_CHARACTER_URL, 0);
+    if (url_ret != 0)
+    {
+        printf("device getCharacter: Failed to set URL\n");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 建立HTTPS连接 */
+    int conn_ret = ZhihuiHttpClientSetupConnection(http_client);
+    if (conn_ret != 0)
+    {
+        printf("device getCharacter: Failed to setup HTTPS connection\n");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 设置请求头 */
+    int header_ret = ZhihuiHttpClientSetHeader(http_client, header_buf);
+    if (header_ret != 0)
+    {
+        printf("device getCharacter: Failed to set header\n");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 准备响应数据缓冲区 */
+    HttpClientData clientData = {0};
+    clientData.responseBuf = (char *)malloc(8 * 1024);
+    clientData.responseBufLen = 8 * 1024;
+    if (!clientData.responseBuf)
     {
-        printf("device getCharacter prepare failed %d\n", prep);
+        printf("device getCharacter: Failed to allocate response buffer\n");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
-    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_DEVICE_GET_CHARACTER_URL, &clientData);
-    if (res != HTTP_SUCCESS)
+    /* 发送GET请求 */
+    int get_ret = ZhihuiHttpClientGet(http_client, NULL, &clientData);
+    if (get_ret != 0)
     {
-        printf("device getCharacter get failed %d\n", res);
-        goto end;
+        printf("device getCharacter: GET request failed %d\n", get_ret);
+        free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
     }
-    int code = HttpClientGetResponseCode(&client);
+
+    /* 检查HTTP响应码 */
+    int code = ZhihuiHttpClientGetResponseCode(http_client);
     if (code != 200)
     {
         printf("device getCharacter: HTTP %d\n", code);
-        goto end;
+        free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
     }
 
     if (clientData.responseBuf && clientData.responseBufLen > 0)
@@ -530,10 +579,9 @@ uint32_t ln_device_get_character_http(const char *device_id,
         ln_parse_character_config_startUp(clientData.responseBuf, real_len);
     }
 
-end:
-    ZhihuiHttpClientUnprepare(&clientData);
-    ZhihuiHttpClientClse(&client);
     free(header_buf);
+    free(clientData.responseBuf);
+    ZhihuiHttpClientClose(http_client);
     return ERRCODE_SUCC;
 }
 
@@ -551,46 +599,95 @@ uint32_t ln_device_get_user_id_http(const char *device_id,
     LOS_Msleep(200);
 #endif
 
-    HttpClient client = (HttpClient){0};
-    HttpClientData clientData = (HttpClientData){0};
-    client.isHttp = false;
-    client.remotePort = 443;
-
-    char *header_buf2 = (char *)malloc(512);
-    if (!header_buf2)
+    char *header_buf = (char *)malloc(512);
+    if (!header_buf)
         return ERRCODE_FAIL;
-    int hdr = snprintf(header_buf2, 512,
+    int hdr = snprintf(header_buf, 512,
                        "deviceId: %s\r\n"
                        "deviceToken: %s\r\n"
                        "Content-Type: application/json\r\n",
                        device_id, device_token);
     if (hdr <= 0)
     {
-        free(header_buf2);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    printf("device getUserId header %s\n", header_buf);
+
+    /* 初始化HTTP客户端(使用通用接口) */
+    /* 注意:HTTPS 证书和 SSL 配置已自动在 n706a_http_client_setup_connection 中处理 */
+    ZhihuiHttpClientHandle http_client = NULL;
+    int init_ret = ZhihuiHttpClientInitAdvanced(&http_client);
+    if (init_ret != 0)
+    {
+        printf("device getUserId: Failed to init HTTP client\n");
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    /* 设置URL */
+    int url_ret = ZhihuiHttpClientSetUrl(http_client, LN_DEVICE_GET_USER_ID_URL, 0);
+    if (url_ret != 0)
+    {
+        printf("device getUserId: Failed to set URL\n");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 建立HTTPS连接 */
+    int conn_ret = ZhihuiHttpClientSetupConnection(http_client);
+    if (conn_ret != 0)
+    {
+        printf("device getUserId: Failed to setup HTTPS connection\n");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
-    ZhihuiHttpClientSetCustomHeader(&client, header_buf2);
 
-    printf("device getUserId header %s\n", header_buf2);
+    /* 设置请求头 */
+    int header_ret = ZhihuiHttpClientSetHeader(http_client, header_buf);
+    if (header_ret != 0)
+    {
+        printf("device getUserId: Failed to set header\n");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
 
-    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024);
-    if (prep != HTTP_SUCCESS)
+    /* 准备响应数据缓冲区 */
+    HttpClientData clientData = {0};
+    clientData.responseBuf = (char *)malloc(8 * 1024);
+    clientData.responseBufLen = 8 * 1024;
+    if (!clientData.responseBuf)
     {
-        printf("device getUserId prepare failed %d\n", prep);
+        printf("device getUserId: Failed to allocate response buffer\n");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
-    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_DEVICE_GET_USER_ID_URL, &clientData);
-    if (res != HTTP_SUCCESS)
+    /* 发送GET请求 */
+    int get_ret = ZhihuiHttpClientGet(http_client, NULL, &clientData);
+    if (get_ret != 0)
     {
-        printf("device getUserId get failed %d\n", res);
-        goto end;
+        printf("device getUserId: GET request failed %d\n", get_ret);
+        free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
     }
-    int code = HttpClientGetResponseCode(&client);
+
+    /* 检查HTTP响应码 */
+    int code = ZhihuiHttpClientGetResponseCode(http_client);
     if (code != 200)
     {
         printf("device getUserId: HTTP %d\n", code);
-        goto end;
+        free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
     }
 
     if (clientData.responseBuf && clientData.responseBufLen > 0)
@@ -636,10 +733,9 @@ uint32_t ln_device_get_user_id_http(const char *device_id,
         }
     }
 
-end:
-    ZhihuiHttpClientUnprepare(&clientData);
-    ZhihuiHttpClientClse(&client);
-    free(header_buf2);
+    free(header_buf);
+    free(clientData.responseBuf);
+    ZhihuiHttpClientClose(http_client);
     return ERRCODE_SUCC;
 }
 
@@ -658,11 +754,6 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
     LOS_Msleep(200);
 #endif
 
-    HttpClient client = (HttpClient){0};
-    HttpClientData clientData = (HttpClientData){0};
-    client.isHttp = false;
-    client.remotePort = 443;
-
     char *header_buf = (char *)malloc(512);
     if (!header_buf)
     {
@@ -680,44 +771,90 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] snprintf failed for header_buf");
         return ERRCODE_FAIL;
     }
-    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] header_buf: %s", header_buf);
 
-    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE * 2);
-    if (prep != HTTP_SUCCESS)
+    /* 初始化HTTP客户端(使用通用接口) */
+    /* 注意:HTTPS 证书和 SSL 配置已自动在 n706a_http_client_setup_connection 中处理 */
+    ZhihuiHttpClientHandle http_client = NULL;
+    int init_ret = ZhihuiHttpClientInitAdvanced(&http_client);
+    if (init_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] Failed to init HTTP client");
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    /* 设置URL */
+    int url_ret = ZhihuiHttpClientSetUrl(http_client, LN_DEVICE_GET_CHAT_STAMINA_URL, 0);
+    if (url_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] Failed to set URL");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 建立HTTPS连接 */
+    int conn_ret = ZhihuiHttpClientSetupConnection(http_client);
+    if (conn_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] Failed to setup HTTPS connection");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 设置请求头 */
+    int header_ret = ZhihuiHttpClientSetHeader(http_client, header_buf);
+    if (header_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] Failed to set header");
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 准备响应数据缓冲区 */
+    HttpClientData clientData = {0};
+    clientData.responseBuf = (char *)malloc(LN_CLIENT_PREPARE_SIZE * 2);
+    clientData.responseBufLen = LN_CLIENT_PREPARE_SIZE * 2;
+    if (!clientData.responseBuf)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] prepare failed: %d", prep);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] Failed to allocate response buffer");
         free(header_buf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
-    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_DEVICE_GET_CHAT_STAMINA_URL, &clientData);
-    if (res != HTTP_SUCCESS)
+    /* 发送GET请求 */
+    int get_ret = ZhihuiHttpClientGet(http_client, NULL, &clientData);
+    if (get_ret != 0)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] request failed: %d", res);
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] GET request failed: %d", get_ret);
         free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
-    int code = HttpClientGetResponseCode(&client);
+    /* 检查HTTP响应码 */
+    int code = ZhihuiHttpClientGetResponseCode(http_client);
     int body_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
     if (body_len <= 0 || !clientData.responseBuf)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] empty body, code=%d", code);
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
         free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
     char *json_text = (char *)malloc((size_t)body_len + 1);
     if (!json_text)
     {
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
         free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
     memcpy(json_text, clientData.responseBuf, (size_t)body_len);
@@ -736,9 +873,9 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] json parse failed");
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] raw body: %s", json_text);
         free(json_text);
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
         free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
@@ -754,9 +891,9 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
 
         cJSON_Delete(root);
         free(json_text);
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
         free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
@@ -766,9 +903,9 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] no data");
         cJSON_Delete(root);
         free(json_text);
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
         free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
@@ -795,8 +932,8 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
 
     cJSON_Delete(root);
     free(json_text);
-    ZhihuiHttpClientUnprepare(&clientData);
-    ZhihuiHttpClientClse(&client);
     free(header_buf);
+    free(clientData.responseBuf);
+    ZhihuiHttpClientClose(http_client);
     return ERRCODE_SUCC;
 }
\ No newline at end of file
index abb8e1e..7b9688a 100644 (file)
@@ -35,6 +35,8 @@ typedef struct
 
 /* 全局变量 */
 EmojiPackage g_emoji_package = {0};
+static app_task_definition_t g_emoji_download_taskcfg = {0};
+static int g_emoji_download_task_running = 0;
 /* ========== ETA 粗略版辅助函数 ========== */
 /* 前置声明,便于在本节工具函数中调用 */
 static int ln_emoji_is_cached(const EmojiPackage *pkg, const EmotionEmoji *emoji);
@@ -418,7 +420,24 @@ static int ln_emoji_is_cached(const EmojiPackage *pkg, const EmotionEmoji *emoji
     char path[MAX_PATH_LEN] = {0};
     ln_gen_emoji_cache_path(pkg, emoji, path, sizeof(path));
     struct stat st;
-    return (stat(path, &st) == 0 && S_ISREG(st.st_mode)) ? 1 : 0;
+    if (stat(path, &st) != 0 || !S_ISREG(st.st_mode))
+    {
+        return 0; // 文件不存在或不是普通文件
+    }
+    
+    /* 如果知道文件大小,检查文件大小是否匹配 */
+    if (emoji->size > 0)
+    {
+        unsigned long long file_size = ln_get_file_size(path);
+        if (file_size != (unsigned long long)emoji->size)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s 文件大小不匹配: 期望=%lld, 实际=%llu, 需要重新下载", 
+                         emoji->emotion, emoji->size, file_size);
+            return 0; // 文件大小不匹配,需要重新下载
+        }
+    }
+    
+    return 1; // 文件存在且大小匹配(如果知道大小)
 }
 
 /* 批量下载表情并上报结果 */
@@ -476,7 +495,19 @@ static void ln_emoji_download(void *arg)
         }
     }
     downloaded_count = cached_count;
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 初始统计已下载数量,downloaded_count=%d", downloaded_count);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 初始统计已下载数量,downloaded_count=%d/%d", downloaded_count, pkg->emojiCount);
+    
+    /* 如果所有表情都已缓存,直接返回成功,不需要下载 */
+    if (downloaded_count >= pkg->emojiCount)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 所有表情都已缓存,无需下载");
+        all_success = 1;
+        /* 上报100%进度 */
+        push_download_progress_notification(100);
+        /* 直接跳转到上报状态 */
+        goto report_status;
+    }
+    
     if (pkg->emojiCount > 0)
     {
         int init_progress = (int)(((double)downloaded_count / pkg->emojiCount) * 100);
@@ -492,7 +523,7 @@ static void ln_emoji_download(void *arg)
         // 跳过已缓存的文件
         if (ln_emoji_is_cached(pkg, &pkg->emojis[i]))
         {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s 已缓存", pkg->emojis[i].emotion);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s 已缓存,跳过", pkg->emojis[i].emotion);
             continue;
         }
 
@@ -528,6 +559,9 @@ static void ln_emoji_download(void *arg)
             int progress = (int)(((double)downloaded_count / pkg->emojiCount) * 100);
             push_download_progress_notification(progress);
             WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s 下载完成", pkg->emojis[i].emotion);
+            
+            /* 下载完成后让出CPU,避免PPP缓冲区溢出 */
+            LOS_Msleep(50);  /* 50ms延迟,让PPP数据处理任务有机会运行 */
 
             /* 粗略版 ETA(平滑版):在单个文件完成后估算一次 */
             if (total_bytes_all > 0)
@@ -618,6 +652,7 @@ static void ln_emoji_download(void *arg)
                       pkg->emojiCount, downloaded_count, failed_count, remaining_count);
     }
 
+report_status:
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "上报表情下载状态");
 
     // 下载完成后上报状态
@@ -648,19 +683,57 @@ static void ln_emoji_download(void *arg)
     }
 }
 
+/* 表情下载任务包装函数(在独立线程中执行) */
+static void ln_emoji_download_task_wrapper(void *arg)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 表情下载任务线程启动");
+    uapi_ln_set_file_download_type(LN_FILE_DOWNLOAD_TYPE_EMOJI);
+    
+    // 执行下载
+    ln_emoji_download(&g_emoji_package);
+    
+    // 清理
+    uapi_ln_set_file_download_type(LN_FILE_DOWNLOAD_TYPE_NONE);
+    g_emoji_download_task_running = 0;
+    
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 表情下载任务线程结束");
+}
+
 /* 启动表情下载任务 */
 void ln_start_emoji_download(void)
 {
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 启动表情下载任务");
-    uapi_ln_set_file_download_type(LN_FILE_DOWNLOAD_TYPE_EMOJI);
+    
     if (g_emoji_package.emojiCount <= 0 || !g_emoji_package.emojis)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] no emojis to download");
         return;
     }
-    // 下载表情
-    ln_emoji_download(&g_emoji_package);
-    uapi_ln_set_file_download_type(LN_FILE_DOWNLOAD_TYPE_NONE);
+    
+    // 如果任务已经在运行,不重复创建
+    if (g_emoji_download_task_running)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 表情下载任务已在运行中");
+        return;
+    }
+    
+    // 创建独立任务执行下载,避免在GUI线程中执行导致栈溢出
+    g_emoji_download_taskcfg.attr.name = "ln_emoji_download_task";
+    g_emoji_download_taskcfg.attr.priority = osPriorityNormal;
+    g_emoji_download_taskcfg.attr.stack_size = 0x3000; // 12KB栈空间,足够下载任务使用
+    g_emoji_download_taskcfg.func = ln_emoji_download_task_wrapper;
+    g_emoji_download_taskcfg.use_mem = USE_EXTEND_MEM; // 使用扩展内存(PSRAM)
+    g_emoji_download_taskcfg.create_queue_handle = NULL; // 不需要消息队列
+    
+    errcode_t ret = uapi_create_osThread_custom(&g_emoji_download_taskcfg);
+    if (ret != ERRCODE_SUCC)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 创建表情下载任务失败: %d", ret);
+        return;
+    }
+    
+    g_emoji_download_task_running = 1;
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 表情下载任务已创建");
 }
 
 /* 解析下载表情命令JSON */
@@ -1026,13 +1099,31 @@ static uint32_t ln_emoji_report_status_http(const char *device_id,
     LOS_Msleep(200);
 #endif
 
-    HttpClient client = (HttpClient){0};
-    HttpClientData clientData = (HttpClientData){0};
+    /* 组装 JSON */
+    cJSON *root = cJSON_CreateObject();
+    if (!root)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] create root object failed");
+        return ERRCODE_FAIL;
+    }
+    cJSON_AddStringToObject(root, "characterId", character_id);
+    cJSON_AddStringToObject(root, "eyesColor", eyes_color);
+    cJSON_AddStringToObject(root, "eyesShape", eyes_shape);
+    cJSON_AddStringToObject(root, "status", status);
+    cJSON_AddStringToObject(root, "description", description ? description : "");
+    char *body = cJSON_PrintUnformatted(root);
+    cJSON_Delete(root);
+    if (!body)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] print unformatted failed");
+        return ERRCODE_FAIL;
+    }
 
-    /* 直接构造自定义请求头,生命周期贯穿整个请求,结束后再释放 */
+    /* 构造请求头 */
     char *header_buf = (char *)malloc(512);
     if (!header_buf)
     {
+        free(body);
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] malloc header_buf failed");
         return ERRCODE_FAIL;
     }
@@ -1043,45 +1134,71 @@ static uint32_t ln_emoji_report_status_http(const char *device_id,
                        device_id, device_token);
     if (hdr <= 0)
     {
+        free(body);
         free(header_buf);
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] build header failed");
         return ERRCODE_FAIL;
     }
-    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
-    /* 组装 JSON */
-    cJSON *root = cJSON_CreateObject();
-    if (!root)
+    /* 初始化HTTP客户端(使用通用接口) */
+    /* 注意:HTTPS 证书和 SSL 配置已自动在 n706a_http_client_setup_connection 中处理 */
+    ZhihuiHttpClientHandle http_client = NULL;
+    int init_ret = ZhihuiHttpClientInitAdvanced(&http_client);
+    if (init_ret != 0)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] create root object failed");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] Failed to init HTTP client");
+        free(body);
+        free(header_buf);
         return ERRCODE_FAIL;
     }
-    cJSON_AddStringToObject(root, "characterId", character_id);
-    cJSON_AddStringToObject(root, "eyesColor", eyes_color);
-    cJSON_AddStringToObject(root, "eyesShape", eyes_shape);
-    cJSON_AddStringToObject(root, "status", status);
-    cJSON_AddStringToObject(root, "description", description ? description : "");
-    char *body = cJSON_PrintUnformatted(root);
-    cJSON_Delete(root);
-    if (!body)
+
+    /* 设置URL */
+    int url_ret = ZhihuiHttpClientSetUrl(http_client, LN_EMOJI_REPORT_URL, 0);
+    if (url_ret != 0)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] print unformatted failed");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] Failed to set URL");
+        free(body);
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 建立HTTPS连接 */
+    int conn_ret = ZhihuiHttpClientSetupConnection(http_client);
+    if (conn_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] Failed to setup HTTPS connection");
+        free(body);
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
-    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE / 2);
-    if (prep != HTTP_SUCCESS)
+    /* 设置请求头 */
+    int header_ret = ZhihuiHttpClientSetHeader(http_client, header_buf);
+    if (header_ret != 0)
     {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] Failed to set header");
         free(body);
         free(header_buf);
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] prepare auto failed");
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
-    clientData.postBuf = body;
-    clientData.postBufLen = (int)strlen(body);
-    clientData.postContentType = (char *)"application/json";
 
-    // 打印请求请求响应
+    /* 准备响应数据缓冲区 */
+    HttpClientData clientData = {0};
+    clientData.responseBuf = (char *)malloc(LN_CLIENT_PREPARE_SIZE / 2);
+    clientData.responseBufLen = LN_CLIENT_PREPARE_SIZE / 2;
+    if (!clientData.responseBuf)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] Failed to allocate response buffer");
+        free(body);
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    // 打印请求体
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] request body: %s", body);
     // 使用说明:
     // - 该接口用于设备向服务器同步表情下载/删除状态
@@ -1089,25 +1206,30 @@ static uint32_t ln_emoji_report_status_http(const char *device_id,
     // - 服务器会根据上报的信息更新小程序端的表情状态显示
     // - characterId、eyesColor、eyesShape三个字段组合标识一个唯一的表情组
 
-    HTTPC_RESULT res = HttpClientPostRequest(&client, LN_EMOJI_REPORT_URL, &clientData);
-    if (res != HTTP_SUCCESS)
+    /* 发送POST请求 */
+    int post_ret = ZhihuiHttpClientPost(http_client, NULL, "application/json", body, strlen(body), &clientData);
+    if (post_ret != 0)
     {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] POST request failed");
         free(body);
-        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] request failed");
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
-    int code = HttpClientGetResponseCode(&client);
+    /* 检查HTTP响应码 */
+    int code = ZhihuiHttpClientGetResponseCode(http_client);
     if (code != 200)
     {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response code not 200");
         free(body);
-        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response code not 200");
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
+
     if (clientData.responseBuf && clientData.responseBufLen > 0)
     {
         // {
@@ -1121,6 +1243,10 @@ static uint32_t ln_emoji_report_status_http(const char *device_id,
         if (!response_buf)
         {
             WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] malloc failed for response_buf");
+            free(body);
+            free(header_buf);
+            free(clientData.responseBuf);
+            ZhihuiHttpClientClose(http_client);
             return ERRCODE_FAIL;
         }
         memcpy(response_buf, clientData.responseBuf, real_len);
@@ -1129,10 +1255,10 @@ static uint32_t ln_emoji_report_status_http(const char *device_id,
         free(response_buf);
     }
 
-
     free(body);
-    ZhihuiHttpClientUnprepare(&clientData);
     free(header_buf);
+    free(clientData.responseBuf);
+    ZhihuiHttpClientClose(http_client);
     return ERRCODE_SUCC;
 }
 /* 7.2 上报设备所有表情组状态 */
@@ -1150,27 +1276,6 @@ static uint32_t ln_emoji_report_all_http(const char *device_id,
     LOS_Msleep(200);
 #endif
 
-    HttpClient client = (HttpClient){0};
-    HttpClientData clientData = (HttpClientData){0};
-    client.isHttp = false;
-    client.remotePort = 443;
-
-    /* 直接构造自定义请求头,生命周期贯穿整个请求,结束后再释放 */
-    char *header_buf = (char *)malloc(512);
-    if (!header_buf)
-        return ERRCODE_FAIL;
-    int hdr = snprintf(header_buf, 512,
-                       "deviceId: %s\r\n"
-                       "deviceToken: %s\r\n"
-                       "Content-Type: application/json\r\n",
-                       device_id, device_token);
-    if (hdr <= 0)
-    {
-        free(header_buf);
-        return ERRCODE_FAIL;
-    }
-    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
-
     /* 组装 JSON 数组 */
     cJSON *root = cJSON_CreateObject();
     if (!root)
@@ -1200,56 +1305,133 @@ static uint32_t ln_emoji_report_all_http(const char *device_id,
     if (!body)
         return ERRCODE_FAIL;
 
-    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE * 2);
-    if (prep != HTTP_SUCCESS)
+    /* 构造请求头 */
+    char *header_buf = (char *)malloc(512);
+    if (!header_buf)
     {
+        free(body);
+        return ERRCODE_FAIL;
+    }
+    int hdr = snprintf(header_buf, 512,
+                       "deviceId: %s\r\n"
+                       "deviceToken: %s\r\n"
+                       "Content-Type: application/json\r\n",
+                       device_id, device_token);
+    if (hdr <= 0)
+    {
+        free(body);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    /* 初始化HTTP客户端(使用通用接口) */
+    /* 注意:HTTPS 证书和 SSL 配置已自动在 n706a_http_client_setup_connection 中处理 */
+    ZhihuiHttpClientHandle http_client = NULL;
+    int init_ret = ZhihuiHttpClientInitAdvanced(&http_client);
+    if (init_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] Failed to init HTTP client");
         free(body);
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    clientData.postBuf = body;
-    clientData.postBufLen = (int)strlen(body);
-    clientData.postContentType = (char *)"application/json";
+
+    /* 设置URL */
+    int url_ret = ZhihuiHttpClientSetUrl(http_client, LN_EMOJI_REPORT_ALL_URL, 0);
+    if (url_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] Failed to set URL");
+        free(body);
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 建立HTTPS连接 */
+    int conn_ret = ZhihuiHttpClientSetupConnection(http_client);
+    if (conn_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] Failed to setup HTTPS connection");
+        free(body);
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 设置请求头 */
+    int header_ret = ZhihuiHttpClientSetHeader(http_client, header_buf);
+    if (header_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] Failed to set header");
+        free(body);
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 准备响应数据缓冲区 */
+    HttpClientData clientData = {0};
+    clientData.responseBuf = (char *)malloc(LN_CLIENT_PREPARE_SIZE * 2);
+    clientData.responseBufLen = LN_CLIENT_PREPARE_SIZE * 2;
+    if (!clientData.responseBuf)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] Failed to allocate response buffer");
+        free(body);
+        free(header_buf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
     
     //打印请求体
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] request body: %s", body);
 
-    HTTPC_RESULT res = HttpClientPostRequest(&client, LN_EMOJI_REPORT_ALL_URL, &clientData);
-    if (res != HTTP_SUCCESS)
+    /* 发送POST请求 */
+    int post_ret = ZhihuiHttpClientPost(http_client, NULL, "application/json", body, strlen(body), &clientData);
+    if (post_ret != 0)
     {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] POST request failed");
         free(body);
-        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
 
-    int code = HttpClientGetResponseCode(&client);
+    /* 检查HTTP响应码 */
+    int code = ZhihuiHttpClientGetResponseCode(http_client);
     if (code != 200)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] HTTP %d", code);
         free(body);
-        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         return ERRCODE_FAIL;
     }
+
     if (clientData.responseBuf && clientData.responseBufLen > 0)
     {
         int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
         char *response_buf = (char *)malloc(real_len + 1);
         if (!response_buf)
         {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] malloc failed for response_buf");
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] malloc failed for response_buf");
+            free(body);
+            free(header_buf);
+            free(clientData.responseBuf);
+            ZhihuiHttpClientClose(http_client);
             return ERRCODE_FAIL;
         }
         memcpy(response_buf, clientData.responseBuf, real_len);
         response_buf[real_len] = '\0';
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response body: %s", response_buf);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] response body: %s", response_buf);
         free(response_buf);
     }
 
     free(body);
-    ZhihuiHttpClientUnprepare(&clientData);
     free(header_buf);
+    free(clientData.responseBuf);
+    ZhihuiHttpClientClose(http_client);
     return ERRCODE_SUCC;
 }
 
index 7d99321..4e3af91 100644 (file)
@@ -244,11 +244,6 @@ uint32_t ln_ota_get_task_http(const char *device_id,
     LOS_Msleep(200);
 #endif
 
-    HttpClient client = (HttpClient){0};
-    HttpClientData clientData = (HttpClientData){0};
-    client.isHttp = false;
-    client.remotePort = 443;
-
     /* 头:与 device info 一致,额外带 model/version 可辅助服务端日志定位 */
     size_t hdr_cap = 64 + strlen(device_id) + strlen(device_token) +
                      (uapi_ln_get_device_model() ? strlen(uapi_ln_get_device_model()) : 5) +
@@ -270,21 +265,12 @@ uint32_t ln_ota_get_task_http(const char *device_id,
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
-
-    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024);
-    if (prep != HTTP_SUCCESS)
-    {
-        printf("[OTA] getTask prepare failed %d\n", prep);
-        return ERRCODE_FAIL;
-    }
 
     /* 拼 GET URL 参数 */
     size_t url_cap = strlen(LN_OTA_GET_TASK_URL) + strlen(device_id) + strlen(source_version) + 64;
     char *url_with_params = (char *)malloc(url_cap);
     if (!url_with_params)
     {
-        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -293,7 +279,6 @@ uint32_t ln_ota_get_task_http(const char *device_id,
                         LN_OTA_GET_TASK_URL, device_id, source_version);
     if (ulen <= 0 || (size_t)ulen >= url_cap)
     {
-        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         free(url_with_params);
         return ERRCODE_FAIL;
@@ -301,23 +286,91 @@ uint32_t ln_ota_get_task_http(const char *device_id,
 
     printf("[OTA] GET %s\n", url_with_params);
 
-    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, url_with_params, &clientData);
-    if (res != HTTP_SUCCESS)
+    /* 初始化HTTP客户端(使用通用接口) */
+    /* 注意:HTTPS 证书和 SSL 配置已自动在 n706a_http_client_setup_connection 中处理 */
+    ZhihuiHttpClientHandle http_client = NULL;
+    int init_ret = ZhihuiHttpClientInitAdvanced(&http_client);
+    if (init_ret != 0)
     {
-        printf("[OTA] HttpClientGetRequest error %d\n", res);
-        goto end;
+        printf("[OTA] Failed to init HTTP client\n");
+        free(header_buf);
+        free(url_with_params);
+        return ERRCODE_FAIL;
     }
 
-    int code = HttpClientGetResponseCode(&client);
+    /* 设置URL(带参数) */
+    int url_ret = ZhihuiHttpClientSetUrl(http_client, url_with_params, 0);
+    if (url_ret != 0)
+    {
+        printf("[OTA] Failed to set URL\n");
+        free(header_buf);
+        free(url_with_params);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 建立HTTPS连接 */
+    int conn_ret = ZhihuiHttpClientSetupConnection(http_client);
+    if (conn_ret != 0)
+    {
+        printf("[OTA] Failed to setup HTTPS connection\n");
+        free(header_buf);
+        free(url_with_params);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 设置请求头 */
+    int header_ret = ZhihuiHttpClientSetHeader(http_client, header_buf);
+    if (header_ret != 0)
+    {
+        printf("[OTA] Failed to set header\n");
+        free(header_buf);
+        free(url_with_params);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 准备响应数据缓冲区 */
+    HttpClientData clientData = {0};
+    clientData.responseBuf = (char *)malloc(8 * 1024);
+    clientData.responseBufLen = 8 * 1024;
+    if (!clientData.responseBuf)
+    {
+        printf("[OTA] Failed to allocate response buffer\n");
+        free(header_buf);
+        free(url_with_params);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 发送GET请求 */
+    int get_ret = ZhihuiHttpClientGet(http_client, NULL, &clientData);
+    if (get_ret != 0)
+    {
+        printf("[OTA] GET request error %d\n", get_ret);
+        free(header_buf);
+        free(url_with_params);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
+    }
+
+    /* 检查HTTP响应码 */
+    int code = ZhihuiHttpClientGetResponseCode(http_client);
     if (code != 200)
     {
-        printf("[OTA] HttpClientGetResponseCode %d\n", code);
+        printf("[OTA] HTTP response code %d\n", code);
         if (clientData.responseBuf && clientData.responseBufLen > 0)
         {
             int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
             ln_handle_ota_task_response(clientData.responseBuf, real_len);
         }
-        goto end;
+        free(header_buf);
+        free(url_with_params);
+        free(clientData.responseBuf);
+        ZhihuiHttpClientClose(http_client);
+        return ERRCODE_FAIL;
     }
 
     if (clientData.responseBuf && clientData.responseBufLen > 0)
@@ -326,13 +379,10 @@ uint32_t ln_ota_get_task_http(const char *device_id,
         ln_handle_ota_task_response(clientData.responseBuf, real_len);
     }
 
-end:
-    ZhihuiHttpClientUnprepare(&clientData);
-    ZhihuiHttpClientClse(&client);
-    if (header_buf)
-        free(header_buf);
-    if (url_with_params)
-        free(url_with_params);
+    free(header_buf);
+    free(url_with_params);
+    free(clientData.responseBuf);
+    ZhihuiHttpClientClose(http_client);
     return ERRCODE_SUCC;
 }
 
index 42604fe..e810546 100644 (file)
@@ -69,11 +69,6 @@ uint32_t ln_timeline_get_status_http(const char *device_id,
 #endif
        uapi_pm_add_sleep_veto(PM_ID_DEBUG);
 
-       HttpClient client = (HttpClient){0};
-       HttpClientData clientData = (HttpClientData){0};
-       client.isHttp = false;
-       client.remotePort = 443;
-
        char *header_buf = (char *)malloc(512);
        if (!header_buf)
        {
@@ -91,36 +86,96 @@ uint32_t ln_timeline_get_status_http(const char *device_id,
                uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
                return ERRCODE_FAIL;
        }
-       ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
-       HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE*2);
-       if (prep != HTTP_SUCCESS)
+       /* 初始化HTTP客户端(使用通用接口) */
+       /* 注意:HTTPS 证书和 SSL 配置已自动在 n706a_http_client_setup_connection 中处理 */
+       ZhihuiHttpClientHandle http_client = NULL;
+       int init_ret = ZhihuiHttpClientInitAdvanced(&http_client);
+       if (init_ret != 0)
        {
+               WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TIMELINE] Failed to init HTTP client");
                free(header_buf);
                uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
                return ERRCODE_FAIL;
        }
 
-       HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_TIMELINE_GET_STATUS_URL, &clientData);
-       if (res != HTTP_SUCCESS)
+       /* 设置URL */
+       int url_ret = ZhihuiHttpClientSetUrl(http_client, LN_TIMELINE_GET_STATUS_URL, 0);
+       if (url_ret != 0)
        {
-               goto end;
+               WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TIMELINE] Failed to set URL");
+               free(header_buf);
+               ZhihuiHttpClientClose(http_client);
+               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+               return ERRCODE_FAIL;
        }
-       int code = HttpClientGetResponseCode(&client);
+
+       /* 建立HTTPS连接 */
+       int conn_ret = ZhihuiHttpClientSetupConnection(http_client);
+       if (conn_ret != 0)
+       {
+               WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TIMELINE] Failed to setup HTTPS connection");
+               free(header_buf);
+               ZhihuiHttpClientClose(http_client);
+               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+               return ERRCODE_FAIL;
+       }
+
+       /* 设置请求头 */
+       int header_ret = ZhihuiHttpClientSetHeader(http_client, header_buf);
+       if (header_ret != 0)
+       {
+               WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TIMELINE] Failed to set header");
+               free(header_buf);
+               ZhihuiHttpClientClose(http_client);
+               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+               return ERRCODE_FAIL;
+       }
+
+       /* 准备响应数据缓冲区 */
+       HttpClientData clientData = {0};
+       clientData.responseBuf = (char *)malloc(LN_CLIENT_PREPARE_SIZE * 2);
+       clientData.responseBufLen = LN_CLIENT_PREPARE_SIZE * 2;
+       if (!clientData.responseBuf)
+       {
+               WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TIMELINE] Failed to allocate response buffer");
+               free(header_buf);
+               ZhihuiHttpClientClose(http_client);
+               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+               return ERRCODE_FAIL;
+       }
+
+       /* 发送GET请求 */
+       int get_ret = ZhihuiHttpClientGet(http_client, NULL, &clientData);
+       if (get_ret != 0)
+       {
+               free(header_buf);
+               free(clientData.responseBuf);
+               ZhihuiHttpClientClose(http_client);
+               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+               return ERRCODE_FAIL;
+       }
+
+       /* 检查HTTP响应码 */
+       int code = ZhihuiHttpClientGetResponseCode(http_client);
        if (code != 200)
        {
-               goto end;
+               free(header_buf);
+               free(clientData.responseBuf);
+               ZhihuiHttpClientClose(http_client);
+               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+               return ERRCODE_FAIL;
        }
+
        if (clientData.responseBuf && clientData.responseBufLen > 0)
        {
                int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
                ln_parse_timeline_status_and_notify(clientData.responseBuf, real_len);
        }
 
-end:
-       ZhihuiHttpClientUnprepare(&clientData);
-       ZhihuiHttpClientClse(&client);
        free(header_buf);
+       free(clientData.responseBuf);
+       ZhihuiHttpClientClose(http_client);
        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
        return ERRCODE_SUCC;
 }
index bbfb369..bcb8e60 100644 (file)
@@ -9,6 +9,8 @@
 #include "mbedtls/md5.h"
 #include <sys/stat.h>
 #include <time.h>
+/* 智慧网络统一接口(已包含 HTTP 接口) */
+#include "zhihui_net.h"
 
 static void ln_to_hex_lower(const uint8_t *in, size_t in_len, char *out)
 {
@@ -236,11 +238,14 @@ uint32_t ln_get_device_token_http(const char *device_id,
 
     /* 1. 生成时间戳(ms)与nonce */
     uint32_t ts_s = 0; /* epoch seconds */
-    uapi_get_timestampe_ss(&ts_s);
+    if (uapi_get_timestampe_ss(&ts_s) != ERRCODE_SUCC) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Failed to get timestamp");
+        return ERRCODE_FAIL;
+    }
     char timestamp[32] = {0};
-    (void)snprintf(timestamp, sizeof(timestamp), "%u000", ts_s); /* 转毫秒字符串 */
+    (void)snprintf(timestamp, sizeof(timestamp), "%u000", ts_s); /* 毫秒级时间戳字符串 */
 
-    char nonce[17] = {0}; /* 12~16位,取固定16以简化 */
+    char nonce[13] = {0}; /* 12位,与Python实现保持一致 */
     ln_gen_nonce(nonce, sizeof(nonce));
 
     /* 2. 计算签名: deviceId:deviceIccid:timestamp:nonce */
@@ -249,12 +254,21 @@ uint32_t ln_get_device_token_http(const char *device_id,
     if (!sign_data)
         return ERRCODE_FAIL;
     (void)snprintf(sign_data, sd_cap, "%s:%s:%s:%s", device_id, device_iccid, timestamp, nonce);
+    
+    /* 调试日志:打印签名数据 */
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] Sign data: %s", sign_data);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] Timestamp: %s, Nonce: %s (len=%zu)", timestamp, nonce, strlen(nonce));
+    
     char signature[65] = {0};
     if (ln_hmac_sha256_hex(LN_SIGN_SECRET, sign_data, signature, sizeof(signature)) != 0)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: hmac calc failed");
+        free(sign_data);
         return ERRCODE_FAIL;
     }
+    
+    /* 调试日志:打印生成的签名 */
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] Generated signature: %s", signature);
 
     /* 3. 组织自定义请求头 */
     char *header_buf = (char *)malloc(1024);
@@ -291,84 +305,166 @@ uint32_t ln_get_device_token_http(const char *device_id,
 #endif
        uapi_pm_add_sleep_veto(PM_ID_DEBUG);
 
-    /* 5. 发起HTTPS GET */
-    HttpClient client = (HttpClient){0};
-    HttpClientData clientData = (HttpClientData){0};
-    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
+    /* 5. 初始化HTTP客户端(使用通用接口) */
+    /* 注意:HTTPS 证书和 SSL 配置已自动在 n706a_http_client_setup_connection 中处理 */
+    ZhihuiHttpClientHandle http_client = NULL;
+    int init_ret = ZhihuiHttpClientInitAdvanced(&http_client);
+    if (init_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Failed to init HTTP client");
+        free(sign_data);
+        free(header_buf);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        return ERRCODE_FAIL;
+    }
+
+    /* 7. 设置URL */
+    int url_ret = ZhihuiHttpClientSetUrl(http_client, LN_FULL_URL, 0);
+    if (url_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Failed to set URL");
+        ZhihuiHttpClientClose(http_client);
+        free(sign_data);
+        free(header_buf);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        return ERRCODE_FAIL;
+    }
+
+    /* 8. 建立HTTPS连接 */
+    int conn_ret = ZhihuiHttpClientSetupConnection(http_client);
+    if (conn_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Failed to setup HTTPS connection");
+        ZhihuiHttpClientClose(http_client);
+        free(sign_data);
+        free(header_buf);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        return ERRCODE_FAIL;
+    }
 
-    HTTPC_RESULT prep = ZhihuiHttpClientPrepare(&clientData, 2048, 4096);
-    if (prep != HTTP_SUCCESS)
+    /* 9. 设置自定义请求头 */
+    int header_ret = ZhihuiHttpClientSetHeader(http_client, header_buf);
+    if (header_ret != 0)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: HttpClientPrepare failed %d", prep);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Failed to set header");
+        ZhihuiHttpClientClose(http_client);
         free(sign_data);
         free(header_buf);
-               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
         return ERRCODE_FAIL;
     }
 
-    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_FULL_URL, &clientData);
-    if (res != HTTP_SUCCESS)
+    /* 10. 准备响应数据缓冲区(使用通用接口的数据结构) */
+    HttpClientData client_data = {0};
+    client_data.responseBuf = (char *)malloc(4096);
+    client_data.responseBufLen = 4096;
+    if (!client_data.responseBuf)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: request failed %d", res);
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Failed to allocate response buffer");
+        ZhihuiHttpClientClose(http_client);
         free(sign_data);
         free(header_buf);
-               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
         return ERRCODE_FAIL;
     }
 
-    int code = HttpClientGetResponseCode(&client);
+    /* 11. 发送GET请求(带自定义headers,使用mode=99) */
+    /* 注意:URL已经在set_url中设置完整,这里传入NULL表示使用已设置的URL路径 */
+    int get_ret = ZhihuiHttpClientGet(http_client, NULL, &client_data);
+    if (get_ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: GET request failed %d", get_ret);
+        free(client_data.responseBuf);
+        ZhihuiHttpClientClose(http_client);
+        free(sign_data);
+        free(header_buf);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        return ERRCODE_FAIL;
+    }
+
+    /* 12. 检查HTTP响应码 */
+    int code = ZhihuiHttpClientGetResponseCode(http_client);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: HTTP response code=%d", code);
     if (code != 200)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: HTTP %d", code);
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: HTTP %d (expected 200)", code);
+        if (client_data.responseBuf && client_data.responseBufLen > 0)
+        {
+            int print_len = (client_data.responseBufLen > 512) ? 512 : client_data.responseBufLen;
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Response body (first %d bytes): %.512s", print_len, client_data.responseBuf);
+        }
+        free(client_data.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         free(sign_data);
         free(header_buf);
-               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
         return ERRCODE_FAIL;
     }
 
-    /* 6. 解析响应JSON */
-    if (clientData.responseBuf == NULL || clientData.responseBufLen <= 0)
+    /* 13. 解析响应JSON */
+    if (client_data.responseBuf == NULL || client_data.responseBufLen <= 0)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: empty body");
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: empty body (responseBuf=%p, responseBufLen=%d)", 
+                     client_data.responseBuf, client_data.responseBufLen);
+        free(client_data.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         free(sign_data);
         free(header_buf);
-               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
         return ERRCODE_FAIL;
     }
 
-    int body_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
+    /* 使用实际接收到的响应体长度,而不是Content-Length头部值
+     * 因为Content-Length可能不准确,或者响应体可能包含额外的数据
+     * responseBufLen是实际从HTTP响应中提取的响应体长度
+     */
+    int body_len = client_data.responseBufLen > 0 ? client_data.responseBufLen : (int)strlen(client_data.responseBuf);
     if (body_len <= 0)
-        body_len = (int)strlen(clientData.responseBuf);
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Invalid body length (responseBufLen=%d)", client_data.responseBufLen);
+        free(client_data.responseBuf);
+        ZhihuiHttpClientClose(http_client);
+        free(sign_data);
+        free(header_buf);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        return ERRCODE_FAIL;
+    }
+
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Response body length=%d bytes", body_len);
+    if (body_len > 0 && body_len <= 512)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Response body %d bytes: %.*s", body_len, client_data.responseBuf);
+    }
+    else if (body_len > 512)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Response body (first 512 bytes): %.512s", client_data.responseBuf);
+    }
 
     char *json_text = (char *)malloc((size_t)body_len + 1);
     if (json_text == NULL)
     {
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: failed to allocate memory for JSON text (%d bytes)", body_len);
+        free(client_data.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         free(sign_data);
         free(header_buf);
-               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
         return ERRCODE_FAIL;
     }
-    memcpy(json_text, clientData.responseBuf, (size_t)body_len);
+    memcpy(json_text, client_data.responseBuf, (size_t)body_len);
     json_text[body_len] = '\0';
 
     cJSON *root = cJSON_Parse(json_text);
     if (!root)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: JSON parse failed");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: JSON text (first 256 chars): %.256s", json_text);
         free(json_text);
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
+        free(client_data.responseBuf);
+        ZhihuiHttpClientClose(http_client);
         free(sign_data);
         free(header_buf);
-               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
         return ERRCODE_FAIL;
     }
 
@@ -377,13 +473,17 @@ uint32_t ln_get_device_token_http(const char *device_id,
     {
         cJSON *msg = cJSON_GetObjectItem(root, "message");
         /* 失败响应,打印errorCode和message 并返回失败 */
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: failed, err=%s", errorCode && cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown");
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: failed, message=%s", msg && cJSON_IsString(msg) ? msg->valuestring : "unknown");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Server returned error, errorCode=%s", 
+                     errorCode && cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Server error message=%s", 
+                     msg && cJSON_IsString(msg) ? msg->valuestring : "unknown");
         cJSON_Delete(root);
         free(json_text);
-        ZhihuiHttpClientUnprepare(&clientData);
-        ZhihuiHttpClientClse(&client);
-               uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+        free(client_data.responseBuf);
+        ZhihuiHttpClientClose(http_client);
+        free(sign_data);
+        free(header_buf);
+        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
         return ERRCODE_FAIL;
     }
 
@@ -392,43 +492,80 @@ uint32_t ln_get_device_token_http(const char *device_id,
     {
         cJSON *token = cJSON_GetObjectItem(data, "token");
         cJSON *expire = cJSON_GetObjectItem(data, "expireTime");
+        
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Found 'data' field in response");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: token field exists=%s, expireTime field exists=%s", 
+                     token ? "yes" : "no", expire ? "yes" : "no");
+        
         if (g_device_token)
         {
             free(g_device_token);
             g_device_token = NULL;
         }
+        
         if (token && cJSON_IsString(token))
         {
             size_t tlen = strlen(token->valuestring);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Extracted token from response, length=%zu", tlen);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Token (first 64 chars)=%.64s", token->valuestring);
+            
             g_device_token = (char *)malloc(tlen + 1);
             if (g_device_token)
             {
                 memcpy(g_device_token, token->valuestring, tlen + 1);
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Token copied to g_device_token successfully");
+            }
+            else
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: ERROR - failed to allocate memory for token (%zu bytes)", tlen);
             }
         }
+        else
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: WARNING - token field is missing or not a string");
+        }
+        
         if (g_device_token == NULL)
         {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: WARNING - g_device_token is NULL, setting to <null>");
             g_device_token = strdup("<null>");
         }
+        
         /* 落盘保存 */
         uint64_t exp_ts = 0;
         if (expire && cJSON_IsNumber(expire))
         {
             exp_ts = (uint64_t)expire->valuedouble;
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Expire time from server: %llu", (unsigned long long)exp_ts);
+        }
+        else
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: WARNING - expireTime field is missing or not a number");
+        }
+        
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Saving token to file...");
+        uint32_t save_ret = ln_token_save_json(g_device_token, exp_ts);
+        if (save_ret == ERRCODE_SUCC)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Token saved successfully, length=%zu", strlen(g_device_token));
+        }
+        else
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: WARNING - Token save failed (ret=%u)", save_ret);
         }
-        (void)ln_token_save_json(g_device_token, exp_ts);
     }
     else
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: no data field");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: ERROR - no 'data' field in response");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: Response JSON: %s", json_text);
     }
 
     cJSON_Delete(root);
     free(json_text);
-    ZhihuiHttpClientUnprepare(&clientData);
-    ZhihuiHttpClientClse(&client);
+    free(client_data.responseBuf);
+    ZhihuiHttpClientClose(http_client);
     free(sign_data);
     free(header_buf);
-       uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
+    uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
     return ERRCODE_SUCC;
 }
index 1a0f9a7..de464d8 100644 (file)
@@ -36,7 +36,8 @@ typedef enum {
 } ln_ws_log_level_t;
 
 // 当前日志等级(可通过配置修改)
-static ln_ws_log_level_t g_log_level =  LN_WS_LOG_INFO;
+// static ln_ws_log_level_t g_log_level =  LN_WS_LOG_INFO;
+static ln_ws_log_level_t g_log_level =  LN_WS_LOG_DEBUG;
 
 // 打印函数
 // 日志等级字符串数组
@@ -237,7 +238,17 @@ static void ln_ws_message_callback(const char *raw_data, uint32_t timestamp)
     (void)timestamp;
     if (raw_data == NULL) return;
     
+    /* 注意:raw_data 应该已经是以 \0 结尾的字符串(由 zhihui_net_websocket.c 保证)
+     * 但为了安全,使用 strlen 获取长度,并限制最大长度避免读取超出范围
+     */
     size_t length = strlen(raw_data);
+    /* 限制最大长度,避免读取超出实际数据范围(例如 64KB) */
+    const size_t MAX_MESSAGE_LENGTH = 65536;
+    if (length > MAX_MESSAGE_LENGTH) {
+        LN_WS_WARN("Message length (%zu) exceeds maximum (%zu), truncating\n", length, MAX_MESSAGE_LENGTH);
+        length = MAX_MESSAGE_LENGTH;
+    }
+    
     char *buf = (char *)malloc(length + 1);
     if (!buf) return;
     memcpy(buf, raw_data, length);
@@ -266,7 +277,29 @@ static void ln_ws_message_callback(const char *raw_data, uint32_t timestamp)
         ln_ws_incomplete_callback(raw_data,length,false);
         LN_WS_WARN("Invalid message\n");
     }
-    LN_WS_DEBUG("on_message: %s\n", buf);
+    
+    /* 打印消息内容(限制长度,避免打印超出实际数据范围的内容) */
+    /* 注意:buf 已经是以 \0 结尾的,length 是实际数据长度 */
+    if (length > 0 && length <= MAX_MESSAGE_LENGTH) {
+        /* 只打印实际数据长度,避免打印缓冲区中的额外数据 */
+        char *print_buf = (char *)malloc(length + 1);
+        if (print_buf) {
+            memcpy(print_buf, buf, length);
+            print_buf[length] = '\0';
+            LN_WS_DEBUG("on_message: %s\n", print_buf);
+            free(print_buf);
+        } else {
+            /* 如果分配失败,直接打印(但限制长度) */
+            const size_t MAX_PRINT_LEN = 256;
+            size_t print_len = (length < MAX_PRINT_LEN) ? length : MAX_PRINT_LEN;
+            char temp_buf[MAX_PRINT_LEN + 1];
+            memcpy(temp_buf, buf, print_len);
+            temp_buf[print_len] = '\0';
+            LN_WS_DEBUG("on_message: %s%s\n", temp_buf, (length > MAX_PRINT_LEN) ? "..." : "");
+        }
+    } else {
+        LN_WS_DEBUG("on_message: (empty or invalid length=%zu)\n", length);
+    }
 
     free(buf);
 }
@@ -276,8 +309,13 @@ static void ln_ws_event_callback(ws_event_type_t event_type)
 {
     switch (event_type) {
         case WS_EVENT_CONNECTING:
-            LN_WS_INFO("Event: Connecting\n");
-            g_ws_connected = WS_STATE_CONNECTING;
+            // 如果当前已经是CONNECTED状态,不应该被重置为CONNECTING
+            if (g_ws_connected != WS_STATE_CONNECTED) {
+                LN_WS_INFO("Event: Connecting\n");
+                g_ws_connected = WS_STATE_CONNECTING;
+            } else {
+                LN_WS_WARN("Event: Connecting ignored, already CONNECTED\n");
+            }
             break;
         case WS_EVENT_CONNECTED:
             LN_WS_INFO("Event: Connected\n");
@@ -314,15 +352,29 @@ static void ln_ws_event_callback(ws_event_type_t event_type)
 static void ln_ws_url_callback(char *url, size_t url_size, char *header_buf, size_t header_size)
 {
     const char *base = "ws://ambybox-api.test.leiniao.com/ambybox-device-api/v1/websocket"; 
+    // const char *base = "ws://snicker.com.cn:8910/ambybox-device-api/v1/websocket"; 
     const char *device_id = uapi_ln_get_device_id();
     const char *token = uapi_ln_get_device_token();
-    if (!device_id) device_id = "";
-    if (!token) token = "";
+    
+    /* 检查device_id和token是否有效 */
+    if (!device_id) {
+        device_id = "";
+        LN_WS_WARN("Device ID is NULL, using empty string\n");
+    }
+    
+    if (!token || strlen(token) == 0) {
+        LN_WS_WARN("Device token is empty or NULL, WebSocket connection will fail\n");
+        LN_WS_WARN("Please ensure token is obtained via HTTPS before connecting\n");
+        /* 即使token为空,也构建URL,但会在服务器端被拒绝(返回400) */
+        token = "";
+    }
     
     /* 仅使用 deviceToken 也可通过;额外带上 deviceId 与 reconnect 便于后端管理 */
     snprintf(url, url_size, "%s?deviceToken=%s&deviceId=%s&reconnect=%u",
              base, token, device_id, g_ws_reconnect_count);
     
+    LN_WS_DEBUG("WebSocket URL: %s\n", url);
+    
     // /* 构建头部信息 */
     snprintf(header_buf, header_size, "deviceId: %s\r\n", device_id);
     // 110.42.224.129 8910
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_http_download.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_http_download.c
new file mode 100644 (file)
index 0000000..41afaba
--- /dev/null
@@ -0,0 +1,891 @@
+/**
+ * @file ln_http_download.c
+ * @brief HTTP文件下载模块实现 - 使用统一的zhihui_net高级接口
+ */
+
+#include "ln_http_download.h"
+#include "wearable_log.h"
+#include "ln_api.h"  /* 用于 LN_CLIENT_PREPARE_SIZE 等常量 */
+#include "soc_osal.h"  /* 用于 osal_msleep */
+#include "custom_utils.h"  /* 用于 app_task_definition_t, USE_EXTEND_MEM 和 PM 投票接口 */
+#include "thread_init.h"  /* 确保包含 app_task_definition_t 和 USE_EXTEND_MEM */
+#include "cmsis_os2.h"  /* 用于 osPriorityNormal */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+/* 流式下载上下文结构(前向声明) */
+typedef struct {
+    FILE *fd;
+    size_t total_written;
+    int expected_content_length;
+    bool download_complete;
+    bool download_error;
+    size_t last_progress_bytes;  /* 上次更新进度时的字节数(用于写入任务中的进度跟踪) */
+} ln_http_download_ctx_t;
+
+/* HTTP下载数据块结构(用于队列传递) */
+typedef struct {
+    FILE *fd;                    /* 文件句柄 */
+    uint8_t *data;               /* 数据指针(动态分配) */
+    size_t length;               /* 数据长度 */
+    bool is_finished;            /* 是否最后一个数据块 */
+    ln_http_download_ctx_t *ctx; /* 下载上下文指针(用于更新统计) */
+} ln_http_download_block_t;
+
+/* 全局变量:文件写入队列和任务 */
+static void *g_http_download_queue = NULL;
+static app_task_definition_t g_http_download_write_taskcfg = {0};
+static int g_http_download_write_task_running = 0;
+
+/* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */
+int ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size)
+{
+    if (header_size < 2048)
+        header_size = 2048;
+    if (resp_size < 2048)
+        resp_size = 2048;
+    HTTPC_RESULT ret = ZhihuiHttpClientPrepare(clientData, header_size, resp_size);
+    if (ret == HTTP_SUCCESS)
+        return ret;
+    int tries = 0;
+    while (ret != HTTP_SUCCESS && tries < 3)
+    {
+        header_size = header_size < 4096 ? 4096 : header_size * 2;
+        resp_size = resp_size < (16 * 1024) ? (16 * 1024) : resp_size * 2;
+        if (resp_size > (256 * 1024))
+            resp_size = (256 * 1024);
+        ret = ZhihuiHttpClientPrepare(clientData, header_size, resp_size);
+        tries++;
+    }
+    return ret; 
+}
+
+/* 流式下载上下文结构(已在前面定义) */
+
+/* HTTP下载文件写入任务(从队列中取出数据块并写入文件) */
+static void ln_http_download_write_task_wrapper(void *arg)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] HTTP下载文件写入任务启动, queue=%p", g_http_download_queue);
+    
+    while (g_http_download_write_task_running)
+    {
+        ln_http_download_block_t block = {0};
+        uint32_t msgsize = sizeof(ln_http_download_block_t);
+        
+        /* 从队列中读取数据块(阻塞等待) */
+        errcode_t ret = osal_msg_queue_read_copy(g_http_download_queue, (void *)&block, &msgsize,
+                                                 OSAL_MSGQ_WAIT_FOREVER);
+        if (ret != OSAL_SUCCESS)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] Write task: queue read failed: ret=%d", ret);
+            LOS_Msleep(10);
+            continue;
+        }
+        
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] Write task: received block: length=%zu, fd=%p, ctx=%p", 
+                      block.length, block.fd, block.ctx);
+        
+        /* 检查是否是结束标记(fd为NULL表示结束) */
+        if (!block.fd || !block.data || block.length == 0)
+        {
+            if (block.data)
+            {
+                free(block.data);
+            }
+            continue;
+        }
+        
+        /* 写入文件 */
+        size_t written = fwrite(block.data, 1, block.length, block.fd);
+        if (written != block.length)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] Write task: write failed: written=%zu, expected=%zu", 
+                          written, block.length);
+            if (block.ctx)
+            {
+                block.ctx->download_error = true;
+            }
+        }
+        else
+        {
+            /* 强制刷新文件缓冲区,确保数据写入磁盘 */
+            fflush(block.fd);
+            
+            /* 更新统计 */
+            if (block.ctx)
+            {
+                block.ctx->total_written += written;
+                
+                /* 如果是最后一个数据块,标记下载完成 */
+                if (block.is_finished)
+                {
+                    block.ctx->download_complete = true;
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] Write task: download complete: %zu bytes written", 
+                                  block.ctx->total_written);
+                }
+                else
+                {
+                    /* 定期打印进度(每100KB或每1%,取较小值) */
+                    size_t progress_interval = 100 * 1024;  /* 100KB */
+                    if (block.ctx->expected_content_length > 0) {
+                        /* 如果有期望大小,使用1%的间隔 */
+                        size_t percent_interval = block.ctx->expected_content_length / 100;
+                        if (percent_interval < progress_interval) {
+                            progress_interval = percent_interval;
+                        }
+                    }
+                    
+                    /* 检查是否需要更新进度 */
+                    size_t last_progress = block.ctx->last_progress_bytes;
+                    if (block.ctx->total_written - last_progress >= progress_interval || 
+                        last_progress == 0) {
+                        block.ctx->last_progress_bytes = block.ctx->total_written;
+                        
+                        /* 计算百分比 */
+                        int percent = 0;
+                        if (block.ctx->expected_content_length > 0) {
+                            percent = (int)(((double)block.ctx->total_written / block.ctx->expected_content_length) * 100);
+                            if (percent > 100) percent = 100;
+                        }
+                        
+                        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] Write task: progress: %zu/%d bytes written (%d%%)", 
+                                      block.ctx->total_written, 
+                                      block.ctx->expected_content_length,
+                                      percent);
+                    }
+                }
+            }
+        }
+        
+        /* 释放数据内存 */
+        free(block.data);
+    }
+    
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] HTTP下载文件写入任务结束");
+}
+
+/* 初始化HTTP下载文件写入队列和任务 */
+static int ln_http_download_init_write_queue(void)
+{
+    if (g_http_download_queue != NULL)
+    {
+        return ERRCODE_SUCC;  /* 已经初始化 */
+    }
+    
+    /* 创建消息队列 */
+    if (osal_msg_queue_create("ln_http_download_queue", 32, &g_http_download_queue,
+                              0, sizeof(ln_http_download_block_t)) != ERRCODE_SUCC)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 创建HTTP下载队列失败");
+        return ERRCODE_FAIL;
+    }
+    
+    /* 创建文件写入任务 */
+    if (!g_http_download_write_task_running)
+    {
+        g_http_download_write_taskcfg.attr.name = "ln_http_download_write_task";
+        g_http_download_write_taskcfg.attr.priority = osPriorityNormal;
+        g_http_download_write_taskcfg.attr.stack_size = 0x2000;  /* 8KB栈空间 */
+        g_http_download_write_taskcfg.func = ln_http_download_write_task_wrapper;
+        g_http_download_write_taskcfg.use_mem = USE_EXTEND_MEM;  /* 使用扩展内存 */
+        g_http_download_write_taskcfg.create_queue_handle = NULL;
+        
+        errcode_t ret = uapi_create_osThread_custom(&g_http_download_write_taskcfg);
+        if (ret != ERRCODE_SUCC)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 创建HTTP下载写入任务失败: %d", ret);
+            return ERRCODE_FAIL;
+        }
+        
+        g_http_download_write_task_running = 1;
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] HTTP下载文件写入队列和任务初始化成功");
+    }
+    
+    return ERRCODE_SUCC;
+}
+
+/* 流式接收回调函数(在URC到达时立即调用,将数据放入队列而不是直接写入文件) */
+static int ln_http_download_stream_callback(void *user_data, 
+                                            const uint8_t *data, 
+                                            size_t length, 
+                                            bool is_finished)
+{
+    ln_http_download_ctx_t *ctx = (ln_http_download_ctx_t *)user_data;
+    
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] Stream callback called: length=%zu, is_finished=%d, ctx=%p, fd=%p", 
+                  length, is_finished, ctx, ctx ? ctx->fd : NULL);
+    
+    if (!ctx || !ctx->fd || !data || length == 0) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] Stream callback: invalid parameters");
+        return -1;
+    }
+    
+    /* 如果队列未初始化,降级处理:直接写入文件 */
+    if (g_http_download_queue == NULL)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 队列未初始化,直接写入文件");
+        /* 降级处理:直接写入文件 */
+        size_t written = fwrite(data, 1, length, ctx->fd);
+        if (written != length) {
+            ctx->download_error = true;
+            return -1;
+        }
+        ctx->total_written += written;
+        if (is_finished) {
+            ctx->download_complete = true;
+        }
+        return 0;
+    }
+    
+    /* 分配内存并复制数据(避免在PPP缓冲区处理上下文中长时间持有数据) */
+    /* 优化:使用calloc避免memset,减少栈使用 */
+    uint8_t *data_copy = (uint8_t *)malloc(length);
+    if (!data_copy)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 分配内存失败,直接写入文件");
+        /* 降级处理:直接写入文件 */
+        size_t written = fwrite(data, 1, length, ctx->fd);
+        if (written != length) {
+            ctx->download_error = true;
+            return -1;
+        }
+        ctx->total_written += written;
+        if (is_finished) {
+            ctx->download_complete = true;
+        }
+        return 0;
+    }
+    
+    memcpy(data_copy, data, length);
+    
+    /* 构造数据块(优化:使用局部变量而非结构体初始化,减少栈使用) */
+    /* 注意:osal_msg_queue_write_copy需要连续内存,所以不能直接传递指针 */
+    /* 使用静态缓冲区存储结构体,避免在栈上分配 */
+    static ln_http_download_block_t block_storage;  /* 静态存储,避免栈分配 */
+    ln_http_download_block_t *block = &block_storage;
+    block->fd = ctx->fd;
+    block->data = data_copy;
+    block->length = length;
+    block->is_finished = is_finished;
+    block->ctx = ctx;
+    
+    /* 将数据块放入队列(非阻塞,如果队列满则降级处理) */
+    errcode_t ret = osal_msg_queue_write_copy(g_http_download_queue, (void *)block,
+                                              sizeof(ln_http_download_block_t), 0);
+    if (ret != OSAL_SUCCESS)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 队列写入失败(队列可能已满,ret=%d),直接写入文件", ret);
+        /* 降级处理:直接写入文件并释放内存 */
+        size_t written = fwrite(data_copy, 1, length, ctx->fd);
+        free(data_copy);
+        if (written != length) {
+            ctx->download_error = true;
+            return -1;
+        }
+        ctx->total_written += written;
+        if (is_finished) {
+            ctx->download_complete = true;
+        }
+        return 0;
+    }
+    
+    /* 成功放入队列,返回0继续接收 */
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 数据块已放入队列: length=%zu, total_written=%zu, is_finished=%d, queue=%p", 
+                  length, ctx->total_written, is_finished, g_http_download_queue);
+    return 0;
+}
+
+/* 下载文件(基本功能) */
+int ln_http_download_file(const char *url, const char *save_path)
+{
+    if (!url || !save_path)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] url or save_path is NULL");
+        return ERRCODE_FAIL;
+    }
+
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 开始下载: %s -> %s", url, save_path);
+
+    /* 添加 PM 投票,确保 HTTP 下载过程中有足够的 CPU 资源 */
+    uapi_net_app_add_net_app_vote();
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 添加 PM_ID_NET_APP 投票,防止系统睡眠");
+
+    /* 确保队列和任务已初始化(在下载开始时初始化,而不是在回调中) */
+    if (ln_http_download_init_write_queue() != ERRCODE_SUCC)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 初始化写入队列失败");
+        /* 继续执行,回调中会降级处理 */
+    }
+
+    ZhihuiHttpClientHandle http_client = NULL;
+    HttpClientData client_data = {0};
+    FILE *fd = NULL;
+    int ret = ERRCODE_FAIL;
+    ln_http_download_ctx_t download_ctx = {0};
+    bool need_unlink = false;  /* 标记是否需要删除文件 */
+    
+    /* 打开文件准备写入 */
+    fd = fopen(save_path, "wb+");
+    if (!fd)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] fopen failed for %s", save_path);
+        /* 移除 PM 投票 */
+        uapi_net_app_remove_net_app_vote();
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 移除 PM_ID_NET_APP 投票(fopen失败)");
+        return ERRCODE_FAIL;
+    }
+
+    /* 准备HTTP客户端缓冲区(用于响应头解析) */
+    HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, 2048);
+    if (prep != HTTP_SUCCESS)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] prepare failed: %d", prep);
+        goto cleanup;
+    }
+
+    /* 初始化高级HTTP客户端 */
+    if (ZhihuiHttpClientInitAdvanced(&http_client) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] init failed");
+        goto cleanup;
+    }
+
+    /* 设置流式接收回调(在URC到达时立即写入文件) */
+    download_ctx.fd = fd;
+    download_ctx.total_written = 0;
+    download_ctx.expected_content_length = 0;
+    download_ctx.download_complete = false;
+    download_ctx.download_error = false;
+    download_ctx.last_progress_bytes = 0;
+    
+    if (ZhihuiHttpClientSetStreamCallback(http_client, 
+                                         ln_http_download_stream_callback, 
+                                         &download_ctx) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] set stream callback failed");
+        goto cleanup;
+    }
+
+    /* 设置URL */
+    if (ZhihuiHttpClientSetUrl(http_client, url, 0) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] set url failed");
+        need_unlink = true;  /* 下载失败,需要删除文件 */
+        goto cleanup;
+    }
+
+    /* 建立连接(自动处理HTTPS证书) */
+    if (ZhihuiHttpClientSetupConnection(http_client) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] setup connection failed");
+        need_unlink = true;  /* 下载失败,需要删除文件 */
+        goto cleanup;
+    }
+
+    /* 发送GET请求(响应头会立即解析,响应体数据会通过回调写入文件) */
+    if (ZhihuiHttpClientGet(http_client, NULL, &client_data) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] send GET request failed");
+        need_unlink = true;  /* 下载失败,需要删除文件 */
+        goto cleanup;
+    }
+
+    /* 检查响应码 */
+    int response_code = ZhihuiHttpClientGetResponseCode(http_client);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] response code: %d", response_code);
+    
+    if (response_code != 200)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] HTTP error: %d", response_code);
+        need_unlink = true;  /* 下载失败,需要删除文件 */
+        goto cleanup;
+    }
+
+    /* 保存期望的内容长度(用于验证) */
+    download_ctx.expected_content_length = client_data.responseContentLen;
+    
+    /* 流式模式下,数据已经通过回调写入文件 */
+    /* 需要等待所有数据接收完成(通过循环调用 ZhihuiHttpClientRecvResponseAdvanced 等待后续URC) */
+    size_t last_logged_bytes = download_ctx.total_written;
+    int wait_count = 0;
+    while (client_data.isMore && !download_ctx.download_complete && !download_ctx.download_error)
+    {
+        /* 等待后续URC数据到达(数据会通过回调写入文件) */
+        int recv_res = ZhihuiHttpClientRecvResponseAdvanced(http_client, &client_data);
+        if (recv_res != HTTP_SUCCESS && recv_res != HTTP_EAGAIN)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] recv failed: %d", recv_res);
+            download_ctx.download_error = true;
+            break;
+        }
+        
+        /* 检查是否完成(通过回调函数设置的标志) */
+        if (download_ctx.download_complete) {
+            break;
+        }
+        
+        /* 定期打印进度(每500ms或每100KB增加时) */
+        wait_count++;
+        if (wait_count % 50 == 0 || (download_ctx.total_written - last_logged_bytes) >= (100 * 1024)) {
+            /* 检查实际文件大小(通过ftell) */
+            long file_pos = -1;
+            if (download_ctx.fd) {
+                file_pos = ftell(download_ctx.fd);
+            }
+            
+            int percent = 0;
+            if (download_ctx.expected_content_length > 0) {
+                percent = (int)(((double)download_ctx.total_written / download_ctx.expected_content_length) * 100);
+                if (percent > 100) percent = 100;
+            }
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 下载进度: total_written=%zu, file_pos=%ld, expected=%d bytes (%d%%), isMore=%d", 
+                          download_ctx.total_written, 
+                          file_pos,
+                          download_ctx.expected_content_length,
+                          percent,
+                          client_data.isMore);
+            last_logged_bytes = download_ctx.total_written;
+        }
+        
+        /* 短暂延迟,避免CPU占用过高 */
+        osal_msleep(10);
+    }
+
+    /* 检查下载是否成功 */
+    if (download_ctx.download_error) {
+        ret = ERRCODE_FAIL;
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 文件下载失败(写入错误),删除不完整文件: %s", save_path);
+        need_unlink = true;
+    } else if (download_ctx.download_complete || !client_data.isMore) {
+        /* 验证下载完整性 */
+        /* 注意:download_complete 可能在没有 Content-Length 时被错误设置为 true(第一个数据块) */
+        /* 因此,需要同时检查 isMore 是否为 false,确保所有数据都已接收 */
+        if (download_ctx.expected_content_length > 0) {
+            /* 有 Content-Length,验证长度是否匹配 */
+            if (download_ctx.total_written == (size_t)download_ctx.expected_content_length) {
+                ret = ERRCODE_SUCC;
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 文件下载成功: %zu bytes", download_ctx.total_written);
+            } else {
+                ret = ERRCODE_FAIL;
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 文件下载失败(长度不匹配),删除不完整文件: %s", save_path);
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] Expected: %d, Actual: %zu", 
+                              download_ctx.expected_content_length, download_ctx.total_written);
+                need_unlink = true;
+            }
+        } else {
+            /* 没有 Content-Length,需要确保 isMore 为 false(所有数据已接收) */
+            if (!client_data.isMore) {
+                /* isMore 为 false,说明服务器已关闭连接,所有数据已接收 */
+                ret = ERRCODE_SUCC;
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 文件下载成功: %zu bytes (no Content-Length, connection closed)", 
+                              download_ctx.total_written);
+            } else {
+                /* isMore 仍为 true,说明还有更多数据,但 download_complete 被错误设置为 true */
+                ret = ERRCODE_FAIL;
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 文件下载失败(未完成,isMore=true),删除不完整文件: %s", save_path);
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] Written: %zu bytes, isMore: %d", 
+                              download_ctx.total_written, client_data.isMore);
+                need_unlink = true;
+            }
+        }
+    } else {
+        ret = ERRCODE_FAIL;
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 文件下载失败(未完成),删除不完整文件: %s", save_path);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] download_complete: %d, isMore: %d", 
+                      download_ctx.download_complete, client_data.isMore);
+        need_unlink = true;
+    }
+
+cleanup:
+    /* 统一的资源清理路径,确保HTTP客户端完整生命周期结束 */
+    /* 注意:清理顺序很重要,必须先禁用回调,再关闭HTTP客户端,最后释放缓冲区 */
+    
+    /* 1. 禁用流式回调(如果已设置) */
+    if (http_client != NULL)
+    {
+        ZhihuiHttpClientSetStreamCallback(http_client, NULL, NULL);
+    }
+    
+    /* 2. 关闭HTTP客户端(确保g_http_client被正确清理) */
+    if (http_client != NULL)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 关闭HTTP客户端,确保完整生命周期结束");
+        ZhihuiHttpClientClose(http_client);
+        http_client = NULL;  /* 防止重复关闭 */
+    }
+    
+    /* 3. 释放HTTP客户端缓冲区 */
+    ZhihuiHttpClientUnprepare(&client_data);
+    
+    /* 4. 关闭文件句柄 */
+    if (fd != NULL)
+    {
+        fclose(fd);
+        fd = NULL;
+    }
+    
+    /* 5. 如果下载失败,删除不完整的文件 */
+    if (need_unlink && save_path != NULL)
+    {
+        unlink(save_path);
+    }
+    
+    /* 6. 移除 PM 投票,允许系统进入睡眠 */
+    uapi_net_app_remove_net_app_vote();
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 移除 PM_ID_NET_APP 投票,允许系统睡眠");
+    
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] HTTP客户端完整生命周期结束,返回码: %d", ret);
+    
+    return ret;
+}
+
+/* 下载文件(带重试) */
+int ln_http_download_with_retry(const char *url, const char *save_path, int max_retries)
+{
+    if (url == NULL || save_path == NULL || (max_retries <= 0 || max_retries > 10))
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 参数无效");
+        return ERRCODE_FAIL;
+    }
+    
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 开始下载(带重试): %s -> %s", url, save_path);
+    
+    int ret = ln_http_download_file(url, save_path);
+    if (ERRCODE_SUCC != ret)
+    {
+        for (int i = 1; i <= max_retries; i++)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 重试 %d/%d", i, max_retries);
+            ret = ln_http_download_file(url, save_path);
+            if (ERRCODE_SUCC == ret)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 重试成功 %d", i);
+                break;
+            }
+        }
+    }
+    
+    return ret;
+}
+
+/* 下载文件(支持断点续传) */
+int ln_http_download_file_resume(const char *url, const char *save_path)
+{
+    if (!url || !save_path)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] url or save_path is NULL");
+        return ERRCODE_FAIL;
+    }
+
+    long long resume_offset = 0;
+    /* 判断是否存在部分文件,计算续传偏移 */
+    {
+        FILE *fexist = fopen(save_path, "rb");
+        if (fexist)
+        {
+            if (fseek(fexist, 0, SEEK_END) == 0)
+            {
+                long pos = ftell(fexist);
+                if (pos > 0)
+                    resume_offset = (long long)pos;
+            }
+            fclose(fexist);
+        }
+    }
+
+    ZhihuiHttpClientHandle http_client = NULL;
+    HttpClientData client_data = {0};
+    FILE *fd = NULL;
+    char *header_buf = NULL;
+    int ret = ERRCODE_FAIL;
+    size_t total_written = 0;
+    ln_http_download_ctx_t download_ctx = {0};
+
+    if (resume_offset > 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 断点续传,resume_offset=%lld", resume_offset);
+    }
+    
+    /* 添加 PM 投票,确保 HTTP 下载过程中有足够的 CPU 资源 */
+    uapi_net_app_add_net_app_vote();
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 添加 PM_ID_NET_APP 投票,防止系统睡眠");
+    
+    /* 以追加模式打开(若服务端返回200将切换为覆盖写)*/
+    fd = fopen(save_path, resume_offset > 0 ? "ab+" : "wb+");
+    if (!fd)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] fopen failed for %s", save_path);
+        return ERRCODE_FAIL;
+    }
+
+    /* 准备HTTP客户端缓冲区 */
+    HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, LN_CLIENT_PREPARE_SIZE * 2);
+    if (prep != HTTP_SUCCESS)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] prepare failed: %d", prep);
+        goto cleanup;
+    }
+
+    /* 初始化高级HTTP客户端 */
+    if (ZhihuiHttpClientInitAdvanced(&http_client) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] init failed");
+        goto cleanup;
+    }
+
+    /* 设置URL */
+    if (ZhihuiHttpClientSetUrl(http_client, url, 0) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] set url failed");
+        goto cleanup;
+    }
+
+    /* 设置Range请求头(仅当存在偏移时)*/
+    if (resume_offset > 0)
+    {
+        header_buf = (char *)malloc(128);
+        if (!header_buf)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] malloc header_buf failed");
+            goto cleanup;
+        }
+        int hdr = snprintf(header_buf, 128, "Range: bytes=%lld-\r\n", resume_offset);
+        if (hdr <= 0)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] snprintf header_buf failed");
+            goto cleanup;
+        }
+        ZhihuiHttpClientSetHeader(http_client, header_buf);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] set header %s", header_buf);
+    }
+
+    /* 确保队列和任务已初始化(在下载开始时初始化,而不是在回调中) */
+    if (ln_http_download_init_write_queue() != ERRCODE_SUCC)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 初始化写入队列失败");
+        /* 继续执行,回调中会降级处理 */
+    }
+
+    /* 设置流式接收回调(在URC到达时立即写入文件) */
+    download_ctx.fd = fd;
+    download_ctx.total_written = resume_offset;  /* 初始化为续传偏移 */
+    download_ctx.expected_content_length = 0;
+    download_ctx.download_complete = false;
+    download_ctx.download_error = false;
+    download_ctx.last_progress_bytes = resume_offset;  /* 初始化进度跟踪 */
+    
+    if (ZhihuiHttpClientSetStreamCallback(http_client, 
+                                         ln_http_download_stream_callback, 
+                                         &download_ctx) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] set stream callback failed");
+        goto cleanup;
+    }
+
+    /* 建立连接(自动处理HTTPS证书) */
+    if (ZhihuiHttpClientSetupConnection(http_client) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] setup connection failed");
+        goto cleanup;
+    }
+
+    /* 发送GET请求(响应头会立即解析,响应体数据会通过回调写入文件) */
+    if (ZhihuiHttpClientGet(http_client, NULL, &client_data) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] send GET request failed");
+        goto cleanup;
+    }
+
+    /* 首次接收,判定状态码(200则改为覆盖写)*/
+    int code = ZhihuiHttpClientGetResponseCode(http_client);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] response code: %d (offset=%lld)", code, resume_offset);
+    if (code == 200 && resume_offset > 0)
+    {
+        /* 服务端未按Range返回,改为覆盖写 */
+        fclose(fd);
+        fd = NULL;
+        fd = fopen(save_path, "wb+");
+        if (!fd)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] fopen failed for %s (overwrite mode)", save_path);
+            goto cleanup;
+        }
+        resume_offset = 0;
+        download_ctx.fd = fd;  /* 更新文件句柄 */
+        download_ctx.total_written = 0;  /* 重置写入计数 */
+    }
+
+    /* 保存期望的内容长度(用于验证) */
+    download_ctx.expected_content_length = client_data.responseContentLen;
+    
+    /* 流式模式下,数据已经通过回调写入文件 */
+    /* 需要等待所有数据接收完成(通过循环调用 ZhihuiHttpClientRecvResponseAdvanced 等待后续URC) */
+    size_t last_logged_bytes = download_ctx.total_written;
+    int wait_count = 0;
+    while (client_data.isMore && !download_ctx.download_complete && !download_ctx.download_error)
+    {
+        /* 等待后续URC数据到达(数据会通过回调写入文件) */
+        int recv_res = ZhihuiHttpClientRecvResponseAdvanced(http_client, &client_data);
+        if (recv_res != HTTP_SUCCESS && recv_res != HTTP_EAGAIN)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] recv failed: %d", recv_res);
+            download_ctx.download_error = true;
+            break;
+        }
+        
+        /* 检查是否完成(通过回调函数设置的标志) */
+        if (download_ctx.download_complete) {
+            break;
+        }
+        
+        /* 定期打印进度(每500ms或每100KB增加时) */
+        wait_count++;
+        if (wait_count % 50 == 0 || (download_ctx.total_written - last_logged_bytes) >= (100 * 1024)) {
+            /* 检查实际文件大小(通过ftell) */
+            long file_pos = -1;
+            if (download_ctx.fd) {
+                file_pos = ftell(download_ctx.fd);
+            }
+            
+            int percent = 0;
+            if (download_ctx.expected_content_length > 0) {
+                percent = (int)(((double)download_ctx.total_written / download_ctx.expected_content_length) * 100);
+                if (percent > 100) percent = 100;
+            }
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 下载进度: total_written=%zu, file_pos=%ld, expected=%d bytes (%d%%), isMore=%d", 
+                          download_ctx.total_written, 
+                          file_pos,
+                          download_ctx.expected_content_length,
+                          percent,
+                          client_data.isMore);
+            last_logged_bytes = download_ctx.total_written;
+        }
+        
+        /* 短暂延迟,避免CPU占用过高 */
+        osal_msleep(10);
+    }
+    
+    /* 使用回调中的写入计数 */
+    total_written = download_ctx.total_written;
+
+    /* 禁用流式回调(必须在关闭HTTP客户端之前) */
+    ZhihuiHttpClientSetStreamCallback(http_client, NULL, NULL);
+
+    /* 验证下载完整性 */
+    int final_code = ZhihuiHttpClientGetResponseCode(http_client);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] final code=%d, total_written=%zu", final_code, total_written);
+    
+    /* 检查下载是否成功 */
+    if (download_ctx.download_error) {
+        ret = ERRCODE_FAIL;
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 文件下载失败(写入错误),保留部分文件供下次续传");
+    } else if (download_ctx.download_complete || !client_data.isMore) {
+        /* 验证下载完整性 */
+        if (download_ctx.expected_content_length > 0) {
+            if (total_written == (size_t)download_ctx.expected_content_length) {
+                ret = ERRCODE_SUCC;
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 下载成功 (%s): %zu bytes", 
+                             resume_offset > 0 ? "续传" : "全量", total_written);
+            } else {
+                ret = ERRCODE_FAIL;
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 下载失败(长度不匹配),保留部分文件供下次续传");
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] Expected: %d, Actual: %zu", 
+                             download_ctx.expected_content_length, total_written);
+            }
+        } else {
+            /* 没有 Content-Length,需要确保 isMore 为 false(所有数据已接收) */
+            if (!client_data.isMore) {
+                ret = ERRCODE_SUCC;
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 下载成功 (%s): %zu bytes (no Content-Length, connection closed)", 
+                             resume_offset > 0 ? "续传" : "全量", total_written);
+            } else {
+                ret = ERRCODE_FAIL;
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 下载失败(未完成,isMore=true),保留部分文件供下次续传");
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] Written: %zu bytes, isMore: %d", 
+                             total_written, client_data.isMore);
+            }
+        }
+    } else {
+        ret = ERRCODE_FAIL;
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 下载失败(未完成),保留部分文件供下次续传");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] download_complete: %d, isMore: %d", 
+                     download_ctx.download_complete, client_data.isMore);
+    }
+
+cleanup:
+    /* 统一的资源清理路径,确保HTTP客户端完整生命周期结束 */
+    /* 注意:清理顺序很重要,必须先禁用回调,再关闭HTTP客户端,最后释放缓冲区 */
+    
+    /* 1. 禁用流式回调(如果已设置) */
+    if (http_client != NULL)
+    {
+        ZhihuiHttpClientSetStreamCallback(http_client, NULL, NULL);
+    }
+    
+    /* 2. 关闭HTTP客户端(确保g_http_client被正确清理) */
+    if (http_client != NULL)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 关闭HTTP客户端,确保完整生命周期结束");
+        ZhihuiHttpClientClose(http_client);
+        http_client = NULL;  /* 防止重复关闭 */
+    }
+    
+    /* 3. 释放HTTP客户端缓冲区 */
+    ZhihuiHttpClientUnprepare(&client_data);
+    
+    /* 4. 释放请求头缓冲区 */
+    if (header_buf != NULL)
+    {
+        free(header_buf);
+        header_buf = NULL;
+    }
+    
+    /* 5. 关闭文件句柄 */
+    if (fd != NULL)
+    {
+        fclose(fd);
+        fd = NULL;
+    }
+    
+    /* 6. 移除 PM 投票,允许系统进入睡眠 */
+    uapi_net_app_remove_net_app_vote();
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 移除 PM_ID_NET_APP 投票,允许系统睡眠");
+    
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] HTTP客户端完整生命周期结束,返回码: %d", ret);
+    
+    return ret;
+}
+
+/* 下载文件(支持断点续传,带重试) */
+int ln_http_download_file_resume_with_retry(const char *url, const char *save_path, int max_retries)
+{
+    if (url == NULL || save_path == NULL || (max_retries <= 0 || max_retries > 10))
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME_WITH_RETRY] 参数无效");
+        return ERRCODE_FAIL;
+    }
+    
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME_WITH_RETRY] 开始下载(带重试): %s -> %s", url, save_path);
+    
+    int ret = ln_http_download_file_resume(url, save_path);
+    if (ERRCODE_SUCC != ret)
+    {
+        for (int i = 1; i <= max_retries; i++)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME_WITH_RETRY] 重试 %d/%d", i, max_retries);
+            ret = ln_http_download_file_resume(url, save_path);
+            if (ERRCODE_SUCC == ret)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME_WITH_RETRY] 重试成功 %d", i);
+                break;
+            }
+        }
+    }
+    
+    return ret;
+}
+
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_http_download.h b/code/sdk/zhihui/leiniao/imsl_ln/ln_http_download.h
new file mode 100644 (file)
index 0000000..2d16b91
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * @file ln_http_download.h
+ * @brief HTTP文件下载模块 - 使用统一的zhihui_net高级接口
+ * 
+ * 此模块提供HTTP/HTTPS文件下载功能,支持:
+ * - 基本下载
+ * - 带重试的下载
+ * - 断点续传下载
+ * - 带重试的断点续传下载
+ * 
+ * 所有下载功能均使用统一的zhihui_net高级接口,支持流式下载以节省内存。
+ */
+
+#ifndef LN_HTTP_DOWNLOAD_H
+#define LN_HTTP_DOWNLOAD_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "errcode.h"
+#include "zhihui_net_http.h"
+
+/**
+ * @brief 下载文件(基本功能)
+ * @param url 文件URL
+ * @param save_path 保存路径
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ * @note 如果下载失败,会自动删除不完整的文件
+ */
+int ln_http_download_file(const char *url, const char *save_path);
+
+/**
+ * @brief 下载文件(带重试)
+ * @param url 文件URL
+ * @param save_path 保存路径
+ * @param max_retries 最大重试次数(1-10)
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ */
+int ln_http_download_with_retry(const char *url, const char *save_path, int max_retries);
+
+/**
+ * @brief 下载文件(支持断点续传)
+ * @param url 文件URL
+ * @param save_path 保存路径(若存在则尝试从已下载大小处续传)
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败(失败不删除部分文件)
+ * @note 如果服务端不支持Range请求(返回200而非206),会从头开始下载
+ */
+int ln_http_download_file_resume(const char *url, const char *save_path);
+
+/**
+ * @brief 下载文件(支持断点续传,带重试)
+ * @param url 文件URL
+ * @param save_path 保存路径
+ * @param max_retries 最大重试次数(1-10)
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ */
+int ln_http_download_file_resume_with_retry(const char *url, const char *save_path, int max_retries);
+
+/**
+ * @brief 自适应申请HTTP客户端缓冲区
+ * @param clientData HTTP客户端数据结构
+ * @param header_size 响应头缓冲区大小(最小2048)
+ * @param resp_size 响应体缓冲区大小(最小2048)
+ * @return HTTP_SUCCESS 成功,其他值失败
+ * @note 如果申请失败,会自动扩容重试(最多3次)
+ */
+int ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LN_HTTP_DOWNLOAD_H */
+
diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/at_socket.py b/code/sdk/zhihui/leiniao/leiniao_python_test/at_socket.py
new file mode 100644 (file)
index 0000000..69f1cd4
--- /dev/null
@@ -0,0 +1,375 @@
+#!/usr/bin/env python3\r
+# -*- coding: utf-8 -*-\r
+"""\r
+基于AT指令的标准Socket接口实现\r
+提供类似Python标准socket.socket的接口,支持TCP和TCP+SSL\r
+"""\r
+\r
+import socket as std_socket\r
+import time\r
+from n706a_tcp_client import N706ATCPClient\r
+from n706a_at_handler import ATCommandHandler\r
+\r
+\r
+class ATSocket:\r
+    """\r
+    基于AT指令的标准Socket接口\r
+    提供类似Python标准socket.socket的接口,支持TCP和TCP+SSL\r
+    """\r
+    \r
+    # Socket类型常量(兼容标准socket)\r
+    AF_INET = std_socket.AF_INET\r
+    SOCK_STREAM = std_socket.SOCK_STREAM\r
+    \r
+    def __init__(self, family=AF_INET, type=SOCK_STREAM, at_handler=None, socket_id=0):\r
+        """\r
+        创建AT Socket对象\r
+        \r
+        Args:\r
+            family: 地址族(目前仅支持AF_INET)\r
+            type: Socket类型(目前仅支持SOCK_STREAM/TCP)\r
+            at_handler: ATCommandHandler实例(如果为None,需要后续调用set_at_handler设置)\r
+            socket_id: Socket ID(0-5),默认0\r
+        """\r
+        if family != self.AF_INET:\r
+            raise ValueError(f"不支持的地址族: {family},目前仅支持AF_INET")\r
+        if type != self.SOCK_STREAM:\r
+            raise ValueError(f"不支持的Socket类型: {type},目前仅支持SOCK_STREAM")\r
+        \r
+        self.family = family\r
+        self.type = type\r
+        self.at_handler = at_handler\r
+        self.socket_id = socket_id\r
+        self._tcp_client = None\r
+        self._timeout = None\r
+        self._connected = False\r
+        self._use_ssl = False\r
+        self._ssl_config = None\r
+        \r
+        # 如果提供了at_handler,立即创建TCP客户端\r
+        if at_handler:\r
+            self._tcp_client = N706ATCPClient(at_handler, socket_id=socket_id)\r
+    \r
+    def set_at_handler(self, at_handler):\r
+        """\r
+        设置AT指令处理器(如果初始化时未提供)\r
+        \r
+        Args:\r
+            at_handler: ATCommandHandler实例\r
+        """\r
+        self.at_handler = at_handler\r
+        if not self._tcp_client:\r
+            self._tcp_client = N706ATCPClient(at_handler, socket_id=self.socket_id)\r
+    \r
+    def settimeout(self, timeout):\r
+        """\r
+        设置socket超时时间\r
+        \r
+        Args:\r
+            timeout: 超时时间(秒),None表示阻塞模式\r
+        """\r
+        self._timeout = timeout\r
+    \r
+    def gettimeout(self):\r
+        """\r
+        获取socket超时时间\r
+        \r
+        Returns:\r
+            float或None: 超时时间(秒),None表示阻塞模式\r
+        """\r
+        return self._timeout\r
+    \r
+    def configure_ssl(self, ssl_version=3, auth_mode=0, ca_cert=None, client_cert=None, client_key=None):\r
+        """\r
+        配置SSL/TLS参数(用于TCP+SSL连接)\r
+        \r
+        Args:\r
+            ssl_version: SSL版本,0=SSL3.0, 1=TLS1.0, 2=TLS1.1, 3=TLS1.2, 4=TLS1.3\r
+            auth_mode: 认证模式,0=不认证, 1=单向认证, 2=双向认证\r
+            ca_cert: CA证书文件名(单向/双向认证需要)\r
+            client_cert: 客户端证书文件名(双向认证需要)\r
+            client_key: 客户端密钥文件名(双向认证需要)\r
+        \r
+        Returns:\r
+            bool: 是否配置成功\r
+        """\r
+        if not self._tcp_client:\r
+            raise RuntimeError("AT handler未设置,无法配置SSL")\r
+        \r
+        self._use_ssl = True\r
+        self._ssl_config = {\r
+            'ssl_version': ssl_version,\r
+            'auth_mode': auth_mode,\r
+            'ca_cert': ca_cert,\r
+            'client_cert': client_cert,\r
+            'client_key': client_key\r
+        }\r
+        \r
+        return self._tcp_client.configure_ssl(\r
+            ssl_version=ssl_version,\r
+            auth_mode=auth_mode,\r
+            ca_cert=ca_cert,\r
+            client_cert=client_cert,\r
+            client_key=client_key\r
+        )\r
+    \r
+    def connect(self, address):\r
+        """\r
+        连接到服务器\r
+        \r
+        Args:\r
+            address: (host, port) 元组,host可以是IP地址或域名\r
+        \r
+        Raises:\r
+            OSError: 连接失败时抛出\r
+        """\r
+        if not self._tcp_client:\r
+            raise RuntimeError("AT handler未设置,无法连接")\r
+        \r
+        if len(address) != 2:\r
+            raise ValueError("address必须是(host, port)元组")\r
+        \r
+        host, port = address\r
+        \r
+        # 配置SSL(如果需要)\r
+        if self._use_ssl and self._ssl_config:\r
+            if not self._tcp_client.configure_ssl(**self._ssl_config):\r
+                raise OSError("SSL配置失败")\r
+        \r
+        # 建立连接\r
+        if not self._tcp_client.connect(\r
+            server_ip=host,\r
+            server_port=port,\r
+            use_ssl=self._use_ssl,\r
+            transparent_mode=False  # 非透传模式\r
+        ):\r
+            raise OSError("连接失败")\r
+        \r
+        self._connected = True\r
+    \r
+    def send(self, data, flags=0):\r
+        """\r
+        发送数据\r
+        \r
+        Args:\r
+            data: 要发送的数据(bytes)\r
+            flags: 标志位(目前未使用,为兼容性保留)\r
+        \r
+        Returns:\r
+            int: 实际发送的字节数\r
+        \r
+        Raises:\r
+            OSError: 发送失败时抛出\r
+        """\r
+        if not self._connected:\r
+            raise OSError("Socket未连接")\r
+        \r
+        if not isinstance(data, bytes):\r
+            raise TypeError("data必须是bytes类型")\r
+        \r
+        if self._tcp_client.send(data):\r
+            return len(data)\r
+        else:\r
+            raise OSError("数据发送失败")\r
+    \r
+    def recv(self, bufsize, flags=0):\r
+        """\r
+        接收数据\r
+        \r
+        Args:\r
+            bufsize: 最大接收字节数\r
+            flags: 标志位(目前未使用,为兼容性保留)\r
+        \r
+        Returns:\r
+            bytes: 接收到的数据,如果连接关闭返回空bytes\r
+        \r
+        Raises:\r
+            OSError: 接收失败时抛出\r
+            socket.timeout: 超时时抛出\r
+        """\r
+        if not self._connected:\r
+            raise OSError("Socket未连接")\r
+        \r
+        timeout = self._timeout if self._timeout is not None else 30\r
+        \r
+        try:\r
+            data = self._tcp_client.receive(max_length=min(bufsize, 2048), timeout=timeout)\r
+            if data is None:\r
+                # 超时或连接关闭\r
+                if self._timeout is not None:\r
+                    raise std_socket.timeout("接收超时")\r
+                return b""\r
+            return data\r
+        except Exception as e:\r
+            if isinstance(e, std_socket.timeout):\r
+                raise\r
+            raise OSError(f"接收数据失败: {e}")\r
+    \r
+    def recv_into(self, buffer, nbytes=0, flags=0):\r
+        """\r
+        接收数据到缓冲区\r
+        \r
+        Args:\r
+            buffer: 接收缓冲区(可写字节对象)\r
+            nbytes: 最大接收字节数(0表示使用buffer长度)\r
+            flags: 标志位(目前未使用,为兼容性保留)\r
+        \r
+        Returns:\r
+            int: 实际接收的字节数\r
+        """\r
+        if nbytes == 0:\r
+            nbytes = len(buffer)\r
+        \r
+        data = self.recv(nbytes, flags)\r
+        n = len(data)\r
+        buffer[:n] = data\r
+        return n\r
+    \r
+    def close(self):\r
+        """\r
+        关闭socket连接\r
+        """\r
+        if self._connected and self._tcp_client:\r
+            self._tcp_client.close()\r
+        self._connected = False\r
+    \r
+    def shutdown(self, how):\r
+        """\r
+        关闭socket的发送/接收功能\r
+        \r
+        Args:\r
+            how: SHUT_RD, SHUT_WR, 或 SHUT_RDWR\r
+        """\r
+        # AT指令不支持部分关闭,直接关闭连接\r
+        self.close()\r
+    \r
+    def getpeername(self):\r
+        """\r
+        获取远程地址\r
+        \r
+        Returns:\r
+            tuple: (host, port) 元组\r
+        """\r
+        if not self._connected or not self._tcp_client:\r
+            raise OSError("Socket未连接")\r
+        \r
+        return (self._tcp_client.server_ip, self._tcp_client.server_port)\r
+    \r
+    def getsockname(self):\r
+        """\r
+        获取本地地址(AT指令不支持,返回默认值)\r
+        \r
+        Returns:\r
+            tuple: (host, port) 元组\r
+        """\r
+        # AT指令无法获取本地地址,返回默认值\r
+        return ("0.0.0.0", 0)\r
+    \r
+    def fileno(self):\r
+        """\r
+        获取文件描述符(AT Socket不支持,抛出异常)\r
+        \r
+        Raises:\r
+            OSError: AT Socket不支持文件描述符\r
+        """\r
+        raise OSError("AT Socket不支持文件描述符")\r
+    \r
+    def setblocking(self, flag):\r
+        """\r
+        设置阻塞/非阻塞模式\r
+        \r
+        Args:\r
+            flag: True表示阻塞模式,False表示非阻塞模式\r
+        """\r
+        if flag:\r
+            self._timeout = None\r
+        else:\r
+            # 非阻塞模式使用很小的超时时间\r
+            self._timeout = 0.0\r
+    \r
+    def getblocking(self):\r
+        """\r
+        获取阻塞/非阻塞模式\r
+        \r
+        Returns:\r
+            bool: True表示阻塞模式,False表示非阻塞模式\r
+        """\r
+        return self._timeout is None\r
+    \r
+    def makefile(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None):\r
+        """\r
+        创建文件对象(AT Socket不支持,抛出异常)\r
+        \r
+        Raises:\r
+            OSError: AT Socket不支持makefile\r
+        """\r
+        raise OSError("AT Socket不支持makefile")\r
+    \r
+    def __enter__(self):\r
+        """上下文管理器入口"""\r
+        return self\r
+    \r
+    def __exit__(self, exc_type, exc_val, exc_tb):\r
+        """上下文管理器出口"""\r
+        self.close()\r
+        return False\r
+    \r
+    def __del__(self):\r
+        """析构函数"""\r
+        try:\r
+            self.close()\r
+        except:\r
+            pass\r
+\r
+\r
+# 兼容标准socket模块的函数\r
+def create_connection(address, timeout=None, source_address=None, at_handler=None, socket_id=0, use_ssl=False, ssl_config=None):\r
+    """\r
+    创建TCP连接(兼容标准socket.create_connection)\r
+    \r
+    Args:\r
+        address: (host, port) 元组\r
+        timeout: 超时时间(秒)\r
+        source_address: 源地址(AT Socket不支持,忽略)\r
+        at_handler: ATCommandHandler实例(必需)\r
+        socket_id: Socket ID(0-5),默认0\r
+        use_ssl: 是否使用SSL/TLS,默认False\r
+        ssl_config: SSL配置字典,包含ssl_version, auth_mode, ca_cert等\r
+    \r
+    Returns:\r
+        ATSocket: 已连接的AT Socket对象\r
+    \r
+    Raises:\r
+        OSError: 连接失败时抛出\r
+    """\r
+    if at_handler is None:\r
+        raise ValueError("at_handler参数是必需的")\r
+    \r
+    sock = ATSocket(at_handler=at_handler, socket_id=socket_id)\r
+    \r
+    if timeout is not None:\r
+        sock.settimeout(timeout)\r
+    \r
+    if use_ssl and ssl_config:\r
+        sock.configure_ssl(**ssl_config)\r
+    \r
+    sock.connect(address)\r
+    return sock\r
+\r
+\r
+def socket(family=ATSocket.AF_INET, type=ATSocket.SOCK_STREAM, proto=0, at_handler=None, socket_id=0):\r
+    """\r
+    创建AT Socket对象(兼容标准socket.socket)\r
+    \r
+    Args:\r
+        family: 地址族(目前仅支持AF_INET)\r
+        type: Socket类型(目前仅支持SOCK_STREAM)\r
+        proto: 协议(未使用)\r
+        at_handler: ATCommandHandler实例(可选,可后续设置)\r
+        socket_id: Socket ID(0-5),默认0\r
+    \r
+    Returns:\r
+        ATSocket: AT Socket对象\r
+    """\r
+    return ATSocket(family=family, type=type, at_handler=at_handler, socket_id=socket_id)\r
+\r
index d68a08e..4bb0dc3 100644 (file)
@@ -16,7 +16,7 @@ SERVER_HOST = "snicker.com.cn"
 SERVER_PORT = 8910
 
 # AT模块串口配置(根据实际硬件配置修改)
-SERIAL_PORT = "COM19"  # 修改为实际的串口设备
+SERIAL_PORT = "COM8"  # 修改为实际的串口设备
 SERIAL_BAUDRATE = 1000000
 SERIAL_TIMEOUT = 10
 
@@ -80,12 +80,12 @@ def connect_test_server():
         print("\n" + "=" * 50)
         print("【步骤2: 配置网络省电模式】")
         print("=" * 50)
-        success, response = at_handler.send_at_command("AT+NWENPWRSAVE=1", timeout=5)
+        success, response = at_handler.send_at_command("AT+NWENPWRSAVE=0", timeout=5)
         if not success:
             print(f"⚠️ 网络省电模式配置失败: {response}")
             print("继续执行...")
         else:
-            print("✅ 网络省电模式已启用 (AT+NWENPWRSAVE=1)")
+            print("✅ 网络省电模式已启用 (AT+NWENPWRSAVE=0)")
         
         # 初始化TCP客户端
         print("\n" + "=" * 50)
@@ -126,43 +126,196 @@ def connect_test_server():
         
         print(f"✅ {protocol_name}连接成功")
         
-        # 被动接收服务器消息
+        # 交互式发送和接收
         print("\n" + "=" * 50)
-        print("【步骤6: 被动接收服务器消息】")
+        print("【步骤6: 交互式发送和接收数据】")
         print("=" * 50)
-        print(f"等待服务器发送消息(超时时间: {RECEIVE_TIMEOUT}秒)...")
-        print("(按Ctrl+C中断)")
+        print("命令说明:")
+        print("  send <hex_string>     - 发送HEX数据(例如: send 48656C6C6F)")
+        print("  send_ascii <text>     - 发送ASCII文本(例如: send_ascii Hello)")
+        print("  recv                  - 主动接收数据")
+        print("  status                - 查询TCP连接状态(AT+IPSTATUS)")
+        print("  send_status           - 查询TCP发送状态(AT+TCPACK)")
+        print("  wait                  - 切换到被动等待模式(自动接收)")
+        print("  interactive           - 切换到交互模式(手动输入命令)")
+        print("  quit                  - 退出程序")
         print("=" * 50)
         
         message_count = 0
         start_time = time.time()
+        wait_mode = False  # 默认交互模式,方便用户输入命令
+        
+        def print_received_data(data, msg_num, elapsed):
+            """打印接收到的数据"""
+            print(f"\n【接收消息 #{msg_num}】 (连接后 {elapsed:.1f}秒)")
+            print(f"数据长度: {len(data)} 字节")
+            
+            # 打印ASCII格式
+            try:
+                text_data = data.decode('utf-8', errors='replace')
+                # 检查是否是可打印的ASCII文本
+                if all(32 <= ord(c) < 127 or c in '\n\r\t' for c in text_data):
+                    print(f"ASCII文本: {text_data}")
+                else:
+                    print(f"ASCII文本 (含非打印字符): {repr(text_data)}")
+            except:
+                print("ASCII文本: (无法解码为UTF-8)")
+            
+            # 打印HEX格式
+            hex_str = data.hex().upper()
+            if len(hex_str) > 200:
+                print(f"HEX字符串: {hex_str[:200]}... (共{len(hex_str)}字符)")
+            else:
+                print(f"HEX字符串: {hex_str}")
+            print("-" * 50)
         
         try:
             while True:
-                # 等待接收数据
-                data = tcp_client.wait_for_data(timeout=RECEIVE_TIMEOUT)
-                
-                if data:
-                    message_count += 1
-                    elapsed_time = time.time() - start_time
+                if wait_mode:
+                    # 被动等待接收数据
+                    data = tcp_client.wait_for_data(timeout=RECEIVE_TIMEOUT)
                     
-                    print(f"\n【消息 #{message_count}】 (连接后 {elapsed_time:.1f}秒)")
-                    print(f"数据长度: {len(data)} 字节")
-                    
-                    # 尝试解码为文本
-                    try:
-                        text_data = data.decode('utf-8')
-                        print(f"文本内容: {text_data}")
-                    except:
-                        print(f"二进制数据(hex): {data[:100].hex()}...")  # 只显示前100字节
-                    
-                    print("-" * 50)
+                    if data:
+                        message_count += 1
+                        elapsed_time = time.time() - start_time
+                        print_received_data(data, message_count, elapsed_time)
+                    else:
+                        print(f"\n⚠️ 接收超时({RECEIVE_TIMEOUT}秒),继续等待...")
+                        print("提示: 按 Ctrl+C 中断,然后输入 'interactive' 切换到交互模式")
+                        print("-" * 50)
                 else:
-                    print(f"\n⚠️ 接收超时({RECEIVE_TIMEOUT}秒),继续等待...")
-                    print("-" * 50)
+                    # 交互模式:等待用户输入
+                    try:
+                        user_input = input("\n请输入命令 (send/send_ascii/recv/status/send_status/wait/interactive/quit): ").strip()
+                        
+                        if not user_input:
+                            continue
+                        
+                        if user_input.lower() == 'quit':
+                            break
+                        elif user_input.lower() == 'status':
+                            # 查询TCP连接状态
+                            print("\n" + "=" * 50)
+                            print("【查询TCP连接状态】")
+                            print("=" * 50)
+                            status_info = tcp_client.get_status()
+                            if status_info['success']:
+                                print(f"  Socket ID: {status_info['socket_id']}")
+                                print(f"  连接状态: {status_info['status']}")
+                                if status_info['type']:
+                                    print(f"  链路类型: {status_info['type']}")
+                                if status_info['buffer_size'] is not None:
+                                    print(f"  可用Buffer: {status_info['buffer_size']:,} 字节")
+                                
+                                # 根据状态给出提示
+                                if status_info['status'] == 'CONNECT':
+                                    print("  ✅ 连接正常")
+                                    if status_info['buffer_size'] is not None:
+                                        if status_info['buffer_size'] < 100:
+                                            print("  ⚠️ 警告: Buffer空间较小,可能影响数据发送")
+                                elif status_info['status'] == 'DISCONNECT':
+                                    print("  ❌ 连接已断开")
+                                elif status_info['status'] == 'CONNECTING':
+                                    print("  ⏳ 正在连接...")
+                                elif status_info['status'] == 'DISCONNECTING':
+                                    print("  ⏳ 正在断开...")
+                            else:
+                                print(f"❌ 查询状态失败: {status_info.get('error', '未知错误')}")
+                            print("=" * 50)
+                        elif user_input.lower() == 'send_status':
+                            # 查询TCP发送状态
+                            print("\n" + "=" * 50)
+                            print("【查询TCP发送状态】")
+                            print("=" * 50)
+                            send_status = tcp_client.get_send_status()
+                            if send_status['success']:
+                                print(f"  Socket ID: {send_status['socket_id']}")
+                                print(f"  已发送数据: {send_status['data_sent']:,} 字节")
+                                print(f"  已确认接收: {send_status['acked_recv']:,} 字节")
+                                
+                                if send_status['data_sent'] == send_status['acked_recv']:
+                                    print("  ✅ 所有数据已成功发送并被确认")
+                                else:
+                                    diff = send_status['data_sent'] - send_status['acked_recv']
+                                    pending_ratio = (diff / send_status['data_sent']) * 100 if send_status['data_sent'] > 0 else 0
+                                    print(f"  ⚠️ 警告: 有 {diff:,} 字节数据未确认 ({pending_ratio:.1f}%)")
+                                    print(f"    可能原因: 数据正在传输中或网络延迟")
+                                    if pending_ratio > 50:
+                                        print(f"  ⚠️ 严重警告: 超过50%的数据未确认,建议检查网络连接")
+                            else:
+                                error_msg = send_status.get('error', '未知错误')
+                                print(f"❌ 查询发送状态失败: {error_msg}")
+                                if 'SSL TCP' in error_msg:
+                                    print("  提示: SSL TCP连接不支持AT+TCPACK查询")
+                                elif 'DISCONNECT' in error_msg:
+                                    print("  提示: 连接已断开,请先建立TCP连接")
+                                elif 'NO TCP LINK' in error_msg:
+                                    print("  提示: 当前链路不是TCP连接(可能是UDP连接)")
+                            print("=" * 50)
+                        elif user_input.lower() == 'recv':
+                            # 主动接收数据
+                            data = tcp_client.receive()
+                            if data:
+                                message_count += 1
+                                elapsed_time = time.time() - start_time
+                                print_received_data(data, message_count, elapsed_time)
+                            else:
+                                print("❌ 未接收到数据")
+                        elif user_input.lower() == 'wait':
+                            wait_mode = True
+                            print("✅ 切换到被动等待模式(自动接收数据)")
+                            print("提示: 按 Ctrl+C 中断,然后输入 'interactive' 切换回交互模式")
+                        elif user_input.lower() == 'interactive':
+                            wait_mode = False
+                            print("✅ 切换到交互模式(手动输入命令)")
+                        elif user_input.startswith('send '):
+                            # 发送HEX数据
+                            hex_str = user_input[5:].strip().replace(' ', '').upper()
+                            if not hex_str:
+                                print("❌ 错误: send 命令需要HEX字符串参数")
+                                print("   示例: send 48656C6C6F")
+                                continue
+                            try:
+                                send_data = bytes.fromhex(hex_str)
+                                print(f"发送HEX数据: {hex_str} (二进制长度: {len(send_data)} 字节)")
+                                if tcp_client.send(send_data):
+                                    print("✅ 数据发送成功")
+                                else:
+                                    print("❌ 数据发送失败")
+                            except ValueError as e:
+                                print(f"❌ HEX字符串格式错误: {e}")
+                                print("   提示: HEX字符串只能包含 0-9, A-F 字符")
+                        elif user_input.startswith('send_ascii '):
+                            # 发送ASCII文本
+                            ascii_text = user_input[11:].strip()
+                            if not ascii_text:
+                                print("❌ 错误: send_ascii 命令需要文本参数")
+                                print("   示例: send_ascii Hello World")
+                                continue
+                            send_data = ascii_text.encode('utf-8')
+                            hex_str = send_data.hex().upper()
+                            print(f"发送ASCII文本: {ascii_text}")
+                            print(f"  -> 二进制: {send_data}")
+                            print(f"  -> HEX字符串: {hex_str}")
+                            if tcp_client.send(send_data):
+                                print("✅ 数据发送成功")
+                            else:
+                                print("❌ 数据发送失败")
+                        else:
+                            print("❌ 未知命令")
+                            print("   可用命令: send, send_ascii, recv, wait, interactive, quit")
+                    except EOFError:
+                        # 如果没有交互式输入,切换到被动等待模式
+                        wait_mode = True
+                        print("\n⚠️ 检测到非交互式环境,切换到被动等待模式...")
+                    except KeyboardInterrupt:
+                        # 用户按 Ctrl+C,切换到交互模式
+                        wait_mode = False
+                        print("\n\n⚠️ 用户中断,切换到交互模式")
+                        print("提示: 输入 'wait' 可切换回被动等待模式")
         
         except KeyboardInterrupt:
-            print("\n\n用户中断接收")
+            print("\n\n用户中断")
         
         # 关闭连接
         print("\n" + "=" * 50)
index edbec90..d7f2ee7 100644 (file)
@@ -1,12 +1,15 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 """
-使用AT指令通过HTTP/HTTPS下载文件
+使用AT指令通过HTTP/HTTPS批量下载文件
 使用重构后的公共模块实现
+支持从JSON数据中提取文件列表,批量下载并显示进度
 """
 
 import os
 import time
+import json
+import threading
 from n706a_at_handler import ATCommandHandler
 from n706a_http_client import N706AHTTPClient, load_certificate_file
 
@@ -20,7 +23,7 @@ OUTPUT_DIR = "downloads"
 OUTPUT_FILENAME = "downloaded_file"
 
 # AT模块串口配置(根据实际硬件配置修改)
-SERIAL_PORT = "COM19"  # 修改为实际的串口设备
+SERIAL_PORT = "COM8"  # 修改为实际的串口设备
 SERIAL_BAUDRATE = 1000000
 SERIAL_TIMEOUT = 10
 
@@ -30,17 +33,158 @@ AUTH_MODE = 1  # 0=无需认证, 1=单向认证, 2=双向认证
 # CA_CERT_PATH = "certs/ca_chain.pem"
 CA_CERT_PATH = "certs/intermediate_cas.pem"
 
-# -------------------------- 2. 下载文件函数 --------------------------
-def download_file(url, output_path, auth_mode=0, ca_cert_path=None):
+# -------------------------- 2. 进度统计类 --------------------------
+class DownloadProgressTracker:
+    """下载进度跟踪器"""
+    def __init__(self, total_size):
+        self.total_size = total_size
+        self.downloaded_size = 0
+        self.start_time = time.time()
+        self.last_print_time = time.time()
+        self.lock = threading.Lock()
+        self.print_interval = 2.0  # 每2秒打印一次进度
+        
+    def update(self, bytes_downloaded):
+        """更新已下载字节数"""
+        with self.lock:
+            self.downloaded_size += bytes_downloaded
+            
+    def get_progress(self):
+        """获取当前进度信息"""
+        with self.lock:
+            elapsed = time.time() - self.start_time
+            percentage = (self.downloaded_size / self.total_size * 100) if self.total_size > 0 else 0
+            speed = (self.downloaded_size / elapsed) if elapsed > 0 else 0
+            return {
+                'downloaded': self.downloaded_size,
+                'total': self.total_size,
+                'percentage': percentage,
+                'elapsed': elapsed,
+                'speed': speed
+            }
+    
+    def should_print(self):
+        """判断是否应该打印进度"""
+        current_time = time.time()
+        if current_time - self.last_print_time >= self.print_interval:
+            self.last_print_time = current_time
+            return True
+        return False
+    
+    def print_progress(self, current_file_name="", current_file_index=0, total_files=0):
+        """打印进度信息"""
+        progress = self.get_progress()
+        downloaded_mb = progress['downloaded'] / 1024 / 1024
+        total_mb = progress['total'] / 1024 / 1024
+        speed_mbps = progress['speed'] / 1024 / 1024
+        
+        print(f"\r[进度] {current_file_index}/{total_files} 文件 | "
+              f"{progress['percentage']:.1f}% | "
+              f"{downloaded_mb:.2f}/{total_mb:.2f} MB | "
+              f"速度: {speed_mbps:.2f} MB/s | "
+              f"已用: {progress['elapsed']:.1f}s | "
+              f"当前: {current_file_name}", end='', flush=True)
+
+# -------------------------- 3. 下载单个文件函数 --------------------------
+def download_single_file(url, output_path, http_client, auth_mode=0, ca_cert_name=None, progress_tracker=None):
     """
-    ä½¿ç\94¨ATæ\8c\87令é\80\9aè¿\87HTTP/HTTPSä¸\8bè½½文件
+    ä¸\8bè½½å\8d\95个文件
     
     Args:
         url: 文件的HTTP/HTTPS URL
         output_path: 保存路径
-        auth_mode: 认证模式,0=无需认证, 1=单向认证, 2=双向认证(仅HTTPS需要)
-        ca_cert_path: CA证书路径(HTTPS单向认证时需要)
+        http_client: 已初始化的HTTP客户端
+        auth_mode: 认证模式
+        ca_cert_name: CA证书名称
+        progress_tracker: 进度跟踪器
+    
+    Returns:
+        (success, downloaded_size): 下载是否成功,下载的字节数
+    """
+    file_data = None
+    try:
+        # 设置URL
+        http_client.set_url(url)
+        protocol_name = "HTTPS" if http_client.is_https else "HTTP"
+        
+        # 配置HTTPS(如果需要)
+        if http_client.is_https and ca_cert_name:
+            if not http_client.configure_https(
+                ssl_version=3,  # TLS1.2
+                auth_mode=auth_mode,
+                ca_cert=ca_cert_name
+            ):
+                return False, 0
+        
+        # 建立连接
+        if not http_client.setup_connection():
+            return False, 0
+        
+        # 发送GET请求
+        file_data = http_client.send_get_request(headers=None, timeout=600)
+        
+        if not file_data:
+            return False, 0
+        
+        # 更新进度
+        if progress_tracker:
+            progress_tracker.update(len(file_data))
+        
+        # 保存文件
+        os.makedirs(os.path.dirname(output_path) if os.path.dirname(output_path) else '.', exist_ok=True)
+        with open(output_path, 'wb') as f:
+            f.write(file_data)
+        
+        # 关闭连接
+        try:
+            http_client.close_connection()
+        except:
+            pass  # 忽略关闭连接时的错误
+        
+        return True, len(file_data)
+        
+    except Exception as e:
+        # 尝试关闭连接
+        try:
+            http_client.close_connection()
+        except:
+            pass
+        return False, 0
+
+# -------------------------- 4. 批量下载函数 --------------------------
+def download_files_from_json(json_data, output_dir, auth_mode=0, ca_cert_path=None):
     """
+    从JSON数据中提取文件列表并批量下载
+    
+    Args:
+        json_data: JSON字符串或字典,包含emotionEmojis列表
+        output_dir: 输出目录
+        auth_mode: 认证模式
+        ca_cert_path: CA证书路径
+    """
+    # 解析JSON
+    if isinstance(json_data, str):
+        data = json.loads(json_data)
+    else:
+        data = json_data
+    
+    # 提取文件列表
+    emotion_emojis = data.get('data', {}).get('emotionEmojis', [])
+    if not emotion_emojis:
+        print("❌ JSON数据中没有找到emotionEmojis列表")
+        return False
+    
+    # 计算总大小
+    total_size = sum(emoji.get('size', 0) for emoji in emotion_emojis)
+    total_files = len(emotion_emojis)
+    
+    print(f"\n{'='*60}")
+    print(f"【批量下载任务】")
+    print(f"{'='*60}")
+    print(f"文件总数: {total_files}")
+    print(f"总大小: {total_size / 1024 / 1024:.2f} MB ({total_size} 字节)")
+    print(f"{'='*60}\n")
+    
     # 初始化AT指令处理器
     at_handler = ATCommandHandler(SERIAL_PORT, SERIAL_BAUDRATE, SERIAL_TIMEOUT)
     
@@ -58,200 +202,191 @@ def download_file(url, output_path, auth_mode=0, ca_cert_path=None):
         print("✅ AT模块连接正常")
         
         # 检查PPP拨号状态
-        print("\n" + "=" * 50)
-        print("【步骤1: 检查PPP拨号状态】")
-        print("=" * 50)
+        print("\n【检查PPP拨号状态】")
         is_ppp_connected, ppp_ip = at_handler.check_ppp_status()
         
         if is_ppp_connected:
             print(f"✅ PPP已连接,IP地址: {ppp_ip}")
         else:
-            print(f"❌ PPP未连接,IP地址: {ppp_ip or '未知'}")
-            print("\n尝试建立PPP连接...")
-            
+            print(f"❌ PPP未连接,尝试建立连接...")
             apn_name = None
             success, ip = at_handler.establish_ppp_connection(apn_name=apn_name)
-            
             if not success:
-                print(f"❌ PPP连接建立失败,无法继续HTTP/HTTPS访问")
+                print(f"❌ PPP连接建立失败")
                 return False
-            
             ppp_ip = ip
+            print(f"✅ PPP连接建立成功,IP地址: {ppp_ip}")
         
-        # 初始化HTTP/HTTPS客户端(自动判断协议类型)
+        # 初始化HTTP客户端
         http_client = N706AHTTPClient(at_handler)
         
-        # 先设置URL以判断协议类型
-        http_client.set_url(url)
-        
         # 处理证书(如果需要)
         ca_cert_name = None
-        if http_client.is_https and auth_mode == 1 and ca_cert_path:
-            print("\n" + "=" * 50)
-            print("【步骤2: 处理证书】")
-            print("=" * 50)
-            
+        if ca_cert_path:
+            print("\n【处理证书】")
             ca_cert_name = os.path.basename(ca_cert_path)
             
-            # 检查证书是否已存在
-            if http_client.certificate_exists(ca_cert_name):
-                print(f"✅ 证书 {ca_cert_name} 已存在于模组中,跳过添加")
-            else:
-                # 证书不存在,需要添加
-                print(f"证书 {ca_cert_name} 不存在,开始添加...")
-                
-                # 加载证书文件
-                print(f"加载证书文件: {ca_cert_path}")
-                ca_cert_data = load_certificate_file(ca_cert_path)
-                if not ca_cert_data:
-                    print("❌ 无法加载CA证书,终止流程")
-                    return False
-                
-                # 添加证书到模组
-                print(f"添加证书到模组: {ca_cert_name}")
-                if not http_client.add_certificate(ca_cert_name, ca_cert_data):
-                    print("❌ CA证书添加失败,终止流程")
-                    return False
-                
-                # 确认证书添加成功
-                if not http_client.check_certificate(ca_cert_name):
-                    print("❌ CA证书确认失败,终止流程")
-                    return False
-                
-                print(f"✅ 证书 {ca_cert_name} 已成功添加到模组")
-        
-        # ========== 开始计时:HTTP/HTTPS访问耗时 ==========
-        access_start_time = time.time()
-        
-        # 配置HTTPS参数(如果需要)
-        # 重要:这里会明确指定使用哪个证书,确保不会混用其他证书
-        if http_client.is_https:
-            print("\n" + "=" * 50)
-            print("【步骤3: 配置HTTPS参数】")
-            print("=" * 50)
-            if ca_cert_name:
-                print(f"明确指定使用证书: {ca_cert_name}")
-            if not http_client.configure_https(
-                ssl_version=3,  # TLS1.2
-                auth_mode=auth_mode,
-                ca_cert=ca_cert_name  # 明确指定:本次HTTPS连接使用此证书
-            ):
-                print("❌ HTTPS配置失败,终止流程")
-                return False
+            # 检查第一个URL判断是否需要HTTPS
+            first_url = emotion_emojis[0].get('emojiUrl', '')
+            http_client.set_url(first_url)
             
-            # 确认HTTPS配置
-            print("\n确认HTTPS配置...")
-            config = http_client.verify_https_config()
-            if config:
-                print("✅ HTTPS配置确认成功")
-            else:
-                print("⚠️ HTTPS配置确认失败,但继续执行")
+            if http_client.is_https and auth_mode == 1:
+                if http_client.certificate_exists(ca_cert_name):
+                    print(f"✅ 证书 {ca_cert_name} 已存在")
+                else:
+                    print(f"添加证书 {ca_cert_name}...")
+                    ca_cert_data = load_certificate_file(ca_cert_path)
+                    if not ca_cert_data:
+                        print("❌ 无法加载CA证书")
+                        return False
+                    if not http_client.add_certificate(ca_cert_name, ca_cert_data):
+                        print("❌ CA证书添加失败")
+                        return False
+                    if not http_client.check_certificate(ca_cert_name):
+                        print("❌ CA证书确认失败")
+                        return False
+                    print(f"✅ 证书 {ca_cert_name} 添加成功")
         
-        # URL已在前面设置,这里只显示信息
-        protocol_name = "HTTPS" if http_client.is_https else "HTTP"
-        print("\n" + "=" * 50)
-        print(f"【步骤{3 if not http_client.is_https else 4}: {protocol_name}请求参数已设置】")
-        print("=" * 50)
-        print(f"请求URL: {url}")
+        # 创建进度跟踪器
+        progress_tracker = DownloadProgressTracker(total_size)
         
-        # 建立连接
-        print("\n" + "=" * 50)
-        print(f"【步骤{4 if not http_client.is_https else 5}: 建立{protocol_name}连接】")
-        print("=" * 50)
-        http_client.setup_connection()
-        print(f"✅ {protocol_name}连接建立成功")
+        # 开始批量下载
+        print(f"\n{'='*60}")
+        print(f"【开始批量下载】")
+        print(f"{'='*60}\n")
         
-        # 发送GET请求
-        print("\n" + "=" * 50)
-        print(f"【步骤{5 if not http_client.is_https else 6}: 发送{protocol_name} GET请求】")
-        print("=" * 50)
+        success_count = 0
+        failed_count = 0
         
-        # 使用标准GET模式(不传headers参数)
-        file_data = http_client.send_get_request(headers=None, timeout=600)
-        
-        # 保存文件
-        print("\n" + "=" * 50)
-        print("【步骤7: 保存文件】")
-        print("=" * 50)
-        
-        # 创建输出目录
-        os.makedirs(os.path.dirname(output_path), exist_ok=True)
-        
-        # 写入文件
-        with open(output_path, 'wb') as f:
-            f.write(file_data)
-        
-        file_size = len(file_data)
-        print(f"✅ 文件保存成功: {output_path}")
-        print(f"   文件大小: {file_size} 字节 ({file_size/1024/1024:.2f} MB)")
-        
-        # 关闭连接
-        print("\n" + "=" * 50)
-        print(f"【步骤8: 关闭{protocol_name}连接】")
-        print("=" * 50)
-        try:
-            http_client.close_connection()
-            print(f"✅ {protocol_name}连接已关闭")
-        except Exception as e:
-            print(f"警告: 关闭{protocol_name}连接时出错: {str(e)}")
-        
-        # ========== 结束计时:HTTP/HTTPS访问耗时 ==========
-        access_end_time = time.time()
-        access_duration = access_end_time - access_start_time
+        for index, emoji in enumerate(emotion_emojis, 1):
+            emotion = emoji.get('emotion', 'unknown')
+            emoji_url = emoji.get('emojiUrl', '')
+            emoji_size = emoji.get('size', 0)
+            
+            if not emoji_url:
+                print(f"\n⚠️ 跳过文件 {index}/{total_files}: {emotion} (无URL)")
+                failed_count += 1
+                continue
+            
+            # 生成保存路径
+            file_name = f"{emotion}.mp4"
+            output_path = os.path.join(output_dir, file_name)
+            
+            # 打印开始下载信息
+            progress = progress_tracker.get_progress()
+            print(f"\n[{index}/{total_files}] 开始下载: {emotion} ({emoji_size/1024/1024:.2f} MB)")
+            print(f"  当前总进度: {progress['percentage']:.1f}% | "
+                  f"已下载: {progress['downloaded']/1024/1024:.2f}/{progress['total']/1024/1024:.2f} MB | "
+                  f"速度: {progress['speed']/1024/1024:.2f} MB/s")
+            
+            # 下载文件
+            file_start_time = time.time()
+            success, downloaded_size = download_single_file(
+                url=emoji_url,
+                output_path=output_path,
+                http_client=http_client,
+                auth_mode=auth_mode,
+                ca_cert_name=ca_cert_name,
+                progress_tracker=progress_tracker
+            )
+            
+            file_duration = time.time() - file_start_time
+            
+            if success:
+                success_count += 1
+                file_speed = (downloaded_size / 1024 / 1024 / file_duration) if file_duration > 0 else 0
+                progress = progress_tracker.get_progress()
+                print(f"✅ [{index}/{total_files}] {emotion}: 完成 | "
+                      f"大小: {downloaded_size/1024/1024:.2f} MB | "
+                      f"速度: {file_speed:.2f} MB/s | "
+                      f"耗时: {file_duration:.1f}s | "
+                      f"总进度: {progress['percentage']:.1f}%")
+            else:
+                failed_count += 1
+                progress = progress_tracker.get_progress()
+                print(f"❌ [{index}/{total_files}] {emotion}: 下载失败 | "
+                      f"总进度: {progress['percentage']:.1f}%")
+            
+            # 每2秒打印一次总体进度(在下载过程中)
+            if progress_tracker.should_print():
+                progress_tracker.print_progress(emotion, index, total_files)
         
-        print("\n" + "=" * 50)
-        print(f"【{protocol_name}访问耗时统计】")
-        print("=" * 50)
-        print(f"{protocol_name}访问总耗时: {access_duration:.3f} 秒 ({access_duration*1000:.1f} 毫秒)")
-        print(f"下载速度: {file_size/1024/1024/access_duration:.2f} MB/s")
-        print("\n耗时包含以下步骤:")
-        if http_client.is_https:
-            print("  - HTTPS配置和验证")
-        print("  - URL和端口设置")
-        print(f"  - {protocol_name}连接建立")
-        print("  - HTTP请求发送")
-        print("  - 文件数据接收")
-        print("  - 连接关闭")
-        print("=" * 50)
+        # 打印最终统计
+        print(f"\n\n{'='*60}")
+        print(f"【下载完成统计】")
+        print(f"{'='*60}")
+        final_progress = progress_tracker.get_progress()
+        print(f"成功: {success_count}/{total_files}")
+        print(f"失败: {failed_count}/{total_files}")
+        print(f"总进度: {final_progress['percentage']:.1f}%")
+        print(f"已下载: {final_progress['downloaded']/1024/1024:.2f}/{final_progress['total']/1024/1024:.2f} MB")
+        print(f"总耗时: {final_progress['elapsed']:.1f} 秒")
+        print(f"平均速度: {final_progress['speed']/1024/1024:.2f} MB/s")
+        print(f"{'='*60}\n")
         
-        return True
+        return failed_count == 0
         
     except Exception as e:
-        print("\n" + "=" * 50)
-        print("【错误处理】")
-        print("=" * 50)
-        print(f"❌ 下载异常: {str(e)}")
+        print(f"\n❌ 批量下载异常: {str(e)}")
         import traceback
         traceback.print_exc()
         return False
     finally:
-        # 断开串口连接
         print("\n断开串口连接...")
         at_handler.disconnect()
 
-# -------------------------- 3. 主程序 --------------------------
+# -------------------------- 5. 主程序 --------------------------
 if __name__ == "__main__":
-    print("=" * 50)
-    print("HTTP/HTTPS文件下载工具(AT指令版本)")
-    print("=" * 50)
-    print(f"目标URL: {FILE_URL}")
+    # JSON数据
+    EMOJI_JSON_DATA = {
+        "deviceId": "860132064629751",
+        "timestamp": 1763690727,
+        "messageId": "33d5ba5f6fe9499e",
+        "command": "DownloadEmoji",
+        "data": {
+            "characterId": "bf1b16908b33",
+            "eyesColor": "yellow-green-gradient",
+            "eyesShape": "calm-eyes",
+            "emotionEmojis": [
+                {"emotion": "happy", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Smile.mp4", "size": 730359},
+                {"emotion": "laughing", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Laugh.mp4", "size": 777316},
+                {"emotion": "angry", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Angry.mp4", "size": 770986},
+                {"emotion": "wronged", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Wronged.mp4", "size": 1191639},
+                {"emotion": "heartbeat", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Heartbeat.mp4", "size": 952770},
+                {"emotion": "awkward", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Embarrassed.mp4", "size": 807173},
+                {"emotion": "confused", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Doubt.mp4", "size": 833790},
+                {"emotion": "shocked", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Shocked.mp4", "size": 844919},
+                {"emotion": "crying", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Tears.mp4", "size": 910162},
+                {"emotion": "blowing-kiss", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Kiss.mp4", "size": 544297},
+                {"emotion": "sleepy", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Sleepy.mp4", "size": 674698},
+                {"emotion": "mischievous", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Naughty.mp4", "size": 646516},
+                {"emotion": "speechless", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Speechless.mp4", "size": 899444},
+                {"emotion": "dizzy", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Dizzy.mp4", "size": 1135889},
+                {"emotion": "thinking", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Think.mp4", "size": 963503},
+                {"emotion": "working", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/WorkAndStudy.mp4", "size": 1052905},
+                {"emotion": "exercising", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Exercise.mp4", "size": 1077737},
+                {"emotion": "playing", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Play.mp4", "size": 1098020},
+                {"emotion": "daydreaming", "emojiUrl": "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/StraightEyes/YellowGreen/Daze.mp4", "size": 1057085}
+            ],
+            "size": 16969208
+        }
+    }
     
-    # 构建输出路径
-    output_path = os.path.join(OUTPUT_DIR, OUTPUT_FILENAME)
-    print(f"保存路径: {output_path}")
-    print("=" * 50)
+    print("=" * 60)
+    print("HTTP/HTTPS批量文件下载工具(AT指令版本)")
+    print("=" * 60)
     
-    # 执行下载
-    success = download_file(
-        url=FILE_URL,
-        output_path=output_path,
+    # 执行批量下载
+    success = download_files_from_json(
+        json_data=EMOJI_JSON_DATA,
+        output_dir=OUTPUT_DIR,
         auth_mode=AUTH_MODE,
         ca_cert_path=CA_CERT_PATH
     )
     
     if success:
-        print("\n✅ 文件下载完成!")
+        print("\nâ\9c\85 æ\89\80æ\9c\89æ\96\87ä»¶ä¸\8bè½½å®\8cæ\88\90ï¼\81")
     else:
-        print("\n❌ 文件下载失败!")
+        print("\n❌ 部分文件下载失败!")
         exit(1)
 
index 9600d56..aa018b3 100644 (file)
@@ -22,7 +22,7 @@ FULL_URL = BASE_URL + INTERFACE_PATH
 SIGN_SECRET = "vDdXVXgPUEUM6_piUjz5AEhMvYTSFzQdPaN-ogKnSus="
 
 # AT模块串口配置(根据实际硬件配置修改)
-SERIAL_PORT = "COM19"  # 修改为实际的串口设备
+SERIAL_PORT = "COM8"  # 修改为实际的串口设备
 SERIAL_BAUDRATE = 1000000
 SERIAL_TIMEOUT = 10
 
diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/at_test_websocket.py b/code/sdk/zhihui/leiniao/leiniao_python_test/at_test_websocket.py
new file mode 100644 (file)
index 0000000..fe8e74f
--- /dev/null
@@ -0,0 +1,647 @@
+#!/usr/bin/env python3\r
+# -*- coding: utf-8 -*-\r
+"""\r
+通过TCP实现WebSocket协议\r
+包括握手、收数据协议解析、发数据协议封包\r
+"""\r
+\r
+import time\r
+import json\r
+import base64\r
+import hashlib\r
+import struct\r
+import random\r
+import string\r
+import os\r
+import urllib.parse\r
+from n706a_at_handler import ATCommandHandler\r
+from n706a_tcp_client import N706ATCPClient\r
+\r
+# 配置参数\r
+SERIAL_PORT = "COM8"  # 修改为实际的串口设备\r
+SERIAL_BAUDRATE = 1000000\r
+SERIAL_TIMEOUT = 10\r
+\r
+# WebSocket服务器配置\r
+WS_URL = "ws://110.42.224.129:8910/websocket"\r
+\r
+\r
+class WebSocketFrame:\r
+    """WebSocket帧处理类"""\r
+    \r
+    # Opcode定义\r
+    OPCODE_CONTINUATION = 0x0\r
+    OPCODE_TEXT = 0x1\r
+    OPCODE_BINARY = 0x2\r
+    OPCODE_CLOSE = 0x8\r
+    OPCODE_PING = 0x9\r
+    OPCODE_PONG = 0xA\r
+    \r
+    @staticmethod\r
+    def encode_frame(payload: bytes, opcode: int = OPCODE_TEXT, fin: bool = True, mask: bool = True) -> bytes:\r
+        """\r
+        编码WebSocket帧(客户端发送必须使用掩码)\r
+        \r
+        Args:\r
+            payload: 负载数据\r
+            opcode: 操作码\r
+            fin: 是否为最后一帧\r
+            mask: 是否使用掩码(客户端必须为True)\r
+        \r
+        Returns:\r
+            bytes: 编码后的帧数据\r
+        """\r
+        payload_len = len(payload)\r
+        \r
+        # 第一个字节:FIN(1) + RSV(3) + Opcode(4)\r
+        first_byte = (0x80 if fin else 0x00) | (opcode & 0x0F)\r
+        \r
+        # 第二个字节:MASK(1) + Payload length(7)\r
+        if payload_len < 126:\r
+            second_byte = (0x80 if mask else 0x00) | payload_len\r
+            header_len = 2\r
+        elif payload_len < 65536:\r
+            second_byte = (0x80 if mask else 0x00) | 126\r
+            header_len = 4\r
+        else:\r
+            second_byte = (0x80 if mask else 0x00) | 127\r
+            header_len = 10\r
+        \r
+        # 构建帧头\r
+        frame = bytearray([first_byte, second_byte])\r
+        \r
+        # 添加扩展长度字段\r
+        if payload_len >= 126 and payload_len < 65536:\r
+            frame.extend(struct.pack('!H', payload_len))\r
+        elif payload_len >= 65536:\r
+            frame.extend(struct.pack('!Q', payload_len))\r
+        \r
+        # 添加掩码(客户端必须使用)\r
+        if mask:\r
+            mask_key = os.urandom(4)\r
+            frame.extend(mask_key)\r
+            # 对负载进行掩码处理\r
+            masked_payload = bytearray(payload)\r
+            for i in range(len(masked_payload)):\r
+                masked_payload[i] ^= mask_key[i % 4]\r
+            frame.extend(masked_payload)\r
+        else:\r
+            frame.extend(payload)\r
+        \r
+        return bytes(frame)\r
+    \r
+    @staticmethod\r
+    def decode_frame(data: bytes) -> tuple:\r
+        """\r
+        解码WebSocket帧(服务器发送的数据没有掩码)\r
+        \r
+        Args:\r
+            data: 帧数据\r
+        \r
+        Returns:\r
+            tuple: (opcode, payload, fin, remaining_data) 或 (None, None, None, data) 如果数据不完整\r
+        """\r
+        if len(data) < 2:\r
+            return None, None, None, data\r
+        \r
+        first_byte = data[0]\r
+        second_byte = data[1]\r
+        \r
+        fin = (first_byte & 0x80) != 0\r
+        opcode = first_byte & 0x0F\r
+        masked = (second_byte & 0x80) != 0\r
+        payload_len = second_byte & 0x7F\r
+        \r
+        header_len = 2\r
+        \r
+        # 解析扩展长度字段\r
+        if payload_len == 126:\r
+            if len(data) < 4:\r
+                return None, None, None, data\r
+            payload_len = struct.unpack('!H', data[2:4])[0]\r
+            header_len = 4\r
+        elif payload_len == 127:\r
+            if len(data) < 10:\r
+                return None, None, None, data\r
+            payload_len = struct.unpack('!Q', data[2:10])[0]\r
+            header_len = 10\r
+        \r
+        # 解析掩码(服务器发送的数据不应该有掩码,但为了兼容性处理)\r
+        mask_key = None\r
+        if masked:\r
+            if len(data) < header_len + 4:\r
+                return None, None, None, data\r
+            mask_key = data[header_len:header_len + 4]\r
+            header_len += 4\r
+        \r
+        # 检查是否有足够的负载数据\r
+        if len(data) < header_len + payload_len:\r
+            return None, None, None, data\r
+        \r
+        # 提取负载\r
+        payload = data[header_len:header_len + payload_len]\r
+        \r
+        # 去除掩码(如果存在)\r
+        if masked and mask_key:\r
+            payload = bytearray(payload)\r
+            for i in range(len(payload)):\r
+                payload[i] ^= mask_key[i % 4]\r
+            payload = bytes(payload)\r
+        \r
+        remaining_data = data[header_len + payload_len:]\r
+        \r
+        return opcode, payload, fin, remaining_data\r
+\r
+\r
+def websocket_handshake(tcp_client: N706ATCPClient, url: str, headers: dict = None) -> bool:\r
+    """\r
+    执行WebSocket握手\r
+    \r
+    Args:\r
+        tcp_client: TCP客户端实例\r
+        url: WebSocket URL\r
+        headers: 额外的HTTP头\r
+    \r
+    Returns:\r
+        bool: 握手是否成功\r
+    """\r
+    # 解析URL\r
+    parsed = urllib.parse.urlparse(url)\r
+    host = parsed.hostname\r
+    port = parsed.port or (443 if parsed.scheme == 'wss' else 80)\r
+    path = parsed.path or '/'\r
+    if parsed.query:\r
+        path += '?' + parsed.query\r
+    \r
+    # 生成WebSocket Key\r
+    key = base64.b64encode(\r
+        ''.join(random.choices(string.ascii_letters + string.digits, k=16)).encode()\r
+    ).decode()\r
+    \r
+    # 构建握手请求\r
+    request_lines = [\r
+        f"GET {path} HTTP/1.1",\r
+        f"Host: {host}:{port}",\r
+        "Upgrade: websocket",\r
+        "Connection: Upgrade",\r
+        f"Sec-WebSocket-Key: {key}",\r
+        "Sec-WebSocket-Version: 13",\r
+    ]\r
+    \r
+    # 添加额外头部\r
+    if headers:\r
+        for name, value in headers.items():\r
+            request_lines.append(f"{name}: {value}")\r
+    \r
+    request_lines.append("")  # 空行\r
+    request_lines.append("")  # 空行\r
+    \r
+    request = "\r\n".join(request_lines)\r
+    request_bytes = request.encode('utf-8')\r
+    \r
+    print(f"\n【WebSocket握手请求】")\r
+    print(request)\r
+    print("=" * 60)\r
+    \r
+    # 发送握手请求\r
+    print(f"\n【发送握手请求数据】")\r
+    print(f"数据长度: {len(request_bytes)} 字节")\r
+    print(f"HEX格式: {request_bytes.hex().upper()}")\r
+    print(f"ASCII格式:")\r
+    print(request)\r
+    print("=" * 60)\r
+    \r
+    if not tcp_client.send(request_bytes):\r
+        print("❌ 发送握手请求失败")\r
+        return False\r
+    \r
+    print("✅ 握手请求已发送")\r
+    \r
+    # 接收握手响应\r
+    print("\n等待握手响应...")\r
+    response = b""\r
+    start_time = time.time()\r
+    timeout = 30\r
+    \r
+    while time.time() - start_time < timeout:\r
+        # 尝试接收数据\r
+        data = tcp_client.wait_for_data(timeout=5)\r
+        if data:\r
+            print(f"\n【接收握手响应数据片段】")\r
+            print(f"数据长度: {len(data)} 字节")\r
+            print(f"HEX格式: {data.hex().upper()}")\r
+            try:\r
+                print(f"ASCII格式: {data.decode('utf-8', errors='replace')}")\r
+            except:\r
+                print(f"ASCII格式: (无法解码)")\r
+            print("-" * 60)\r
+            response += data\r
+            # 检查是否收到完整的HTTP响应(以\r\n\r\n结尾)\r
+            if b"\r\n\r\n" in response:\r
+                break\r
+        else:\r
+            # 如果没有数据,尝试主动读取\r
+            data = tcp_client.receive(max_length=4096, timeout=1)\r
+            if data:\r
+                print(f"\n【接收握手响应数据片段(主动读取)】")\r
+                print(f"数据长度: {len(data)} 字节")\r
+                print(f"HEX格式: {data.hex().upper()}")\r
+                try:\r
+                    print(f"ASCII格式: {data.decode('utf-8', errors='replace')}")\r
+                except:\r
+                    print(f"ASCII格式: (无法解码)")\r
+                print("-" * 60)\r
+                response += data\r
+                if b"\r\n\r\n" in response:\r
+                    break\r
+    \r
+    if not response:\r
+        print("❌ 未收到握手响应")\r
+        return False\r
+    \r
+    # 解析响应\r
+    print(f"\n【WebSocket握手响应(完整数据)】")\r
+    print(f"总长度: {len(response)} 字节")\r
+    print(f"HEX格式: {response.hex().upper()}")\r
+    print(f"ASCII格式:")\r
+    response_text = response.decode('utf-8', errors='ignore')\r
+    print(response_text)\r
+    print("=" * 60)\r
+    \r
+    lines = response_text.split('\r\n')\r
+    \r
+    # 检查状态行\r
+    if not lines[0].startswith('HTTP/1.1 101'):\r
+        print(f"❌ 握手失败,状态行: {lines[0]}")\r
+        return False\r
+    \r
+    # 检查Upgrade和Connection头\r
+    response_headers = {}\r
+    for line in lines[1:]:\r
+        if ':' in line:\r
+            name, value = line.split(':', 1)\r
+            response_headers[name.strip().lower()] = value.strip()\r
+    \r
+    if response_headers.get('upgrade', '').lower() != 'websocket':\r
+        print("❌ 握手失败,缺少Upgrade头")\r
+        return False\r
+    \r
+    if 'upgrade' not in response_headers.get('connection', '').lower():\r
+        print("❌ 握手失败,Connection头不正确")\r
+        return False\r
+    \r
+    # 验证Sec-WebSocket-Accept\r
+    expected_accept = base64.b64encode(\r
+        hashlib.sha1((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode()).digest()\r
+    ).decode()\r
+    \r
+    actual_accept = response_headers.get('sec-websocket-accept', '')\r
+    if actual_accept != expected_accept:\r
+        print(f"❌ 握手失败,Sec-WebSocket-Accept不匹配")\r
+        print(f"期望: {expected_accept}")\r
+        print(f"实际: {actual_accept}")\r
+        return False\r
+    \r
+    print("✅ WebSocket握手成功")\r
+    return True\r
+\r
+\r
+def test_websocket_via_tcp():\r
+    """通过TCP实现WebSocket协议测试"""\r
+    print("=" * 60)\r
+    print("通过TCP实现WebSocket协议")\r
+    print("=" * 60)\r
+    \r
+    at_handler = ATCommandHandler(SERIAL_PORT, SERIAL_BAUDRATE, SERIAL_TIMEOUT)\r
+    \r
+    try:\r
+        # 连接串口\r
+        if not at_handler.connect():\r
+            print("❌ 无法连接串口")\r
+            return False\r
+        \r
+        # 测试AT连接\r
+        success, response = at_handler.send_at_command("AT", timeout=3)\r
+        if not success:\r
+            print("❌ AT模块无响应")\r
+            return False\r
+        print("✅ AT模块连接正常")\r
+        \r
+        # 检查PPP拨号状态\r
+        is_ppp_connected, ppp_ip = at_handler.check_ppp_status()\r
+        if not is_ppp_connected:\r
+            print("建立PPP连接...")\r
+            success, ip = at_handler.establish_ppp_connection()\r
+            if not success:\r
+                print("❌ PPP连接失败")\r
+                return False\r
+            print(f"✅ PPP连接成功,IP: {ip}")\r
+        else:\r
+            print(f"✅ PPP已连接,IP: {ppp_ip}")\r
+        \r
+        # 解析WebSocket URL\r
+        parsed = urllib.parse.urlparse(WS_URL)\r
+        host = parsed.hostname\r
+        port = parsed.port or (443 if parsed.scheme == 'wss' else 80)\r
+        \r
+        print(f"\n【步骤1: 建立TCP连接】")\r
+        print(f"服务器: {host}:{port}")\r
+        print("=" * 60)\r
+        \r
+        # 在连接前,先关闭可能存在的旧连接\r
+        print("检查并关闭可能存在的旧连接...")\r
+        socket_id = 0\r
+        \r
+        # 尝试关闭所有可能的socket连接\r
+        for close_socket_id in [socket_id]:\r
+            # 尝试关闭普通TCP连接\r
+            success, response = at_handler.send_at_command(f"AT+TCPCLOSE={close_socket_id}", timeout=3)\r
+            if success:\r
+                print(f"✅ Socket {close_socket_id} 已关闭(TCP)")\r
+            else:\r
+                print(f"⚠️ Socket {close_socket_id} TCP关闭命令响应: {response}")\r
+            \r
+            # 尝试关闭SSL TCP连接\r
+            success, response = at_handler.send_at_command(f"AT+SSLTCPCLOSE={close_socket_id}", timeout=3)\r
+            if success:\r
+                print(f"✅ Socket {close_socket_id} 已关闭(SSL TCP)")\r
+            else:\r
+                print(f"⚠️ Socket {close_socket_id} SSL TCP关闭命令响应: {response}")\r
+        \r
+        # 等待模块清理socket状态\r
+        print("等待模块清理socket状态...")\r
+        time.sleep(0.5)\r
+        \r
+        # 再次确认PPP连接状态\r
+        print("\n检查PPP连接状态...")\r
+        is_ppp_connected, ppp_ip = at_handler.check_ppp_status()\r
+        if not is_ppp_connected:\r
+            print("⚠️ PPP连接状态异常,尝试重新建立...")\r
+            success, ip = at_handler.establish_ppp_connection()\r
+            if not success:\r
+                print("❌ PPP连接失败,无法继续TCP连接")\r
+                return False\r
+            print(f"✅ PPP连接已重新建立,IP: {ip}")\r
+            time.sleep(1)  # 等待PPP稳定\r
+        else:\r
+            print(f"✅ PPP连接正常,IP: {ppp_ip}")\r
+        \r
+        # 检查网络注册状态\r
+        print("\n检查网络注册状态...")\r
+        if at_handler.check_network_registration():\r
+            print("✅ 网络已注册")\r
+        else:\r
+            print("⚠️ 网络未注册,可能影响连接")\r
+        \r
+        # 测试DNS解析(如果使用域名)\r
+        if host and not host.replace('.', '').isdigit():\r
+            print(f"\n测试DNS解析: {host}...")\r
+            # 可以尝试ping或DNS查询,这里先跳过\r
+            print("⚠️ 使用域名,确保DNS配置正确")\r
+        \r
+        # 创建TCP客户端\r
+        tcp_client = N706ATCPClient(at_handler, socket_id=socket_id)\r
+        \r
+        # 建立TCP连接\r
+        print(f"\n尝试建立TCP连接到 {host}:{port}...")\r
+        if not tcp_client.connect(host, port, use_ssl=False, transparent_mode=False):\r
+            print("❌ TCP连接失败")\r
+            print("\n可能的原因:")\r
+            print("  1. 服务器地址或端口不正确")\r
+            print("  2. 网络连接问题(检查PPP连接状态)")\r
+            print("  3. Socket ID被占用(尝试使用其他Socket ID)")\r
+            print("  4. 服务器不可达或防火墙阻止")\r
+            return False\r
+        \r
+        print("✅ TCP连接成功")\r
+        \r
+        # WebSocket握手\r
+        print(f"\n【步骤2: WebSocket握手】")\r
+        print("=" * 60)\r
+        \r
+        # 额外的HTTP头(可选)\r
+        headers = {\r
+            "User-Agent": "AT-WebSocket-Client/1.0"\r
+        }\r
+        \r
+        if not websocket_handshake(tcp_client, WS_URL, headers):\r
+            print("❌ WebSocket握手失败")\r
+            tcp_client.close()\r
+            return False\r
+        \r
+        # 发送和接收消息\r
+        print(f"\n【步骤3: 发送和接收消息】")\r
+        print("=" * 60)\r
+        \r
+        # 接收缓冲区\r
+        recv_buffer = b""\r
+        \r
+        # 发送文本消息\r
+        print("\n发送文本消息...")\r
+        test_text = json.dumps({\r
+            "type": "test",\r
+            "message": "Hello from AT WebSocket via TCP!",\r
+            "timestamp": int(time.time())\r
+        })\r
+        text_payload = test_text.encode('utf-8')\r
+        text_frame = WebSocketFrame.encode_frame(text_payload, opcode=WebSocketFrame.OPCODE_TEXT)\r
+        \r
+        print(f"\n【发送文本消息帧】")\r
+        print(f"原始文本: {test_text}")\r
+        print(f"负载长度: {len(text_payload)} 字节")\r
+        print(f"帧长度: {len(text_frame)} 字节")\r
+        print(f"帧HEX: {text_frame.hex().upper()}")\r
+        print(f"帧前16字节HEX: {text_frame[:16].hex().upper()}...")\r
+        print("-" * 60)\r
+        \r
+        if tcp_client.send(text_frame):\r
+            print(f"✅ 文本消息发送成功")\r
+        else:\r
+            print("❌ 文本消息发送失败")\r
+        \r
+        # 发送二进制消息\r
+        print("\n发送二进制消息...")\r
+        test_binary = b"Binary test data: " + bytes(range(10))\r
+        binary_frame = WebSocketFrame.encode_frame(test_binary, opcode=WebSocketFrame.OPCODE_BINARY)\r
+        \r
+        print(f"\n【发送二进制消息帧】")\r
+        print(f"原始数据: {test_binary}")\r
+        print(f"负载长度: {len(test_binary)} 字节")\r
+        print(f"负载HEX: {test_binary.hex().upper()}")\r
+        print(f"帧长度: {len(binary_frame)} 字节")\r
+        print(f"帧HEX: {binary_frame.hex().upper()}")\r
+        print(f"帧前16字节HEX: {binary_frame[:16].hex().upper()}...")\r
+        print("-" * 60)\r
+        \r
+        if tcp_client.send(binary_frame):\r
+            print(f"✅ 二进制消息发送成功")\r
+        else:\r
+            print("❌ 二进制消息发送失败")\r
+        \r
+        # 发送Ping\r
+        print("\n发送Ping...")\r
+        ping_payload = b"ping"\r
+        ping_frame = WebSocketFrame.encode_frame(ping_payload, opcode=WebSocketFrame.OPCODE_PING)\r
+        \r
+        print(f"\n【发送Ping帧】")\r
+        print(f"负载: {ping_payload}")\r
+        print(f"负载长度: {len(ping_payload)} 字节")\r
+        print(f"帧长度: {len(ping_frame)} 字节")\r
+        print(f"帧HEX: {ping_frame.hex().upper()}")\r
+        print("-" * 60)\r
+        \r
+        if tcp_client.send(ping_frame):\r
+            print("✅ Ping发送成功")\r
+        else:\r
+            print("❌ Ping发送失败")\r
+        \r
+        # 接收消息(最多等待30秒)\r
+        print("\n等待接收消息(最多30秒)...")\r
+        start_time = time.time()\r
+        message_count = 0\r
+        \r
+        while time.time() - start_time < 30:\r
+            # 尝试从缓冲区解析帧\r
+            if recv_buffer:\r
+                opcode, payload, fin, remaining = WebSocketFrame.decode_frame(recv_buffer)\r
+                if payload is not None:\r
+                    # 打印解析出的帧信息\r
+                    opcode_names = {\r
+                        WebSocketFrame.OPCODE_TEXT: "TEXT",\r
+                        WebSocketFrame.OPCODE_BINARY: "BINARY",\r
+                        WebSocketFrame.OPCODE_CLOSE: "CLOSE",\r
+                        WebSocketFrame.OPCODE_PING: "PING",\r
+                        WebSocketFrame.OPCODE_PONG: "PONG",\r
+                        WebSocketFrame.OPCODE_CONTINUATION: "CONTINUATION"\r
+                    }\r
+                    opcode_name = opcode_names.get(opcode, f"UNKNOWN({opcode})")\r
+                    \r
+                    print(f"\n【解析WebSocket帧】")\r
+                    print(f"Opcode: {opcode_name} (0x{opcode:02X})")\r
+                    print(f"FIN: {fin}")\r
+                    print(f"负载长度: {len(payload)} 字节")\r
+                    print(f"负载HEX: {payload.hex().upper()}")\r
+                    if len(payload) > 100:\r
+                        print(f"负载HEX(前100字节): {payload[:100].hex().upper()}...")\r
+                    print("-" * 60)\r
+                    \r
+                    recv_buffer = remaining\r
+                    \r
+                    # 处理控制帧\r
+                    if opcode == WebSocketFrame.OPCODE_CLOSE:\r
+                        print("🔌 收到关闭帧")\r
+                        if len(payload) >= 2:\r
+                            close_code = struct.unpack('!H', payload[:2])[0]\r
+                            close_reason = payload[2:].decode('utf-8', errors='ignore') if len(payload) > 2 else ""\r
+                            print(f"   关闭码: {close_code}, 原因: {close_reason}")\r
+                        break\r
+                    elif opcode == WebSocketFrame.OPCODE_PING:\r
+                        print("📨 收到Ping,发送Pong...")\r
+                        pong_frame = WebSocketFrame.encode_frame(payload, opcode=WebSocketFrame.OPCODE_PONG)\r
+                        print(f"【发送Pong帧】")\r
+                        print(f"帧HEX: {pong_frame.hex().upper()}")\r
+                        print("-" * 60)\r
+                        tcp_client.send(pong_frame)\r
+                    elif opcode == WebSocketFrame.OPCODE_PONG:\r
+                        print("📨 收到Pong响应")\r
+                        try:\r
+                            pong_text = payload.decode('utf-8', errors='replace')\r
+                            print(f"   Pong数据: {pong_text}")\r
+                        except:\r
+                            print(f"   Pong数据HEX: {payload.hex().upper()}")\r
+                    elif opcode == WebSocketFrame.OPCODE_TEXT:\r
+                        message_count += 1\r
+                        try:\r
+                            text = payload.decode('utf-8')\r
+                            print(f"📨 收到文本消息 #{message_count}: {text}")\r
+                        except:\r
+                            print(f"📨 收到文本消息 #{message_count} (无法解码为UTF-8)")\r
+                            print(f"   原始HEX: {payload.hex().upper()}")\r
+                    elif opcode == WebSocketFrame.OPCODE_BINARY:\r
+                        message_count += 1\r
+                        print(f"📨 收到二进制消息 #{message_count}, 长度: {len(payload)} 字节")\r
+                        print(f"   完整HEX: {payload.hex().upper()}")\r
+                        if len(payload) > 100:\r
+                            print(f"   HEX(前100字节): {payload[:100].hex().upper()}...")\r
+                    continue\r
+            \r
+            # 接收新数据\r
+            data = tcp_client.wait_for_data(timeout=5)\r
+            if data:\r
+                print(f"\n【接收原始数据】")\r
+                print(f"数据长度: {len(data)} 字节")\r
+                print(f"HEX格式: {data.hex().upper()}")\r
+                if len(data) > 100:\r
+                    print(f"HEX格式(前100字节): {data[:100].hex().upper()}...")\r
+                try:\r
+                    ascii_str = data.decode('utf-8', errors='replace')\r
+                    if all(32 <= ord(c) < 127 or c in '\n\r\t' for c in ascii_str[:100]):\r
+                        print(f"ASCII格式: {ascii_str[:200]}")\r
+                    else:\r
+                        print(f"ASCII格式: (包含非打印字符)")\r
+                except:\r
+                    print(f"ASCII格式: (无法解码为UTF-8)")\r
+                print("-" * 60)\r
+                \r
+                recv_buffer += data\r
+                print(f"📥 缓冲区总长度: {len(recv_buffer)} 字节")\r
+            else:\r
+                # 超时,继续等待\r
+                continue\r
+        \r
+        if message_count == 0:\r
+            print("⚠️ 未收到任何消息")\r
+        \r
+        # 发送关闭帧\r
+        print("\n【步骤4: 关闭连接】")\r
+        print("=" * 60)\r
+        close_payload = struct.pack('!H', 1000) + b"Normal closure"\r
+        close_frame = WebSocketFrame.encode_frame(close_payload, opcode=WebSocketFrame.OPCODE_CLOSE)\r
+        \r
+        print(f"\n【发送关闭帧】")\r
+        print(f"关闭码: 1000")\r
+        print(f"关闭原因: Normal closure")\r
+        print(f"负载长度: {len(close_payload)} 字节")\r
+        print(f"帧长度: {len(close_frame)} 字节")\r
+        print(f"帧HEX: {close_frame.hex().upper()}")\r
+        print("-" * 60)\r
+        \r
+        tcp_client.send(close_frame)\r
+        print("✅ 关闭帧已发送")\r
+        \r
+        # 关闭TCP连接\r
+        tcp_client.close()\r
+        print("✅ TCP连接已关闭")\r
+        \r
+        return True\r
+        \r
+    except Exception as e:\r
+        print(f"❌ 测试异常: {e}")\r
+        import traceback\r
+        traceback.print_exc()\r
+        return False\r
+    finally:\r
+        at_handler.disconnect()\r
+\r
+\r
+def main():\r
+    """主函数"""\r
+    print("=" * 60)\r
+    print("通过TCP实现WebSocket协议测试")\r
+    print(f"WebSocket URL: {WS_URL}")\r
+    print("=" * 60)\r
+    \r
+    # 执行测试\r
+    success = test_websocket_via_tcp()\r
+    \r
+    # 总结\r
+    print("\n" + "=" * 60)\r
+    print("测试总结")\r
+    print("=" * 60)\r
+    print(f"WebSocket测试: {'✅ 通过' if success else '❌ 失败'}")\r
+    print("=" * 60)\r
+\r
+\r
+if __name__ == "__main__":\r
+    main()\r
+\r
diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/at_websocket_client.py b/code/sdk/zhihui/leiniao/leiniao_python_test/at_websocket_client.py
new file mode 100644 (file)
index 0000000..c5e7c9c
--- /dev/null
@@ -0,0 +1,565 @@
+#!/usr/bin/env python3\r
+# -*- coding: utf-8 -*-\r
+"""\r
+基于AT Socket的WebSocket客户端实现\r
+使用标准socket接口实现WebSocket协议\r
+"""\r
+\r
+import base64\r
+import hashlib\r
+import struct\r
+import time\r
+import urllib.parse\r
+from typing import Optional, Callable, Dict, Any\r
+from at_socket import ATSocket, create_connection\r
+from n706a_at_handler import ATCommandHandler\r
+\r
+\r
+class WebSocketFrame:\r
+    """WebSocket帧处理类"""\r
+    \r
+    # Opcode定义\r
+    OPCODE_CONTINUATION = 0x0\r
+    OPCODE_TEXT = 0x1\r
+    OPCODE_BINARY = 0x2\r
+    OPCODE_CLOSE = 0x8\r
+    OPCODE_PING = 0x9\r
+    OPCODE_PONG = 0xA\r
+    \r
+    @staticmethod\r
+    def encode_frame(payload: bytes, opcode: int = OPCODE_TEXT, fin: bool = True, mask: bool = True) -> bytes:\r
+        """\r
+        编码WebSocket帧\r
+        \r
+        Args:\r
+            payload: 负载数据\r
+            opcode: 操作码\r
+            fin: 是否为最后一帧\r
+            mask: 是否使用掩码(客户端必须使用)\r
+        \r
+        Returns:\r
+            bytes: 编码后的帧数据\r
+        """\r
+        payload_len = len(payload)\r
+        \r
+        # 第一个字节:FIN(1) + RSV(3) + Opcode(4)\r
+        first_byte = (0x80 if fin else 0x00) | (opcode & 0x0F)\r
+        \r
+        # 第二个字节:MASK(1) + Payload length(7)\r
+        if payload_len < 126:\r
+            second_byte = (0x80 if mask else 0x00) | payload_len\r
+            header_len = 2\r
+        elif payload_len < 65536:\r
+            second_byte = (0x80 if mask else 0x00) | 126\r
+            header_len = 4\r
+        else:\r
+            second_byte = (0x80 if mask else 0x00) | 127\r
+            header_len = 10\r
+        \r
+        # 构建帧头\r
+        frame = bytearray([first_byte, second_byte])\r
+        \r
+        # 添加扩展长度字段\r
+        if payload_len >= 126 and payload_len < 65536:\r
+            frame.extend(struct.pack('!H', payload_len))\r
+        elif payload_len >= 65536:\r
+            frame.extend(struct.pack('!Q', payload_len))\r
+        \r
+        # 添加掩码(客户端必须使用)\r
+        if mask:\r
+            import os\r
+            mask_key = os.urandom(4)\r
+            frame.extend(mask_key)\r
+            # 对负载进行掩码处理\r
+            masked_payload = bytearray(payload)\r
+            for i in range(len(masked_payload)):\r
+                masked_payload[i] ^= mask_key[i % 4]\r
+            frame.extend(masked_payload)\r
+        else:\r
+            frame.extend(payload)\r
+        \r
+        return bytes(frame)\r
+    \r
+    @staticmethod\r
+    def decode_frame(data: bytes) -> tuple:\r
+        """\r
+        解码WebSocket帧\r
+        \r
+        Args:\r
+            data: 帧数据\r
+        \r
+        Returns:\r
+            tuple: (opcode, payload, fin, remaining_data)\r
+        """\r
+        if len(data) < 2:\r
+            return None, None, None, data\r
+        \r
+        first_byte = data[0]\r
+        second_byte = data[1]\r
+        \r
+        fin = (first_byte & 0x80) != 0\r
+        opcode = first_byte & 0x0F\r
+        masked = (second_byte & 0x80) != 0\r
+        payload_len = second_byte & 0x7F\r
+        \r
+        header_len = 2\r
+        \r
+        # 解析扩展长度字段\r
+        if payload_len == 126:\r
+            if len(data) < 4:\r
+                return None, None, None, data\r
+            payload_len = struct.unpack('!H', data[2:4])[0]\r
+            header_len = 4\r
+        elif payload_len == 127:\r
+            if len(data) < 10:\r
+                return None, None, None, data\r
+            payload_len = struct.unpack('!Q', data[2:10])[0]\r
+            header_len = 10\r
+        \r
+        # 解析掩码\r
+        mask_key = None\r
+        if masked:\r
+            if len(data) < header_len + 4:\r
+                return None, None, None, data\r
+            mask_key = data[header_len:header_len + 4]\r
+            header_len += 4\r
+        \r
+        # 检查是否有足够的负载数据\r
+        if len(data) < header_len + payload_len:\r
+            return None, None, None, data\r
+        \r
+        # 提取负载\r
+        payload = data[header_len:header_len + payload_len]\r
+        \r
+        # 去除掩码(服务器发送的数据不应该有掩码,但为了兼容性处理)\r
+        if masked and mask_key:\r
+            payload = bytearray(payload)\r
+            for i in range(len(payload)):\r
+                payload[i] ^= mask_key[i % 4]\r
+            payload = bytes(payload)\r
+        \r
+        remaining_data = data[header_len + payload_len:]\r
+        \r
+        return opcode, payload, fin, remaining_data\r
+\r
+\r
+class ATWebSocketClient:\r
+    """基于AT Socket的WebSocket客户端"""\r
+    \r
+    def __init__(self, at_handler: ATCommandHandler, socket_id: int = 0):\r
+        """\r
+        初始化WebSocket客户端\r
+        \r
+        Args:\r
+            at_handler: ATCommandHandler实例\r
+            socket_id: Socket ID(0-5),默认0\r
+        """\r
+        self.at_handler = at_handler\r
+        self.socket_id = socket_id\r
+        self.sock: Optional[ATSocket] = None\r
+        self.connected = False\r
+        self.url = None\r
+        self.host = None\r
+        self.port = None\r
+        self.path = None\r
+        self.use_ssl = False\r
+        self.ssl_config = None\r
+        \r
+        # 回调函数\r
+        self.on_error: Optional[Callable[[Exception], None]] = None\r
+        self.on_close: Optional[Callable[[int, str], None]] = None\r
+        self.on_open: Optional[Callable[[], None]] = None\r
+        \r
+        # 接收缓冲区\r
+        self._recv_buffer = b""\r
+    \r
+    def set_ssl_config(self, ssl_version=3, auth_mode=0, ca_cert=None, client_cert=None, client_key=None):\r
+        """\r
+        设置SSL配置(用于wss连接)\r
+        \r
+        Args:\r
+            ssl_version: SSL版本,0=SSL3.0, 1=TLS1.0, 2=TLS1.1, 3=TLS1.2, 4=TLS1.3\r
+            auth_mode: 认证模式,0=不认证, 1=单向认证, 2=双向认证\r
+            ca_cert: CA证书文件名(单向/双向认证需要)\r
+            client_cert: 客户端证书文件名(双向认证需要)\r
+            client_key: 客户端密钥文件名(双向认证需要)\r
+        """\r
+        self.ssl_config = {\r
+            'ssl_version': ssl_version,\r
+            'auth_mode': auth_mode,\r
+            'ca_cert': ca_cert,\r
+            'client_cert': client_cert,\r
+            'client_key': client_key\r
+        }\r
+    \r
+    def connect(self, url: str, headers: Optional[Dict[str, str]] = None, timeout: Optional[float] = None) -> bool:\r
+        """\r
+        连接到WebSocket服务器\r
+        \r
+        Args:\r
+            url: WebSocket URL(ws://或wss://)\r
+            headers: 额外的HTTP头\r
+            timeout: 连接超时时间(秒)\r
+        \r
+        Returns:\r
+            bool: 是否连接成功\r
+        """\r
+        try:\r
+            # 解析URL\r
+            parsed = urllib.parse.urlparse(url)\r
+            \r
+            if parsed.scheme not in ('ws', 'wss'):\r
+                raise ValueError(f"不支持的协议: {parsed.scheme},仅支持ws和wss")\r
+            \r
+            self.url = url\r
+            self.use_ssl = (parsed.scheme == 'wss')\r
+            self.host = parsed.hostname\r
+            self.port = parsed.port or (443 if self.use_ssl else 80)\r
+            self.path = parsed.path or '/'\r
+            if parsed.query:\r
+                self.path += '?' + parsed.query\r
+            \r
+            # 创建socket连接\r
+            if self.use_ssl:\r
+                if not self.ssl_config:\r
+                    # 默认SSL配置\r
+                    self.ssl_config = {\r
+                        'ssl_version': 3,  # TLS1.2\r
+                        'auth_mode': 0,\r
+                        'ca_cert': None,\r
+                        'client_cert': None,\r
+                        'client_key': None\r
+                    }\r
+                \r
+                self.sock = create_connection(\r
+                    address=(self.host, self.port),\r
+                    timeout=timeout,\r
+                    at_handler=self.at_handler,\r
+                    socket_id=self.socket_id,\r
+                    use_ssl=True,\r
+                    ssl_config=self.ssl_config\r
+                )\r
+            else:\r
+                self.sock = create_connection(\r
+                    address=(self.host, self.port),\r
+                    timeout=timeout,\r
+                    at_handler=self.at_handler,\r
+                    socket_id=self.socket_id,\r
+                    use_ssl=False\r
+                )\r
+            \r
+            # 执行WebSocket握手\r
+            if not self._handshake(headers):\r
+                self.sock.close()\r
+                self.sock = None\r
+                return False\r
+            \r
+            self.connected = True\r
+            \r
+            # 调用on_open回调\r
+            if self.on_open:\r
+                try:\r
+                    self.on_open()\r
+                except Exception as e:\r
+                    if self.on_error:\r
+                        self.on_error(e)\r
+            \r
+            return True\r
+            \r
+        except Exception as e:\r
+            if self.on_error:\r
+                self.on_error(e)\r
+            if self.sock:\r
+                self.sock.close()\r
+                self.sock = None\r
+            return False\r
+    \r
+    def _handshake(self, extra_headers: Optional[Dict[str, str]] = None) -> bool:\r
+        """\r
+        执行WebSocket握手\r
+        \r
+        Args:\r
+            extra_headers: 额外的HTTP头\r
+        \r
+        Returns:\r
+            bool: 握手是否成功\r
+        """\r
+        # 生成WebSocket Key\r
+        import random\r
+        import string\r
+        key = base64.b64encode(\r
+            ''.join(random.choices(string.ascii_letters + string.digits, k=16)).encode()\r
+        ).decode()\r
+        \r
+        # 构建握手请求\r
+        request_lines = [\r
+            f"GET {self.path} HTTP/1.1",\r
+            f"Host: {self.host}:{self.port}",\r
+            "Upgrade: websocket",\r
+            "Connection: Upgrade",\r
+            f"Sec-WebSocket-Key: {key}",\r
+            "Sec-WebSocket-Version: 13",\r
+        ]\r
+        \r
+        # 添加额外头部\r
+        if extra_headers:\r
+            for name, value in extra_headers.items():\r
+                request_lines.append(f"{name}: {value}")\r
+        \r
+        request_lines.append("")  # 空行\r
+        request_lines.append("")  # 空行\r
+        \r
+        request = "\r\n".join(request_lines)\r
+        request_bytes = request.encode('utf-8')\r
+        \r
+        # 发送握手请求\r
+        try:\r
+            self.sock.send(request_bytes)\r
+        except Exception as e:\r
+            print(f"发送握手请求失败: {e}")\r
+            return False\r
+        \r
+        # 接收握手响应\r
+        response = b""\r
+        timeout = self.sock.gettimeout() or 30\r
+        start_time = time.time()\r
+        \r
+        while time.time() - start_time < timeout:\r
+            try:\r
+                chunk = self.sock.recv(4096)\r
+                if not chunk:\r
+                    break\r
+                response += chunk\r
+                \r
+                # 检查是否收到完整的HTTP响应(以\r\n\r\n结尾)\r
+                if b"\r\n\r\n" in response:\r
+                    break\r
+            except Exception as e:\r
+                print(f"接收握手响应失败: {e}")\r
+                return False\r
+        \r
+        if not response:\r
+            print("未收到握手响应")\r
+            return False\r
+        \r
+        # 解析响应\r
+        response_text = response.decode('utf-8', errors='ignore')\r
+        lines = response_text.split('\r\n')\r
+        \r
+        # 检查状态行\r
+        if not lines[0].startswith('HTTP/1.1 101'):\r
+            print(f"握手失败,状态行: {lines[0]}")\r
+            return False\r
+        \r
+        # 检查Upgrade和Connection头\r
+        headers = {}\r
+        for line in lines[1:]:\r
+            if ':' in line:\r
+                name, value = line.split(':', 1)\r
+                headers[name.strip().lower()] = value.strip()\r
+        \r
+        if headers.get('upgrade', '').lower() != 'websocket':\r
+            print("握手失败,缺少Upgrade头")\r
+            return False\r
+        \r
+        if 'upgrade' not in headers.get('connection', '').lower():\r
+            print("握手失败,Connection头不正确")\r
+            return False\r
+        \r
+        # 验证Sec-WebSocket-Accept\r
+        expected_accept = base64.b64encode(\r
+            hashlib.sha1((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode()).digest()\r
+        ).decode()\r
+        \r
+        actual_accept = headers.get('sec-websocket-accept', '')\r
+        if actual_accept != expected_accept:\r
+            print(f"握手失败,Sec-WebSocket-Accept不匹配")\r
+            print(f"期望: {expected_accept}")\r
+            print(f"实际: {actual_accept}")\r
+            return False\r
+        \r
+        print("✅ WebSocket握手成功")\r
+        return True\r
+    \r
+    def send(self, data: bytes, opcode: int = WebSocketFrame.OPCODE_BINARY) -> bool:\r
+        """\r
+        发送WebSocket消息\r
+        \r
+        Args:\r
+            data: 要发送的数据(bytes)\r
+            opcode: 操作码(OPCODE_TEXT或OPCODE_BINARY)\r
+        \r
+        Returns:\r
+            bool: 是否发送成功\r
+        """\r
+        if not self.connected or not self.sock:\r
+            return False\r
+        \r
+        try:\r
+            frame = WebSocketFrame.encode_frame(data, opcode=opcode, fin=True, mask=True)\r
+            self.sock.send(frame)\r
+            return True\r
+        except Exception as e:\r
+            if self.on_error:\r
+                self.on_error(e)\r
+            return False\r
+    \r
+    def send_text(self, text: str) -> bool:\r
+        """\r
+        发送文本消息\r
+        \r
+        Args:\r
+            text: 要发送的文本\r
+        \r
+        Returns:\r
+            bool: 是否发送成功\r
+        """\r
+        return self.send(text.encode('utf-8'), opcode=WebSocketFrame.OPCODE_TEXT)\r
+    \r
+    def send_binary(self, data: bytes) -> bool:\r
+        """\r
+        发送二进制消息\r
+        \r
+        Args:\r
+            data: 要发送的二进制数据\r
+        \r
+        Returns:\r
+            bool: 是否发送成功\r
+        """\r
+        return self.send(data, opcode=WebSocketFrame.OPCODE_BINARY)\r
+    \r
+    def ping(self, payload: bytes = b"") -> bool:\r
+        """\r
+        发送Ping帧\r
+        \r
+        Args:\r
+            payload: Ping负载(可选)\r
+        \r
+        Returns:\r
+            bool: 是否发送成功\r
+        """\r
+        return self.send(payload, opcode=WebSocketFrame.OPCODE_PING)\r
+    \r
+    def pong(self, payload: bytes = b"") -> bool:\r
+        """\r
+        发送Pong帧\r
+        \r
+        Args:\r
+            payload: Pong负载(可选)\r
+        \r
+        Returns:\r
+            bool: 是否发送成功\r
+        """\r
+        return self.send(payload, opcode=WebSocketFrame.OPCODE_PONG)\r
+    \r
+    def recv(self, timeout: Optional[float] = None) -> Optional[bytes]:\r
+        """\r
+        接收WebSocket消息(阻塞模式)\r
+        \r
+        Args:\r
+            timeout: 超时时间(秒)\r
+        \r
+        Returns:\r
+            bytes: 接收到的消息,如果超时或连接关闭返回None\r
+        """\r
+        if not self.connected or not self.sock:\r
+            return None\r
+        \r
+        original_timeout = self.sock.gettimeout()\r
+        try:\r
+            if timeout is not None:\r
+                self.sock.settimeout(timeout)\r
+            \r
+            # 处理缓冲区中的数据\r
+            if self._recv_buffer:\r
+                opcode, payload, fin, remaining = WebSocketFrame.decode_frame(self._recv_buffer)\r
+                if payload is not None:\r
+                    self._recv_buffer = remaining\r
+                    return payload\r
+            \r
+            # 接收新数据\r
+            while True:\r
+                data = self.sock.recv(4096)\r
+                if not data:\r
+                    return None\r
+                \r
+                self._recv_buffer += data\r
+                \r
+                # 尝试解码帧\r
+                opcode, payload, fin, remaining = WebSocketFrame.decode_frame(self._recv_buffer)\r
+                \r
+                if payload is not None:\r
+                    self._recv_buffer = remaining\r
+                    \r
+                    # 处理控制帧\r
+                    if opcode == WebSocketFrame.OPCODE_CLOSE:\r
+                        self.connected = False\r
+                        return None\r
+                    elif opcode == WebSocketFrame.OPCODE_PING:\r
+                        # 自动回复Pong\r
+                        self.pong(payload)\r
+                        continue\r
+                    elif opcode == WebSocketFrame.OPCODE_PONG:\r
+                        # 忽略Pong\r
+                        continue\r
+                    else:\r
+                        return payload\r
+                else:\r
+                    # 数据不完整,继续接收\r
+                    continue\r
+                    \r
+        except Exception as e:\r
+            if self.on_error:\r
+                self.on_error(e)\r
+            return None\r
+        finally:\r
+            if timeout is not None:\r
+                self.sock.settimeout(original_timeout)\r
+    \r
+    def recv_text(self, timeout: Optional[float] = None) -> Optional[str]:\r
+        """\r
+        接收文本消息\r
+        \r
+        Args:\r
+            timeout: 超时时间(秒)\r
+        \r
+        Returns:\r
+            str: 接收到的文本,如果超时或连接关闭返回None\r
+        """\r
+        data = self.recv(timeout)\r
+        if data is None:\r
+            return None\r
+        return data.decode('utf-8')\r
+    \r
+    def close(self, code: int = 1000, reason: str = "Normal closure"):\r
+        """\r
+        关闭WebSocket连接\r
+        \r
+        Args:\r
+            code: 关闭码\r
+            reason: 关闭原因\r
+        """\r
+        if not self.connected:\r
+            return\r
+        \r
+        try:\r
+            # 发送关闭帧\r
+            close_payload = struct.pack('!H', code) + reason.encode('utf-8')\r
+            self.send(close_payload, opcode=WebSocketFrame.OPCODE_CLOSE)\r
+        except:\r
+            pass\r
+        \r
+        self.connected = False\r
+        \r
+        if self.sock:\r
+            self.sock.close()\r
+            self.sock = None\r
+        \r
+        # 调用on_close回调\r
+        if self.on_close:\r
+            try:\r
+                self.on_close(code, reason)\r
+            except Exception as e:\r
+                    if self.on_error:\r
+                        self.on_error(e)\r
+\r
diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/download_mp4_at.py b/code/sdk/zhihui/leiniao/leiniao_python_test/download_mp4_at.py
deleted file mode 100644 (file)
index c19025a..0000000
+++ /dev/null
@@ -1,496 +0,0 @@
-#!/usr/bin/env python3\r
-# -*- coding: utf-8 -*-\r
-"""\r
-使用AT指令通过HTTPS下载MP4文件\r
-参考 get_device_token_at.py 实现\r
-"""\r
-\r
-import serial\r
-import time\r
-import re\r
-import urllib.parse\r
-import os\r
-from get_device_token_at import ATCommandHandler, HTTPSATClient, HTTPATClient\r
-\r
-# -------------------------- 1. 配置参数 --------------------------\r
-# 目标文件URL\r
-MP4_URL = "https://file-virtual-person-qcl.test.leiniao.com/ambybox/Emotions/RoundEyes/Blue/Angry.mp4"\r
-# MP4_URL = "http://snicker.com.cn:8089/LEINIAO/blue/angry.zip"\r
-\r
-# 保存路径\r
-OUTPUT_DIR = "downloads"\r
-OUTPUT_FILENAME = "Angry.mp4"\r
-\r
-# AT模块串口配置(根据实际硬件配置修改)\r
-SERIAL_PORT = "COM19"  # 修改为实际的串口设备\r
-SERIAL_BAUDRATE = 2000000\r
-SERIAL_TIMEOUT = 10\r
-\r
-# 认证模式配置\r
-AUTH_MODE = 1  # 0=无需认证, 1=单向认证, 2=双向认证\r
-# CA_CERT_PATH = None  # 例如: "certs/ca.crt"\r
-CA_CERT_PATH = "certs/ca_chain.pem"\r
-\r
-# -------------------------- 2. 下载MP4文件函数 --------------------------\r
-def download_mp4_file(url, output_path, auth_mode=0, ca_cert_path=None):\r
-    """\r
-    使用AT指令通过HTTP/HTTPS下载MP4文件\r
-    \r
-    Args:\r
-        url: MP4文件的HTTP/HTTPS URL\r
-        output_path: 保存路径\r
-        auth_mode: 认证模式,0=无需认证, 1=单向认证, 2=双向认证(仅HTTPS需要)\r
-        ca_cert_path: CA证书路径(HTTPS单向认证时需要)\r
-    """\r
-    # 解析URL,判断协议类型\r
-    parsed_url = urllib.parse.urlparse(url)\r
-    is_https = parsed_url.scheme.lower() == 'https'\r
-    is_http = parsed_url.scheme.lower() == 'http'\r
-    \r
-    if not (is_http or is_https):\r
-        raise ValueError(f"不支持的URL协议: {parsed_url.scheme},仅支持 http:// 或 https://")\r
-    \r
-    protocol_name = "HTTPS" if is_https else "HTTP"\r
-    print(f"检测到协议类型: {protocol_name}")\r
-    # 初始化AT指令处理器\r
-    at_handler = ATCommandHandler(SERIAL_PORT, SERIAL_BAUDRATE, SERIAL_TIMEOUT)\r
-    \r
-    try:\r
-        # 连接串口\r
-        if not at_handler.connect():\r
-            print("❌ 无法连接串口,请检查串口配置")\r
-            return False\r
-        \r
-        # 测试AT连接\r
-        success, response = at_handler.send_at_command("AT", timeout=3)\r
-        if not success:\r
-            print("❌ AT模块无响应,请检查连接")\r
-            return False\r
-        print("✅ AT模块连接正常")\r
-        \r
-        # 检查PPP拨号状态\r
-        print("\n" + "=" * 50)\r
-        print("【步骤1: 检查PPP拨号状态】")\r
-        print("=" * 50)\r
-        is_ppp_connected, ppp_ip = at_handler.check_ppp_status()\r
-        \r
-        if is_ppp_connected:\r
-            print(f"✅ PPP已连接,IP地址: {ppp_ip}")\r
-        else:\r
-            print(f"❌ PPP未连接,IP地址: {ppp_ip or '未知'}")\r
-            print("\n尝试建立PPP连接...")\r
-            \r
-            apn_name = None\r
-            success, ip = at_handler.establish_ppp_connection(apn_name=apn_name)\r
-            \r
-            if not success:\r
-                print(f"❌ PPP连接建立失败,无法继续{protocol_name}访问")\r
-                return False\r
-            \r
-            ppp_ip = ip\r
-        \r
-        # 根据协议类型初始化客户端\r
-        if is_https:\r
-            # 初始化HTTPS客户端\r
-            http_client = HTTPSATClient(at_handler)\r
-            \r
-            # 处理证书(如果需要)\r
-            ca_cert_name = None\r
-            if auth_mode == 1 and ca_cert_path:\r
-                print("\n" + "=" * 50)\r
-                print("【步骤2: 处理证书】")\r
-                print("=" * 50)\r
-                \r
-                ca_cert_name = os.path.basename(ca_cert_path)\r
-                \r
-                # 检查证书是否已存在\r
-                if http_client.certificate_exists(ca_cert_name):\r
-                    print(f"✅ 证书 {ca_cert_name} 已存在,跳过添加")\r
-                else:\r
-                    print(f"证书 {ca_cert_name} 不存在,开始添加...")\r
-                    from get_device_token_at import load_certificate_file\r
-                    \r
-                    ca_cert_data = load_certificate_file(ca_cert_path)\r
-                    if not ca_cert_data:\r
-                        print("❌ 无法加载CA证书,终止流程")\r
-                        return False\r
-                    \r
-                    if not http_client.add_certificate(ca_cert_name, ca_cert_data):\r
-                        print("❌ CA证书添加失败,终止流程")\r
-                        return False\r
-                    \r
-                    if not http_client.check_certificate(ca_cert_name):\r
-                        print("❌ CA证书确认失败,终止流程")\r
-                        return False\r
-            \r
-            # ========== 开始计时:HTTPS访问耗时 ==========\r
-            access_start_time = time.time()\r
-            \r
-            # 配置HTTPS参数\r
-            print("\n" + "=" * 50)\r
-            print("【步骤3: 配置HTTPS参数】")\r
-            print("=" * 50)\r
-            if not http_client.configure_https(\r
-                ssl_version=3,  # TLS1.2\r
-                auth_mode=auth_mode,\r
-                ca_cert=ca_cert_name\r
-            ):\r
-                print("❌ HTTPS配置失败,终止流程")\r
-                return False\r
-            \r
-            # 确认HTTPS配置\r
-            print("\n确认HTTPS配置...")\r
-            config = http_client.verify_https_config()\r
-            if config:\r
-                print("✅ HTTPS配置确认成功")\r
-            else:\r
-                print("⚠️ HTTPS配置确认失败,但继续执行")\r
-            \r
-            # 设置URL\r
-            print("\n" + "=" * 50)\r
-            print("【步骤4: 设置HTTPS请求参数】")\r
-            print("=" * 50)\r
-            print(f"设置请求URL: {url}")\r
-            http_client.set_url(url)\r
-            \r
-            # 建立HTTPS连接\r
-            print("\n" + "=" * 50)\r
-            print("【步骤5: 建立HTTPS连接】")\r
-            print("=" * 50)\r
-            http_client.setup_connection()\r
-            print("✅ HTTPS连接建立成功")\r
-            \r
-            # 发送GET请求\r
-            print("\n" + "=" * 50)\r
-            print("【步骤6: 发送HTTPS GET请求】")\r
-            print("=" * 50)\r
-            \r
-            # 使用标准GET模式 (mode=0)\r
-            success, response = at_handler.send_at_command("AT+HTTPSACTION=0", timeout=5)\r
-            if not success:\r
-                raise Exception(f"发送HTTPS请求失败: {response}")\r
-            \r
-            # 等待响应数据\r
-            print("等待HTTPS响应...")\r
-            recv_indicator = b"+HTTPSRECV:"\r
-        else:\r
-            # 初始化HTTP客户端\r
-            http_client = HTTPATClient(at_handler)\r
-            \r
-            # ========== 开始计时:HTTP访问耗时 ==========\r
-            access_start_time = time.time()\r
-            \r
-            # 设置URL\r
-            print("\n" + "=" * 50)\r
-            print("【步骤3: 设置HTTP请求参数】")\r
-            print("=" * 50)\r
-            print(f"设置请求URL: {url}")\r
-            http_client.set_url(url)\r
-            \r
-            # 建立HTTP连接\r
-            print("\n" + "=" * 50)\r
-            print("【步骤4: 建立HTTP连接】")\r
-            print("=" * 50)\r
-            http_client.setup_connection()\r
-            print("✅ HTTP连接建立成功")\r
-            \r
-            # 发送GET请求\r
-            print("\n" + "=" * 50)\r
-            print("【步骤5: 发送HTTP GET请求】")\r
-            print("=" * 50)\r
-            \r
-            # 使用标准GET模式 (mode=0)\r
-            success, response = at_handler.send_at_command("AT+HTTPACTION=0", timeout=5)\r
-            if not success:\r
-                raise Exception(f"发送HTTP请求失败: {response}")\r
-            \r
-            # 等待响应数据\r
-            print("等待HTTP响应...")\r
-            recv_indicator = b"+HTTPRECV:"\r
-        \r
-        # 重要:直接读取二进制数据,不使用wait_for_unsolicited(它会解码为文本,可能丢失二进制数据)\r
-        # 手动等待+HTTPRECV:/+HTTPSRECV:提示符,然后立即开始读取二进制数据\r
-        response_data = b""\r
-        start_time = time.time()\r
-        last_data_time = time.time()\r
-        header_received = False\r
-        content_length = None\r
-        body_start_pos = None\r
-        recv_found = False\r
-        \r
-        # 等待+HTTPRECV:/+HTTPSRECV:提示符\r
-        indicator_name = protocol_name + "RECV"\r
-        print(f"等待+{indicator_name}:提示符...")\r
-        while time.time() - start_time < 60:\r
-            if at_handler.serial_conn.in_waiting > 0:\r
-                # 读取数据(二进制模式)\r
-                chunk = at_handler.serial_conn.read(at_handler.serial_conn.in_waiting)\r
-                if chunk:\r
-                    response_data += chunk\r
-                    last_data_time = time.time()\r
-                    \r
-                    # 检查是否包含+HTTPRECV:/+HTTPSRECV:(可能在字节串中)\r
-                    if recv_indicator in response_data:\r
-                        recv_found = True\r
-                        print(f"✅ 收到+{indicator_name}:提示符")\r
-                        # 继续读取,查找HTTP响应头\r
-                        break\r
-            time.sleep(0.1)\r
-        \r
-        if not recv_found:\r
-            # 尝试查找HTTP响应头(可能+HTTPRECV:/+HTTPSRECV:已经被读取)\r
-            if b"HTTP/" in response_data:\r
-                recv_found = True\r
-                print("✅ 检测到HTTP响应数据")\r
-        \r
-        if not recv_found:\r
-            raise Exception(f"未收到+{indicator_name}:提示符或HTTP响应")\r
-        \r
-        # 查找HTTP响应头开始位置(可能在+HTTPSRECV:之后)\r
-        http_start = response_data.find(b"HTTP/")\r
-        if http_start == -1:\r
-            # 如果没有找到HTTP/,继续读取直到找到\r
-            print("继续读取数据以查找HTTP响应头...")\r
-            while time.time() - start_time < 10:\r
-                if at_handler.serial_conn.in_waiting > 0:\r
-                    chunk = at_handler.serial_conn.read(at_handler.serial_conn.in_waiting)\r
-                    if chunk:\r
-                        response_data += chunk\r
-                        http_start = response_data.find(b"HTTP/")\r
-                        if http_start != -1:\r
-                            break\r
-                time.sleep(0.1)\r
-        \r
-        if http_start == -1:\r
-            raise Exception("未找到HTTP响应头")\r
-        \r
-        # 从HTTP响应头开始位置处理数据\r
-        # 跳过+HTTPSRECV:前缀(如果存在)\r
-        if http_start > 0:\r
-            print(f"跳过+HTTPSRECV:前缀 ({http_start} 字节)")\r
-            response_data = response_data[http_start:]\r
-        \r
-        # 查找HTTP响应头结束位置(\r\n\r\n)\r
-        print("读取响应头...")\r
-        header_timeout = 10\r
-        header_start = time.time()\r
-        \r
-        while time.time() - header_start < header_timeout:\r
-            if at_handler.serial_conn.in_waiting > 0:\r
-                chunk = at_handler.serial_conn.read(at_handler.serial_conn.in_waiting)\r
-                if chunk:\r
-                    response_data += chunk\r
-                    \r
-                    # 查找HTTP响应头结束位置(\r\n\r\n)\r
-                    header_end = response_data.find(b"\r\n\r\n")\r
-                    if header_end != -1:\r
-                        header_received = True\r
-                        body_start_pos = header_end + 4\r
-                        \r
-                        # 解析响应头\r
-                        header_text = response_data[:header_end].decode('utf-8', errors='ignore')\r
-                        print("\nHTTP响应头:")\r
-                        print(header_text)\r
-                        \r
-                        # 提取Content-Length\r
-                        content_length_match = re.search(r'Content-Length:\s*(\d+)', header_text, re.IGNORECASE)\r
-                        if content_length_match:\r
-                            content_length = int(content_length_match.group(1))\r
-                            print(f"\n文件大小: {content_length} 字节 ({content_length/1024/1024:.2f} MB)")\r
-                        else:\r
-                            print("⚠️ 未找到Content-Length,将接收所有数据直到连接关闭")\r
-                        \r
-                        # 检查状态码\r
-                        status_match = re.search(r'HTTP/\d\.\d\s+(\d+)', header_text)\r
-                        if status_match:\r
-                            status_code = int(status_match.group(1))\r
-                            if status_code != 200:\r
-                                raise Exception(f"HTTP请求失败,状态码: {status_code}")\r
-                            print(f"HTTP状态码: {status_code} ✅")\r
-                        \r
-                        # 检查文件数据开始位置的前几个字节(用于调试)\r
-                        if len(response_data) > body_start_pos:\r
-                            preview = response_data[body_start_pos:body_start_pos+20]\r
-                            print(f"\n文件数据前20字节(hex): {preview.hex()}")\r
-                            print(f"文件数据前20字节(repr): {repr(preview)}")\r
-                        \r
-                        break\r
-            time.sleep(0.1)\r
-        \r
-        if not header_received:\r
-            raise Exception("未收到完整的HTTP响应头")\r
-        \r
-        # 继续接收文件数据\r
-        print("\n开始接收文件数据...")\r
-        last_progress_time = time.time()  # 上次打印进度的时间\r
-        progress_interval = 5  # 每5秒打印一次进度\r
-        \r
-        while time.time() - start_time < 600:  # 最多等待10分钟\r
-            if at_handler.serial_conn.in_waiting > 0:\r
-                chunk = at_handler.serial_conn.read(at_handler.serial_conn.in_waiting)\r
-                if chunk:\r
-                    response_data += chunk\r
-                    last_data_time = time.time()\r
-                    \r
-                    # 如果知道文件大小,显示进度\r
-                    if content_length:\r
-                        body_data = response_data[body_start_pos:]\r
-                        received = len(body_data)\r
-                        if received >= content_length:\r
-                            print(f"\n✅ 文件接收完成: {received} / {content_length} 字节")\r
-                            break\r
-                        else:\r
-                            # 每5秒打印一次进度\r
-                            current_time = time.time()\r
-                            if current_time - last_progress_time >= progress_interval:\r
-                                progress = (received / content_length) * 100\r
-                                print(f"接收进度: {progress:.1f}% ({received}/{content_length} 字节, {received/1024/1024:.2f} MB)")\r
-                                last_progress_time = current_time\r
-                    else:\r
-                        # 不知道文件大小,每5秒显示已接收的数据量\r
-                        current_time = time.time()\r
-                        if current_time - last_progress_time >= progress_interval:\r
-                            body_data = response_data[body_start_pos:]\r
-                            received = len(body_data)\r
-                            print(f"已接收: {received} 字节 ({received/1024/1024:.2f} MB)")\r
-                            last_progress_time = current_time\r
-            else:\r
-                # 如果超过10秒没有新数据,检查是否接收完成\r
-                if time.time() - last_data_time > 10:\r
-                    if content_length:\r
-                        body_data = response_data[body_start_pos:]\r
-                        received = len(body_data)\r
-                        if received >= content_length:\r
-                            print(f"\n✅ 文件接收完成: {received} / {content_length} 字节")\r
-                            break\r
-                        else:\r
-                            print(f"\n⚠️ 接收超时,已接收: {received} / {content_length} 字节")\r
-                            break\r
-                    else:\r
-                        # 没有Content-Length,如果10秒没有新数据,认为接收完成\r
-                        print("\n响应数据接收完成(超时)")\r
-                        break\r
-                time.sleep(0.1)\r
-        \r
-        # 提取文件数据(跳过HTTP响应头)\r
-        file_data = response_data[body_start_pos:]\r
-        \r
-        # 验证文件数据(MP4文件格式)\r
-        # MP4文件结构:Box size (4字节) + Box type (4字节,通常是"ftyp")\r
-        # 所以文件应该以 0x00 0x00 0x00 0x1C 开头(或类似的大小值),然后是 "ftyp"\r
-        print(f"\n文件数据前16字节(hex): {file_data[:16].hex()}")\r
-        print(f"文件数据前16字节(repr): {repr(file_data[:16])}")\r
-        \r
-        # MP4文件应该以Box size开头(4字节大端整数),然后是"ftyp"\r
-        # 检查前4字节是否为有效的Box size(通常不会太大,比如小于100MB)\r
-        if len(file_data) >= 8:\r
-            box_size_bytes = file_data[:4]\r
-            box_type = file_data[4:8]\r
-            \r
-            # 计算Box size(大端序)\r
-            box_size = int.from_bytes(box_size_bytes, byteorder='big')\r
-            \r
-            print(f"检测到的Box size: {box_size} (hex: {box_size_bytes.hex()})")\r
-            print(f"检测到的Box type: {box_type} (hex: {box_type.hex()})")\r
-            \r
-            # 验证是否为有效的MP4文件\r
-            if box_type == b"ftyp":\r
-                print("✅ 检测到有效的MP4文件格式(ftyp box)")\r
-            elif box_size > 0 and box_size < 100 * 1024 * 1024:  # Box size在合理范围内\r
-                print(f"⚠️ Box size看起来合理,但Box type不是'ftyp',可能是其他格式")\r
-            else:\r
-                # 如果前4字节不是有效的Box size,尝试查找ftyp\r
-                ftyp_pos = file_data.find(b"ftyp")\r
-                if ftyp_pos != -1 and ftyp_pos < 100:\r
-                    print(f"⚠️ 检测到文件数据偏移,ftyp在位置 {ftyp_pos}")\r
-                    print(f"   丢失了前 {ftyp_pos} 字节数据")\r
-                    print(f"   建议检查数据接收过程,确保没有丢失数据")\r
-                    # 不自动调整,保留原始数据\r
-                    # file_data = file_data[ftyp_pos:]\r
-        \r
-        # 保存文件\r
-        print("\n" + "=" * 50)\r
-        print("【步骤7: 保存文件】")\r
-        print("=" * 50)\r
-        \r
-        # 创建输出目录\r
-        os.makedirs(os.path.dirname(output_path), exist_ok=True)\r
-        \r
-        # 写入文件\r
-        with open(output_path, 'wb') as f:\r
-            f.write(file_data)\r
-        \r
-        file_size = len(file_data)\r
-        print(f"✅ 文件保存成功: {output_path}")\r
-        print(f"   文件大小: {file_size} 字节 ({file_size/1024/1024:.2f} MB)")\r
-        \r
-        # 关闭连接\r
-        print("\n" + "=" * 50)\r
-        print(f"【步骤8: 关闭{protocol_name}连接】")\r
-        print("=" * 50)\r
-        try:\r
-            http_client.close_connection()\r
-            print(f"✅ {protocol_name}连接已关闭")\r
-        except Exception as e:\r
-            print(f"警告: 关闭{protocol_name}连接时出错: {str(e)}")\r
-        \r
-        # ========== 结束计时:HTTP/HTTPS访问耗时 ==========\r
-        access_end_time = time.time()\r
-        access_duration = access_end_time - access_start_time\r
-        \r
-        print("\n" + "=" * 50)\r
-        print(f"【{protocol_name}访问耗时统计】")\r
-        print("=" * 50)\r
-        print(f"{protocol_name}访问总耗时: {access_duration:.3f} 秒 ({access_duration*1000:.1f} 毫秒)")\r
-        print(f"下载速度: {file_size/1024/1024/access_duration:.2f} MB/s")\r
-        print("\n耗时包含以下步骤:")\r
-        if is_https:\r
-            print("  - HTTPS配置和验证")\r
-        print("  - URL和端口设置")\r
-        print(f"  - {protocol_name}连接建立")\r
-        print("  - HTTP请求发送")\r
-        print("  - 文件数据接收")\r
-        print("  - 连接关闭")\r
-        print("=" * 50)\r
-        \r
-        return True\r
-        \r
-    except Exception as e:\r
-        print("\n" + "=" * 50)\r
-        print("【错误处理】")\r
-        print("=" * 50)\r
-        print(f"❌ 下载异常: {str(e)}")\r
-        import traceback\r
-        traceback.print_exc()\r
-        return False\r
-    finally:\r
-        # 断开串口连接\r
-        print("\n断开串口连接...")\r
-        at_handler.disconnect()\r
-\r
-# -------------------------- 3. 主程序 --------------------------\r
-if __name__ == "__main__":\r
-    print("=" * 50)\r
-    print("HTTPS MP4文件下载工具(AT指令版本)")\r
-    print("=" * 50)\r
-    print(f"目标URL: {MP4_URL}")\r
-    \r
-    # 构建输出路径\r
-    output_path = os.path.join(OUTPUT_DIR, OUTPUT_FILENAME)\r
-    print(f"保存路径: {output_path}")\r
-    print("=" * 50)\r
-    \r
-    # 执行下载\r
-    success = download_mp4_file(\r
-        url=MP4_URL,\r
-        output_path=output_path,\r
-        auth_mode=AUTH_MODE,\r
-        ca_cert_path=CA_CERT_PATH\r
-    )\r
-    \r
-    if success:\r
-        print("\n✅ MP4文件下载完成!")\r
-    else:\r
-        print("\n❌ MP4文件下载失败!")\r
-        exit(1)\r
-\r
diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/get_device_token_at.py b/code/sdk/zhihui/leiniao/leiniao_python_test/get_device_token_at.py
deleted file mode 100644 (file)
index 667db3f..0000000
+++ /dev/null
@@ -1,1320 +0,0 @@
-import serial
-import time
-import hashlib
-import hmac
-import random
-import string
-import json
-import re
-import urllib.parse
-import os
-
-# -------------------------- 1. 接口基础配置 --------------------------
-BASE_URL = "https://ambybox-api.test.leiniao.com/ambybox-device-api"
-INTERFACE_PATH = "/v1/auth/getDeviceToken"
-FULL_URL = BASE_URL + INTERFACE_PATH
-SIGN_SECRET = "vDdXVXgPUEUM6_piUjz5AEhMvYTSFzQdPaN-ogKnSus="
-
-# AT模块串口配置(根据实际硬件配置修改)
-SERIAL_PORT = "COM19"  # 修改为实际的串口设备
-SERIAL_BAUDRATE = 1000000
-SERIAL_TIMEOUT = 10
-
-# -------------------------- 2. AT指令通信类 --------------------------
-class ATCommandHandler:
-    """AT指令处理器,用于与N706A模组通信"""
-    
-    def __init__(self, port=SERIAL_PORT, baudrate=SERIAL_BAUDRATE, timeout=SERIAL_TIMEOUT):
-        self.port = port
-        self.baudrate = baudrate
-        self.timeout = timeout
-        self.serial_conn = None
-        
-    def connect(self):
-        """打开串口连接"""
-        try:
-            self.serial_conn = serial.Serial(
-                port=self.port,
-                baudrate=self.baudrate,
-                timeout=self.timeout,
-                bytesize=serial.EIGHTBITS,
-                parity=serial.PARITY_NONE,
-                stopbits=serial.STOPBITS_ONE
-            )
-            print(f"串口连接成功: {self.port} @ {self.baudrate}")
-            return True
-        except Exception as e:
-            print(f"串口连接失败: {e}")
-            return False
-    
-    def disconnect(self):
-        """关闭串口连接"""
-        if self.serial_conn and self.serial_conn.is_open:
-            self.serial_conn.close()
-            print("串口连接已关闭")
-    
-    def send_at_command(self, command, expected_response="OK", timeout=5):
-        """
-        发送AT指令并等待响应
-        
-        Args:
-            command: AT指令字符串(不需要包含\r\n)
-            expected_response: 期望的响应(可以是字符串或正则表达式)
-            timeout: 超时时间(秒)
-        
-        Returns:
-            tuple: (success: bool, response: str)
-        """
-        if not self.serial_conn or not self.serial_conn.is_open:
-            return False, "串口未连接"
-        
-        # 清空输入缓冲区
-        self.serial_conn.reset_input_buffer()
-        
-        # 发送AT指令(添加\r\n)
-        cmd_bytes = (command + "\r\n").encode('utf-8')
-        self.serial_conn.write(cmd_bytes)
-        print(f"发送: {command}")
-        
-        # 等待响应
-        start_time = time.time()
-        response_lines = []
-        
-        while time.time() - start_time < timeout:
-            if self.serial_conn.in_waiting > 0:
-                line = self.serial_conn.readline().decode('utf-8', errors='ignore').strip()
-                if line:
-                    response_lines.append(line)
-                    print(f"接收: {line}")
-                    
-                    # 检查是否收到期望的响应
-                    if isinstance(expected_response, str):
-                        if expected_response in line:
-                            return True, "\n".join(response_lines)
-                    else:  # 正则表达式
-                        if expected_response.search(line):
-                            return True, "\n".join(response_lines)
-                    
-                    # 检查错误响应
-                    if "ERROR" in line:
-                        return False, "\n".join(response_lines)
-            
-            time.sleep(0.1)
-        
-        # 超时
-        return False, f"超时: {timeout}秒内未收到期望响应\n" + "\n".join(response_lines)
-    
-    def wait_for_unsolicited(self, pattern, timeout=30):
-        """
-        等待主动上报消息(如+HTTPSRECV)
-        
-        Args:
-            pattern: 匹配模式(字符串或正则表达式)
-            timeout: 超时时间(秒)
-        
-        Returns:
-            tuple: (success: bool, response: str)
-        """
-        if not self.serial_conn or not self.serial_conn.is_open:
-            return False, "串口未连接"
-        
-        start_time = time.time()
-        response_lines = []
-        pattern_compiled = re.compile(pattern) if isinstance(pattern, str) else pattern
-        
-        while time.time() - start_time < timeout:
-            if self.serial_conn.in_waiting > 0:
-                line = self.serial_conn.readline().decode('utf-8', errors='ignore').strip()
-                if line:
-                    response_lines.append(line)
-                    print(f"接收: {line}")
-                    
-                    if pattern_compiled.search(line):
-                        return True, "\n".join(response_lines)
-            
-            time.sleep(0.1)
-        
-        return False, f"超时: {timeout}秒内未收到主动上报\n" + "\n".join(response_lines)
-    
-    def check_ppp_status(self):
-        """
-        检查PPP拨号状态
-        
-        Returns:
-            tuple: (is_connected: bool, ip_address: str)
-        """
-        success, response = self.send_at_command("AT+XIIC?", timeout=5)
-        if not success:
-            return False, None
-        
-        # 解析响应格式: +XIIC: <state>,<ip>
-        # 注意:手册说明state前面可能有4个空格
-        xiic_pattern = re.compile(r'\+XIIC:\s*(\d+),([0-9.]+)')
-        match = xiic_pattern.search(response)
-        
-        if match:
-            state = int(match.group(1))
-            ip = match.group(2)
-            
-            # state=1表示已连接,且IP不是0.0.0.0
-            if state == 1 and ip != "0.0.0.0":
-                return True, ip
-            else:
-                return False, ip
-        else:
-            print(f"警告: 无法解析PPP状态响应: {response}")
-            return False, None
-    
-    def check_network_registration(self):
-        """
-        检查网络注册状态
-        
-        Returns:
-            bool: True表示已注册,False表示未注册
-        """
-        success, response = self.send_at_command("AT+CREG?", timeout=5)
-        if not success:
-            return False
-        
-        # 解析响应格式: +CREG: <n>,<stat>
-        # stat=1或5表示已注册
-        creg_pattern = re.compile(r'\+CREG:\s*\d+,([15])')
-        match = creg_pattern.search(response)
-        
-        if match:
-            return True
-        else:
-            print(f"警告: 网络未注册,响应: {response}")
-            return False
-    
-    def set_apn(self, apn_name, cid=1, pdp_type="IP"):
-        """
-        设置APN参数
-        
-        Args:
-            apn_name: APN名称(如"CMNET")
-            cid: 上下文标识符,默认1
-            pdp_type: PDP类型,默认"IP"
-        
-        Returns:
-            bool: 是否设置成功
-        """
-        command = f'AT+CGDCONT={cid},"{pdp_type}","{apn_name}"'
-        success, response = self.send_at_command(command, timeout=5)
-        return success
-    
-    def establish_ppp_connection(self, apn_name=None, max_retries=3):
-        """
-        建立PPP连接
-        
-        Args:
-            apn_name: APN名称(可选,如果为None则不设置APN)
-            max_retries: 最大重试次数
-        
-        Returns:
-            tuple: (success: bool, ip_address: str)
-        """
-        # 检查网络注册状态
-        print("检查网络注册状态...")
-        if not self.check_network_registration():
-            print("❌ 网络未注册,无法建立PPP连接")
-            return False, None
-        
-        print("✅ 网络已注册")
-        
-        # 设置APN(如果提供)
-        if apn_name:
-            print(f"设置APN: {apn_name}")
-            if not self.set_apn(apn_name):
-                print(f"警告: APN设置失败,继续尝试拨号")
-        
-        # 尝试建立PPP连接
-        for attempt in range(1, max_retries + 1):
-            print(f"\n尝试建立PPP连接 (第{attempt}次)...")
-            success, response = self.send_at_command("AT+XIIC=1", timeout=30)
-            
-            if not success:
-                print(f"❌ PPP连接失败: {response}")
-                if attempt < max_retries:
-                    print(f"等待3秒后重试...")
-                    time.sleep(3)
-                continue
-            
-            # 等待一下,然后查询状态确认
-            time.sleep(2)
-            is_connected, ip = self.check_ppp_status()
-            
-            if is_connected:
-                print(f"✅ PPP连接成功,IP地址: {ip}")
-                return True, ip
-            else:
-                print(f"❌ PPP连接状态检查失败,IP: {ip}")
-                if attempt < max_retries:
-                    print(f"等待3秒后重试...")
-                    time.sleep(3)
-        
-        return False, None
-
-# -------------------------- 3. HTTPS AT指令封装 --------------------------
-class HTTPSATClient:
-    """基于AT指令的HTTPS客户端"""
-    
-    def __init__(self, at_handler):
-        self.at = at_handler
-        self.response_data = None
-    
-    def add_certificate(self, cert_name, cert_data):
-        """
-        添加SSL证书到模块(根据SSL/TLS应用指南)
-        
-        注意:AT+CERTADD命令的响应格式特殊:
-        1. 发送 AT+CERTADD=<file_name>,<length>
-        2. 立即返回 CONNECT(不是OK)
-        3. 发送证书数据
-        4. 返回 +CERTADD: <length>,OK 或 +CERTADD: ERROR
-        
-        Args:
-            cert_name: 证书文件名(如 "ca.crt")
-            cert_data: 证书内容(字节数据或字符串)
-        
-        Returns:
-            bool: 是否添加成功
-        """
-        # 如果是字符串,转换为字节
-        if isinstance(cert_data, str):
-            cert_data = cert_data.encode('utf-8')
-        
-        cert_length = len(cert_data)
-        
-        # 清空输入缓冲区
-        self.at.serial_conn.reset_input_buffer()
-        
-        # 直接发送AT+CERTADD命令(不使用send_at_command,因为响应是CONNECT而不是OK)
-        command = f'AT+CERTADD={cert_name},{cert_length}'
-        cmd_bytes = (command + "\r\n").encode('utf-8')
-        self.at.serial_conn.write(cmd_bytes)
-        print(f"发送: {command}")
-        
-        # 等待CONNECT提示符(AT+CERTADD会立即返回CONNECT,而不是OK)
-        print("等待'CONNECT'提示符...")
-        start_time = time.time()
-        connect_received = False
-        response_lines = []
-        
-        while time.time() - start_time < 5:
-            if self.at.serial_conn.in_waiting > 0:
-                line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
-                if line:
-                    response_lines.append(line)
-                    print(f"接收: {line}")
-                    
-                    # 检查是否收到CONNECT
-                    if "CONNECT" in line.upper():
-                        connect_received = True
-                        break
-                    
-                    # 检查错误响应
-                    if "ERROR" in line:
-                        print(f"❌ 证书添加命令失败: {line}")
-                        return False
-            time.sleep(0.1)
-        
-        if not connect_received:
-            newline = '\n'
-            print(f"❌ 错误: 未收到'CONNECT'提示符,响应: {newline.join(response_lines)}")
-            return False
-        
-        # 发送证书数据
-        print(f"发送证书数据 ({cert_length} 字节)...")
-        self.at.serial_conn.write(cert_data)
-        # 确保数据发送完成
-        self.at.serial_conn.flush()
-        
-        # 等待证书添加完成响应(+CERTADD: <length>,OK 或 +CERTADD: ERROR)
-        print("等待证书添加完成响应...")
-        start_time = time.time()
-        certadd_received = False
-        response_lines = []
-        
-        while time.time() - start_time < 30:  # 证书写入可能需要较长时间
-            if self.at.serial_conn.in_waiting > 0:
-                line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
-                if line:
-                    response_lines.append(line)
-                    print(f"接收: {line}")
-                    
-                    # 检查是否收到+CERTADD响应
-                    if "+CERTADD:" in line:
-                        certadd_received = True
-                        # 检查是否成功
-                        if f"{cert_length},OK" in line or ",OK" in line:
-                            print(f"✅ 证书 {cert_name} 添加成功")
-                            return True
-                        elif "ERROR" in line:
-                            print(f"❌ 证书添加失败: {line}")
-                            return False
-            time.sleep(0.1)
-        
-        if not certadd_received:
-            newline = '\n'
-            print(f"❌ 错误: 证书添加超时,响应: {newline.join(response_lines)}")
-            return False
-        
-        return False
-    
-    def check_certificate(self, cert_name):
-        """
-        确认证书是否成功写入(根据SSL/TLS应用指南)
-        
-        Args:
-            cert_name: 证书文件名
-        
-        Returns:
-            bool: 证书是否存在
-        """
-        success, response = self.at.send_at_command(
-            f'AT+CERTCHECK={cert_name}',
-            timeout=5
-        )
-        if not success:
-            return False
-        
-        # 检查响应格式: +CERTCHECK: <file_name>,OK
-        if f"{cert_name},OK" in response:
-            print(f"✅ 证书 {cert_name} 确认成功")
-            return True
-        else:
-            print(f"❌ 证书 {cert_name} 不存在或验证失败: {response}")
-            return False
-    
-    def list_certificates(self):
-        """
-        查询设备中所有已添加的证书列表
-        
-        Returns:
-            list: 证书文件名列表,如果查询失败返回None
-        """
-        success, response = self.at.send_at_command("AT+CERTCHECK?", timeout=5)
-        if not success:
-            print(f"警告: 证书列表查询失败: {response}")
-            return None
-        
-        # 解析响应格式: 每行一个证书文件名,最后是OK
-        # 例如:
-        # cacert.pem
-        # keycert.pem
-        # OK
-        cert_list = []
-        for line in response.split('\n'):
-            line = line.strip()
-            if line and line not in ['OK', 'AT+CERTCHECK?']:
-                # 跳过响应前缀(如果有)
-                if line.startswith('+CERTCHECK:'):
-                    continue
-                cert_list.append(line)
-        
-        return cert_list
-    
-    def certificate_exists(self, cert_name):
-        """
-        检查证书是否已存在于设备中(避免重复添加)
-        
-        Args:
-            cert_name: 证书文件名
-        
-        Returns:
-            bool: 证书是否存在
-        """
-        success, response = self.at.send_at_command(
-            f'AT+CERTCHECK={cert_name}',
-            timeout=5
-        )
-        if not success:
-            return False
-        
-        # 检查响应格式: +CERTCHECK: <file_name>,OK 或 +CERTCHECK: ERROR
-        if f"{cert_name},OK" in response:
-            return True
-        else:
-            return False
-    
-    def configure_https(self, ssl_version=3, auth_mode=0, ca_cert=None, client_cert=None, client_key=None):
-        """
-        配置HTTPS SSL参数
-        
-        Args:
-            ssl_version: SSL版本,0=SSL3.0, 1=TLS1.0, 2=TLS1.1, 3=TLS1.2
-            auth_mode: 认证模式,0=不认证, 1=单向认证, 2=双向认证
-            ca_cert: CA证书文件名(单向/双向认证需要)
-            client_cert: 客户端证书文件名(双向认证需要)
-            client_key: 客户端密钥文件名(双向认证需要)
-        """
-        # 设置SSL版本
-        success, response = self.at.send_at_command(
-            f'AT+HTTPSCFG="sslversion",{ssl_version}',
-            timeout=5
-        )
-        if not success:
-            print(f"警告: SSL版本配置失败: {response}")
-            return False
-        
-        # 设置认证模式
-        success, response = self.at.send_at_command(
-            f'AT+HTTPSCFG="authmode",{auth_mode}',
-            timeout=5
-        )
-        if not success:
-            print(f"警告: 认证模式配置失败: {response}")
-            return False
-        
-        # 单向认证:配置CA证书
-        if auth_mode == 1:
-            if not ca_cert:
-                print("错误: 单向认证需要CA证书")
-                return False
-            
-            success, response = self.at.send_at_command(
-                f'AT+HTTPSCFG="cacert",{ca_cert}',
-                timeout=5
-            )
-            if not success:
-                print(f"错误: CA证书配置失败: {response}")
-                return False
-            print(f"✅ 已配置CA证书: {ca_cert}")
-        
-        # 双向认证:配置CA证书、客户端证书和密钥
-        elif auth_mode == 2:
-            if not ca_cert or not client_cert or not client_key:
-                print("错误: 双向认证需要CA证书、客户端证书和密钥")
-                return False
-            
-            # 配置CA证书
-            success, response = self.at.send_at_command(
-                f'AT+HTTPSCFG="cacert",{ca_cert}',
-                timeout=5
-            )
-            if not success:
-                print(f"错误: CA证书配置失败: {response}")
-                return False
-            
-            # 配置客户端证书
-            success, response = self.at.send_at_command(
-                f'AT+HTTPSCFG="clientcert",{client_cert}',
-                timeout=5
-            )
-            if not success:
-                print(f"错误: 客户端证书配置失败: {response}")
-                return False
-            
-            # 配置客户端密钥
-            success, response = self.at.send_at_command(
-                f'AT+HTTPSCFG="clientkey",{client_key}',
-                timeout=5
-            )
-            if not success:
-                print(f"错误: 客户端密钥配置失败: {response}")
-                return False
-            
-            print(f"✅ 已配置双向认证证书: CA={ca_cert}, ClientCert={client_cert}, ClientKey={client_key}")
-        
-        return True
-    
-    def verify_https_config(self):
-        """
-        确认HTTPS配置(根据SSL/TLS应用指南要求)
-        
-        Returns:
-            dict: 包含配置信息的字典,如果查询失败返回None
-        """
-        success, response = self.at.send_at_command("AT+HTTPSCFG?", timeout=5)
-        if not success:
-            print(f"警告: HTTPS配置查询失败: {response}")
-            return None
-        
-        # 解析响应格式: +HTTPSCFG: <sslversion>,<authmode>,<cacert>,<clientcert>,<clientkey>
-        # 例如: +HTTPSCFG: 3,0,,,
-        httpcfg_pattern = re.compile(r'\+HTTPSCFG:\s*(\d+),(\d+),([^,]*),([^,]*),([^,\r\n]*)')
-        match = httpcfg_pattern.search(response)
-        
-        if match:
-            config = {
-                'sslversion': int(match.group(1)),
-                'authmode': int(match.group(2)),
-                'cacert': match.group(3).strip() if match.group(3).strip() else None,
-                'clientcert': match.group(4).strip() if match.group(4).strip() else None,
-                'clientkey': match.group(5).strip() if match.group(5).strip() else None
-            }
-            
-            # 打印配置信息
-            ssl_version_names = {0: "SSL3.0", 1: "TLS1.0", 2: "TLS1.1", 3: "TLS1.2"}
-            auth_mode_names = {0: "无需认证", 1: "单向认证", 2: "双向认证"}
-            
-            print(f"  SSL版本: {config['sslversion']} ({ssl_version_names.get(config['sslversion'], '未知')})")
-            print(f"  认证模式: {config['authmode']} ({auth_mode_names.get(config['authmode'], '未知')})")
-            if config['cacert']:
-                print(f"  CA证书: {config['cacert']}")
-            if config['clientcert']:
-                print(f"  客户端证书: {config['clientcert']}")
-            if config['clientkey']:
-                print(f"  客户端密钥: {config['clientkey']}")
-            
-            return config
-        else:
-            print(f"警告: 无法解析HTTPS配置响应: {response}")
-            return None
-    
-    def set_url(self, url, keepalive=0):
-        """
-        设置HTTPS请求URL
-        
-        Args:
-            url: 完整的URL(如 https://example.com/path)
-            keepalive: 是否启用长连接,0=关闭(默认),1=启用
-                     根据SSL/TLS应用指南,启用后可以多次请求而无需重新建立连接
-        """
-        # 解析URL
-        parsed = urllib.parse.urlparse(url)
-        host = parsed.netloc
-        path = parsed.path
-        if parsed.query:
-            path += "?" + parsed.query
-        
-        # 设置URL(不包含协议和端口)
-        url_param = f"{host}{path}"
-        success, response = self.at.send_at_command(
-            f'AT+HTTPSPARA=url,"{url_param}"',
-            timeout=5
-        )
-        if not success:
-            raise Exception(f"设置URL失败: {response}")
-        
-        # 设置端口(HTTPS默认443)
-        port = parsed.port if parsed.port else 443
-        success, response = self.at.send_at_command(
-            f'AT+HTTPSPARA=port,{port}',
-            timeout=5
-        )
-        if not success:
-            raise Exception(f"设置端口失败: {response}")
-        
-        # 设置keepalive(根据SSL/TLS应用指南,可选配置)
-        if keepalive in [0, 1]:
-            success, response = self.at.send_at_command(
-                f'AT+HTTPSPARA=keepalive,{keepalive}',
-                timeout=5
-            )
-            if not success:
-                print(f"警告: keepalive配置失败: {response}")
-            else:
-                if keepalive == 1:
-                    print("  ✅ 已启用长连接(keepalive=1)")
-                else:
-                    print("  ✅ 已关闭长连接(keepalive=0,默认)")
-        
-        return True
-    
-    def setup_connection(self):
-        """建立HTTPS连接"""
-        success, response = self.at.send_at_command("AT+HTTPSSETUP", timeout=15)
-        if not success or "ERROR" in response:
-            raise Exception(f"HTTPS连接建立失败: {response}")
-        return True
-    
-    def send_get_request(self, headers=None):
-        """
-        发送HTTPS GET请求
-        
-        Args:
-            headers: 自定义HTTP头字典
-        
-        Returns:
-            str: 响应数据
-        """
-        if headers:
-            # 使用OPEN_MODE (mode=99) 自定义HTTP报文
-            # 根据手册要求:用户自定义报文时需遵循HTTP协议
-            # 构建自定义HTTP请求
-            parsed = urllib.parse.urlparse(FULL_URL)
-            path = parsed.path
-            if parsed.query:
-                path += "?" + parsed.query
-            
-            # 构建HTTP请求报文(严格按照HTTP协议格式)
-            # 请求行:GET /path HTTP/1.1<CRLF>
-            http_request = f"GET {path} HTTP/1.1\r\n"
-            # Host头:Host: hostname<CRLF>
-            http_request += f"Host: {parsed.netloc}\r\n"
-            
-            # 添加自定义头(每行格式:Header-Name: value<CRLF>)
-            for key, value in headers.items():
-                http_request += f"{key}: {value}\r\n"
-            
-            # Connection头
-            http_request += "Connection: close\r\n"
-            # 请求头和请求体之间的空行(即使没有请求体也需要)<CRLF><CRLF>
-            http_request += "\r\n"
-            
-            # 计算请求长度(字节数,用于AT+HTTPSACTION=99,<length>)
-            request_length = len(http_request.encode('utf-8'))
-            
-            # 步骤1:直接发送AT+HTTPSACTION=99,<length>命令
-            # 注意:根据手册,AT+HTTPSACTION=99会先返回">"提示符,而不是OK
-            # 所以不能使用send_at_command(它等待OK),需要直接发送并等待">"
-            self.at.serial_conn.reset_input_buffer()
-            command = f"AT+HTTPSACTION=99,{request_length}"
-            cmd_bytes = (command + "\r\n").encode('utf-8')
-            self.at.serial_conn.write(cmd_bytes)
-            print(f"发送: {command}")
-            
-            # 步骤2:等待">"提示符(根据手册,发送AT+HTTPSACTION=99后会收到">"提示符)
-            print("等待'>'提示符...")
-            start_time = time.time()
-            prompt_received = False
-            response_lines = []
-            
-            while time.time() - start_time < 5:
-                if self.at.serial_conn.in_waiting > 0:
-                    line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
-                    if line:
-                        response_lines.append(line)
-                        print(f"接收: {line}")
-                        
-                        # 检查是否收到">"提示符
-                        if ">" in line:
-                            prompt_received = True
-                            break
-                        
-                        # 检查错误响应
-                        if "ERROR" in line:
-                            newline = '\n'
-                            raise Exception(f"HTTPS请求命令失败: {newline.join(response_lines)}")
-                time.sleep(0.1)
-            
-            if not prompt_received:
-                newline = '\n'
-                raise Exception(f"未收到'>'提示符,响应: {newline.join(response_lines)}")
-            
-            # 步骤3:发送完整的HTTP请求报文(根据手册,需要发送完整的HTTP协议报文)
-            # 注意:手册要求遵循HTTP协议,使用<CRLF>作为行结束符
-            print(f"发送HTTP请求 ({request_length} 字节)...")
-            self.at.serial_conn.write(http_request.encode('utf-8'))
-            # 确保数据发送完成
-            self.at.serial_conn.flush()
-            print(f"HTTP请求内容:\n{http_request}")
-            
-            # 步骤4:等待OK响应(发送数据后应该会收到OK)
-            print("等待OK响应...")
-            start_time = time.time()
-            ok_received = False
-            
-            while time.time() - start_time < 5:
-                if self.at.serial_conn.in_waiting > 0:
-                    line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
-                    if line:
-                        print(f"接收: {line}")
-                        if "OK" in line:
-                            ok_received = True
-                            break
-                        if "ERROR" in line:
-                            raise Exception(f"HTTPS请求发送失败: {line}")
-                time.sleep(0.1)
-            
-            if not ok_received:
-                print("⚠️ 未收到OK响应,但继续等待HTTPS响应...")
-        else:
-            # 使用标准GET模式 (mode=0)
-            success, response = self.at.send_at_command("AT+HTTPSACTION=0", timeout=5)
-            if not success:
-                raise Exception(f"发送HTTPS请求失败: {response}")
-        
-        # 等待响应数据
-        print("等待HTTPS响应...")
-        success, response = self.at.wait_for_unsolicited(r"\+HTTPSRECV:", timeout=30)
-        if not success:
-            raise Exception(f"等待HTTPS响应超时: {response}")
-        
-        # 继续读取响应数据
-        response_data = response
-        start_time = time.time()
-        last_data_time = time.time()
-        
-        while time.time() - start_time < 30:  # 最多等待30秒接收完整响应
-            if self.at.serial_conn.in_waiting > 0:
-                line = self.at.serial_conn.readline().decode('utf-8', errors='ignore')
-                if line.strip():
-                    response_data += line
-                    print(f"接收: {line.strip()[:100]}...")  # 只打印前100个字符
-                    last_data_time = time.time()
-                
-                # 检查是否收到连接关闭消息
-                if "+HTTPSCLOSED" in line or "+HTTPSCLOSE" in line:
-                    print("收到连接关闭消息")
-                    break
-            else:
-                # 如果超过2秒没有新数据,认为响应接收完成
-                if time.time() - last_data_time > 2:
-                    print("响应数据接收完成(超时)")
-                    break
-                time.sleep(0.1)
-        
-        return response_data
-    
-    def close_connection(self):
-        """关闭HTTPS连接"""
-        success, response = self.at.send_at_command("AT+HTTPSCLOSE", timeout=5)
-        return success
-
-# -------------------------- 4. HTTP AT指令封装 --------------------------
-class HTTPATClient:
-    """基于AT指令的HTTP客户端"""
-    
-    def __init__(self, at_handler):
-        self.at = at_handler
-        self.response_data = None
-    
-    def set_url(self, url, keepalive=0):
-        """
-        设置HTTP请求URL
-        
-        Args:
-            url: 完整的URL(如 http://example.com/path)
-            keepalive: 是否启用长连接,0=关闭(默认),1=启用
-        """
-        # 解析URL
-        parsed = urllib.parse.urlparse(url)
-        host = parsed.netloc
-        path = parsed.path
-        if parsed.query:
-            path += "?" + parsed.query
-        
-        # 设置URL(不包含协议和端口)
-        url_param = f"{host}{path}"
-        success, response = self.at.send_at_command(
-            f'AT+HTTPPARA=url,"{url_param}"',
-            timeout=5
-        )
-        if not success:
-            raise Exception(f"设置URL失败: {response}")
-        
-        # 设置端口(如果URL中指定了端口)
-        if parsed.port:
-            port = parsed.port
-        else:
-            port = 80  # HTTP默认端口
-        
-        success, response = self.at.send_at_command(
-            f'AT+HTTPPARA=port,{port}',
-            timeout=5
-        )
-        if not success:
-            raise Exception(f"设置端口失败: {response}")
-        
-        # 设置keepalive(可选)
-        if keepalive:
-            success, response = self.at.send_at_command(
-                f'AT+HTTPPARA=keepalive,{keepalive}',
-                timeout=5
-            )
-            if not success:
-                print(f"警告: 设置keepalive失败: {response}")
-    
-    def setup_connection(self):
-        """
-        建立HTTP连接
-        
-        Returns:
-            bool: 是否建立成功
-        """
-        success, response = self.at.send_at_command("AT+HTTPSETUP", timeout=15)
-        if not success:
-            raise Exception(f"建立HTTP连接失败: {response}")
-        return True
-    
-    def send_get_request(self, timeout=60):
-        """
-        发送HTTP GET请求(使用标准GET模式)
-        
-        Args:
-            timeout: 超时时间(秒)
-        
-        Returns:
-            tuple: (success: bool, response: str)
-        """
-        # 使用标准GET模式 (mode=0)
-        success, response = self.at.send_at_command("AT+HTTPACTION=0", timeout=5)
-        if not success:
-            raise Exception(f"发送HTTP请求失败: {response}")
-        
-        # 等待响应(+HTTPRECV:)
-        success, response = self.at.wait_for_unsolicited(r"\+HTTPRECV:", timeout=timeout)
-        return success, response
-    
-    def close_connection(self):
-        """
-        关闭HTTP连接
-        
-        Returns:
-            bool: 是否关闭成功
-        """
-        success, response = self.at.send_at_command("AT+HTTPCLOSE", timeout=5)
-        return success
-    
-    def parse_http_response(self, raw_response):
-        """
-        解析HTTP响应
-        
-        Returns:
-            dict: 包含status_code, headers, body的字典
-        """
-        # 查找HTTP响应开始位置(可能在+HTTPSRECV:之后)
-        http_start = raw_response.find("HTTP/")
-        if http_start == -1:
-            # 如果没有找到HTTP/,尝试查找状态码
-            status_match = re.search(r'(\d{3})\s+', raw_response)
-            if status_match:
-                http_start = status_match.start()
-            else:
-                raise Exception("未找到HTTP响应")
-        
-        response_text = raw_response[http_start:]
-        
-        # 分离头部和正文
-        header_end = response_text.find("\r\n\r\n")
-        if header_end == -1:
-            header_end = response_text.find("\n\n")
-        
-        if header_end == -1:
-            # 如果没有找到空行分隔符,尝试查找第一个{作为JSON开始
-            json_start = response_text.find("{")
-            if json_start != -1:
-                headers_text = response_text[:json_start]
-                body_text = response_text[json_start:]
-            else:
-                raise Exception("无法解析HTTP响应格式")
-        else:
-            headers_text = response_text[:header_end]
-            body_text = response_text[header_end:].strip()
-        
-        # 解析状态行
-        lines = headers_text.split("\n")
-        status_line = lines[0].strip()
-        status_match = re.search(r'HTTP/\d\.\d\s+(\d+)', status_line)
-        status_code = int(status_match.group(1)) if status_match else 0
-        
-        # 解析头部
-        headers = {}
-        for line in lines[1:]:
-            line = line.strip()
-            if ":" in line:
-                key, value = line.split(":", 1)
-                headers[key.strip()] = value.strip()
-        
-        # 提取JSON正文(去除+HTTPSRECV:前缀等)
-        # 查找JSON开始位置
-        json_start = body_text.find("{")
-        json_end = body_text.rfind("}") + 1
-        
-        if json_start != -1 and json_end > json_start:
-            body_json = body_text[json_start:json_end]
-        else:
-            # 如果没有找到JSON,返回原始正文
-            body_json = body_text.strip()
-            # 清理可能的AT指令响应前缀
-            for prefix in ["+HTTPSRECV:", "+HTTPSCLOSED:", "+HTTPSCLOSE:"]:
-                if body_json.startswith(prefix):
-                    body_json = body_json[len(prefix):].strip()
-        
-        return {
-            "status_code": status_code,
-            "headers": headers,
-            "body": body_json
-        }
-
-# -------------------------- 4. 生成合法请求参数 --------------------------
-def generate_request_params():
-    device_id = "DEVICE_PC_TEST_001"
-    device_iccid = "86123456789012345678"
-    
-    # 1. 时间戳格式修正:确保是字符串类型的毫秒级时间戳
-    timestamp = str(int(time.time() * 1000))
-    
-    # 2. 随机数生成:确保长度在8-16位之间(当前12位符合要求)
-    nonce_length = 12
-    nonce = ''.join(random.choices(string.ascii_letters + string.digits, k=nonce_length))
-    
-    # 3. 签名生成(保持不变,符合协议要求)
-    sign_data = f"{device_id}:{device_iccid}:{timestamp}:{nonce}"
-    hmac_obj = hmac.new(SIGN_SECRET.encode("utf-8"), sign_data.encode("utf-8"), hashlib.sha256)
-    signature = hmac_obj.hexdigest().lower()
-    
-    # 4. 返回参数(键名严格匹配文档,保持不变)
-    return {
-        "deviceId": device_id,
-        "deviceIccid": device_iccid,
-        "timestamp": timestamp,
-        "nonce": nonce,
-        "signature": signature,
-        "deviceModel": "AB101",
-        "deviceVersion": "1.0.0"
-    }
-
-# -------------------------- 5. 证书管理辅助函数 --------------------------
-def load_certificate_file(cert_path):
-    """
-    从文件加载证书内容
-    
-    Args:
-        cert_path: 证书文件路径
-    
-    Returns:
-        bytes: 证书内容(字节数据)
-    """
-    try:
-        with open(cert_path, 'rb') as f:
-            cert_data = f.read()
-        print(f"✅ 成功加载证书文件: {cert_path} ({len(cert_data)} 字节)")
-        return cert_data
-    except FileNotFoundError:
-        print(f"❌ 证书文件不存在: {cert_path}")
-        return None
-    except Exception as e:
-        print(f"❌ 读取证书文件失败: {e}")
-        return None
-
-# -------------------------- 6. 发送请求并解析响应 --------------------------
-def send_get_device_token_request(auth_mode=0, ca_cert_path=None, client_cert_path=None, client_key_path=None):
-    """使用AT指令发送HTTPS GET请求"""
-    
-    # 初始化AT指令处理器
-    at_handler = ATCommandHandler()
-    
-    try:
-        # 连接串口
-        if not at_handler.connect():
-            print("❌ 无法连接串口,请检查串口配置")
-            return
-        
-        # 测试AT连接
-        success, response = at_handler.send_at_command("AT", timeout=3)
-        if not success:
-            print("❌ AT模块无响应,请检查连接")
-            return
-        print("✅ AT模块连接正常")
-        
-        # 查询版本号
-        print("\n查询模块版本号...")
-        success, response = at_handler.send_at_command("AT+GMR", timeout=5)
-        if success:
-            print("✅ 版本信息:")
-            # 打印响应中的所有行(去除OK等状态行)
-            for line in response.split('\n'):
-                line = line.strip()
-                if line and line not in ['OK', 'AT+GMR']:
-                    print(f"  {line}")
-        else:
-            print(f"⚠️  版本查询失败: {response}")
-        
-        # 检查PPP拨号状态
-        print("\n" + "=" * 50)
-        print("【步骤1: 检查PPP拨号状态】")
-        print("=" * 50)
-        is_ppp_connected, ppp_ip = at_handler.check_ppp_status()
-        
-        if is_ppp_connected:
-            print(f"✅ PPP已连接,IP地址: {ppp_ip}")
-        else:
-            print(f"❌ PPP未连接,IP地址: {ppp_ip or '未知'}")
-            print("\n尝试建立PPP连接...")
-            
-            # 建立PPP连接(可以根据实际情况设置APN)
-            # 如果不需要设置APN,可以传入None
-            apn_name = None  # 可以根据实际情况修改,如 "CMNET"、"3GNET" 等
-            success, ip = at_handler.establish_ppp_connection(apn_name=apn_name)
-            
-            if not success:
-                print("❌ PPP连接建立失败,无法继续HTTPS访问")
-                return
-            
-            ppp_ip = ip
-        
-        # 初始化HTTPS客户端
-        https_client = HTTPSATClient(at_handler)
-        
-        # 查询设备中已有的证书列表(可选,用于调试)
-        if auth_mode in [1, 2]:
-            print("\n" + "=" * 50)
-            print("【步骤2: 查询设备证书列表】")
-            print("=" * 50)
-            cert_list = https_client.list_certificates()
-            if cert_list:
-                print(f"设备中已有 {len(cert_list)} 个证书:")
-                for cert in cert_list:
-                    print(f"  - {cert}")
-            else:
-                print("设备中暂无证书")
-        
-        # 配置HTTPS(根据认证模式)
-        print("\n" + "=" * 50)
-        print("【步骤3: 配置HTTPS参数】")
-        print("=" * 50)
-        
-        # 根据认证模式处理证书
-        ca_cert_name = None
-        client_cert_name = None
-        client_key_name = None
-        
-        if auth_mode == 1:  # 单向认证
-            print("配置模式: 单向认证")
-            if not ca_cert_path:
-                print("❌ 错误: 单向认证需要提供CA证书路径")
-                return
-            
-            # 提取证书文件名
-            ca_cert_name = os.path.basename(ca_cert_path)
-            
-            # 检查证书是否已存在(避免重复添加)
-            print(f"\n检查证书是否已存在: {ca_cert_name}")
-            if https_client.certificate_exists(ca_cert_name):
-                print(f"✅ 证书 {ca_cert_name} 已存在于设备中,跳过添加步骤")
-            else:
-                print(f"证书 {ca_cert_name} 不存在,开始添加...")
-                
-                # 加载CA证书
-                ca_cert_data = load_certificate_file(ca_cert_path)
-                if not ca_cert_data:
-                    print("❌ 无法加载CA证书,终止流程")
-                    return
-                
-                # 添加CA证书到模块
-                print(f"\n添加CA证书: {ca_cert_name}")
-                if not https_client.add_certificate(ca_cert_name, ca_cert_data):
-                    print("❌ CA证书添加失败,终止流程")
-                    return
-                
-                # 确认证书
-                if not https_client.check_certificate(ca_cert_name):
-                    print("❌ CA证书确认失败,终止流程")
-                    return
-        
-        elif auth_mode == 2:  # 双向认证
-            print("配置模式: 双向认证")
-            if not ca_cert_path or not client_cert_path or not client_key_path:
-                print("❌ 错误: 双向认证需要提供CA证书、客户端证书和密钥路径")
-                return
-            
-            ca_cert_name = os.path.basename(ca_cert_path)
-            client_cert_name = os.path.basename(client_cert_path)
-            client_key_name = os.path.basename(client_key_path)
-            
-            # 检查并添加CA证书
-            print(f"\n检查CA证书: {ca_cert_name}")
-            if https_client.certificate_exists(ca_cert_name):
-                print(f"✅ 证书 {ca_cert_name} 已存在,跳过添加")
-            else:
-                ca_cert_data = load_certificate_file(ca_cert_path)
-                if not ca_cert_data:
-                    return
-                if not https_client.add_certificate(ca_cert_name, ca_cert_data):
-                    return
-                if not https_client.check_certificate(ca_cert_name):
-                    return
-            
-            # 检查并添加客户端证书
-            print(f"\n检查客户端证书: {client_cert_name}")
-            if https_client.certificate_exists(client_cert_name):
-                print(f"✅ 证书 {client_cert_name} 已存在,跳过添加")
-            else:
-                client_cert_data = load_certificate_file(client_cert_path)
-                if not client_cert_data:
-                    return
-                if not https_client.add_certificate(client_cert_name, client_cert_data):
-                    return
-                if not https_client.check_certificate(client_cert_name):
-                    return
-            
-            # 检查并添加客户端密钥
-            print(f"\n检查客户端密钥: {client_key_name}")
-            if https_client.certificate_exists(client_key_name):
-                print(f"✅ 密钥 {client_key_name} 已存在,跳过添加")
-            else:
-                client_key_data = load_certificate_file(client_key_path)
-                if not client_key_data:
-                    return
-                if not https_client.add_certificate(client_key_name, client_key_data):
-                    return
-                if not https_client.check_certificate(client_key_name):
-                    return
-        else:
-            print("配置模式: 无需认证")
-        
-        # ========== 开始计时:HTTPS访问耗时 ==========
-        https_start_time = time.time()
-        
-        # 配置HTTPS参数
-        print("\n" + "=" * 50)
-        print("【步骤4: 配置HTTPS参数】")
-        print("=" * 50)
-        if not https_client.configure_https(
-            ssl_version=3,  # TLS1.2
-            auth_mode=auth_mode,
-            ca_cert=ca_cert_name,
-            client_cert=client_cert_name,
-            client_key=client_key_name
-        ):
-            print("❌ HTTPS配置失败,终止流程")
-            return
-        
-        # 确认HTTPS配置(根据SSL/TLS应用指南要求)
-        print("\n确认HTTPS配置...")
-        config = https_client.verify_https_config()
-        if config:
-            print("✅ HTTPS配置确认成功")
-        else:
-            print("⚠️ HTTPS配置确认失败,但继续执行")
-        
-        # 设置URL
-        print("\n" + "=" * 50)
-        print("【步骤5: 设置HTTPS请求参数】")
-        print("=" * 50)
-        print(f"设置请求URL: {FULL_URL}")
-        https_client.set_url(FULL_URL)
-        
-        # 建立HTTPS连接
-        print("\n" + "=" * 50)
-        print("【步骤6: 建立HTTPS连接】")
-        print("=" * 50)
-        https_client.setup_connection()
-        print("✅ HTTPS连接建立成功")
-        
-        # 生成请求参数
-        params = generate_request_params()
-        
-        # 构建HTTP头(根据API文档要求,参数需要作为HTTP头传递,而不是URL查询参数)
-        # 参考:设备API接口文档.md - 请求头参数部分
-        # 注意:为了与get_device_token_1.py保持一致,先添加参数,最后添加Content-Type
-        headers = {}
-        # 将参数添加到HTTP头(deviceId, deviceIccid, timestamp, nonce, signature等)
-        for key, value in params.items():
-            headers[key] = str(value)
-        # Content-Type放在最后(与requests库的行为保持一致)
-        headers["Content-Type"] = "application/json"
-        
-        print("\n" + "=" * 50)
-        print("【步骤7: 发送HTTPS GET请求】")
-        print("=" * 50)
-        print("【请求信息】")
-        print(f"请求 URL: {FULL_URL}")
-        print(f"请求头: {headers}")
-        print("=" * 50)
-        
-        # 发送请求
-        try:
-            raw_response = https_client.send_get_request(headers=headers)
-        except Exception as e:
-            print(f"❌ HTTPS请求发送失败: {str(e)}")
-            raise
-        
-        # 解析响应
-        print("\n" + "=" * 50)
-        print("【步骤8: 解析HTTPS响应】")
-        print("=" * 50)
-        try:
-            parsed_response = https_client.parse_http_response(raw_response)
-        except Exception as e:
-            print(f"❌ HTTP响应解析失败: {str(e)}")
-            print(f"原始响应: {raw_response[:500]}...")  # 只打印前500字符
-            raise
-        
-        print(f"HTTP状态码: {parsed_response['status_code']}")
-        print(f"响应头: {parsed_response['headers']}")
-        print(f"响应体: {parsed_response['body']}")
-        
-        # 解析JSON响应
-        print("\n" + "=" * 50)
-        print("【步骤9: 解析JSON响应】")
-        print("=" * 50)
-        try:
-            response_data = json.loads(parsed_response['body'])
-            print("\n【JSON响应】")
-            print(json.dumps(response_data, ensure_ascii=False, indent=2))
-            
-            # 响应判断逻辑
-            if response_data.get("errorCode") is None:
-                print("\n✅ 请求成功!")
-                if "data" in response_data:
-                    print(f"设备 Token: {response_data['data'].get('token')}")
-                    print(f"Token 过期时间: {response_data['data'].get('expireTime')} 秒({response_data['data'].get('expireTime', 0)//86400} 天)")
-            else:
-                print(f"\n❌ 请求失败!错误码: {response_data['errorCode']}, 原因: {response_data.get('message', '未知错误')}")
-        except json.JSONDecodeError as e:
-            print(f"\n❌ JSON解析失败: {e}")
-            print(f"原始响应体: {parsed_response['body']}")
-            raise
-        
-        # 关闭连接
-        print("\n" + "=" * 50)
-        print("【步骤10: 关闭HTTPS连接】")
-        print("=" * 50)
-        try:
-            https_client.close_connection()
-            print("✅ HTTPS连接已关闭")
-        except Exception as e:
-            print(f"警告: 关闭HTTPS连接时出错: {str(e)}")
-        
-        # ========== 结束计时:HTTPS访问耗时 ==========
-        https_end_time = time.time()
-        https_duration = https_end_time - https_start_time
-        
-        print("\n" + "=" * 50)
-        print("【HTTPS访问耗时统计】")
-        print("=" * 50)
-        print(f"HTTPS访问总耗时: {https_duration:.3f} 秒 ({https_duration*1000:.1f} 毫秒)")
-        print("\n耗时包含以下步骤:")
-        print("  - HTTPS配置和验证")
-        print("  - URL和端口设置")
-        print("  - HTTPS连接建立")
-        print("  - HTTP请求发送")
-        print("  - HTTP响应接收")
-        print("  - 响应解析")
-        print("  - 连接关闭")
-        print("=" * 50)
-        
-    except Exception as e:
-        print("\n" + "=" * 50)
-        print("【错误处理】")
-        print("=" * 50)
-        print(f"❌ 请求异常: {str(e)}")
-        import traceback
-        traceback.print_exc()
-    finally:
-        # 断开串口连接
-        print("\n断开串口连接...")
-        at_handler.disconnect()
-
-# -------------------------- 7. 执行请求 --------------------------
-if __name__ == "__main__":
-    # 认证模式配置
-    # auth_mode: 0=无需认证, 1=单向认证, 2=双向认证
-    # AUTH_MODE = 0  # 默认无需认证
-    
-    # 证书文件路径(单向/双向认证时需要)
-    # CA_CERT_PATH = None  # 例如: "certs/ca.crt"
-    CLIENT_CERT_PATH = None  # 例如: "certs/client.crt"
-    CLIENT_KEY_PATH = None  # 例如: "certs/client.key"
-    
-    # 如果使用单向认证,取消下面的注释并设置证书路径
-    AUTH_MODE = 1
-    CA_CERT_PATH = "certs/ca_chain.pem"
-    
-    # 如果使用双向认证,取消下面的注释并设置所有证书路径
-    # AUTH_MODE = 2
-    # CA_CERT_PATH = "certs/ca.crt"
-    # CLIENT_CERT_PATH = "certs/client.crt"
-    # CLIENT_KEY_PATH = "certs/client.key"
-    
-    send_get_device_token_request(
-        auth_mode=AUTH_MODE,
-        ca_cert_path=CA_CERT_PATH,
-        client_cert_path=CLIENT_CERT_PATH,
-        client_key_path=CLIENT_KEY_PATH
-    )
index 4d68bf2..c476be1 100644 (file)
@@ -480,7 +480,14 @@ class N706AHTTPClient:
             http_request += "Connection: close\r\n"
             http_request += "\r\n"
             
-            request_length = len(http_request.encode('utf-8'))
+            request_bytes = http_request.encode('utf-8')
+            request_length = len(request_bytes)
+            
+            # HTTP OPEN_MODE限制:最大2048字节
+            max_http_length = 2048
+            
+            if request_length > max_http_length:
+                raise Exception(f"HTTP请求长度 {request_length} 字节超过限制({max_http_length} 字节),无法发送")
             
             # 发送AT+HTTPSACTION=99,<length>命令
             self.at.serial_conn.reset_input_buffer()
@@ -511,7 +518,7 @@ class N706AHTTPClient:
             
             # 发送HTTP请求报文
             print(f"发送HTTP请求 ({request_length} 字节)...")
-            self.at.serial_conn.write(http_request.encode('utf-8'))
+            self.at.serial_conn.write(request_bytes)
             self.at.serial_conn.flush()
             
             # 等待OK响应
@@ -541,10 +548,85 @@ class N706AHTTPClient:
     
     def _send_http_get_request(self, headers=None, timeout=60):
         """发送HTTP GET请求"""
-        # 使用标准GET模式 (mode=0)
-        success, response = self.at.send_at_command("AT+HTTPACTION=0", timeout=5)
-        if not success:
-            raise Exception(f"发送HTTP请求失败: {response}")
+        if headers:
+            # 使用OPEN_MODE (mode=99) 自定义HTTP报文
+            parsed = urllib.parse.urlparse(self.current_url)
+            path = parsed.path
+            if parsed.query:
+                path += "?" + parsed.query
+            
+            # 构建HTTP请求报文
+            http_request = f"GET {path} HTTP/1.1\r\n"
+            http_request += f"Host: {parsed.netloc}\r\n"
+            
+            for key, value in headers.items():
+                http_request += f"{key}: {value}\r\n"
+            
+            http_request += "Connection: close\r\n"
+            http_request += "\r\n"
+            
+            request_bytes = http_request.encode('utf-8')
+            request_length = len(request_bytes)
+            
+            # HTTP OPEN_MODE限制:最大2048字节
+            max_http_length = 2048
+            
+            if request_length > max_http_length:
+                raise Exception(f"HTTP请求长度 {request_length} 字节超过限制({max_http_length} 字节),无法发送")
+            
+            # 发送AT+HTTPACTION=99,<length>命令
+            self.at.serial_conn.reset_input_buffer()
+            command = f"AT+HTTPACTION=99,{request_length}"
+            cmd_bytes = (command + "\r\n").encode('utf-8')
+            self.at.serial_conn.write(cmd_bytes)
+            print(f"发送: {command}")
+            
+            # 等待">"提示符
+            print("等待'>'提示符...")
+            start_time = time.time()
+            prompt_received = False
+            
+            while time.time() - start_time < 5:
+                if self.at.serial_conn.in_waiting > 0:
+                    line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
+                    if line:
+                        print(f"接收: {line}")
+                        if ">" in line:
+                            prompt_received = True
+                            break
+                        if "ERROR" in line:
+                            raise Exception(f"HTTP请求命令失败: {line}")
+                time.sleep(0.1)
+            
+            if not prompt_received:
+                raise Exception("未收到'>'提示符")
+            
+            # 发送HTTP请求报文
+            print(f"发送HTTP请求 ({request_length} 字节)...")
+            self.at.serial_conn.write(request_bytes)
+            self.at.serial_conn.flush()
+            
+            # 等待OK响应
+            print("等待OK响应...")
+            start_time = time.time()
+            ok_received = False
+            
+            while time.time() - start_time < 5:
+                if self.at.serial_conn.in_waiting > 0:
+                    line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
+                    if line:
+                        print(f"接收: {line}")
+                        if "OK" in line:
+                            ok_received = True
+                            break
+                        if "ERROR" in line:
+                            raise Exception(f"HTTP请求发送失败: {line}")
+                time.sleep(0.1)
+        else:
+            # 使用标准GET模式 (mode=0)
+            success, response = self.at.send_at_command("AT+HTTPACTION=0", timeout=5)
+            if not success:
+                raise Exception(f"发送HTTP请求失败: {response}")
         
         # 等待响应数据(二进制模式)
         return self._receive_http_response(b"+HTTPRECV:", timeout)
index 9ece1e6..062a14a 100644 (file)
@@ -131,6 +131,31 @@ class N706ATCPClient:
         self.is_ssl = use_ssl
         self.transparent_mode = transparent_mode
         
+        # 在连接前,先尝试关闭可能存在的旧连接
+        if self.is_connected:
+            print(f"⚠️ Socket {self.socket_id} 已连接,先关闭旧连接...")
+            self.close()
+            time.sleep(0.3)
+        
+        # 强制关闭socket(即使状态显示未连接,模块端可能仍有残留)
+        print(f"清理Socket {self.socket_id} 状态...")
+        # 尝试关闭普通TCP
+        self.at.send_at_command(f"AT+TCPCLOSE={self.socket_id}", timeout=2)
+        time.sleep(0.1)
+        # 尝试关闭SSL TCP
+        self.at.send_at_command(f"AT+SSLTCPCLOSE={self.socket_id}", timeout=2)
+        time.sleep(0.2)
+        
+        # 配置HEX接收模式(AT+RECVMODE=1,1)
+        # 这样所有TCP/UDP数据接收都会以HEX格式上报
+        print("配置HEX接收模式 (AT+RECVMODE=1,1)...")
+        success, response = self.at.send_at_command("AT+RECVMODE=1,1", timeout=5)
+        if not success:
+            print(f"⚠️ HEX接收模式配置失败: {response}")
+            print("继续执行,但接收可能使用ASCII模式...")
+        else:
+            print("✅ HEX接收模式已启用 (AT+RECVMODE=1,1)")
+        
         if use_ssl:
             # SSL TCP连接
             mode = 1 if transparent_mode else 0
@@ -186,8 +211,8 @@ class N706ATCPClient:
                 print(f"❌ TCP连接失败: {response}")
                 return False
             
-            # 等待连接结果(+TCPSETUP: <n>,OK 或 +TCPSETUP: <n>,FAIL)
-            tcpsetup_pattern = re.compile(r'\+TCPSETUP:\s*(\d+),(OK|FAIL|ERROR)')
+            # 等待连接结果(+TCPSETUP: <n>,OK 或 +TCPSETUP: <n>,FAIL 或 +TCPSETUP: <n>,ERROR[数字])
+            tcpsetup_pattern = re.compile(r'\+TCPSETUP:\s*(\d+),(OK|FAIL|ERROR\d*)')
             match = tcpsetup_pattern.search(response)
             if match:
                 status = match.group(2)
@@ -196,14 +221,24 @@ class N706ATCPClient:
                     print(f"✅ TCP连接成功: {server_ip}:{server_port}")
                     return True
                 else:
-                    print(f"❌ TCP连接失败: {status}")
+                    error_msg = f"❌ TCP连接失败: {status}"
+                    if status.startswith("ERROR"):
+                        error_code = status.replace("ERROR", "")
+                        error_msg += f" (错误码: {error_code})"
+                        if error_code == "1":
+                            error_msg += "\n   可能原因: Socket ID被占用或网络连接问题"
+                        elif error_code == "2":
+                            error_msg += "\n   可能原因: 参数错误"
+                        elif error_code == "3":
+                            error_msg += "\n   可能原因: DNS解析失败"
+                    print(error_msg)
                     return False
             
             # 如果没有匹配到预期响应,尝试等待主动上报
             print("等待TCP连接结果...")
             success, response = self.at.wait_for_unsolicited(r"\+TCPSETUP:", timeout=30)
             if success:
-                tcpsetup_pattern = re.compile(r'\+TCPSETUP:\s*(\d+),(OK|FAIL|ERROR)')
+                tcpsetup_pattern = re.compile(r'\+TCPSETUP:\s*(\d+),(OK|FAIL|ERROR\d*)')
                 match = tcpsetup_pattern.search(response)
                 if match:
                     status = match.group(2)
@@ -211,13 +246,22 @@ class N706ATCPClient:
                         self.is_connected = True
                         print(f"✅ TCP连接成功: {server_ip}:{server_port}")
                         return True
+                    else:
+                        error_msg = f"❌ TCP连接失败: {status}"
+                        if status.startswith("ERROR"):
+                            error_code = status.replace("ERROR", "")
+                            error_msg += f" (错误码: {error_code})"
+                            if error_code == "1":
+                                error_msg += "\n   可能原因: Socket ID被占用或网络连接问题"
+                        print(error_msg)
+                        return False
             
             print(f"❌ TCP连接失败: {response}")
             return False
     
     def send(self, data):
         """
-        发送TCP/TCP+SSL数据
+        发送TCP/TCP+SSL数据(自动分片)
         
         Args:
             data: 要发送的数据(bytes或str)
@@ -235,85 +279,276 @@ class N706ATCPClient:
         
         data_length = len(data)
         
+        # 定义最大发送长度限制
         if self.is_ssl:
-            # SSL TCP发送
-            return self._send_ssl_tcp_data(data, data_length)
+            # SSL TCP: 最大4096字节(二进制数据)
+            max_chunk_size = 4096
         else:
-            # 普通TCP发送
-            return self._send_tcp_data(data, data_length)
+            # 普通TCP HEX模式: 最大2048字节(二进制数据)
+            max_chunk_size = 2048
+        
+        # 如果数据超过限制,分片发送
+        if data_length > max_chunk_size:
+            print(f"数据长度 {data_length} 字节超过限制({max_chunk_size} 字节),将分片发送...")
+            offset = 0
+            chunk_num = 0
+            
+            while offset < data_length:
+                chunk_num += 1
+                chunk_size = min(max_chunk_size, data_length - offset)
+                chunk = data[offset:offset + chunk_size]
+                
+                print(f"\n【分片 {chunk_num}】发送 {chunk_size} 字节 (总进度: {offset}/{data_length})")
+                
+                if self.is_ssl:
+                    success = self._send_ssl_tcp_data(chunk, chunk_size)
+                else:
+                    success = self._send_tcp_data(chunk, chunk_size)
+                
+                if not success:
+                    print(f"❌ 分片 {chunk_num} 发送失败")
+                    return False
+                
+                offset += chunk_size
+                # 分片之间短暂延迟,避免发送过快
+                if offset < data_length:
+                    time.sleep(0.1)
+            
+            print(f"✅ 所有分片发送完成(共 {chunk_num} 个分片,总计 {data_length} 字节)")
+            return True
+        else:
+            # 数据不超过限制,直接发送
+            if self.is_ssl:
+                return self._send_ssl_tcp_data(data, data_length)
+            else:
+                return self._send_tcp_data(data, data_length)
     
     def _send_tcp_data(self, data, data_length):
-        """发送普通TCP数据"""
-        # 使用buffer模式发送(不包含content参数)
-        command = f'AT+TCPSEND={self.socket_id},{data_length}'
+        """发送普通TCP数据(HEX模式)"""
+        # 将二进制数据转换为HEX字符串
+        hex_data = data.hex().upper()  # 转换为大写HEX字符串
+        hex_length = len(hex_data)  # HEX字符串长度(二进制长度 * 2)
+        
+        # 使用buffer模式,HEX模式发送:AT+TCPSEND=<n>,<binary_length>,,1
+        # 注意:根据AT命令手册,length参数是指二进制数据的字节数,而不是HEX字符串长度
+        # 示例:AT+TCPSEND=0,1024,,1 表示发送1024字节的二进制数据(对应2048个HEX字符)
+        command = f'AT+TCPSEND={self.socket_id},{data_length},,1'
         self.at.serial_conn.reset_input_buffer()
         cmd_bytes = (command + "\r\n").encode('utf-8')
         self.at.serial_conn.write(cmd_bytes)
-        print(f"发送: {command}")
+        print(f"发送: {command} (二进制长度: {data_length} 字节, HEX字符串长度: {hex_length} 字符)")
         
-        # 等待">"提示符
-        print("等待'>'提示符...")
+        # 根据TCP应用指南:无论是否返回">",都延时50-100ms,再将数据发送到模块串口
+        # 所以先等待一段时间接收响应(包括">"和可能的错误),然后无论是否收到">"都延时发送数据
+        print("等待响应(可能包含'>'提示符或错误信息)...")
         start_time = time.time()
+        response_buffer = b""
+        has_error = False
         prompt_received = False
         
-        while time.time() - start_time < 5:
+        # 读取响应,最多等待2秒
+        while time.time() - start_time < 2.0:
             if self.at.serial_conn.in_waiting > 0:
-                line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
-                if line:
+                chunk = self.at.serial_conn.read(self.at.serial_conn.in_waiting)
+                response_buffer += chunk
+                
+                # 检查错误响应(优先检查错误)
+                response_text = response_buffer.decode('utf-8', errors='ignore')
+                if "ERROR" in response_text or "SOCKET ID OPEN FAILED" in response_text or "DATA LENGTH ERROR" in response_text:
+                    has_error = True
+                    print(f"❌ TCP发送命令失败: {response_text}")
+                    return False
+                
+                # 如果收到">"提示符,可以提前结束等待
+                if b">" in response_buffer:
+                    prompt_received = True
+                    # 打印所有响应内容
+                    for line in response_text.split('\n'):
+                        line = line.strip()
+                        if line and line not in ['>']:
+                            print(f"接收: {line}")
+                    break
+            time.sleep(0.05)
+        
+        # 打印收到的响应(如果有,且还没打印过)
+        if response_buffer and not prompt_received:
+            response_text = response_buffer.decode('utf-8', errors='ignore')
+            for line in response_text.split('\n'):
+                line = line.strip()
+                if line and line not in ['OK', '>']:
                     print(f"接收: {line}")
-                    if ">" in line:
-                        prompt_received = True
-                        break
-                    if "ERROR" in line:
-                        print(f"❌ TCP发送命令失败: {line}")
-                        return False
-            time.sleep(0.1)
         
-        if not prompt_received:
-            print("❌ 未收到'>'提示符")
+        # 注意:根据TCP应用指南,无论是否收到">",都延时50-100ms后发送数据
+        # 但如果没有收到">"也没有错误,可能是响应还没到,再等待一小段时间
+        if not prompt_received and not has_error:
+            print("⚠️ 未收到'>'提示符,但根据指南要求继续发送数据...")
+            # 再等待一小段时间,看是否有响应
+            time.sleep(0.1)
+            if self.at.serial_conn.in_waiting > 0:
+                additional = self.at.serial_conn.read(self.at.serial_conn.in_waiting)
+                response_buffer += additional
+                response_text = response_buffer.decode('utf-8', errors='ignore')
+                if "ERROR" in response_text or "SOCKET ID OPEN FAILED" in response_text:
+                    print(f"❌ TCP发送命令失败: {response_text}")
+                    return False
+                if b">" in additional:
+                    print("接收: >")
+        
+        # 注意:数据长度限制检查已在send()方法中完成,这里不再检查
+        # 如果数据超过2048字节,应该已经分片发送
+        
+        # 准备HEX字符串数据
+        hex_bytes = hex_data.encode('utf-8')
+        print(f"\n准备发送TCP数据 (HEX模式: {hex_length} 字符, 原始二进制: {data_length} 字节)...")
+        print(f"HEX数据预览(前64字符): {hex_data[:64]}...")
+        print(f"完整HEX数据长度: {len(hex_bytes)} 字节")
+        
+        # 延迟50-100ms(根据TCP应用指南:无论是否返回">",都延时50-100ms)
+        time.sleep(0.075)  # 使用75ms,在建议范围内
+        
+        # 发送HEX字符串数据
+        # 根据TCP应用指南,数据应该尽快发送到模块串口
+        try:
+            # 一次性写入所有HEX数据(串口缓冲区通常足够大)
+            bytes_written = self.at.serial_conn.write(hex_bytes)
+            
+            # 立即刷新,确保数据发送
+            self.at.serial_conn.flush()
+            
+            # 如果写入不完整,继续写入剩余数据
+            if bytes_written < len(hex_bytes):
+                print(f"⚠️ 警告: 第一次只写入了 {bytes_written} 字节,继续写入剩余数据...")
+                remaining = hex_bytes[bytes_written:]
+                additional = self.at.serial_conn.write(remaining)
+                self.at.serial_conn.flush()
+                bytes_written += additional
+                print(f"   继续写入 {additional} 字节,总计 {bytes_written} 字节")
+            
+            if bytes_written != len(hex_bytes):
+                print(f"❌ 错误: 只写入了 {bytes_written} 字节,期望 {len(hex_bytes)} 字节")
+                return False
+            else:
+                print(f"✅ HEX数据已写入串口: {bytes_written} 字节 (期望: {len(hex_bytes)} 字节)")
+            
+        except Exception as e:
+            print(f"❌ 发送HEX数据时出错: {e}")
+            import traceback
+            traceback.print_exc()
             return False
         
-        # 延迟50-100ms(根据手册建议)
-        time.sleep(0.1)
-        
-        # 发送数据
-        print(f"发送TCP数据 ({data_length} 字节)...")
-        self.at.serial_conn.write(data)
-        self.at.serial_conn.flush()
-        
         # 等待发送完成响应(+TCPSEND: <n>,<length>)
+        # 注意:根据AT命令手册示例:AT+TCPSEND=0,1024,,1 -> +TCPSEND: 0,1024
+        # 响应返回的length应该与命令参数一致,即二进制数据的字节数(data_length)
         print("等待发送完成响应...")
         start_time = time.time()
         send_complete = False
+        timeout = 30
+        all_received_data = b""  # 保存所有接收到的数据,用于调试
         
-        while time.time() - start_time < 30:
+        while time.time() - start_time < timeout:
             if self.at.serial_conn.in_waiting > 0:
-                line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
-                if line:
-                    print(f"接收: {line}")
-                    if f"+TCPSEND: {self.socket_id},{data_length}" in line:
-                        send_complete = True
-                        print(f"✅ TCP数据发送成功: {data_length} 字节")
-                        break
-                    if "ERROR" in line or "FAIL" in line:
-                        print(f"❌ TCP数据发送失败: {line}")
+                chunk = self.at.serial_conn.read(self.at.serial_conn.in_waiting)
+                all_received_data += chunk
+                
+                # 尝试解析响应
+                response_text = all_received_data.decode('utf-8', errors='ignore')
+                
+                # 按行处理响应
+                lines = response_text.split('\n')
+                for line in lines:
+                    line = line.strip()
+                    if not line:
+                        continue
+                    
+                    # 只打印包含关键信息的行,避免重复打印
+                    if any(keyword in line for keyword in ['+TCPSEND', 'ERROR', '+TCPCLOSE', 'OK']):
+                        print(f"接收: {line}")
+                    
+                    # 检查成功响应
+                    # 格式:+TCPSEND: <n>,<length>
+                    import re
+                    tcpsend_pattern = re.compile(r'\+TCPSEND:\s*(\d+),(\d+)')
+                    match = tcpsend_pattern.match(line)
+                    if match:
+                        socket_id = int(match.group(1))
+                        returned_length = int(match.group(2))
+                        
+                        if socket_id == self.socket_id:
+                            # 根据手册,响应返回的length应该与命令参数一致,即二进制数据长度
+                            if returned_length == data_length:
+                                send_complete = True
+                                print(f"✅ TCP数据发送成功: {data_length} 字节 (HEX字符串长度: {hex_length}, 返回长度: {returned_length})")
+                                break
+                            elif returned_length == hex_length:
+                                # 兼容处理:如果返回的是HEX字符串长度,也认为成功(可能是旧版本)
+                                send_complete = True
+                                print(f"✅ TCP数据发送成功: {data_length} 字节 (HEX字符串长度: {hex_length}, 返回长度: {returned_length} - HEX长度)")
+                                break
+                            else:
+                                # 长度不匹配,但可能是模块的问题,仍然认为发送成功
+                                print(f"⚠️ 警告: 返回长度 {returned_length} 与期望HEX长度 {hex_length} 或二进制长度 {data_length} 不匹配,但继续...")
+                                send_complete = True
+                                break
+                    
+                    # 检查错误响应
+                    if "+TCPSEND:" in line:
+                        if "OPERATION EXPIRED" in line:
+                            print(f"❌ TCP数据发送超时: 模块在等待数据时超时")
+                            print(f"   可能原因: 数据发送太慢或数据格式错误")
+                            return False
+                        elif "ERROR" in line or "FAIL" in line or "SOCKET" in line or "DATA LENGTH ERROR" in line:
+                            print(f"❌ TCP数据发送失败: {line}")
+                            return False
+                    
+                    # 检查连接关闭
+                    if "+TCPCLOSE:" in line:
+                        print(f"❌ TCP连接已关闭: {line}")
+                        self.is_connected = False
                         return False
-            time.sleep(0.1)
+                
+                if send_complete:
+                    break
+            else:
+                time.sleep(0.05)  # 减少CPU占用
         
         if not send_complete:
-            print("❌ TCP数据发送超时")
+            # 打印所有收到的响应,帮助调试
+            print(f"\n❌ TCP数据发送超时(未收到+TCPSEND响应)")
+            if all_received_data:
+                response_text = all_received_data.decode('utf-8', errors='ignore')
+                print(f"收到的所有响应数据:")
+                print(f"  HEX格式: {all_received_data.hex().upper()}")
+                print(f"  ASCII格式: {repr(response_text)}")
+                print(f"  按行显示:")
+                for i, line in enumerate(response_text.split('\n')):
+                    if line.strip():
+                        print(f"    行{i+1}: {repr(line)}")
+            else:
+                print("未收到任何响应数据")
             return False
         
         return True
     
     def _send_ssl_tcp_data(self, data, data_length):
-        """发送SSL TCP数据"""
+        """发送SSL TCP数据(HEX模式,单次发送,不超过4096字节)"""
+        # 检查数据长度限制(SSL TCP最大4096字节二进制数据)
+        if data_length > 4096:
+            print(f"❌ 数据长度 {data_length} 字节超过SSL TCP限制(4096字节)")
+            return False
+        
+        # 将二进制数据转换为HEX字符串
+        hex_data = data.hex().upper()  # 转换为大写HEX字符串
+        hex_length = len(hex_data)  # HEX字符串长度(二进制长度 * 2)
+        
         # SSL TCP发送命令格式: AT+SSLTCPSEND=<n>,<data_length>
+        # 注意:根据AT命令手册,SSL TCP没有HEX模式参数,HEX模式通过AT+RECVMODE设置
+        # length参数是指二进制数据的字节数,而不是HEX字符串长度
+        # 示例:AT+SSLTCPSEND=0,1024 表示发送1024字节的二进制数据
         command = f'AT+SSLTCPSEND={self.socket_id},{data_length}'
         self.at.serial_conn.reset_input_buffer()
         cmd_bytes = (command + "\r\n").encode('utf-8')
         self.at.serial_conn.write(cmd_bytes)
-        print(f"发送: {command}")
+        print(f"发送: {command} (二进制长度: {data_length} 字节, HEX字符串长度: {hex_length} 字符)")
         
         # 等待">"提示符
         print("等待'>'提示符...")
@@ -337,9 +572,9 @@ class N706ATCPClient:
             print("❌ 未收到'>'提示符")
             return False
         
-        # 发送数据
-        print(f"发送SSL TCP数据 ({data_length} 字节)...")
-        self.at.serial_conn.write(data)
+        # 发送HEX字符串数据
+        print(f"发送SSL TCP数据 (HEX模式: {hex_length} 字符, 原始二进制: {data_length} 字节)...")
+        self.at.serial_conn.write(hex_data.encode('utf-8'))
         self.at.serial_conn.flush()
         
         # 等待发送完成响应(+SSLTCPSEND: <n>,OK 或 +SSLTCPSEND: <n>,FAIL)
@@ -354,7 +589,7 @@ class N706ATCPClient:
                     print(f"接收: {line}")
                     if f"+SSLTCPSEND: {self.socket_id},OK" in line:
                         send_complete = True
-                        print(f"✅ SSL TCP数据发送成功: {data_length} 字节")
+                        print(f"✅ SSL TCP数据发送成功: {data_length} 字节 (HEX长度: {hex_length})")
                         break
                     if "FAIL" in line or "ERROR" in line:
                         print(f"❌ SSL TCP数据发送失败: {line}")
@@ -369,14 +604,14 @@ class N706ATCPClient:
     
     def receive(self, max_length=2048, timeout=30):
         """
-        接收TCP/TCP+SSL数据(非透传模式)
+        æ\8e¥æ\94¶TCP/TCP+SSLæ\95°æ\8d®ï¼\88é\9d\9eé\80\8f传模å¼\8fï¼\8cHEX模å¼\8fï¼\89
         
         Args:
             max_length: 最大接收长度(1-2048字节),默认2048
             timeout: 超时时间(秒),默认30
         
         Returns:
-            bytes: 接收到的数据,如果失败返回None
+            bytes: 接收到的二进制数据,如果失败返回None
         """
         if not self.is_connected:
             print("❌ 连接未建立,无法接收数据")
@@ -398,46 +633,44 @@ class N706ATCPClient:
             print(f"❌ 读取数据失败: {response}")
             return None
         
-        # 解析响应
+        # 解析响应(HEX模式)
         if self.is_ssl:
-            # +SSLTCPREAD: <n>,<len>,<data>
+            # +SSLTCPREAD: <n>,<hex_len>,<hex_data>
             pattern = re.compile(r'\+SSLTCPREAD:\s*(\d+),(\d+),(.+)')
         else:
-            # +TCPREAD: <n>,<len>,<data>
+            # +TCPREAD: <n>,<hex_len>,<hex_data>
             pattern = re.compile(r'\+TCPREAD:\s*(\d+),(\d+),(.+)')
         
         match = pattern.search(response)
         if match:
-            length = int(match.group(2))
-            data_str = match.group(3).strip()
+            hex_length = int(match.group(2))  # HEX字符串长度
+            hex_data_str = match.group(3).strip()
             
-            # å°\9dè¯\95è§£æ\9e\90æ\95°æ\8d®ï¼\88å\8f¯è\83½æ\98¯ASCIIæ\88\96HEXï¼\89
+            # å°\86HEXå­\97符串转æ\8d¢ä¸ºäº\8cè¿\9bå\88¶æ\95°æ\8d®
             try:
-                # 先尝试作为ASCII字符串
-                data = data_str.encode('utf-8')
-                if len(data) == length:
+                data = bytes.fromhex(hex_data_str)
+                # 验证长度:HEX字符串长度应该是二进制长度的2倍
+                if len(hex_data_str) == hex_length and len(data) * 2 == hex_length:
                     return data
-                # 如果长度不匹配,尝试作为HEX字符串
-                data = bytes.fromhex(data_str)
-                if len(data) == length:
+                else:
+                    print(f"⚠️ HEX数据长度不匹配: hex_str_len={len(hex_data_str)}, hex_len={hex_length}, binary_len={len(data)}")
+                    # 即使长度不匹配,也返回解析的数据
                     return data
-            except:
-                pass
-            
-            # 如果解析失败,返回原始字符串的字节
-            return data_str.encode('utf-8')
+            except ValueError as e:
+                print(f"❌ HEX字符串解析失败: {e}, hex_data={hex_data_str[:100]}...")
+                return None
         
         return None
     
     def wait_for_data(self, timeout=30):
         """
-        被动等待接收数据(透传或非透传模式都支持)
+        è¢«å\8a¨ç­\89å¾\85æ\8e¥æ\94¶æ\95°æ\8d®ï¼\88é\80\8fä¼ æ\88\96é\9d\9eé\80\8f传模å¼\8fé\83½æ\94¯æ\8c\81ï¼\8cHEX模å¼\8fï¼\89
         
         Args:
             timeout: 超时时间(秒),默认30
         
         Returns:
-            bytes: 接收到的数据,如果超时返回None
+            bytes: 接收到的二进制数据,如果超时返回None
         """
         if not self.is_connected:
             print("❌ 连接未建立,无法接收数据")
@@ -455,45 +688,277 @@ class N706ATCPClient:
             print(f"等待数据超时: {timeout}秒")
             return None
         
-        # 解析接收到的数据
+        # 解析接收到的数据(HEX模式)
         if self.is_ssl:
-            # +SSLTCPRECV: <n>,<len>,<data>
+            # +SSLTCPRECV: <n>,<hex_len>,<hex_data>
             recv_pattern = re.compile(r'\+SSLTCPRECV:\s*(\d+),(\d+),(.+)')
         else:
-            # +TCPRECV: <n>,<len>,<data> 或 +TCPRECV: <n>
+            # +TCPRECV: <n>,<hex_len>,<hex_data> 或 +TCPRECV: <n>
             recv_pattern = re.compile(r'\+TCPRECV:\s*(\d+)(?:,(\d+),(.+))?')
         
         match = recv_pattern.search(response)
         if match:
             if self.is_ssl:
-                length = int(match.group(2))
-                data_str = match.group(3).strip()
+                hex_length = int(match.group(2))  # HEX字符串长度
+                hex_data_str = match.group(3).strip()
             else:
                 if match.group(2):  # 有数据
-                    length = int(match.group(2))
-                    data_str = match.group(3).strip()
+                    hex_length = int(match.group(2))  # HEX字符串长度
+                    hex_data_str = match.group(3).strip()
                 else:  # 只有socket ID,需要主动读取
                     print("收到+TCPRECV提示,需要主动读取数据")
                     return self.receive()
             
-            # å°\9dè¯\95è§£æ\9e\90数据
+            # å°\86HEXå­\97符串转æ\8d¢ä¸ºäº\8cè¿\9bå\88数据
             try:
-                # 先尝试作为ASCII字符串
-                data = data_str.encode('utf-8')
-                if len(data) == length:
+                data = bytes.fromhex(hex_data_str)
+                # 验证长度:HEX字符串长度应该是二进制长度的2倍
+                if len(hex_data_str) == hex_length and len(data) * 2 == hex_length:
                     return data
-                # 如果长度不匹配,尝试作为HEX字符串
-                data = bytes.fromhex(data_str)
-                if len(data) == length:
+                else:
+                    print(f"⚠️ HEX数据长度不匹配: hex_str_len={len(hex_data_str)}, hex_len={hex_length}, binary_len={len(data)}")
+                    # 即使长度不匹配,也返回解析的数据
                     return data
-            except:
-                pass
-            
-            # 如果解析失败,返回原始字符串的字节
-            return data_str.encode('utf-8')
+            except ValueError as e:
+                print(f"❌ HEX字符串解析失败: {e}, hex_data={hex_data_str[:100]}...")
+                return None
         
         return None
     
+    def get_status(self):
+        """
+        查询TCP/UDP链路状态(AT+IPSTATUS)
+        
+        Returns:
+            dict: 包含状态信息的字典,格式:
+                {
+                    'success': bool,
+                    'socket_id': int | None,
+                    'status': 'CONNECT' | 'DISCONNECT' | 'CONNECTING' | 'DISCONNECTING' | None,
+                    'type': 'TCP' | 'UDP' | None,
+                    'buffer_size': int | None,  # 可用send buffer大小(字节)
+                    'error': str | None
+                }
+        """
+        # 直接发送命令并读取响应,不依赖send_at_command的success标志
+        # 因为AT+IPSTATUS可能不返回OK,只返回+IPSTATUS响应
+        command = f'AT+IPSTATUS={self.socket_id}'
+        if not self.at.serial_conn or not self.at.serial_conn.is_open:
+            return {
+                'success': False,
+                'socket_id': None,
+                'status': None,
+                'type': None,
+                'buffer_size': None,
+                'error': '串口未连接'
+            }
+        
+        # 清空输入缓冲区
+        self.at.serial_conn.reset_input_buffer()
+        
+        # 发送命令
+        cmd_bytes = (command + "\r\n").encode('utf-8')
+        self.at.serial_conn.write(cmd_bytes)
+        print(f"发送: {command}")
+        
+        # 读取响应(最多等待5秒)
+        start_time = time.time()
+        timeout = 5
+        response_lines = []
+        
+        while time.time() - start_time < timeout:
+            if self.at.serial_conn.in_waiting > 0:
+                line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
+                if line:
+                    response_lines.append(line)
+                    print(f"接收: {line}")
+                    
+                    # 如果收到+IPSTATUS响应,可以提前结束
+                    if '+IPSTATUS:' in line:
+                        # 继续读取一小段时间,看是否还有更多响应(如OK)
+                        time.sleep(0.1)
+                        if self.at.serial_conn.in_waiting > 0:
+                            additional = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
+                            if additional:
+                                response_lines.append(additional)
+                                print(f"接收: {additional}")
+                        break
+                    
+                    # 检查错误响应
+                    if 'ERROR' in line:
+                        break
+        
+        response = "\n".join(response_lines)
+        
+        # 解析响应格式: +IPSTATUS: <n>,<STATUS>[,<TYPE>,<send-buffer-size>]
+        # 示例: +IPSTATUS: 0,CONNECT,TCP,4096
+        # 或: +IPSTATUS: 0,DISCONNECT
+        pattern = re.compile(r'\+IPSTATUS:\s*(\d+),(\w+)(?:,(\w+)(?:,(\d+))?)?')
+        match = pattern.search(response)
+        
+        if match:
+            socket_id = int(match.group(1))
+            status = match.group(2)  # CONNECT, DISCONNECT, CONNECTING, DISCONNECTING
+            link_type = match.group(3) if match.group(3) else None  # TCP, UDP
+            buffer_size = int(match.group(4)) if match.group(4) else None
+            
+            return {
+                'success': True,
+                'socket_id': socket_id,
+                'status': status,
+                'type': link_type,
+                'buffer_size': buffer_size,
+                'error': None
+            }
+        elif 'ERROR' in response:
+            return {
+                'success': False,
+                'socket_id': None,
+                'status': None,
+                'type': None,
+                'buffer_size': None,
+                'error': f'命令执行失败: {response}'
+            }
+        else:
+            return {
+                'success': False,
+                'socket_id': None,
+                'status': None,
+                'type': None,
+                'buffer_size': None,
+                'error': f'未收到有效响应: {response}'
+            }
+    
+    def get_send_status(self):
+        """
+        查询TCP链路发送数据状态(AT+TCPACK)
+        注意:SSL TCP不支持此命令
+        
+        Returns:
+            dict: 包含发送状态信息的字典,格式:
+                {
+                    'success': bool,
+                    'socket_id': int | None,
+                    'data_sent': int | None,  # 已发送的数据大小(字节)
+                    'acked_recv': int | None,  # 对方已确认接收的数据大小(字节)
+                    'error': str | None
+                }
+        """
+        if self.is_ssl:
+            # SSL TCP不支持AT+TCPACK,返回错误
+            return {
+                'success': False,
+                'socket_id': None,
+                'data_sent': None,
+                'acked_recv': None,
+                'error': 'SSL TCP不支持AT+TCPACK查询'
+            }
+        
+        # 直接发送命令并读取响应,不依赖send_at_command的success标志
+        # 因为AT+TCPACK不返回OK,只返回+TCPACK响应
+        command = f'AT+TCPACK={self.socket_id}'
+        if not self.at.serial_conn or not self.at.serial_conn.is_open:
+            return {
+                'success': False,
+                'socket_id': None,
+                'data_sent': None,
+                'acked_recv': None,
+                'error': '串口未连接'
+            }
+        
+        # 清空输入缓冲区
+        self.at.serial_conn.reset_input_buffer()
+        
+        # 发送命令
+        cmd_bytes = (command + "\r\n").encode('utf-8')
+        self.at.serial_conn.write(cmd_bytes)
+        print(f"发送: {command}")
+        
+        # 读取响应(最多等待5秒)
+        start_time = time.time()
+        timeout = 5
+        response_lines = []
+        
+        while time.time() - start_time < timeout:
+            if self.at.serial_conn.in_waiting > 0:
+                line = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
+                if line:
+                    response_lines.append(line)
+                    print(f"接收: {line}")
+                    
+                    # 如果收到+TCPACK响应,可以提前结束
+                    if '+TCPACK:' in line:
+                        # 继续读取一小段时间,看是否还有更多响应
+                        time.sleep(0.1)
+                        if self.at.serial_conn.in_waiting > 0:
+                            additional = self.at.serial_conn.readline().decode('utf-8', errors='ignore').strip()
+                            if additional:
+                                response_lines.append(additional)
+                                print(f"接收: {additional}")
+                        break
+                    
+                    # 检查错误响应
+                    if 'ERROR' in line:
+                        break
+        
+        response = "\n".join(response_lines)
+        
+        # 解析响应格式: +TCPACK: <n>,<data_sent>,<acked_recv>
+        # 或: +TCPACK: <n>,DISCONNECT
+        # 或: +TCPACK: NO TCP LINK
+        pattern = re.compile(r'\+TCPACK:\s*(\d+),(\d+),(\d+)')
+        match = pattern.search(response)
+        
+        if match:
+            socket_id = int(match.group(1))
+            data_sent = int(match.group(2))
+            acked_recv = int(match.group(3))
+            
+            return {
+                'success': True,
+                'socket_id': socket_id,
+                'data_sent': data_sent,
+                'acked_recv': acked_recv,
+                'error': None
+            }
+        elif 'DISCONNECT' in response:
+            # 提取socket_id
+            disconnect_pattern = re.compile(r'\+TCPACK:\s*(\d+),DISCONNECT')
+            disconnect_match = disconnect_pattern.search(response)
+            socket_id = int(disconnect_match.group(1)) if disconnect_match else None
+            
+            return {
+                'success': False,
+                'socket_id': socket_id,
+                'data_sent': None,
+                'acked_recv': None,
+                'error': '连接已断开'
+            }
+        elif 'NO TCP LINK' in response:
+            return {
+                'success': False,
+                'socket_id': None,
+                'data_sent': None,
+                'acked_recv': None,
+                'error': '未建立TCP连接(可能是UDP连接)'
+            }
+        elif 'ERROR' in response:
+            return {
+                'success': False,
+                'socket_id': None,
+                'data_sent': None,
+                'acked_recv': None,
+                'error': f'命令执行失败: {response}'
+            }
+        else:
+            return {
+                'success': False,
+                'socket_id': None,
+                'data_sent': None,
+                'acked_recv': None,
+                'error': f'未收到有效响应: {response}'
+            }
+    
     def close(self):
         """
         关闭TCP/TCP+SSL连接
diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/tcp_test_client.py b/code/sdk/zhihui/leiniao/leiniao_python_test/tcp_test_client.py
new file mode 100644 (file)
index 0000000..c74b895
--- /dev/null
@@ -0,0 +1,425 @@
+#!/usr/bin/env python3\r
+# -*- coding: utf-8 -*-\r
+"""\r
+TCP测试客户端(标准Python socket版本)\r
+使用标准Python socket接口连接TCP服务器\r
+支持交互式发送和接收数据\r
+"""\r
+\r
+import socket\r
+import time\r
+import threading\r
+import sys\r
+\r
+# -------------------------- 1. 配置参数 --------------------------\r
+# 服务器配置\r
+SERVER_HOST = "snicker.com.cn"\r
+SERVER_PORT = 8910\r
+\r
+# 接收超时时间(秒)\r
+RECEIVE_TIMEOUT = 30\r
+\r
+# -------------------------- 2. TCP客户端类 --------------------------\r
+class TCPTestClient:\r
+    """标准Python socket TCP客户端"""\r
+    \r
+    def __init__(self, host, port):\r
+        """\r
+        初始化TCP客户端\r
+        \r
+        Args:\r
+            host: 服务器地址\r
+            port: 服务器端口\r
+        """\r
+        self.host = host\r
+        self.port = port\r
+        self.socket = None\r
+        self.connected = False\r
+        self.receive_thread = None\r
+        self.running = False\r
+        self.received_data_queue = []\r
+        self.data_lock = threading.Lock()\r
+    \r
+    def connect(self):\r
+        """\r
+        连接到服务器\r
+        \r
+        Returns:\r
+            bool: 是否连接成功\r
+        """\r
+        try:\r
+            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\r
+            self.socket.settimeout(5.0)  # 连接超时5秒\r
+            \r
+            print(f"正在连接服务器: {self.host}:{self.port}...")\r
+            self.socket.connect((self.host, self.port))\r
+            \r
+            # 连接成功后,设置接收超时\r
+            self.socket.settimeout(RECEIVE_TIMEOUT)\r
+            self.connected = True\r
+            \r
+            # 启动接收线程\r
+            self.running = True\r
+            self.receive_thread = threading.Thread(target=self._receive_loop, daemon=True)\r
+            self.receive_thread.start()\r
+            \r
+            print(f"✅ TCP连接成功: {self.host}:{self.port}")\r
+            return True\r
+            \r
+        except socket.timeout:\r
+            print(f"❌ 连接超时: {self.host}:{self.port}")\r
+            return False\r
+        except socket.error as e:\r
+            print(f"❌ 连接失败: {e}")\r
+            return False\r
+        except Exception as e:\r
+            print(f"❌ 连接异常: {e}")\r
+            return False\r
+    \r
+    def _receive_loop(self):\r
+        """接收数据循环(后台线程)"""\r
+        while self.running and self.connected:\r
+            try:\r
+                data = self.socket.recv(4096)\r
+                if not data:\r
+                    # 服务器关闭连接\r
+                    print("\n⚠️ 服务器关闭了连接")\r
+                    self.connected = False\r
+                    break\r
+                \r
+                # 将接收到的数据放入队列\r
+                with self.data_lock:\r
+                    self.received_data_queue.append(data)\r
+                    \r
+            except socket.timeout:\r
+                # 超时是正常的,继续等待\r
+                continue\r
+            except socket.error as e:\r
+                if self.running:\r
+                    print(f"\n❌ 接收数据时出错: {e}")\r
+                self.connected = False\r
+                break\r
+            except Exception as e:\r
+                if self.running:\r
+                    print(f"\n❌ 接收数据异常: {e}")\r
+                self.connected = False\r
+                break\r
+    \r
+    def send(self, data):\r
+        """\r
+        发送数据\r
+        \r
+        Args:\r
+            data: 要发送的数据(bytes)\r
+        \r
+        Returns:\r
+            bool: 是否发送成功\r
+        """\r
+        if not self.connected:\r
+            print("❌ 连接未建立,无法发送数据")\r
+            return False\r
+        \r
+        try:\r
+            self.socket.sendall(data)\r
+            return True\r
+        except socket.error as e:\r
+            print(f"❌ 发送数据失败: {e}")\r
+            self.connected = False\r
+            return False\r
+        except Exception as e:\r
+            print(f"❌ 发送数据异常: {e}")\r
+            return False\r
+    \r
+    def receive(self, timeout=None):\r
+        """\r
+        主动接收数据(从队列中获取)\r
+        \r
+        Args:\r
+            timeout: 超时时间(秒),None表示立即返回\r
+        \r
+        Returns:\r
+            bytes: 接收到的数据,如果没有数据返回None\r
+        """\r
+        if timeout is None:\r
+            timeout = 0\r
+        \r
+        start_time = time.time()\r
+        while time.time() - start_time < timeout:\r
+            with self.data_lock:\r
+                if self.received_data_queue:\r
+                    return self.received_data_queue.pop(0)\r
+            time.sleep(0.1)\r
+        \r
+        # 检查是否有数据\r
+        with self.data_lock:\r
+            if self.received_data_queue:\r
+                return self.received_data_queue.pop(0)\r
+        \r
+        return None\r
+    \r
+    def wait_for_data(self, timeout=30):\r
+        """\r
+        被动等待接收数据\r
+        \r
+        Args:\r
+            timeout: 超时时间(秒)\r
+        \r
+        Returns:\r
+            bytes: 接收到的数据,超时返回None\r
+        """\r
+        start_time = time.time()\r
+        while time.time() - start_time < timeout:\r
+            with self.data_lock:\r
+                if self.received_data_queue:\r
+                    return self.received_data_queue.pop(0)\r
+            \r
+            if not self.connected:\r
+                return None\r
+            \r
+            time.sleep(0.1)\r
+        \r
+        return None\r
+    \r
+    def close(self):\r
+        """关闭连接"""\r
+        self.running = False\r
+        self.connected = False\r
+        \r
+        if self.receive_thread and self.receive_thread.is_alive():\r
+            # 等待接收线程结束\r
+            self.receive_thread.join(timeout=1.0)\r
+        \r
+        if self.socket:\r
+            try:\r
+                self.socket.close()\r
+            except:\r
+                pass\r
+        \r
+        print("✅ TCP连接已关闭")\r
+\r
+# -------------------------- 3. 连接测试服务器函数 --------------------------\r
+def connect_test_server():\r
+    """连接测试服务器并交互式发送和接收消息"""\r
+    \r
+    # 初始化TCP客户端\r
+    print("\n" + "=" * 50)\r
+    print("【步骤1: 初始化TCP客户端】")\r
+    print("=" * 50)\r
+    tcp_client = TCPTestClient(SERVER_HOST, SERVER_PORT)\r
+    \r
+    try:\r
+        # 建立TCP连接\r
+        print("\n" + "=" * 50)\r
+        print("【步骤2: 建立TCP连接】")\r
+        print("=" * 50)\r
+        print(f"连接服务器: {SERVER_HOST}:{SERVER_PORT}")\r
+        \r
+        if not tcp_client.connect():\r
+            print(f"❌ TCP连接失败")\r
+            return False\r
+        \r
+        print(f"✅ TCP连接成功")\r
+        \r
+        # 交互式发送和接收\r
+        print("\n" + "=" * 50)\r
+        print("【步骤3: 交互式发送和接收数据】")\r
+        print("=" * 50)\r
+        print("命令说明:")\r
+        print("  send <hex_string>     - 发送HEX数据(例如: send 48656C6C6F)")\r
+        print("  send_ascii <text>     - 发送ASCII文本(例如: send_ascii Hello)")\r
+        print("  recv                  - 主动接收数据")\r
+        print("  wait                  - 切换到被动等待模式(自动接收)")\r
+        print("  interactive           - 切换到交互模式(手动输入命令)")\r
+        print("  quit                  - 退出程序")\r
+        print("=" * 50)\r
+        \r
+        message_count = 0\r
+        start_time = time.time()\r
+        wait_mode = False  # 默认交互模式,方便用户输入命令\r
+        \r
+        def print_received_data(data, msg_num, elapsed):\r
+            """打印接收到的数据"""\r
+            print(f"\n【接收消息 #{msg_num}】 (连接后 {elapsed:.1f}秒)")\r
+            print(f"数据长度: {len(data)} 字节")\r
+            \r
+            # 打印首字节信息\r
+            if len(data) > 0:\r
+                first_byte = data[0]\r
+                print(f"首字节: 0x{first_byte:02X} ({first_byte:3d})")\r
+            \r
+            # 打印完整数据 - HEX格式\r
+            hex_str = data.hex().upper()\r
+            if len(hex_str) > 200:\r
+                print(f"HEX格式(前200字符): {hex_str[:200]}...")\r
+                print(f"HEX格式(完整,共{len(hex_str)}字符):")\r
+                # 每行显示64个字符(32字节)\r
+                for i in range(0, len(hex_str), 64):\r
+                    chunk = hex_str[i:i+64]\r
+                    offset = i // 2\r
+                    print(f"  {offset:06X}: {chunk}")\r
+            else:\r
+                print(f"HEX格式: {hex_str}")\r
+            \r
+            # 打印完整数据 - ASCII格式\r
+            try:\r
+                ascii_str = data.decode('utf-8', errors='replace')\r
+                # 检查是否包含可打印字符\r
+                if all(32 <= ord(c) < 127 or c in '\n\r\t' for c in ascii_str):\r
+                    print(f"ASCII格式: {repr(ascii_str)}")\r
+                else:\r
+                    # 包含非打印字符,显示为repr格式\r
+                    print(f"ASCII格式(含非打印字符): {repr(ascii_str)}")\r
+                    # 同时显示可打印部分\r
+                    printable = ''.join(c if 32 <= ord(c) < 127 or c in '\n\r\t' else '.' for c in ascii_str)\r
+                    if printable.strip():\r
+                        print(f"可打印部分: {printable}")\r
+            except:\r
+                print(f"ASCII格式: (无法解码为UTF-8)")\r
+            \r
+            # 打印数据的前几个字节(十六进制和十进制)\r
+            if len(data) > 0:\r
+                preview_bytes = min(16, len(data))\r
+                hex_preview = ' '.join(f'{b:02X}' for b in data[:preview_bytes])\r
+                dec_preview = ' '.join(f'{b:3d}' for b in data[:preview_bytes])\r
+                print(f"前{preview_bytes}字节 HEX: {hex_preview}")\r
+                print(f"前{preview_bytes}字节 DEC: {dec_preview}")\r
+            \r
+            print("-" * 50)\r
+        \r
+        try:\r
+            while True:\r
+                if wait_mode:\r
+                    # 被动等待接收数据\r
+                    data = tcp_client.wait_for_data(timeout=RECEIVE_TIMEOUT)\r
+                    \r
+                    if data:\r
+                        message_count += 1\r
+                        elapsed_time = time.time() - start_time\r
+                        print_received_data(data, message_count, elapsed_time)\r
+                    else:\r
+                        if not tcp_client.connected:\r
+                            print("\n⚠️ 连接已断开")\r
+                            break\r
+                        print(f"\n⚠️ 接收超时({RECEIVE_TIMEOUT}秒),继续等待...")\r
+                        print("提示: 按 Ctrl+C 中断,然后输入 'interactive' 切换到交互模式")\r
+                        print("-" * 50)\r
+                else:\r
+                    # 交互模式:等待用户输入\r
+                    try:\r
+                        user_input = input("\n请输入命令 (send/send_ascii/recv/wait/interactive/quit): ").strip()\r
+                        \r
+                        if not user_input:\r
+                            continue\r
+                        \r
+                        if user_input.lower() == 'quit':\r
+                            break\r
+                        elif user_input.lower() == 'recv':\r
+                            # 主动接收数据\r
+                            data = tcp_client.receive(timeout=0.1)\r
+                            if data:\r
+                                message_count += 1\r
+                                elapsed_time = time.time() - start_time\r
+                                print_received_data(data, message_count, elapsed_time)\r
+                            else:\r
+                                print("❌ 未接收到数据")\r
+                        elif user_input.lower() == 'wait':\r
+                            wait_mode = True\r
+                            print("✅ 切换到被动等待模式(自动接收数据)")\r
+                            print("提示: 按 Ctrl+C 中断,然后输入 'interactive' 切换回交互模式")\r
+                        elif user_input.lower() == 'interactive':\r
+                            wait_mode = False\r
+                            print("✅ 切换到交互模式(手动输入命令)")\r
+                        elif user_input.startswith('send '):\r
+                            # 发送HEX数据\r
+                            hex_str = user_input[5:].strip().replace(' ', '').upper()\r
+                            if not hex_str:\r
+                                print("❌ 错误: send 命令需要HEX字符串参数")\r
+                                print("   示例: send 48656C6C6F")\r
+                                continue\r
+                            try:\r
+                                send_data = bytes.fromhex(hex_str)\r
+                                print(f"发送HEX数据: {hex_str} (二进制长度: {len(send_data)} 字节)")\r
+                                if tcp_client.send(send_data):\r
+                                    print("✅ 数据发送成功")\r
+                                else:\r
+                                    print("❌ 数据发送失败")\r
+                            except ValueError as e:\r
+                                print(f"❌ HEX字符串格式错误: {e}")\r
+                                print("   提示: HEX字符串只能包含 0-9, A-F 字符")\r
+                        elif user_input.startswith('send_ascii '):\r
+                            # 发送ASCII文本\r
+                            ascii_text = user_input[11:].strip()\r
+                            if not ascii_text:\r
+                                print("❌ 错误: send_ascii 命令需要文本参数")\r
+                                print("   示例: send_ascii Hello World")\r
+                                continue\r
+                            send_data = ascii_text.encode('utf-8')\r
+                            hex_str = send_data.hex().upper()\r
+                            print(f"发送ASCII文本: {ascii_text}")\r
+                            print(f"  -> 二进制: {send_data}")\r
+                            print(f"  -> HEX字符串: {hex_str}")\r
+                            if tcp_client.send(send_data):\r
+                                print("✅ 数据发送成功")\r
+                            else:\r
+                                print("❌ 数据发送失败")\r
+                        else:\r
+                            print("❌ 未知命令")\r
+                            print("   可用命令: send, send_ascii, recv, wait, interactive, quit")\r
+                    except EOFError:\r
+                        # 如果没有交互式输入,切换到被动等待模式\r
+                        wait_mode = True\r
+                        print("\n⚠️ 检测到非交互式环境,切换到被动等待模式...")\r
+                    except KeyboardInterrupt:\r
+                        # 用户按 Ctrl+C,切换到交互模式\r
+                        wait_mode = False\r
+                        print("\n\n⚠️ 用户中断,切换到交互模式")\r
+                        print("提示: 输入 'wait' 可切换回被动等待模式")\r
+        \r
+        except KeyboardInterrupt:\r
+            print("\n\n用户中断")\r
+        \r
+        # 关闭连接\r
+        print("\n" + "=" * 50)\r
+        print("【步骤4: 关闭TCP连接】")\r
+        print("=" * 50)\r
+        tcp_client.close()\r
+        \r
+        # 统计信息\r
+        total_time = time.time() - start_time\r
+        print("\n" + "=" * 50)\r
+        print("【连接统计】")\r
+        print("=" * 50)\r
+        print(f"连接持续时间: {total_time:.1f} 秒")\r
+        print(f"接收消息数量: {message_count}")\r
+        if message_count > 0:\r
+            print(f"平均消息间隔: {total_time/message_count:.1f} 秒")\r
+        print("=" * 50)\r
+        \r
+        return True\r
+        \r
+    except Exception as e:\r
+        print("\n" + "=" * 50)\r
+        print("【错误处理】")\r
+        print("=" * 50)\r
+        print(f"❌ 连接异常: {str(e)}")\r
+        import traceback\r
+        traceback.print_exc()\r
+        return False\r
+\r
+# -------------------------- 4. 主程序 --------------------------\r
+if __name__ == "__main__":\r
+    print("=" * 50)\r
+    print("TCP测试客户端(标准Python socket版本)")\r
+    print("=" * 50)\r
+    print(f"服务器地址: {SERVER_HOST}:{SERVER_PORT}")\r
+    print("=" * 50)\r
+    \r
+    # 执行连接\r
+    success = connect_test_server()\r
+    \r
+    if success:\r
+        print("\n✅ 连接测试完成!")\r
+    else:\r
+        print("\n❌ 连接测试失败!")\r
+        sys.exit(1)\r
+\r
index c4c30ca..b3312dd 100644 (file)
@@ -123,30 +123,50 @@ class TCPTestServer:
                     \r
                     # 显示接收到的数据\r
                     timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')\r
+                    packet_size = len(data)\r
+                    first_byte = data[0] if data else 0\r
+                    \r
                     print(f"\n[{timestamp}] 📥 收到数据 [客户端ID: {client_id}, {address[0]}:{address[1]}]")\r
-                    print(f"  数据长度: {len(data)} 字节")\r
+                    print(f"数据长度: {packet_size} 字节, 首字节: 0x{first_byte:02X} ({first_byte:3d})")\r
+                    \r
+                    # 打印完整数据 - HEX格式\r
+                    hex_str = data.hex().upper()\r
+                    if len(hex_str) > 200:\r
+                        print(f"HEX格式(前200字符): {hex_str[:200]}...")\r
+                        print(f"HEX格式(完整,共{len(hex_str)}字符):")\r
+                        # 每行显示64个字符(32字节)\r
+                        for i in range(0, len(hex_str), 64):\r
+                            chunk = hex_str[i:i+64]\r
+                            offset = i // 2\r
+                            print(f"  {offset:06X}: {chunk}")\r
+                    else:\r
+                        print(f"HEX格式: {hex_str}")\r
                     \r
-                    # 尝试以文本和十六进制显示\r
+                    # 打印完整数据 - ASCII格式\r
                     try:\r
-                        text = data.decode('utf-8', errors='ignore')\r
-                        if text.isprintable() or '\n' in text or '\r' in text:\r
-                            print(f"  文本内容:")\r
-                            for line in text.splitlines():\r
-                                print(f"    {line}")\r
+                        ascii_str = data.decode('utf-8', errors='replace')\r
+                        # 检查是否包含可打印字符\r
+                        if all(32 <= ord(c) < 127 or c in '\n\r\t' for c in ascii_str):\r
+                            print(f"ASCII格式: {repr(ascii_str)}")\r
                         else:\r
-                            print(f"  十六进制: {data.hex()}")\r
+                            # 包含非打印字符,显示为repr格式\r
+                            print(f"ASCII格式(含非打印字符): {repr(ascii_str)}")\r
+                            # 同时显示可打印部分\r
+                            printable = ''.join(c if 32 <= ord(c) < 127 or c in '\n\r\t' else '.' for c in ascii_str)\r
+                            if printable.strip():\r
+                                print(f"可打印部分: {printable}")\r
                     except:\r
-                        print(f"  十六进制: {data.hex()}")\r
+                        print(f"ASCII格式: (无法解码为UTF-8)")\r
                     \r
-                    # æ\98¾ç¤ºå\89\8d64å­\97è\8a\82ç\9a\84ASCII表示\r
-                    if len(data) <= 64:\r
-                        ascii_repr = ''.join(chr(b) if 32 <= b < 127 else '.' for b in data)\r
-                        print(f"  ASCII: {ascii_repr}")\r
-                    else:\r
-                        ascii_repr = ''.join(chr(b) if 32 <= b < 127 else '.' for b in data[:64])\r
-                        print(f"  ASCII (前64字节): {ascii_repr}...")\r
+                    # æ\89\93å\8d°æ\95°æ\8d®ç\9a\84å\89\8då\87 ä¸ªå­\97è\8a\82ï¼\88å\8d\81å\85­è¿\9bå\88¶å\92\8cå\8d\81è¿\9bå\88¶ï¼\89\r
+                    if packet_size > 0:\r
+                        preview_bytes = min(16, packet_size)\r
+                        hex_preview = ' '.join(f'{b:02X}' for b in data[:preview_bytes])\r
+                        dec_preview = ' '.join(f'{b:3d}' for b in data[:preview_bytes])\r
+                        print(f"前{preview_bytes}字节 HEX: {hex_preview}")\r
+                        print(f"前{preview_bytes}字节 DEC: {dec_preview}")\r
                     \r
-                    print()\r
+                    print("-" * 60)\r
                     \r
                 except socket.timeout:\r
                     continue\r
diff --git a/code/sdk/zhihui/n706a_net/AT_COMMAND_FIXES.md b/code/sdk/zhihui/n706a_net/AT_COMMAND_FIXES.md
new file mode 100644 (file)
index 0000000..ba7e2ec
--- /dev/null
@@ -0,0 +1,95 @@
+# N706A AT指令实现修复说明
+
+## 问题分析
+
+对比Python脚本(已验证工作)和C语言实现的AT指令步骤,发现以下关键差异:
+
+### Python脚本的AT指令流程(TCP/SSL TCP)
+
+1. **SSL配置(仅HTTPS需要)**:
+   - `AT+SSLTCPCFG="sslversion",{version}` - 设置SSL版本
+   - `AT+SSLTCPCFG="authmode",{mode}` - 设置认证模式
+   - `AT+SSLTCPCFG="cacert","{cert_file}"` - 设置CA证书(如果需要)
+
+2. **建立连接**:
+   - 普通TCP: `AT+TCPSETUP={socket_id},{host},{port}`
+   - SSL TCP: `AT+SSLTCPSETUP={socket_id},{host},{port},{mode}`
+     - **注意**:第4个参数是**透传模式**(mode),不是认证模式
+     - mode=0: 非透传模式(默认,推荐)
+     - mode=1: 透传模式
+
+3. **发送数据**:
+   - 普通TCP: `AT+TCPSEND={socket_id},{length}`
+   - SSL TCP: `AT+SSLTCPSEND={socket_id},{length}`
+
+4. **接收数据**:
+   - 主动读取: `AT+TCPREAD={socket_id},{length}` / `AT+SSLTCPREAD={socket_id},{length}`
+   - 被动接收: 等待 `+TCPRECV:` / `+SSLTCPRECV:` URC
+
+### C语言实现的问题
+
+**关键错误**:`AT+SSLTCPSETUP` 的第4个参数错误地使用了 `auth_mode`(认证模式),而实际上应该是 `transparent_mode`(透传模式)。
+
+```c
+// 错误实现(修复前)
+snprintf(at_cmd, sizeof(at_cmd),
+        "AT+SSLTCPSETUP=%d,%s,%d,%d",
+        sock->socket_id, host, port, sock->auth_mode);  // ❌ 错误:使用了auth_mode
+
+// 正确实现(修复后)
+int transparent_mode = 0;  // 默认非透传模式
+snprintf(at_cmd, sizeof(at_cmd),
+        "AT+SSLTCPSETUP=%d,%s,%d,%d",
+        sock->socket_id, host, port, transparent_mode);  // ✅ 正确:使用transparent_mode
+```
+
+## 修复内容
+
+### 1. 修复 `AT+SSLTCPSETUP` 参数错误
+
+**文件**: `code/sdk/zhihui/n706a_net/src/n706a_tcp_client.c`
+
+**修复位置**: `n706a_tcp_connect()` 函数中的SSL TCP连接部分
+
+**修复说明**:
+- 将第4个参数从 `sock->auth_mode` 改为 `transparent_mode = 0`
+- 添加注释说明参数含义
+- SSL认证模式通过 `AT+SSLTCPCFG` 配置,不在连接命令中指定
+
+### 2. 其他注意事项
+
+1. **SSL配置时机**:
+   - SSL参数(sslversion, authmode, cacert)通过 `AT+SSLTCPCFG` 配置
+   - 应在建立SSL连接**之前**调用 `n706a_tcp_config_ssl()` 配置SSL参数
+   - 如果使用HTTPS,建议在 `n706a_http_client_connect()` 中检查并配置SSL
+
+2. **接收模式配置**:
+   - C语言实现已在 `n706a_net_api.c` 中配置了 `AT+RECVMODE=1,1`(HEX模式)
+   - 这与Python脚本一致,无需修改
+
+3. **URC处理**:
+   - C语言实现使用异步URC接收机制
+   - 已正确注册 `+TCPSETUP`、`+TCPRECV`、`+SSLTCPRECV` 等URC回调
+   - 与Python脚本的同步等待机制不同,但功能等价
+
+## 验证建议
+
+1. **测试普通TCP连接**:
+   - 验证 `AT+TCPSETUP` 连接是否正常
+   - 验证数据收发是否正常
+
+2. **测试SSL TCP连接**:
+   - 验证修复后的 `AT+SSLTCPSETUP` 连接是否正常
+   - 验证SSL握手是否成功
+   - 验证HTTPS请求是否正常
+
+3. **对比Python脚本**:
+   - 使用相同的URL和参数测试
+   - 对比AT指令序列是否一致
+   - 验证响应是否一致
+
+## 总结
+
+主要修复了 `AT+SSLTCPSETUP` 命令的参数错误,这是导致SSL TCP连接失败的关键原因。修复后,C语言实现的AT指令序列应与Python脚本一致。
+
+
index 7cb2f32..22d8bb0 100644 (file)
@@ -27,13 +27,12 @@ set(PUBLIC_HEADER
 set(PUBLIC_DEFINES
     SUPPORT_N706A_NET           # 定义此宏启用 N706A AT 模式(独立于PPP)
     SUPPORT_AT_N706A_NET        # 开机自动注册 N706A AT 命令
-    # SSL实现方式(二选一,仅影响n706a_net内部):
-    # N706A_SSL_USE_AT_COMMAND    # 使用AT指令实现SSL(推荐,利用N706A内置SSL)
-    N706A_SSL_USE_MBEDTLS     # 使用mbedtls库实现SSL(不推荐:与AT模式不兼容)
+    # SSL实现方式(仅支持AT指令模式):
+    N706A_SSL_USE_AT_COMMAND    # 使用AT指令实现SSL(利用N706A内置SSL)
 )
 
 set(PRIVATE_HEADER
-    ${CMAKE_CURRENT_SOURCE_DIR}/../ppp/include
+    ${CMAKE_CURRENT_SOURCE_DIR}/include              # n706a_net自己的头文件优先(避免与ppp同名文件冲突)
 )
 
 set(PRIVATE_DEFINES
index e48c131..874cbfd 100644 (file)
@@ -85,7 +85,15 @@ int n706a_net_env_init(void)
     if (ret != ERRCODE_SUCC) {
         PRINT("Warning: Failed to enable power save mode\r\n");
     }
+    
+
     #endif
+    /* 启用数据Ring功能 */
+    PRINT("Enabling data ring...\r\n");
+    ret = n706a_enable_data_ring();
+    if (ret != ERRCODE_SUCC) {
+        PRINT("Warning: Failed to enable data ring\r\n");
+    }
     
     PRINT("N706A network ready for use\r\n");
     
index fca032a..20c80c4 100644 (file)
@@ -183,6 +183,47 @@ void n706a_at_register_tcpsend_urc_callback(n706a_tcpsend_urc_callback_t callbac
  */
 void n706a_at_register_network_status_urc_callback(n706a_network_status_urc_callback_t callback);
 
+/**
+ * @brief 设置TCP接收模式(HEX模式或ASCII模式)
+ * @param hex_mode true表示HEX模式(AT+RECVMODE=1,1),false表示ASCII模式(AT+RECVMODE=1,0)
+ */
+void n706a_at_set_tcp_recv_hex_mode(bool hex_mode);
+
+/**
+ * @brief 获取TCP接收模式(HEX模式或ASCII模式)
+ * @return true表示HEX模式,false表示ASCII模式
+ */
+bool n706a_at_get_tcp_recv_hex_mode(void);
+
+/**
+ * @brief 获取累积缓冲区长度(用于调试)
+ * @return 累积缓冲区当前长度
+ */
+uint32_t n706a_at_get_accumulate_buffer_length(void);
+
+/**
+ * @brief 获取累积缓冲区的最后几个字节(用于调试)
+ * @param buffer 输出缓冲区
+ * @param max_len 最大长度
+ * @param actual_len 实际长度(输出)
+ * @return true=成功,false=失败
+ */
+bool n706a_at_get_accumulate_buffer_preview(uint8_t *buffer, uint32_t max_len, uint32_t *actual_len);
+
+/**
+ * @brief 获取累积缓冲区的完整内容(从开头开始,用于调试)
+ * @param buffer 输出缓冲区
+ * @param max_len 最大长度
+ * @param actual_len 实际长度(输出)
+ * @return true=成功,false=失败
+ */
+bool n706a_at_get_accumulate_buffer_all(uint8_t *buffer, uint32_t max_len, uint32_t *actual_len);
+
+/**
+ * @brief 清空累积缓冲区(用于socket关闭后清理残留数据)
+ */
+void n706a_at_clear_accumulate_buffer(void);
+
 #ifdef __cplusplus
 }
 #endif
index 530fd3f..37e3587 100644 (file)
@@ -1,15 +1,14 @@
 /**
  * @file n706a_http_client.h
- * @brief N706A HTTP/HTTPS客户端接口(基于TCP实现)
+ * @brief N706A HTTP/HTTPS客户端接口(基于HTTP专用AT指令)
  * 
- * 本实现采用统一的TCP底层架构:
- * - HTTP通过普通TCP连接实现
- * - HTTPS通过SSL TCP连接实现
- * - 手动构造HTTP协议,突破AT+HTTP*单连接限制
- * - 支持6个并发HTTP/HTTPS连接
+ * 本实现使用N706A模块的HTTP专用AT指令:
+ * - HTTP: AT+HTTPPARA, AT+HTTPSETUP, AT+HTTPACTION
+ * - HTTPS: AT+HTTPSPARA, AT+HTTPSSETUP, AT+HTTPSACTION
+ * - 与Python脚本实现逻辑一致
  * 
- * @version 3.0
- * @date 2025-10-30
+ * @version 4.0
+ * @date 2025-01-XX
  */
 
 #ifndef PPP_N706A_HTTP_CLIENT_H
 #include <stdint.h>
 #include <stdbool.h>
 #include <stddef.h>
-#include "n706a_tcp_client.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* ========== HTTP收发分片限制(根据AT指令手册) ========== */
+/**
+ * @brief HTTP接收单次URC最大数据长度
+ * 
+ * 根据AT指令手册:
+ * - AT+TCPREAD最大长度为2048字节
+ * - AT+UDPREAD最大长度为2048字节
+ * - HTTP接收通过+HTTPRECV URC主动上报,虽然没有明确说明最大长度,
+ *   但考虑到TCP/UDP的限制,HTTP接收的URC数据长度也应该遵循类似的限制
+ * 
+ * 注意:模块可能会自动分片,单次URC的数据长度不应超过此限制
+ */
+#define N706A_HTTP_MAX_URC_DATA_LEN  2048  /**< HTTP接收单次URC最大数据长度(字节) */
+
+/**
+ * @brief HTTP发送请求最大长度
+ * 
+ * 根据AT指令手册(第13311行):
+ * - AT+HTTPACTION=99,<length> 的 <length> 最大长度为2048字节
+ * - AT+HTTPSACTION=99,<length> 的 <length> 最大长度为2048字节
+ * 
+ * 注意:HTTP请求(包括头部和body)必须作为一个完整的报文发送,不能分片。
+ *       如果请求超过此限制,应该返回错误或使用其他方式发送(如TCP直接发送)。
+ */
+#define N706A_HTTP_MAX_REQUEST_LEN  2048  /**< HTTP发送请求最大长度(字节) */
+
 /**
  * @brief N706A HTTP请求类型
  */
@@ -54,24 +78,58 @@ typedef enum {
     N706A_HTTP_EUNKOWN  = -11, /**< 未知错误          */
     N706A_HTTP_ETIMEOUT = -12, /**< 超时                */
     N706A_HTTP_ENOMEM   = -13, /**< 内存分配失败         */
+    N706A_HTTP_ERROR    = -14, /**< 通用错误            */
 } n706a_httpc_result_t;
 
 /**
- * @brief N706A HTTP客户端结构(基于TCP实现)
+ * @brief HTTP流式接收数据回调函数类型
+ * @param user_data 用户数据指针(由应用层传入)
+ * @param data 响应体数据块指针
+ * @param length 数据块长度
+ * @param is_finished 是否是最后一个数据块(true=接收完成,false=还有更多数据)
+ * @return 0表示成功,非0表示错误(会导致接收中断)
+ * 
+ * @note 此回调在URC到达时立即调用,应用层可以在此回调中直接写入文件
+ *       回调函数应该尽快返回,避免阻塞后续数据接收
+ */
+typedef int (*n706a_http_stream_callback_t)(void *user_data, 
+                                             const uint8_t *data, 
+                                             size_t length, 
+                                             bool is_finished);
+
+/**
+ * @brief N706A HTTP客户端结构(基于HTTP专用AT指令)
  */
 typedef struct {
-    n706a_tcp_socket_t tcp_sock;    /**< 底层TCP socket */
-    int socket_id;                   /**< TCP socket编号(0-5) */
-    bool is_https;                   /**< HTTPS标志(决定是否使用SSL) */
+    bool is_https;                   /**< HTTPS标志 */
     uint16_t remote_port;            /**< 远程端口 */
     int response_code;               /**< HTTP响应码 */
     char *header;                    /**< 自定义请求头 */
     char *auth_user;                 /**< 基本认证用户名 */
     char *auth_password;             /**< 基本认证密码 */
     char host[256];                  /**< 主机名 */
+    char url_path[512];              /**< URL路径(包含查询参数) */
     bool connected;                  /**< 连接状态 */
     bool keepalive;                  /**< 长连接模式 */
     uint32_t timeout_ms;             /**< 超时时间(毫秒) */
+    
+    /* HTTP响应接收相关 */
+    uint8_t *response_buffer;        /**< 响应数据缓冲区 */
+    size_t response_buffer_size;     /**< 缓冲区大小 */
+    size_t response_received;        /**< 已接收的响应数据长度 */
+    bool response_complete;          /**< 响应是否接收完成 */
+    void *response_mutex;            /**< 响应缓冲区互斥锁 */
+    void *response_sem;              /**< 响应数据可用信号量 */
+    
+    /* 流式接收相关(可选) */
+    n706a_http_stream_callback_t stream_callback;  /**< 流式接收回调函数 */
+    void *stream_user_data;          /**< 流式接收回调的用户数据 */
+    bool stream_mode_enabled;        /**< 是否启用流式模式 */
+    size_t stream_body_received;     /**< 流式模式下已接收的响应体总长度 */
+    int stream_content_length;       /**< 流式模式下的Content-Length(用于判断是否完成) */
+    bool stream_header_parsed;       /**< 流式模式下响应头是否已解析 */
+    bool stream_is_chunked;          /**< 流式模式下是否chunked编码 */
+    bool stream_connection_closed;   /**< 流式模式下连接是否已关闭(检测到+HTTPSCLOSED/+HTTPCLOSED) */
 } n706a_http_client_t;
 
 /**
@@ -98,23 +156,30 @@ typedef struct {
 /**
  * @brief 初始化HTTP客户端
  * @param client HTTP客户端结构体指针
- * @param socket_id socket ID (0-5),如果传入-1则自动分配空闲的socket_id(推荐,避免冲突)
  * @return N706A_HTTP_SUCCESS 成功,其他值失败
+ * @note HTTP专用AT指令不占用TCP socket,无需socket_id
  */
-n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client, int socket_id);
+n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client);
+
+/**
+ * @brief 设置HTTP/HTTPS请求URL(自动判断协议类型)
+ * @param client HTTP客户端结构指针
+ * @param url 完整URL(如"http://example.com/path"或"https://example.com/path")
+ * @param keepalive 是否启用长连接,0=关闭(默认),1=启用
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 此函数会解析URL并设置AT+HTTPPARA/AT+HTTPSPARA参数
+ */
+n706a_httpc_result_t n706a_http_client_set_url(n706a_http_client_t *client,
+                                               const char *url,
+                                               int keepalive);
 
 /**
- * @brief 连接到HTTP/HTTPS服务器
+ * @brief 建立HTTP/HTTPS连接(根据URL自动选择)
  * @param client HTTP客户端结构指针
- * @param host 主机名或IP地址
- * @param port 端口号(HTTP默认80,HTTPS默认443)
- * @param use_ssl 是否使用SSL(HTTPS)
  * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 调用AT+HTTPSETUP或AT+HTTPSSETUP建立连接
  */
-n706a_httpc_result_t n706a_http_client_connect(n706a_http_client_t *client,
-                                               const char *host,
-                                               uint16_t port,
-                                               bool use_ssl);
+n706a_httpc_result_t n706a_http_client_setup_connection(n706a_http_client_t *client);
 
 /**
  * @brief 发送HTTP GET请求
@@ -183,6 +248,57 @@ n706a_httpc_result_t n706a_http_client_head(n706a_http_client_t *client,
                                             const char *path,
                                             n706a_http_client_data_t *client_data);
 
+/**
+ * @brief 添加SSL证书到N706A模块(仅HTTPS需要)
+ * @param cert_name 证书文件名(如"ca.crt")
+ * @param cert_data 证书内容(PEM格式,字节数据)
+ * @param cert_len 证书数据长度
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 使用AT+CERTADD命令添加证书
+ */
+n706a_httpc_result_t n706a_http_client_add_certificate(const char *cert_name,
+                                                       const uint8_t *cert_data,
+                                                       size_t cert_len);
+
+/**
+ * @brief 检查证书是否已存在于设备中
+ * @param cert_name 证书文件名
+ * @return true证书存在,false不存在
+ */
+bool n706a_http_client_certificate_exists(const char *cert_name);
+
+/**
+ * @brief 删除SSL证书
+ * @param cert_name 证书文件名
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 使用AT+CERTDEL命令删除证书
+ */
+n706a_httpc_result_t n706a_http_client_delete_certificate(const char *cert_name);
+
+/**
+ * @brief 初始化HTTPS通用证书(自动检查并添加)
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 此函数会自动检查证书是否存在,如果存在则先删除再重新添加(用于测试)
+ *       建议在首次使用HTTPS前调用
+ */
+n706a_httpc_result_t n706a_http_client_init_certificate(void);
+
+/**
+ * @brief 配置HTTPS SSL参数(仅HTTPS需要)
+ * @param ssl_version SSL版本,0=SSL3.0, 1=TLS1.0, 2=TLS1.1, 3=TLS1.2, 4=TLS1.0/1.1/1.2, 5=TLS1.3
+ * @param auth_mode 认证模式,0=不认证, 1=单向认证, 2=双向认证
+ * @param ca_cert CA证书文件名(单向/双向认证需要,如果为NULL且auth_mode=1则使用默认证书)
+ * @param client_cert 客户端证书文件名(双向认证需要,可为NULL)
+ * @param client_key 客户端密钥文件名(双向认证需要,可为NULL)
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 使用AT+HTTPSCFG命令配置SSL参数
+ */
+n706a_httpc_result_t n706a_http_client_config_https(int ssl_version,
+                                                    int auth_mode,
+                                                    const char *ca_cert,
+                                                    const char *client_cert,
+                                                    const char *client_key);
+
 /**
  * @brief 设置自定义HTTP请求头
  * @param client HTTP客户端结构指针
@@ -233,6 +349,35 @@ bool n706a_http_client_is_connected(n706a_http_client_t *client);
  */
 int n706a_http_client_get_response_code(n706a_http_client_t *client);
 
+/**
+ * @brief 设置HTTP流式接收回调(用于大文件下载等场景)
+ * @param client HTTP客户端结构指针
+ * @param callback 流式接收回调函数(NULL表示禁用流式模式)
+ * @param user_data 回调函数的用户数据指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * 
+ * @note 启用流式模式后,响应体数据会通过回调函数立即传递给应用层
+ *       应用层可以在回调中直接写入文件,无需等待所有数据接收完成
+ *       流式模式与传统的累积模式互斥,启用流式模式后,responseBuf将不再累积数据
+ */
+n706a_httpc_result_t n706a_http_client_set_stream_callback(n706a_http_client_t *client,
+                                                           n706a_http_stream_callback_t callback,
+                                                           void *user_data);
+
+/**
+ * @brief 流式接收HTTP响应数据(用于在发送请求后继续接收后续数据块)
+ * @param client HTTP客户端结构指针
+ * @param client_data 响应数据结构指针
+ * @param timeout_ms 超时时间(毫秒)
+ * @return N706A_HTTP_SUCCESS成功,N706A_HTTP_EAGAIN还有更多数据,其他失败
+ * 
+ * @note 此函数用于在流式模式下等待后续的URC数据到达
+ *       数据会通过回调函数传递给应用层,此函数只用于等待和更新状态
+ */
+n706a_httpc_result_t n706a_http_client_recv_response_stream(n706a_http_client_t *client,
+                                                            n706a_http_client_data_t *client_data,
+                                                            uint32_t timeout_ms);
+
 /* ========== 辅助函数 ========== */
 
 /**
index 3ea7d89..f08249e 100644 (file)
@@ -100,6 +100,18 @@ errcode_t n706a_get_network_signal_info(void);
  */
 errcode_t n706a_enable_power_save_mode(void);
 
+/**
+ * @brief 启用4G模块数据Ring功能
+ * 
+ * @return ERRCODE_SUCC 成功,其他值失败
+ * 
+ * 功能说明:
+ * - 执行 AT+NWURCCFG=0,1,200 命令
+ * - 使能 data ring,持续 200ms
+ * - 用于在省电模式下通过 Ring 引脚唤醒 MCU
+ */
+errcode_t n706a_enable_data_ring(void);
+
 /**
  * @brief 更新所有runtime变量值
  * 
@@ -109,6 +121,19 @@ errcode_t n706a_enable_power_save_mode(void);
  */
 errcode_t n706a_api_update_all_runtime_status(void);
 
+/**
+ * @brief 通过AT+CCLK命令获取网络时间并同步到系统时间
+ * 
+ * @return ERRCODE_SUCC 成功,其他值失败
+ * 
+ * 功能说明:
+ * - 发送 AT+CCLK? 命令获取网络时间
+ * - 解析响应,获取UTC时间和时区偏移
+ * - 计算本地时间 = UTC时间 + 时区偏移
+ * - 使用 settimeofday 设置系统时间为本地时间
+ */
+errcode_t n706a_api_sync_time_from_network(void);
+
 /**
  * @brief 动态设置N706A模块波特率(在所有模式下)
  * 
index a54eb91..504320c 100644 (file)
@@ -6,7 +6,6 @@
  * 
  * SSL实现方式:
  *   - N706A_SSL_USE_AT_COMMAND: 使用AT+SSLTCP*指令(默认,推荐)
- *   - N706A_SSL_USE_MBEDTLS: 使用mbedtls库(需要额外库支持)
  */
 
 #ifndef PPP_N706A_TCP_CLIENT_H
 /**
  * 编译时选择SSL实现方式:
  *   gcc -DN706A_SSL_USE_AT_COMMAND ...  (使用AT指令,默认)
- *   gcc -DN706A_SSL_USE_MBEDTLS ...     (使用mbedtls)
  */
-#if !defined(N706A_SSL_USE_AT_COMMAND) && !defined(N706A_SSL_USE_MBEDTLS)
+#if !defined(N706A_SSL_USE_AT_COMMAND)
     /* 默认使用AT指令实现 */
     #define N706A_SSL_USE_AT_COMMAND 1
 #endif
 
-#if defined(N706A_SSL_USE_AT_COMMAND) && defined(N706A_SSL_USE_MBEDTLS)
-    #error "Cannot define both N706A_SSL_USE_AT_COMMAND and N706A_SSL_USE_MBEDTLS"
-#endif
-
-/* 如果使用mbedtls,包含相关头文件 */
-#ifdef N706A_SSL_USE_MBEDTLS
-    #include "mbedtls/net_sockets.h"
-    #include "mbedtls/ssl.h"
-    #include "mbedtls/entropy.h"
-    #include "mbedtls/ctr_drbg.h"
-    #include "mbedtls/x509.h"
-    #include "mbedtls/error.h"
-#endif
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -98,27 +82,10 @@ typedef struct {
     void *urc_recv_mutex;           /**< 缓冲区互斥锁 */
     void *urc_data_available_sem;   /**< 数据可用信号量(用于mbedtls recv等待) */
     
-    /* SSL相关字段(根据编译宏不同而不同) */
-#ifdef N706A_SSL_USE_AT_COMMAND
-    /* AT指令模式:SSL由模组处理 */
+    /* SSL相关字段(AT指令模式:SSL由模组处理) */
     int ssl_version;                /**< SSL版本(0-5) */
     int auth_mode;                  /**< 认证模式(0-2) */
     char ca_cert_file[64];          /**< CA证书文件名 */
-#endif
-
-#ifdef N706A_SSL_USE_MBEDTLS
-    /* mbedtls模式:需要SSL上下文 */
-    int ssl_version;                /**< SSL版本 */
-    int auth_mode;                  /**< 认证模式 */
-    mbedtls_ssl_context ssl;            /**< SSL上下文 */
-    mbedtls_ssl_config conf;            /**< SSL配置 */
-    mbedtls_entropy_context entropy;    /**< 熵源 */
-    mbedtls_ctr_drbg_context ctr_drbg;  /**< 随机数生成器 */
-    mbedtls_x509_crt cacert;            /**< CA证书 */
-    mbedtls_x509_crt clicert;           /**< 客户端证书(可选) */
-    mbedtls_pk_context pkey;            /**< 客户端私钥(可选) */
-    bool ssl_initialized;               /**< SSL是否已初始化 */
-#endif
 } n706a_tcp_socket_t;
 
 /**
@@ -162,15 +129,9 @@ n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock, const char *host,
 /**
  * @brief 配置SSL参数(在SSL连接前调用)
  * @param sock TCP socket指针
- * @param ssl_version SSL版本
- *        AT指令模式:0-5 (0=SSL3.0, 1=TLS1.0, 2=TLS1.1, 3=TLS1.2, 4=TLS1.0/1.1/1.2, 5=TLS1.3)
- *        mbedtls模式:使用MBEDTLS_SSL_MINOR_VERSION_*常量
- * @param auth_mode 认证模式
- *        AT指令模式:0=不认证, 1=服务器认证, 2=双向认证
- *        mbedtls模式:MBEDTLS_SSL_VERIFY_NONE/OPTIONAL/REQUIRED
- * @param ca_cert CA证书
- *        AT指令模式:证书文件名(如"ca.crt")
- *        mbedtls模式:证书内容(PEM格式字符串)
+ * @param ssl_version SSL版本:0-5 (0=SSL3.0, 1=TLS1.0, 2=TLS1.1, 3=TLS1.2, 4=TLS1.0/1.1/1.2, 5=TLS1.3)
+ * @param auth_mode 认证模式:0=不认证, 1=服务器认证, 2=双向认证
+ * @param ca_cert CA证书文件名(如"ca.crt")
  * @return N706A_TCP_SUCCESS成功,其他值表示失败
  */
 n706a_tcp_result_t n706a_tcp_config_ssl(n706a_tcp_socket_t *sock, 
@@ -178,19 +139,6 @@ n706a_tcp_result_t n706a_tcp_config_ssl(n706a_tcp_socket_t *sock,
                                         int auth_mode,
                                         const char *ca_cert);
 
-#ifdef N706A_SSL_USE_MBEDTLS
-/**
- * @brief 配置客户端证书和私钥(mbedtls模式,双向认证)
- * @param sock TCP socket指针
- * @param client_cert 客户端证书(PEM格式)
- * @param client_key 客户端私钥(PEM格式)
- * @return N706A_TCP_SUCCESS成功,其他值表示失败
- */
-n706a_tcp_result_t n706a_tcp_config_ssl_client_cert(n706a_tcp_socket_t *sock,
-                                                     const char *client_cert,
-                                                     const char *client_key);
-#endif
-
 /**
  * @brief 发送TCP数据
  * @param sock TCP socket指针
@@ -235,6 +183,15 @@ bool n706a_tcp_is_connected(const n706a_tcp_socket_t *sock);
  */
 n706a_tcp_state_t n706a_tcp_get_state(const n706a_tcp_socket_t *sock);
 
+/**
+ * @brief 查询socket在模块端的状态(使用AT+IPSTATUS)
+ * @param socket_id socket ID (0-5)
+ * @param is_connected 输出:是否已连接(true=CONNECT, false=DISCONNECT)
+ * @param is_ssl 输出:是否是SSL连接(true=SSL, false=TCP,仅在已连接时有效)
+ * @return N706A_TCP_SUCCESS=查询成功,其他值=查询失败
+ */
+n706a_tcp_result_t n706a_tcp_query_socket_status(int socket_id, bool *is_connected, bool *is_ssl);
+
 /**
  * @brief 初始化TCP socket的URC接收缓存
  * @param sock TCP socket指针
@@ -273,6 +230,31 @@ void n706a_tcp_on_urc_received(int socket_id, const uint8_t *data, size_t length
  */
 void n706a_tcp_on_tcpsetup_urc(int socket_id, bool success);
 
+/**
+ * @brief +TCPSEND URC回调(由n706a_at_handler调用)
+ * @param socket_id Socket ID
+ * @param sent_bytes 实际发送的字节数
+ * 
+ * 注意:此函数由n706a_at_handler.c通过URC回调调用,不是应用层API
+ */
+void n706a_tcp_on_tcpsend_urc(int socket_id, size_t sent_bytes);
+
+/**
+ * @brief +TCPCLOSE URC回调(由n706a_at_handler调用)
+ * @param socket_id Socket ID (0-5)
+ * 
+ * 注意:此函数由n706a_at_handler.c通过URC回调调用,不是应用层API
+ * 当服务器主动关闭连接时,会调用此函数更新socket状态
+ */
+void n706a_tcp_on_tcpclose_urc(int socket_id);
+
+/**
+ * @brief 提前初始化+TCPSEND URC等待机制(供n706a_net_init调用)
+ * 
+ * 在系统初始化时提前初始化信号量和互斥锁,避免在发送数据时初始化失败
+ */
+void n706a_tcp_preinit_tcpsend_urc_wait(void);
+
 #ifdef __cplusplus
 }
 #endif
index a710a84..57999d5 100644 (file)
 #include <stddef.h>
 #include "n706a_tcp_client.h"
 
+/* 包含 WebSocket 解析器头文件(定义 ws_parser_t 类型) */
+#include "../../websocket/include/websocket_parser_wrapper.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /* WebSocket最大客户端数量 */
-#define N706A_WS_MAX_CLIENTS 6
+#define N706A_WS_MAX_CLIENTS 3
 
 /* WebSocket URL最大长度 */
 #define N706A_WS_MAX_URL_LEN 512
@@ -135,7 +138,7 @@ typedef struct n706a_websocket_client {
     char url[N706A_WS_MAX_URL_LEN];     /**< 完整URL */
     char host[128];                     /**< 主机名 */
     uint16_t port;                      /**< 端口号 */
-    char path[256];                     /**< 路径 */
+    char path[N706A_WS_MAX_URL_LEN];                     /**< 路径 */
     
     /* WebSocket协议 */
     char sec_key[32];                   /**< Sec-WebSocket-Key */
@@ -156,6 +159,10 @@ typedef struct n706a_websocket_client {
     bool running;                       /**< 接收线程运行标志 */
     void *recv_thread;                  /**< 接收线程句柄 */
     
+    /* WebSocket 帧解析器 */
+    ws_parser_t *parser;                /**< WebSocket 帧解析器实例 */
+    n706a_ws_opcode_t fragment_opcode;  /**< 分片消息的初始 opcode(用于 CONTINUATION 帧) */
+    
 } n706a_websocket_client_t;
 
 /**
@@ -256,6 +263,13 @@ int n706a_websocket_close_with_reason(int client_id,
  */
 n706a_ws_state_t n706a_websocket_get_state(int client_id);
 
+/**
+ * @brief 获取WebSocket的底层TCP socket ID
+ * @param client_id 客户端ID
+ * @return socket ID (0-5),失败返回-1
+ */
+int n706a_websocket_get_socket_id(int client_id);
+
 /**
  * @brief 检查WebSocket是否已连接
  * @param client_id 客户端ID
index a45c70c..9266e41 100644 (file)
@@ -28,11 +28,11 @@ extern "C" {
 #define SIO_UART_IRQ_NUM            UART_0_IRQN         // UART IRQ引脚
 #endif
 
-#define PPP_UART_BAUDRATE          1000000
+#define PPP_UART_BAUDRATE          921600
 
 /* 支持的波特率列表(自适应模式) */
-#define PPP_BAUDRATE_AUTO_LIST      {115200,921600,1000000,2000000}
-#define PPP_BAUDRATE_AUTO_COUNT     4
+#define PPP_BAUDRATE_AUTO_LIST      {9600,115200,750000,921600,1000000}
+#define PPP_BAUDRATE_AUTO_COUNT     5
 
 /* 支持的固定波特率列表 */
 #define PPP_BAUDRATE_FIXED_LIST     {4800, 9600, 57600, 115200, 230400, 460800, 750000, 921600, 1000000, 2000000, 4000000}
@@ -83,20 +83,26 @@ extern "C" {
 
 /* 调试配置 */
 #define PPP_DEBUG_ENABLE            1                   // 启用调试输出
-#define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_DEBUG // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
-// #define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_INFO // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
-// #define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_WARN // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
+// #define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_INFO // 默认显示信息、警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
+#define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_DEBUG // 显示所有调试信息
+// #define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_WARN // 只显示警告和错误
+
+/* 详细调试日志配置(HEX打印、详细状态信息等) */
+#define PPP_DEBUG_VERBOSE_ENABLE    1                  // 默认关闭详细调试日志(HEX打印、详细状态等)
+                                                         // 设置为1以启用详细调试日志
 
 /*
  * 调试级别说明:
  * PPP_DEBUG_LEVEL_ERROR (1)  - 只显示错误信息
  * PPP_DEBUG_LEVEL_WARN  (2)  - 显示警告和错误信息  
- * PPP_DEBUG_LEVEL_INFO  (3)  - 显示信息、警告和错误
+ * PPP_DEBUG_LEVEL_INFO  (3)  - 显示信息、警告和错误(默认)
  * PPP_DEBUG_LEVEL_DEBUG (4)  - 显示所有调试信息
  * 
  * 关键连接状态信息(PPP_DEBUG_CRITICAL)始终显示,不受此级别控制
+ * 详细调试日志(PPP_DEBUG_VERBOSE)需要单独启用PPP_DEBUG_VERBOSE_ENABLE=1
  * 
  * 如需完整调试信息,可将上面的PPP_DEBUG_LEVEL改为PPP_DEBUG_LEVEL_DEBUG
+ * 如需详细HEX打印等,可将PPP_DEBUG_VERBOSE_ENABLE改为1
  */
 
 /* PPP状态定义 */
index ecf7b4a..9cceabe 100644 (file)
@@ -70,6 +70,22 @@ extern int ppp_debug_get_level(void);
         PRINT("[PPP] " fmt "\r\n", ##__VA_ARGS__); \
     } while(0)
 
+/* 详细调试日志宏 - 用于HEX打印、详细状态信息等,默认关闭 */
+#ifndef PPP_DEBUG_VERBOSE_ENABLE
+#define PPP_DEBUG_VERBOSE_ENABLE    0  /* 默认关闭 */
+#endif
+
+#if PPP_DEBUG_VERBOSE_ENABLE
+#define PPP_DEBUG_VERBOSE(fmt, ...) \
+    do { \
+        if (ppp_debug_get_level() >= PPP_DEBUG_LEVEL_DEBUG) { \
+            PRINT("[PPP VERBOSE] " fmt "\r\n", ##__VA_ARGS__); \
+        } \
+    } while(0)
+#else
+#define PPP_DEBUG_VERBOSE(fmt, ...)  /* 默认关闭详细调试日志 */
+#endif
+
 #else
 
 #define PPP_DEBUG_ERROR(fmt, ...)
@@ -79,6 +95,7 @@ extern int ppp_debug_get_level(void);
 #define PPP_DEBUG_HEXDUMP(data, len)
 #define PPP_DEBUG_PRINT(fmt, ...)
 #define PPP_DEBUG_CRITICAL(fmt, ...) PRINT("[PPP] " fmt "\r\n", ##__VA_ARGS__)
+#define PPP_DEBUG_VERBOSE(fmt, ...)  /* 默认关闭详细调试日志 */
 
 #endif /* PPP_DEBUG_ENABLE */
 
index 27fd4c8..9c64e62 100644 (file)
@@ -3,15 +3,17 @@
 #include "ppp_types.h"
 #include "ppp_debug.h"
 #include "ppp_serial.h"
-#include "uart.h"
+#include "uart.h"  // 用于波特率设置等UART配置操作
 #include "schedule/osal_task.h"
 #include "lock/osal_mutex.h"
 #include "semaphore/osal_semaphore.h"
+#include "time/osal_timer.h"  // For osal_get_jiffies and osal_jiffies_to_msecs
 #include "soc_osal.h"
 #include "securec.h"
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h> // For malloc and free
+#include <stdint.h> // For UINT32_MAX
 
 /* ========== AT响应异步缓冲区(由ppp_interrupt.c写入) ========== */
 #define N706A_AT_RESPONSE_RING_BUFFER_SIZE  4096  // 4KB环形缓冲区
@@ -22,6 +24,13 @@ static volatile uint32_t g_at_response_tail = 0;
 static osal_mutex g_at_response_mutex;
 static osal_semaphore g_at_response_sem;
 
+/* ========== AT响应累积缓冲区(用于存储不完整的响应) ========== */
+#define N706A_AT_ACCUMULATE_BUFFER_SIZE  8192  // 8KB静态缓冲区(避免频繁malloc/free)
+
+static uint8_t g_at_accumulate_buffer[N706A_AT_ACCUMULATE_BUFFER_SIZE];
+static uint32_t g_at_accumulate_length = 0;
+static osal_mutex g_at_accumulate_mutex;
+
 /* AT命令处理上下文 */
 static struct {
     bool initialized;
@@ -41,6 +50,27 @@ static const uint32_t g_fixed_baudrates[] = PPP_BAUDRATE_FIXED_LIST;
 static uint8_t *g_at_response_buffer = NULL;
 static uint32_t g_at_buffer_size = 1024;
 
+/* ========== AT命令解析临时缓冲区(动态分配,在初始化时分配,反初始化时释放) ========== */
+/* 
+ * 内存占用说明:
+ * - 静态变量(static):在程序启动时分配,放在BSS/DATA段,程序运行期间一直占用,无法释放
+ * - 动态分配(malloc):在初始化时分配,在反初始化时释放,只在需要时占用内存
+ * 
+ * 对于嵌入式MCU,使用动态分配更合适:
+ * - 只在AT模块初始化时占用内存
+ * - 可以在不需要时释放(通过deinit)
+ * - 避免程序启动时就占用约1.9KB的RAM
+ * 
+ * 缓冲区大小:
+ * - g_at_cmd_buffer: 260字节 (AT命令+ \r\n)
+ * - g_at_printable_buffer: 1124字节 (调试打印)
+ * - g_at_command_response_buffer: 512字节 (命令响应提取)
+ * - 总计:约1896字节 (1.85KB)
+ */
+static char *g_at_cmd_buffer = NULL;  // AT命令+ \r\n
+static char *g_at_printable_buffer = NULL;  // 调试打印缓冲区
+static char *g_at_command_response_buffer = NULL;  // 命令响应部分提取缓冲区
+
 /* TCP URC回调 */
 static n706a_tcp_urc_callback_t g_tcp_urc_callback = NULL;
 
@@ -56,8 +86,90 @@ static n706a_tcpsend_urc_callback_t g_tcpsend_urc_callback = NULL;
 /* 网络状态URC回调 */
 static n706a_network_status_urc_callback_t g_network_status_urc_callback = NULL;
 
+/* TCP接收模式标志(HEX模式=1,1,ASCII模式=1,0) */
+static bool g_tcp_recv_hex_mode = true;  // 默认使用HEX模式(因为初始化时设置了AT+RECVMODE=1,1)
+
+/* 累积缓冲区超时清除机制 */
+static unsigned long long g_at_accumulate_last_update_time = 0;  // 累积缓冲区最后更新时间(tick数)
+#define N706A_AT_ACCUMULATE_TIMEOUT_MS  5000  // 5秒超时,如果累积缓冲区中的数据超过5秒还没被识别,则清除
+
+/* 流式解析器开关(新方案) */
+#define USE_STREAM_PARSER  1  // 1=使用流式解析器(新方案),0=使用旧方案
+
+/* 丢弃数据统计 */
+static uint32_t g_dropped_data_count = 0;  // 丢弃数据块的次数
+static uint32_t g_dropped_data_bytes = 0;  // 丢弃数据的总字节数
+
+/* ========== 流式解析器状态机(原始设计) ========== */
+typedef enum {
+    AT_PARSER_STATE_IDLE,           // 空闲状态:扫描数据,寻找响应开始
+    AT_PARSER_STATE_AT_RESPONSE,    // AT响应处理中
+    AT_PARSER_STATE_URC_HEADER,     // URC头部处理中(解析参数)
+    AT_PARSER_STATE_URC_DATA,       // URC数据处理中
+    AT_PARSER_STATE_COMMAND_ECHO    // 命令回显处理中
+} at_parser_state_t;
+
+typedef enum {
+    URC_TYPE_NONE = 0,
+    URC_TYPE_TCPRECV,
+    URC_TYPE_SSLTCPRECV,
+    URC_TYPE_HTTPSRECV,
+    URC_TYPE_HTTPRECV,
+    URC_TYPE_TCPSETUP,
+    URC_TYPE_SSLTCPSETUP,
+    URC_TYPE_TCPCLOSE,
+    URC_TYPE_SSLTCPCLOSE,
+    URC_TYPE_TCPSEND,
+    URC_TYPE_SSLTCPEND,
+    URC_TYPE_CREG,
+    URC_TYPE_CGREG,
+    URC_TYPE_CGATT
+} urc_type_t;
+
+/* 解析器上下文(原始设计 + 方案2修复) */
+typedef struct {
+    at_parser_state_t state;            // 当前状态
+    
+    /* 当前处理的响应信息 */
+    uint32_t response_start_pos;        // 响应开始位置(相对于当前数据块)
+    uint32_t response_expected_len;     // 期望的响应长度(用于URC数据)
+    uint32_t response_received_len;     // 已接收的响应长度
+    
+    /* URC相关信息 */
+    urc_type_t urc_type;                // URC类型
+    bool is_https;                      // 是否是HTTPS(用于HTTPRECV)
+    int tcp_socket_id;                  // TCP socket ID(用于TCPRECV)
+    
+    /* 跨数据包状态保持 */
+    uint8_t partial_buffer[512];        // 部分数据缓冲区(跨包使用)
+    uint32_t partial_buffer_len;        // 部分数据长度
+    
+    /* URC处理字段 */
+    size_t remaining_data_len;          // 剩余数据长度(用于URC数据)
+    size_t received_data_len;           // 已接收数据长度
+    char urc_header_buf[256];           // URC头部缓冲区(用于解析参数)
+    uint32_t urc_header_len;            // URC头部长度
+    
+    /* 方案2:状态转换标记 */
+    bool state_transition_pending;      // 标记是否有待处理的状态转换(用于修复consumed=0问题)
+    
+    /* URC匹配状态(用于跨数据块匹配) */
+    uint32_t urc_match_pos;             // URC模式匹配位置
+    const char *urc_pattern;            // 当前匹配的URC模式
+    uint32_t urc_pattern_len;           // URC模式长度
+    
+    /* 统计信息 */
+    uint32_t total_bytes_processed;     // 总处理字节数
+    uint32_t urc_count;                 // URC计数
+    uint32_t at_response_count;         // AT响应计数
+} at_parser_context_t;
+
+static at_parser_context_t g_parser_ctx = {0};
+
 /* AT命令响应解析 */
+static const char *ppp_at_skip_echo(const char *response, size_t response_len, char *cleaned_response, size_t cleaned_size);
 static bool ppp_at_parse_response(const char *response, const char *expected);
+static bool ppp_at_parse_response_with_len(const char *response, size_t response_len, const char *expected);
 static ppp_error_t ppp_at_wait_response(ppp_at_response_t *response, uint32_t timeout);
 static ppp_error_t ppp_at_set_baudrate(uint32_t baudrate);
 static ppp_error_t ppp_at_send_command(const char *command, const char *expected_response, 
@@ -66,17 +178,37 @@ static ppp_error_t ppp_at_send_command(const char *command, const char *expected
 /* URC解析函数 */
 static bool n706a_parse_tcprecv_urc(const char *urc_data, size_t urc_len, 
                                     int *socket_id, size_t *data_len, const uint8_t **data);
+/* 非static版本,供其他模块使用 */
+bool n706a_parse_tcprecv_urc_public(const char *urc_data, size_t urc_len, 
+                                    int *socket_id, size_t *data_len, const uint8_t **data);
 static bool n706a_parse_tcpsetup_urc(const char *urc_data, size_t urc_len,
                                      int *socket_id, bool *success);
 static bool n706a_parse_tcpclose_urc(const char *urc_data, size_t urc_len, int *socket_id);
 static bool n706a_parse_tcpsend_urc(const char *urc_data, size_t urc_len, 
                                     int *socket_id, size_t *sent_bytes);
+static bool n706a_parse_httprecv_urc(const char *urc_data, size_t urc_len,
+                                     size_t *declared_len, size_t *actual_len, const uint8_t **data);
 static bool n706a_hex_char_to_value(char c, uint8_t *value);
 static size_t n706a_binary_to_hex_string(const uint8_t *binary_data, size_t binary_len,
                                          char *hex_str, size_t hex_buf_size);
 static size_t n706a_hex_string_to_binary(const char *hex_str, size_t hex_len, 
                                          uint8_t *binary_buf, size_t binary_buf_size);
 
+/* 改进的AT响应处理函数 */
+static bool n706a_extract_complete_response(uint32_t *response_start, uint32_t *response_len);
+static bool n706a_classify_and_dispatch_urc_improved(const uint8_t *response, uint32_t response_len);
+static void n706a_remove_processed_data(uint32_t remove_len);
+
+/* 流式解析器函数(新方案) */
+static void n706a_stream_parser_init(void);
+static void n706a_stream_parser_reset(void);
+static void n706a_stream_parser_process_data(const uint8_t *data, uint32_t length);
+static bool n706a_stream_parser_match_urc_identifier(uint8_t byte, urc_type_t *type, const char **pattern, uint32_t *pattern_len);
+static void n706a_stream_parser_process_urc_header(const uint8_t *data, uint32_t length, uint32_t *consumed);
+static void n706a_stream_parser_process_urc_data(const uint8_t *data, uint32_t length, uint32_t *consumed);
+static void n706a_stream_parser_process_at_response(const uint8_t *data, uint32_t length, uint32_t *consumed);
+static void n706a_stream_parser_dispatch_urc(void);
+
 /**
  * @brief AT响应分类处理和分发
  * @param data 数据指针
@@ -103,20 +235,84 @@ static bool n706a_classify_and_dispatch_urc(const uint8_t *data, uint32_t length
     
     const char *data_str = (const char *)data;
     
-    PPP_DEBUG_DEBUG("[URC分类] Checking data block: length=%u, first 64 bytes: [%.*s]", 
-                   length, (length < 64) ? (int)length : 64, data_str);
+    /* 打印完整的HEX数据用于调试(如果数据较短) */
+    /* 优化:使用静态缓冲区,避免在栈上分配大数组 */
+    if (length <= 128) {
+        static char hex_str[257] = {0};  // 静态缓冲区,避免栈分配
+        /* 只转换前64字节以节省时间 */
+        uint32_t hex_len = (length < 64) ? length : 64;
+        for (uint32_t i = 0; i < hex_len; i++) {
+            uint8_t byte = data[i];
+            hex_str[i * 2] = "0123456789ABCDEF"[byte >> 4];
+            hex_str[i * 2 + 1] = "0123456789ABCDEF"[byte & 0x0F];
+        }
+        hex_str[hex_len * 2] = '\0';
+        PPP_DEBUG_DEBUG("[URC分类] Checking data block: length=%u, HEX=[%s], ASCII=[%.*s]", 
+                       length, hex_str, (length < 64) ? (int)length : 64, data_str);
+    } else {
+        PPP_DEBUG_DEBUG("[URC分类] Checking data block: length=%u, first 64 bytes: [%.*s]", 
+                       length, (length < 64) ? (int)length : 64, data_str);
+    }
     
     /* ========== 类别1:TCP数据接收URC(最高优先级,因为包含数据) ========== */
+    // 检查数据中是否包含+TCPRECV或+SSLTCPRECV(可能在数据中间,不一定是开头)
     if (length >= 10 && g_tcp_urc_callback) {
-        if (memcmp(data, "+TCPRECV: ", 10) == 0 || memcmp(data, "+SSLTCPRECV: ", 13) == 0) {
+        const char *tcprecv_pos = strstr(data_str, "+TCPRECV: ");
+        const char *ssltcprecv_pos = strstr(data_str, "+SSLTCPRECV: ");
+        
+        if (tcprecv_pos || ssltcprecv_pos) {
+            const char *urc_start = tcprecv_pos ? tcprecv_pos : ssltcprecv_pos;
+            // 查找URC结束位置(\r\n或\n或数据结束)
+            const char *urc_end = strstr(urc_start, "\r\n");
+            if (!urc_end) {
+                urc_end = strstr(urc_start, "\n");
+            }
+            if (!urc_end) {
+                urc_end = data_str + length;
+            }
+            
+            // 计算URC长度(包括数据部分,需要找到完整的数据)
+            // +TCPRECV格式:+TCPRECV: <socket_id>,<data_len>,<data>\r\n
+            // 需要解析data_len来确定完整URC长度
+            // 注意:urc_end可能只是URC头部的结束(第一个\r\n),实际URC包含数据部分
             int socket_id;
             size_t data_len;
             const uint8_t *tcp_data;
             
-            if (n706a_parse_tcprecv_urc((const char *)data, length, &socket_id, &data_len, &tcp_data)) {
+            // 先尝试解析URC头部(至少包含socket_id和data_len)
+            // 使用整个数据块长度来解析,让解析函数自己处理数据长度验证
+            // 但需要确保至少能解析到data_len
+            size_t parse_len = length - (urc_start - data_str);
+            if (n706a_parse_tcprecv_urc(urc_start, parse_len, &socket_id, &data_len, &tcp_data)) {
+                // 验证解析结果:tcp_data应该指向数据开始位置
+                // data_len是二进制数据长度,在HEX模式下,实际数据部分(HEX字符串)长度 = data_len × 2
+                // 完整URC长度 = (tcp_data - urc_start) + actual_data_len + 2(\r\n)
+                const char *data_start = (const char *)tcp_data;
+                // 在HEX模式下,实际数据长度是 data_len * 2(HEX字符串长度)
+                // 在ASCII模式下,实际数据长度是 data_len(ASCII字符串长度)
+                size_t actual_data_len = g_tcp_recv_hex_mode ? (data_len * 2) : data_len;
+                const char *data_end = data_start + actual_data_len;
+                const char *full_urc_end = strstr(data_end, "\r\n");
+                if (!full_urc_end) {
+                    full_urc_end = strstr(data_end, "\n");
+                }
+                if (!full_urc_end) {
+                    // 如果没有找到\r\n,检查是否到达数据块末尾
+                    full_urc_end = data_str + length;
+                }
+                
+                // 验证完整URC长度是否合理
+                size_t full_urc_len = full_urc_end - urc_start;
+                if (full_urc_len > parse_len) {
+                    // 如果完整URC长度超过当前数据块,说明数据不完整,使用当前数据块长度
+                    full_urc_len = parse_len;
+                }
+                
+                // 使用完整URC重新解析(确保数据完整)
+                if (n706a_parse_tcprecv_urc(urc_start, full_urc_len, &socket_id, &data_len, &tcp_data)) {
                 // 注意:如果使用HEX模式,tcp_data指向的是HEX字符串,需要转换为二进制
                 // 这里假设当前使用的是HEX模式(AT+RECVMODE=1,1)
-                // HEX字符串长度是实际数据长度的2倍
+                // data_len是二进制数据长度,HEX字符串长度 = data_len × 2
                 
                 // 使用双buffer ping-pong机制避免并发冲突
                 // 注意:解码后的数据会立即拷贝到URC buffer,所以hex buffer可以立即重用
@@ -127,43 +323,98 @@ static bool n706a_classify_and_dispatch_urc(const uint8_t *data, uint32_t length
                 size_t binary_data_len = data_len;
                 const uint8_t *binary_data = tcp_data;
                 
-                // 检查是否是HEX格式(HEX字符串长度应该是数据长度的2倍)
-                // 或者可以通过检查是否所有字符都是0-9A-F来判断
+                // 如果设置了HEX模式(AT+RECVMODE=1,1),强制使用HEX解码
+                // 否则,检查是否是HEX格式(通过检查所有字符是否都是0-9A-F来判断)
                 bool is_hex_format = false;
                 if (data_len > 0) {
-                    // 简单检查:如果数据长度是偶数,且所有字符都是十六进制字符,可能是HEX格式
-                    size_t hex_str_len = 0;
-                    const char *p = (const char *)tcp_data;
-                    while (hex_str_len < data_len && 
-                           ((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'F') || (*p >= 'a' && *p <= 'f'))) {
-                        p++;
-                        hex_str_len++;
-                    }
-                    
-                    // 如果所有字符都是HEX字符,且长度是2的倍数,认为是HEX格式
-                    if (hex_str_len == data_len && data_len % 2 == 0) {
-                        is_hex_format = true;
+                    // 如果设置了HEX模式,强制尝试HEX解码
+                    if (g_tcp_recv_hex_mode) {
+                        // HEX模式下,data_len是二进制数据长度,HEX字符串长度 = data_len × 2
+                        size_t hex_str_len = data_len * 2;
+                        
+                        // 检查实际HEX字符串长度(可能被截断)
+                        size_t actual_hex_str_len = 0;
+                        const char *p = (const char *)tcp_data;
+                        while (actual_hex_str_len < hex_str_len && 
+                               ((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'F') || (*p >= 'a' && *p <= 'f'))) {
+                            p++;
+                            actual_hex_str_len++;
+                        }
                         
-                        // 选择非活跃的buffer进行解码(ping-pong机制)
-                        // 由于解码后立即拷贝到URC buffer,active buffer可以立即切换
-                        int buffer_idx = hex_decode_buffer_active;
-                        uint8_t *decode_buffer = (buffer_idx == 0) ? hex_decode_buffer_0 : hex_decode_buffer_1;
+                        // 如果实际HEX字符串长度足够(至少是期望长度,或者接近期望长度),尝试HEX解码
+                        if (actual_hex_str_len >= hex_str_len || (actual_hex_str_len > 0 && actual_hex_str_len >= hex_str_len - 1)) {
+                            // 使用实际可用的HEX字符串长度(如果是奇数,截断最后一个字符)
+                            size_t hex_len_to_decode = (actual_hex_str_len % 2 == 0) ? actual_hex_str_len : (actual_hex_str_len - 1);
+                            
+                            if (hex_len_to_decode > 0 && hex_len_to_decode % 2 == 0) {
+                                is_hex_format = true;
+                                
+                                // 选择非活跃的buffer进行解码(ping-pong机制)
+                                // 由于解码后立即拷贝到URC buffer,active buffer可以立即切换
+                                int buffer_idx = hex_decode_buffer_active;
+                                uint8_t *decode_buffer = (buffer_idx == 0) ? hex_decode_buffer_0 : hex_decode_buffer_1;
+                                
+                                // 将HEX字符串转换为二进制
+                                binary_data_len = n706a_hex_string_to_binary((const char *)tcp_data, hex_len_to_decode,
+                                                                             decode_buffer, sizeof(hex_decode_buffer_0));
+                                if (binary_data_len > 0) {
+                                    binary_data = decode_buffer;
+                                    
+                                    // 切换active buffer(虽然不必须,但保持ping-pong逻辑)
+                                    // 注意:由于数据会立即拷贝到URC buffer,这里切换是安全的
+                                    hex_decode_buffer_active = 1 - hex_decode_buffer_active;
+                                    
+                                    if (hex_len_to_decode < hex_str_len) {
+                                        PPP_DEBUG_WARN("HEX decoded: binary_len=%u, expected_hex_len=%u, actual_hex_len=%u (truncated) -> decoded=%u bytes (buffer=%d, forced by HEX mode)", 
+                                                      (unsigned int)data_len, (unsigned int)hex_str_len, (unsigned int)hex_len_to_decode, 
+                                                      (unsigned int)binary_data_len, buffer_idx);
+                                    } else {
+                                        PPP_DEBUG_INFO("HEX decoded: binary_len=%u, hex_len=%u -> decoded=%u bytes (buffer=%d, forced by HEX mode)", 
+                                                      (unsigned int)data_len, (unsigned int)hex_str_len, (unsigned int)binary_data_len, buffer_idx);
+                                    }
+                                } else {
+                                    PPP_DEBUG_ERROR("Failed to decode HEX string (HEX mode enabled)");
+                                    is_hex_format = false;
+                                }
+                            } else {
+                                PPP_DEBUG_WARN("HEX mode enabled but HEX data length is invalid: expected_hex_len=%u, actual_hex_len=%u", 
+                                              (unsigned int)hex_str_len, (unsigned int)actual_hex_str_len);
+                            }
+                        } else {
+                            PPP_DEBUG_WARN("HEX mode enabled but HEX data too short: expected_hex_len=%u, actual_hex_len=%u", 
+                                          (unsigned int)hex_str_len, (unsigned int)actual_hex_str_len);
+                        }
+                    } else {
+                        // ASCII模式:检查是否是HEX格式(自动检测)
+                        size_t hex_str_len = 0;
+                        const char *p = (const char *)tcp_data;
+                        while (hex_str_len < data_len && 
+                               ((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'F') || (*p >= 'a' && *p <= 'f'))) {
+                            p++;
+                            hex_str_len++;
+                        }
                         
-                        // 将HEX字符串转换为二进制
-                        binary_data_len = n706a_hex_string_to_binary((const char *)tcp_data, data_len,
-                                                                     decode_buffer, sizeof(hex_decode_buffer_0));
-                        if (binary_data_len > 0) {
-                            binary_data = decode_buffer;
+                        // 如果所有字符都是HEX字符,且长度是2的倍数,认为是HEX格式
+                        if (hex_str_len == data_len && data_len % 2 == 0) {
+                            is_hex_format = true;
                             
-                            // 切换active buffer(虽然不必须,但保持ping-pong逻辑)
-                            // 注意:由于数据会立即拷贝到URC buffer,这里切换是安全的
-                            hex_decode_buffer_active = 1 - hex_decode_buffer_active;
+                            // 选择非活跃的buffer进行解码(ping-pong机制)
+                            int buffer_idx = hex_decode_buffer_active;
+                            uint8_t *decode_buffer = (buffer_idx == 0) ? hex_decode_buffer_0 : hex_decode_buffer_1;
                             
-                            PPP_DEBUG_DEBUG("HEX decoded: hex_len=%u -> binary_len=%u (buffer=%d)", 
-                                          (unsigned int)data_len, (unsigned int)binary_data_len, buffer_idx);
-                        } else {
-                            PPP_DEBUG_ERROR("Failed to decode HEX string");
-                            is_hex_format = false;
+                            // 将HEX字符串转换为二进制
+                            binary_data_len = n706a_hex_string_to_binary((const char *)tcp_data, data_len,
+                                                                         decode_buffer, sizeof(hex_decode_buffer_0));
+                            if (binary_data_len > 0) {
+                                binary_data = decode_buffer;
+                                hex_decode_buffer_active = 1 - hex_decode_buffer_active;
+                                
+                                PPP_DEBUG_DEBUG("HEX decoded: hex_len=%u -> binary_len=%u (buffer=%d, auto-detected)", 
+                                              (unsigned int)data_len, (unsigned int)binary_data_len, buffer_idx);
+                            } else {
+                                PPP_DEBUG_ERROR("Failed to decode HEX string");
+                                is_hex_format = false;
+                            }
                         }
                     }
                 }
@@ -176,13 +427,19 @@ static bool n706a_classify_and_dispatch_urc(const uint8_t *data, uint32_t length
                     // 立即回调应用层(在中断上下文或处理线程中)
                     g_tcp_urc_callback(socket_id, binary_data, binary_data_len);
                     
-                    // URC已处理,不写入AT响应缓冲区
-                    return true;
+                    // URC已处理,如果是独立URC则不写入缓冲区,否则继续(混合响应)
+                    if (urc_start == data_str && full_urc_end >= data_str + length - 2) {
+                        return true;  // 独立URC,不写入缓冲区
+                    }
+                    // 混合响应:URC已处理,但继续处理其他部分(如OK等)
                 } else {
                     PPP_DEBUG_WARN("Invalid TCP URC data length");
                 }
+                } else {
+                    PPP_DEBUG_WARN("Failed to parse +TCPRECV URC (second parse)");
+                }
             } else {
-                PPP_DEBUG_WARN("Failed to parse +TCPRECV URC");
+                PPP_DEBUG_WARN("Failed to parse +TCPRECV URC (first parse)");
             }
         }
     }
@@ -261,35 +518,138 @@ static bool n706a_classify_and_dispatch_urc(const uint8_t *data, uint32_t length
     }
     
     /* ========== 类别3:TCP发送结果URC ========== */
-    if (length >= 10 && g_tcpsend_urc_callback) {
+    if (length >= 10) {
         const char *tcpsend_pos = strstr(data_str, "+TCPSEND: ");
         const char *ssltcpend_pos = strstr(data_str, "+SSLTCPEND: ");
         
         if (tcpsend_pos || ssltcpend_pos) {
             const char *urc_start = tcpsend_pos ? tcpsend_pos : ssltcpend_pos;
+            
+            // 关键改进:检查URC前面是否有命令回显(AT+)
+            // 如果URC前面有命令回显,说明这是命令响应的一部分,不是纯异步URC
+            // 纯异步URC应该以 \r\n 开头,或者直接以 + 开头(没有AT+回显)
+            bool is_pure_async_urc = true;
+            if (urc_start > data_str) {
+                // 检查URC前面是否有 "AT+" 字符串(命令回显)
+                const char *before_urc = urc_start - 1;
+                // 向前查找,看是否有 "AT+" 在同一个响应块中
+                while (before_urc >= data_str && (*before_urc == ' ' || *before_urc == '\r' || *before_urc == '\n')) {
+                    before_urc--;
+                }
+                // 检查是否以 "AT+" 结尾(说明前面有命令回显)
+                if (before_urc >= data_str + 2) {
+                    if (before_urc[-2] == 'A' && before_urc[-1] == 'T' && *before_urc == '+') {
+                        // 找到命令回显,这不是纯异步URC
+                        is_pure_async_urc = false;
+                        PPP_DEBUG_DEBUG("[URC分类] +TCPSEND URC前面有命令回显,可能是命令响应的一部分");
+                    }
+                }
+            }
+            
+            PPP_DEBUG_INFO("[URC分类] +TCPSEND URC candidate found at offset %u (pure_async=%d)", 
+                          (unsigned int)(urc_start - data_str), is_pure_async_urc);
+            
+            // 查找URC结束符:支持 \r\n, \r\r, \n
             const char *urc_end = strstr(urc_start, "\r\n");
             if (!urc_end) {
-                urc_end = strstr(urc_start, "\n");
+                // 检查 \r\r 格式(异常格式,但需要容错处理)
+                const char *cr_cr = strstr(urc_start, "\r\r");
+                if (cr_cr) {
+                    urc_end = cr_cr + 1;  // 指向第二个 \r
+                    PPP_DEBUG_WARN("[URC分类] +TCPSEND URC以 \\r\\r 结尾(异常格式,可能是模组固件问题)");
+                } else {
+                    urc_end = strstr(urc_start, "\n");
+                }
             }
             if (!urc_end) {
                 urc_end = data_str + length;
             }
             
             size_t urc_len = urc_end - urc_start;
+            PPP_DEBUG_INFO("[URC分类] +TCPSEND URC candidate: urc_len=%u, content=[%.*s]", 
+                          (unsigned int)urc_len, (int)(urc_len < 64 ? urc_len : 64), urc_start);
+            
             int socket_id;
             size_t sent_bytes;
             if (n706a_parse_tcpsend_urc(urc_start, urc_len, &socket_id, &sent_bytes)) {
-                PPP_DEBUG_INFO("[URC分类] +TCPSEND URC detected: socket=%d, sent=%u", socket_id, (unsigned int)sent_bytes);
-                g_tcpsend_urc_callback(socket_id, sent_bytes);
-                // URC已处理,如果是独立URC则不写入缓冲区
-                if (urc_start == data_str && urc_end >= data_str + length - 2) {
-                    return true;
+                PPP_DEBUG_INFO("[URC分类] +TCPSEND URC detected: socket=%d, sent=%u (pure_async=%d)", 
+                              socket_id, (unsigned int)sent_bytes, is_pure_async_urc);
+                
+                // 如果是纯异步URC(没有命令回显),立即处理并返回
+                // 如果是命令响应中的URC(有命令回显),需要检查是否还有其他内容
+                if (is_pure_async_urc) {
+                    // 纯异步URC,立即处理
+                    if (g_tcpsend_urc_callback) {
+                        g_tcpsend_urc_callback(socket_id, sent_bytes);
+                    } else {
+                        PPP_DEBUG_WARN("[URC分类] +TCPSEND URC detected but callback not registered!");
+                    }
+                    // 如果是独立URC(整个响应块就是这个URC),不写入缓冲区
+                    if (urc_start == data_str || (urc_start > data_str && 
+                        (urc_start[-1] == '\r' || urc_start[-1] == '\n'))) {
+                        // URC前面只有空白字符或换行符,是独立的异步URC
+                        if (urc_end >= data_str + length - 2) {
+                            return true;  // 独立URC,已处理,不写入缓冲区
+                        }
+                    }
+                } else {
+                    // 命令响应中的URC,需要写入缓冲区(因为可能还有OK等)
+                    PPP_DEBUG_DEBUG("[URC分类] +TCPSEND URC是命令响应的一部分,需要写入缓冲区");
+                    if (g_tcpsend_urc_callback) {
+                        g_tcpsend_urc_callback(socket_id, sent_bytes);
+                    }
+                    // 继续处理,让后续逻辑写入缓冲区
                 }
+            } else {
+                PPP_DEBUG_WARN("[URC分类] +TCPSEND URC found but parse failed, urc_len=%u", (unsigned int)urc_len);
+                // 输出原始URC内容用于调试
+                char debug_urc[128] = {0};
+                size_t debug_len = (urc_len < sizeof(debug_urc) - 1) ? urc_len : (sizeof(debug_urc) - 1);
+                memcpy(debug_urc, urc_start, debug_len);
+                PPP_DEBUG_WARN("[URC分类] Raw +TCPSEND URC content: [%.*s]", (int)debug_len, debug_urc);
+            }
+        } else {
+            // 检查是否包含 "TCPSEND" 字符串(可能是命令响应的一部分)
+            if (strstr(data_str, "TCPSEND") != NULL) {
+                PPP_DEBUG_DEBUG("[URC分类] Data contains 'TCPSEND' but not URC format (likely AT command response)");
+            }
+        }
+    }
+    
+    /* ========== 类别4:HTTP响应URC(+HTTPRECV: / +HTTPSRECV:) ========== */
+    if (length >= 11) {
+        const char *httprecv_pos = strstr(data_str, "+HTTPRECV: ");
+        const char *httpsrecv_pos = strstr(data_str, "+HTTPSRECV: ");
+        
+        if (httprecv_pos || httpsrecv_pos) {
+            /* HTTP响应URC格式:+HTTPRECV: <len>,<data> 或 +HTTPSRECV: <len>,<data> */
+            /* 注意:HTTP响应数据可能很大,需要完整读取 */
+            const char *urc_start = httprecv_pos ? httprecv_pos : httpsrecv_pos;
+            bool is_https = (httpsrecv_pos != NULL);
+            
+            /* 查找URC结束位置(可能是\r\n或数据结束) */
+            /* HTTP响应数据可能包含二进制数据,不能简单用\r\n判断结束 */
+            /* 需要解析URC格式:+HTTPRECV: <len>,<data> */
+            size_t declared_len, actual_len;
+            const uint8_t *http_data;
+            
+            if (n706a_parse_httprecv_urc(urc_start, length - (urc_start - data_str), &declared_len, &actual_len, &http_data)) {
+                PPP_DEBUG_INFO("[URC分类] HTTP URC detected: %s, declared_len=%u, actual_len=%u", 
+                              is_https ? "+HTTPSRECV" : "+HTTPRECV", (unsigned int)declared_len, (unsigned int)actual_len);
+                
+                /* 调用HTTP客户端URC回调,使用 actual_len(实际可用数据长度) */
+                extern void n706a_http_on_urc_received(const uint8_t *data, size_t length, bool is_https);
+                n706a_http_on_urc_received(http_data, actual_len, is_https);
+                
+                /* HTTP URC已处理,不写入AT响应缓冲区 */
+                return true;
+            } else {
+                PPP_DEBUG_WARN("[URC分类] HTTP URC found but parse failed");
             }
         }
     }
     
-    /* ========== 类别4:网络状态URC ========== */
+    /* ========== 类别5:网络状态URC ========== */
     if (length >= 6 && g_network_status_urc_callback) {
         const char *creg_pos = strstr(data_str, "+CREG: ");
         const char *cgreg_pos = strstr(data_str, "+CGREG: ");
@@ -337,1588 +697,5870 @@ static bool n706a_classify_and_dispatch_urc(const uint8_t *data, uint32_t length
 }
 
 /**
- * @brief 写入AT响应数据到环形缓冲区(由ppp_interrupt.c调用)
- * @param data 数据指针
- * @param length 数据长度
+ * @brief 从累积缓冲区中移除已处理的数据
+ * @param remove_len 要移除的数据长度
  */
-void n706a_at_response_buffer_write(const uint8_t *data, uint32_t length)
+static void n706a_remove_processed_data(uint32_t remove_len)
 {
-    if (!data || length == 0) {
+    if (remove_len == 0 || remove_len > g_at_accumulate_length) {
         return;
     }
     
-    /* ========== 第一步:分类处理和分发URC ========== */
-    /* 如果返回true,说明是URC已处理,不需要写入缓冲区 */
-    bool is_urc = n706a_classify_and_dispatch_urc(data, length);
-    if (is_urc) {
-        // URC已通过callback处理,不写入AT响应缓冲区
-        return;
-    }
-    
-    /* ========== 第二步:普通AT响应写入环形缓冲区 ========== */
-    osal_mutex_lock(&g_at_response_mutex);
+    osal_mutex_lock(&g_at_accumulate_mutex);
     
-    for (uint32_t i = 0; i < length; i++) {
-        g_at_response_ring_buffer[g_at_response_head] = data[i];
-        g_at_response_head = (g_at_response_head + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
-        
-        // 检查溢出(缓冲区满时丢弃最老的数据)
-        if (g_at_response_head == g_at_response_tail) {
-            g_at_response_tail = (g_at_response_tail + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
-            PPP_DEBUG_WARN("AT response buffer overflow, dropping oldest byte");
-        }
+    // 将剩余数据向前移动
+    if (remove_len < g_at_accumulate_length) {
+        memmove(g_at_accumulate_buffer, 
+                g_at_accumulate_buffer + remove_len,
+                g_at_accumulate_length - remove_len);
     }
     
-    osal_mutex_unlock(&g_at_response_mutex);
+    g_at_accumulate_length -= remove_len;
     
-    // 通知等待线程有新数据
-    osal_sem_up(&g_at_response_sem);
+    osal_mutex_unlock(&g_at_accumulate_mutex);
     
-    PPP_DEBUG_DEBUG("AT response buffer: wrote %u bytes", length);
+    PPP_DEBUG_DEBUG("[累积缓冲区] 移除 %u 字节,剩余 %u 字节", remove_len, g_at_accumulate_length);
 }
 
 /**
- * @brief 初始化AT命令处理模块
+ * @brief 清空累积缓冲区(用于socket关闭后清理残留数据)
  */
-ppp_error_t ppp_at_handler_init(uint8_t uart_bus, uint32_t current_baudrate, bool auto_baudrate_mode)
+void n706a_at_clear_accumulate_buffer(void)
 {
-    if (g_at_ctx.initialized) {
-        PPP_DEBUG_WARN("AT handler already initialized");
-        return PPP_ERR_OK;
-    }
-
-    g_at_ctx.uart_bus = uart_bus;
-    g_at_ctx.current_baudrate = current_baudrate;
-    g_at_ctx.auto_baudrate_mode = auto_baudrate_mode;
-
-    /* 创建AT命令互斥锁 */
-    if (osal_mutex_init(&g_at_ctx.at_mutex) != OSAL_SUCCESS) {
-        PPP_DEBUG_ERROR("Failed to create AT mutex");
-        return PPP_ERR_MEMORY;
-    }
-
-    /* 创建AT响应缓冲区互斥锁 */
-    if (osal_mutex_init(&g_at_response_mutex) != OSAL_SUCCESS) {
-        PPP_DEBUG_ERROR("Failed to create AT response mutex");
-        osal_mutex_destroy(&g_at_ctx.at_mutex);
-        return PPP_ERR_MEMORY;
-    }
-
-    /* 创建AT响应信号量 */
-    if (osal_sem_init(&g_at_response_sem, 0) != OSAL_SUCCESS) {
-        PPP_DEBUG_ERROR("Failed to create AT response semaphore");
-        osal_mutex_destroy(&g_at_ctx.at_mutex);
-        osal_mutex_destroy(&g_at_response_mutex);
-        return PPP_ERR_MEMORY;
-    }
-
-    /* 初始化环形缓冲区 */
-    g_at_response_head = 0;
-    g_at_response_tail = 0;
-    memset(g_at_response_ring_buffer, 0, sizeof(g_at_response_ring_buffer));
-
-    /* 分配全局AT响应缓存 */
-    g_at_response_buffer = (uint8_t *)malloc(g_at_buffer_size);
-    if (g_at_response_buffer == NULL) {
-        PPP_DEBUG_ERROR("Failed to allocate AT response buffer");
-        osal_mutex_destroy(&g_at_ctx.at_mutex);
-        osal_mutex_destroy(&g_at_response_mutex);
-        osal_sem_destroy(&g_at_response_sem);
-        return PPP_ERR_MEMORY;
-    }
-
-    g_at_ctx.initialized = true;
-    PPP_DEBUG_INFO("n706a_net AT handler initialized on UART%d with async ring buffer", uart_bus);
+    osal_mutex_lock(&g_at_accumulate_mutex);
+    uint32_t old_length = g_at_accumulate_length;
+    g_at_accumulate_length = 0;
+    g_at_accumulate_last_update_time = 0;
+    osal_mutex_unlock(&g_at_accumulate_mutex);
     
-    return PPP_ERR_OK;
+    if (old_length > 0) {
+        PPP_DEBUG_DEBUG("[累积缓冲区] 已清空(socket关闭后清理),清除了 %u 字节残留数据", old_length);
+    }
 }
 
 /**
- * @brief 反初始化AT命令处理模块
+ * @brief 从累积缓冲区中提取一个完整的AT指令响应
+ * @param response_start 输出:响应在缓冲区中的起始位置(偏移量)
+ * @param response_len 输出:响应长度
+ * @return true=找到完整响应,false=数据不完整
+ * 
+ * AT响应格式:
+ * - 普通AT响应: "AT+CMD\r\n\r\n+RESPONSE\r\nOK\r\n"
+ * - URC响应: "\r\n+URC: data\r\n"
+ * - 提示符响应: "AT+TCPSEND=0,100\r\n\r\n> "
+ * 
+ * 响应头识别:
+ * - 以 "\r\n" 开头(回显的命令或URC)
+ * - 以 "+" 开头(URC)
+ * - 以 "AT" 开头(回显的命令)
+ * 
+ * 响应尾识别:
+ * - "\r\nOK\r\n" 或 "\r\nOK"
+ * - "\r\nERROR\r\n" 或 "\r\nERROR"
+ * - ">\r\n" 或 "> "(提示符)
+ * - "\r\n+CME ERROR: ...\r\n"
+ * - "\r\n+CMS ERROR: ...\r\n"
  */
-void ppp_at_handler_deinit(void)
+static bool n706a_extract_complete_response(uint32_t *response_start, uint32_t *response_len)
 {
-    if (!g_at_ctx.initialized) {
-        return;
+    if (!response_start || !response_len || g_at_accumulate_length == 0) {
+        return false;
     }
-
-    /* 销毁互斥锁和信号量 */
-    osal_mutex_destroy(&g_at_ctx.at_mutex);
-    osal_mutex_destroy(&g_at_response_mutex);
-    osal_sem_destroy(&g_at_response_sem);
-
-    /* 释放全局AT响应缓存 */
-    if (g_at_response_buffer) {
-        free(g_at_response_buffer);
-        g_at_response_buffer = NULL;
+    
+    osal_mutex_lock(&g_at_accumulate_mutex);
+    
+    const char *buffer = (const char *)g_at_accumulate_buffer;
+    uint32_t length = g_at_accumulate_length;
+    
+    // 查找响应头(只跳过开头的 \x00 字节,保留 \r、\n 和空格)
+    // 注意:
+    // 1. \x00 字节需要被跳过,因为它们会干扰 strstr 等字符串函数
+    // 2. \r、\n 是 AT 协议的重要分隔符,用于识别行边界(如 \r\nOK\r\n)
+    // 3. 空格也是 AT 协议的一部分,用于提示符识别(如 "> " 是 AT+TCPSEND 的提示符)
+    //    根据 AT 命令手册,AT+TCPSEND 的响应格式是:<CR><LF>> <CR><content>
+    //    其中 "> " 后面的空格是提示符的一部分,必须保留
+    uint32_t start = 0;
+    while (start < length && buffer[start] == '\0') {
+        start++;
     }
-
-    /* 清空环形缓冲区 */
-    g_at_response_head = 0;
-    g_at_response_tail = 0;
-
-    memset_s(&g_at_ctx, sizeof(g_at_ctx), 0, sizeof(g_at_ctx));
-    PPP_DEBUG_INFO("n706a_net AT handler deinitialized");
-}
-
-/**
- * @brief 执行波特率自适应握手
- *
- * 进入自适应波特率,证明1000000没有正常工作,需要先reset,等5秒,开始设置自动握手的第一个波特率,
- * 读取所有缓存数据丢掉,下达at指令,等待结果,重试3次,如果不成功,就设置第二个波特率,读取所有缓存数据丢掉,
- * 下达at指令,等结果,重试3次,直至第三个波特率检测。如果都没有成功,则返回失败。
- * 如果任何一次at指令返回正确ok,直接在当前设置下,下达指 AT+IPR 配置 1000000 波特率,并返回成功。
- * 设置当前波特率为1000000,再次reset 模组,让其自动工作在 1000000波特率下。
- */
-ppp_error_t ppp_at_baudrate_handshake(void)
-{
-    if (!g_at_ctx.initialized) {
-        return PPP_ERR_INVALID_PARAM;
+    
+    if (start >= length) {
+        // 只有 \x00 字节,保留在缓冲区中(可能是不完整的响应的一部分)
+        // 不清除,等待更多数据到来
+        osal_mutex_unlock(&g_at_accumulate_mutex);
+        return false;
     }
-
-    PPP_DEBUG_INFO("Starting baudrate handshake (prove %d not working, then try auto handshake)",g_at_ctx.current_baudrate);
-
-    // uint8_t dummy_buffer[256]={0};
-
-    // 2. 依次尝试自动波特率列表
-    for (int i = 0; i < PPP_BAUDRATE_AUTO_COUNT; i++) {
-        uint32_t baudrate = g_auto_baudrates[i];
-        PPP_DEBUG_DEBUG("=== Trying Baudrate %u (index %d/%d) ===", baudrate, i + 1, PPP_BAUDRATE_AUTO_COUNT);
-
-        // 配置串口波特率
-        uart_attr_t uart_attr = {
-            .baud_rate = baudrate,
-            .data_bits = UART_DATA_BIT_8,
-            .parity = UART_PARITY_NONE,
-            .stop_bits = UART_STOP_BIT_1,
-        };
-        if (uapi_uart_set_attr(g_at_ctx.uart_bus, &uart_attr) != ERRCODE_SUCC) {
-            PPP_DEBUG_WARN("Failed to set baudrate %u", baudrate);
-            continue;
+    
+    // 跳过 \x00 后,保留所有其他字符(包括 \r、\n、空格),因为它们都是协议的一部分
+    // 例如:\r\n+TCPRECV: ... 或 \r\nOK\r\n 或 \r\n> \r\n 等格式
+    
+    // 查找响应尾(从start开始查找终止符)
+    uint32_t end = start;
+    bool found_end = false;
+    
+    
+    // 特殊处理:检查是否是包含数据的URC(如+TCPRECV, +HTTPRECV等)
+    // 这些URC的格式是:+URC: <params>,<data_len>,<data>\r\n
+    // 需要先解析data_len来确定完整响应长度
+    // 注意:由于响应开头可能有 \x00 字节,strstr 可能失败,需要手动查找
+    const char *tcprecv_pos = NULL;
+    const char *ssltcprecv_pos = NULL;
+    const char *httprecv_pos = NULL;
+    const char *httpsrecv_pos = NULL;
+    const char *tcpsend_pos = NULL;
+    const char *ssltcpend_pos = NULL;
+    
+    // 手动查找 URC,跳过 \x00 字节
+    for (uint32_t i = start; i < length; i++) {
+        if (buffer[i] == '\0') {
+            continue;  // 跳过 \x00 字节
         }
-        PPP_DEBUG_DEBUG("UART configured for baudrate %u", baudrate);
-
-        // 清空接收缓冲区
-        // PPP_DEBUG_DEBUG("Clearing UART RX buffer before AT test...");
-        // int clear_cnt = 0;
-        // memset(dummy_buffer,0,sizeof(dummy_buffer));
-        // while (uapi_uart_read(g_at_ctx.uart_bus, dummy_buffer, sizeof(dummy_buffer), 100) > 0) {
-        //     clear_cnt++;
-        // }
-        // if (clear_cnt > 0) {
-        //     PPP_DEBUG_DEBUG("Cleared %d RX buffer rounds", clear_cnt);
-        // }
-
-        // 3次重试AT
-        ppp_error_t ret = PPP_ERR_AT_FAILED;
-        for (int retry = 0; retry < 2; retry++) {
-            PPP_DEBUG_DEBUG("--- AT Test at %u baud, retry %d/3 ---", baudrate, retry + 1);
-
-            // 再次清空缓冲区,防止上次残留
-        // memset(dummy_buffer,0,sizeof(dummy_buffer));
-        // while (uapi_uart_read(g_at_ctx.uart_bus, dummy_buffer, sizeof(dummy_buffer), 100) > 0) {}
-
-            ppp_at_response_t response;
-            ret = ppp_at_send_command("AT", "OK", 1000, &response);
-
-            if (ret == PPP_ERR_OK) {
-                PPP_DEBUG_INFO("AT command successful at baudrate %u", baudrate);
-
-                // 在当前波特率下,设置AT+IPR=1000000
-               ret = ppp_at_set_baudrate(g_at_ctx.current_baudrate);
-               if (ret != PPP_ERR_OK) {
+        // 检查是否匹配 "+TCPRECV: "
+        if (i + 10 <= length && strncmp(&buffer[i], "+TCPRECV: ", 10) == 0) {
+            tcprecv_pos = &buffer[i];
+            break;
+        }
+        // 检查是否匹配 "+SSLTCPRECV: "
+        if (i + 13 <= length && strncmp(&buffer[i], "+SSLTCPRECV: ", 13) == 0) {
+            ssltcprecv_pos = &buffer[i];
+            break;
+        }
+        // 检查是否匹配 "+HTTPRECV: "
+        if (i + 11 <= length && strncmp(&buffer[i], "+HTTPRECV: ", 11) == 0) {
+            httprecv_pos = &buffer[i];
+            break;
+        }
+        // 检查是否匹配 "+HTTPSRECV: "
+        if (i + 12 <= length && strncmp(&buffer[i], "+HTTPSRECV: ", 12) == 0) {
+            httpsrecv_pos = &buffer[i];
+            break;
+        }
+        // 检查是否匹配 "+TCPSEND: "
+        if (i + 10 <= length && strncmp(&buffer[i], "+TCPSEND: ", 10) == 0) {
+            tcpsend_pos = &buffer[i];
+            break;
+        }
+        // 检查是否匹配 "+SSLTCPEND: "
+        if (i + 12 <= length && strncmp(&buffer[i], "+SSLTCPEND: ", 12) == 0) {
+            ssltcpend_pos = &buffer[i];
+            break;
+        }
+    }
+    
+    // 如果手动查找没找到,使用 strstr 作为后备(处理正常情况,没有 \x00 字节)
+    if (!tcprecv_pos && !ssltcprecv_pos && !httprecv_pos && !httpsrecv_pos && !tcpsend_pos && !ssltcpend_pos) {
+        tcprecv_pos = strstr(&buffer[start], "+TCPRECV: ");
+        ssltcprecv_pos = strstr(&buffer[start], "+SSLTCPRECV: ");
+        httprecv_pos = strstr(&buffer[start], "+HTTPRECV: ");
+        httpsrecv_pos = strstr(&buffer[start], "+HTTPSRECV: ");
+        tcpsend_pos = strstr(&buffer[start], "+TCPSEND: ");
+        ssltcpend_pos = strstr(&buffer[start], "+SSLTCPEND: ");
+    }
+    
+    if (tcprecv_pos || ssltcprecv_pos) {
+        // +TCPRECV格式:+TCPRECV: <socket_id>,<data_len>,<data>\r\n
+        const char *urc_start = tcprecv_pos ? tcprecv_pos : ssltcprecv_pos;
+        
+        // ⚠️ 关键:验证 urc_start 是否在 buffer 范围内(这是导致 data_len 解析错误的根本原因)
+        const char *buffer_end = buffer + length;
+        if (urc_start < buffer || urc_start >= buffer_end) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 根本原因:urc_start不在缓冲区范围内!urc_start=%p, buffer=%p, buffer_end=%p, length=%u", 
+                          (void *)urc_start, (void *)buffer, (void *)buffer_end, length);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        
+        // 计算 parse_len(现在可以安全计算,因为 urc_start 已验证在范围内)
+        uint32_t urc_start_offset = urc_start - buffer;
+        if (urc_start_offset > length) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 根本原因:urc_start_offset > length!urc_start_offset=%u, length=%u", 
+                          urc_start_offset, length);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        size_t parse_len = length - urc_start_offset;
+        
+        // ⚠️ 关键:验证 parse_len 是否合理(最大64KB)
+        const size_t MAX_REASONABLE_PARSE_LEN = 65536;
+        if (parse_len > MAX_REASONABLE_PARSE_LEN) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 根本原因:parse_len异常大!parse_len=%u (max=%u), urc_start_offset=%u, length=%u", 
+                          (unsigned int)parse_len, MAX_REASONABLE_PARSE_LEN, urc_start_offset, length);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        
+        int socket_id;
+        size_t data_len;
+        const uint8_t *tcp_data;
+        
+        // 尝试解析URC头部,获取data_len
+        if (n706a_parse_tcprecv_urc(urc_start, parse_len, &socket_id, &data_len, &tcp_data)) {
+            // 计算完整URC长度:URC头部 + 数据部分 + \r\n
+            // 注意:data_len是二进制数据长度,在HEX模式下,实际数据部分(HEX字符串)长度 = data_len × 2
+            const char *data_start = (const char *)tcp_data;
+            uint32_t urc_header_len = data_start - urc_start;
+            
+            // ⚠️ 关键:验证 data_len 的合理性,防止溢出或异常大的值
+            const uint32_t MAX_REASONABLE_DATA_LEN = 65536;  // 最大合理的数据长度(64KB)
+            if (data_len > MAX_REASONABLE_DATA_LEN) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ data_len异常大:%u (max=%u),可能是解析错误!截断到最大值", 
+                              (unsigned int)data_len, MAX_REASONABLE_DATA_LEN);
+                data_len = MAX_REASONABLE_DATA_LEN;
+            }
+            
+            // 在HEX模式下,实际数据长度是 data_len * 2(HEX字符串长度)
+            // 在ASCII模式下,实际数据长度是 data_len(ASCII字符串长度)
+            // 为了兼容两种模式,使用 data_len * 2 作为最大可能长度(HEX模式)
+            // 如果实际数据不足,会在后续的strstr查找中处理
+            uint32_t actual_data_len = g_tcp_recv_hex_mode ? (data_len * 2) : data_len;
+            
+            // ⚠️ 关键:检查 actual_data_len 是否溢出
+            if (g_tcp_recv_hex_mode && data_len > (UINT32_MAX / 2)) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ actual_data_len计算溢出:data_len=%u, actual_data_len=%u (max=%u)", 
+                              (unsigned int)data_len, (unsigned int)actual_data_len, UINT32_MAX / 2);
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+            
+            uint32_t full_urc_len = urc_header_len + actual_data_len;
+            uint32_t urc_start_offset = urc_start - buffer;
+            
+            // ⚠️ 关键:检查 full_urc_len 是否溢出
+            if (full_urc_len < urc_header_len || full_urc_len < actual_data_len) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ full_urc_len计算溢出:urc_header_len=%u, actual_data_len=%u, full_urc_len=%u", 
+                              urc_header_len, (unsigned int)actual_data_len, full_urc_len);
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+            
+            // 检查数据部分是否完整(需要的数据长度是否在当前缓冲区中)
+            if (urc_start_offset + full_urc_len > length) {
+                // 数据不完整,等待更多数据
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                PPP_DEBUG_DEBUG("[提取完整响应] +TCPRECV数据不完整,等待更多数据 (需要=%u, 当前可用=%u, binary_len=%u, hex_mode=%d)", 
+                               full_urc_len, length - urc_start_offset, (unsigned int)data_len, g_tcp_recv_hex_mode);
+                return false;
+            }
+            
+            // 查找数据后的\r\n(使用实际数据长度)
+            // ⚠️ 关键:验证 data_end 是否在缓冲区范围内
+            const char *buffer_end = buffer + length;
+            const char *data_end = data_start + actual_data_len;
+            
+            // 验证 data_end 是否超出缓冲区范围
+            if (data_end > buffer_end) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:data_end超出缓冲区范围!data_start=%p, actual_data_len=%u, data_end=%p, buffer_end=%p, length=%u", 
+                              (void *)data_start, (unsigned int)actual_data_len, (void *)data_end, (void *)buffer_end, length);
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+            
+            // 验证 data_start 是否在缓冲区范围内
+            if (data_start < buffer || data_start >= buffer_end) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:data_start不在缓冲区范围内!data_start=%p, buffer=%p, buffer_end=%p", 
+                              (void *)data_start, (void *)buffer, (void *)buffer_end);
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+            
+            // 限制 strstr 搜索范围,避免越界
+            size_t search_len = buffer_end - data_end;
+            if (search_len > 1024) {
+                search_len = 1024;  // 限制搜索范围,避免搜索过大区域
+            }
+            
+            const char *urc_end = NULL;
+            if (search_len >= 2) {
+                // 手动搜索 \r\n,限制在缓冲区范围内
+                for (size_t i = 0; i <= search_len - 2; i++) {
+                    if (data_end[i] == '\r' && data_end[i + 1] == '\n') {
+                        urc_end = data_end + i;
+                        break;
+                    }
+                }
+            }
+            
+            if (!urc_end && search_len >= 1) {
+                // 手动搜索 \n
+                for (size_t i = 0; i < search_len; i++) {
+                    if (data_end[i] == '\n') {
+                        urc_end = data_end + i;
+                        break;
+                    }
+                }
+            }
+            
+            if (!urc_end) {
+                // 数据后的\r\n不在当前缓冲区中,等待更多数据
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                PPP_DEBUG_DEBUG("[提取完整响应] +TCPRECV数据后的\\r\\n不完整,等待更多数据 (data_end=%p, buffer_end=%p, search_len=%u)", 
+                              (void *)data_end, (void *)buffer_end, (unsigned int)search_len);
+                return false;
+            }
+            
+            // 验证 urc_end 是否在缓冲区范围内
+            if (urc_end < buffer || urc_end >= buffer_end) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:urc_end不在缓冲区范围内!urc_end=%p, buffer=%p, buffer_end=%p", 
+                              (void *)urc_end, (void *)buffer, (void *)buffer_end);
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+            
+            // 找到完整URC,计算 end(验证计算结果)
+            uint32_t urc_end_offset = urc_end - buffer;
+            if (urc_end_offset + 2 > length) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:urc_end_offset+2超出缓冲区长度!urc_end_offset=%u, length=%u", 
+                              urc_end_offset, length);
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+            
+            end = urc_end_offset + 2;  // 包括\r\n
+            found_end = true;
+        }
+    } else if (httprecv_pos || httpsrecv_pos) {
+        // +HTTPRECV格式:+HTTPRECV: <data_len>,<data>
+        const char *urc_start = httprecv_pos ? httprecv_pos : httpsrecv_pos;
+        
+        // ⚠️ 关键:验证 urc_start 是否在 buffer 范围内
+        const char *buffer_end = buffer + length;
+        if (urc_start < buffer || urc_start >= buffer_end) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 根本原因:+HTTPRECV urc_start不在缓冲区范围内!urc_start=%p, buffer=%p, buffer_end=%p", 
+                          (void *)urc_start, (void *)buffer, (void *)buffer_end);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        
+        uint32_t urc_start_offset = urc_start - buffer;
+        if (urc_start_offset > length) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 根本原因:+HTTPRECV urc_start_offset > length!urc_start_offset=%u, length=%u", 
+                          urc_start_offset, length);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        size_t parse_len = length - urc_start_offset;
+        
+        size_t declared_len, actual_len;
+        const uint8_t *http_data;
+        
+        // 尝试解析URC,获取 declared_len 和 actual_len
+        if (n706a_parse_httprecv_urc(urc_start, parse_len, &declared_len, &actual_len, &http_data)) {
+            // 计算完整URC长度:URC头部 + 数据部分(使用 actual_len,实际可用数据长度)
+            const char *data_start = (const char *)http_data;
+            uint32_t urc_header_len = data_start - urc_start;
+            uint32_t full_urc_len = urc_header_len + actual_len;
+            uint32_t urc_start_offset = urc_start - buffer;
+            
+            // HTTP URC可能没有\r\n结尾,数据结束就是URC结束
+            // 检查数据部分是否完整
+            // ⚠️ 关键:验证 full_urc_len 是否溢出
+            if (full_urc_len < urc_header_len || full_urc_len < actual_len) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:+HTTPRECV full_urc_len溢出!urc_header_len=%u, actual_len=%u, full_urc_len=%u", 
+                              urc_header_len, (unsigned int)actual_len, full_urc_len);
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+            
+            // ⚠️ 关键:验证 urc_start_offset + full_urc_len 是否溢出
+            if (urc_start_offset + full_urc_len < urc_start_offset || 
+                urc_start_offset + full_urc_len < full_urc_len) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:+HTTPRECV urc_start_offset+full_urc_len溢出!urc_start_offset=%u, full_urc_len=%u", 
+                              urc_start_offset, full_urc_len);
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+            
+            if (urc_start_offset + full_urc_len <= length) {
+                end = urc_start_offset + full_urc_len;
+                found_end = true;
+            } else {
+                // 数据不完整,等待更多数据
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                PPP_DEBUG_DEBUG("[提取完整响应] +HTTPRECV数据不完整,等待更多数据 (需要=%u, 当前可用=%u)", 
+                               full_urc_len, length - urc_start_offset);
+                return false;
+            }
+        }
+    } else if (tcpsend_pos || ssltcpend_pos) {
+        // +TCPSEND格式:+TCPSEND: <socket_id>,<sent_bytes>\r\n 或 +TCPSEND: <socket_id>,OPERATION EXPIRED\r\r
+        const char *urc_start = tcpsend_pos ? tcpsend_pos : ssltcpend_pos;
+        
+        // ⚠️ 关键:验证 urc_start 是否在 buffer 范围内
+        const char *buffer_end = buffer + length;
+        if (urc_start < buffer || urc_start >= buffer_end) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 根本原因:+TCPSEND urc_start不在缓冲区范围内!urc_start=%p, buffer=%p, buffer_end=%p", 
+                          (void *)urc_start, (void *)buffer, (void *)buffer_end);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        
+        uint32_t urc_start_offset = urc_start - buffer;
+        if (urc_start_offset > length) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 根本原因:+TCPSEND urc_start_offset > length!urc_start_offset=%u, length=%u", 
+                          urc_start_offset, length);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        size_t parse_len = length - urc_start_offset;
+        
+        // 查找URC结束符:支持 \r\n, \r\r, 单个\r, \n
+        const char *urc_end = strstr(urc_start, "\r\n");
+        bool is_crlf = false;
+        bool is_crcr = false;
+        bool is_single_cr = false;
+        
+        if (urc_end) {
+            is_crlf = true;  // 找到 \r\n
+        } else {
+            // 检查 \r\r 格式(异常格式,但需要容错处理)
+            const char *cr_cr = strstr(urc_start, "\r\r");
+            if (cr_cr) {
+                urc_end = cr_cr + 1;  // 指向第二个 \r
+                is_crcr = true;
+                PPP_DEBUG_WARN("[提取完整响应] +TCPSEND URC以 \\r\\r 结尾(异常格式,可能是模组固件问题)");
+            } else {
+                // 检查 \n
+                urc_end = strstr(urc_start, "\n");
+                if (!urc_end) {
+                    // 检查单个 \r(在URC末尾,后面没有 \n 或 \r)
+                    // 从URC末尾向前查找,找到最后一个 \r
+                    const char *urc_end_pos = urc_start + parse_len;
+                    for (const char *p = urc_end_pos - 1; p >= urc_start; p--) {
+                        if (*p == '\r') {
+                            // 检查这个 \r 后面是否有 \n 或 \r
+                            if (p + 1 >= urc_end_pos) {
+                                // 这是最后一个字符,是单个 \r
+                                urc_end = p;
+                                is_single_cr = true;
+                                PPP_DEBUG_DEBUG("[提取完整响应] +TCPSEND URC以单个 \\r 结尾");
+                                break;
+                            } else if (p[1] != '\n' && p[1] != '\r') {
+                                // 后面不是 \n 或 \r,是单个 \r
+                                urc_end = p;
+                                is_single_cr = true;
+                                PPP_DEBUG_DEBUG("[提取完整响应] +TCPSEND URC以单个 \\r 结尾");
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        
+        if (urc_end) {
+            // ⚠️ 关键:验证 urc_end 是否在缓冲区范围内
+            const char *buffer_end = buffer + length;
+            if (urc_end < buffer || urc_end >= buffer_end) {
+                PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:+TCPSEND URC的urc_end不在缓冲区范围内!urc_end=%p, buffer=%p, buffer_end=%p, length=%u", 
+                              (void *)urc_end, (void *)buffer, (void *)buffer_end, length);
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+            
+            // 找到完整URC,根据结束符类型计算end(验证计算结果)
+            uint32_t urc_end_offset = urc_end - buffer;
+            if (is_crlf) {
+                // \r\n 结束符:urc_end指向\r,需要+2包含\n
+                if (urc_end_offset + 2 > length) {
+                    PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:urc_end_offset+2超出缓冲区长度!urc_end_offset=%u, length=%u", 
+                                  urc_end_offset, length);
+                    osal_mutex_unlock(&g_at_accumulate_mutex);
+                    return false;
+                }
+                end = urc_end_offset + 2;
+            } else if (is_crcr) {
+                // \r\r 结束符:urc_end已经指向第二个\r,需要+1包含第二个\r
+                if (urc_end_offset + 1 > length) {
+                    PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:urc_end_offset+1超出缓冲区长度!urc_end_offset=%u, length=%u", 
+                                  urc_end_offset, length);
+                    osal_mutex_unlock(&g_at_accumulate_mutex);
+                    return false;
+                }
+                end = urc_end_offset + 1;
+            } else if (is_single_cr) {
+                // 单个 \r 结束符:urc_end指向\r,需要+1包含\r
+                if (urc_end_offset + 1 > length) {
+                    PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:urc_end_offset+1超出缓冲区长度!urc_end_offset=%u, length=%u", 
+                                  urc_end_offset, length);
+                    osal_mutex_unlock(&g_at_accumulate_mutex);
+                    return false;
+                }
+                end = urc_end_offset + 1;
+            } else {
+                // \n 结束符:urc_end指向\n,需要+1包含\n
+                if (urc_end_offset + 1 > length) {
+                    PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:urc_end_offset+1超出缓冲区长度!urc_end_offset=%u, length=%u", 
+                                  urc_end_offset, length);
+                    osal_mutex_unlock(&g_at_accumulate_mutex);
+                    return false;
+                }
+                end = urc_end_offset + 1;
+            }
+            found_end = true;
+        } else {
+            // URC不完整,等待更多数据
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            PPP_DEBUG_DEBUG("[提取完整响应] +TCPSEND URC不完整,等待更多数据");
+            return false;
+        }
+    }
+    
+    // 如果没有找到包含数据的URC,查找标准终止符
+    // 关键:每次调用都是从缓冲区开头(start位置)开始查找第一个完整响应
+    // 找到第一个终止符后立即返回,不需要检查"新的AT命令"
+    // 因为处理完一个响应后,会清除它,然后重新调用此函数查找下一个响应
+    if (!found_end) {
+        // 通用性检查:如果检测到连续两个AT指令回复的头出现,而中间没有尾,
+        // 说明第一个响应不完整(可能是无效数据或损坏的数据)
+        // 应该丢弃第一个头到第二个头之间的数据,从第二个头开始查找尾
+        // 
+        // 重要:为了避免误报(响应内容中可能包含类似头的字符串),
+        // 需要先尝试从第一个头开始查找终止符,只有在找不到终止符的情况下才考虑丢弃
+        uint32_t second_header_pos = 0;
+        bool found_second_header = false;
+        bool first_response_has_terminator = false;
+        
+        // 先尝试从start位置查找终止符(在合理范围内,比如200字节)
+        // 如果找到了终止符,说明第一个响应可能是完整的,不应该丢弃
+        uint32_t search_limit = (length - start < 200) ? length : (start + 200);
+        for (uint32_t i = start; i < search_limit; i++) {
+            // 检查 "\r\nOK\r\n" 或 "\r\nOK"
+            if (i + 1 < length && buffer[i] == '\r' && buffer[i+1] == '\n') {
+                if (i + 3 < length && buffer[i+2] == 'O' && buffer[i+3] == 'K') {
+                    first_response_has_terminator = true;
+                    break;
+                }
+            }
+            // 检查 "\r\nERROR\r\n" 或 "\r\nERROR"
+            if (i + 1 < length && buffer[i] == '\r' && buffer[i+1] == '\n') {
+                if (i + 6 < length && strncmp(&buffer[i+2], "ERROR", 5) == 0) {
+                    first_response_has_terminator = true;
+                    break;
+                }
+            }
+            // 检查 ">\r\n" 或 "> "
+            if (buffer[i] == '>') {
+                if (i == 0 || buffer[i-1] == '\n' || buffer[i-1] == '\r' || buffer[i-1] == ' ') {
+                    if (i + 1 == length || buffer[i+1] == '\r' || buffer[i+1] == '\n' || buffer[i+1] == ' ') {
+                        first_response_has_terminator = true;
+                        break;
+                    }
+                }
+            }
+            // 检查命令回显格式:AT+...\r\n(启用ATE1时,命令回显本身就是一个完整响应)
+            if (i + 2 < length && buffer[i] == 'A' && buffer[i+1] == 'T' && buffer[i+2] == '+') {
+                // 查找命令回显的结束位置(\r\n)
+                uint32_t cmd_end = i + 3;
+                while (cmd_end < search_limit && cmd_end < length) {
+                    if (cmd_end + 1 < length && buffer[cmd_end] == '\r' && buffer[cmd_end+1] == '\n') {
+                        // 找到命令回显的终止符,说明这是一个完整的响应
+                        first_response_has_terminator = true;
+                        break;
+                    }
+                    cmd_end++;
+                }
+                if (first_response_has_terminator) {
+                    break;
+                }
+            }
+        }
+        
+        // 只有在第一个响应没有找到终止符的情况下,才查找第二个头
+        if (!first_response_has_terminator) {
+            // 从start+1开始查找第二个响应头(URC格式:+XXX: 或 AT命令回显:AT+)
+            // 限制搜索范围,避免在很长的响应内容中误报
+            uint32_t search_end = (length - start < 200) ? length : (start + 200);
+            for (uint32_t i = start + 1; i < search_end; i++) {
+                // 检查是否是URC格式的头(以+开头,包含:)
+                if (i < length && buffer[i] == '+') {
+                    uint32_t colon_pos = i;
+                    while (colon_pos < length && colon_pos < i + 20 && buffer[colon_pos] != ':') {
+                        colon_pos++;
+                    }
+                    if (colon_pos < length && buffer[colon_pos] == ':') {
+                        // 检查前面是否有换行符(确保是新的响应头,而不是响应内容中的"+XXX:")
+                        // 同时检查这个URC是否在第一个响应的合理范围内(避免误报)
+                        if ((i == 0 || buffer[i-1] == '\n' || buffer[i-1] == '\r') && 
+                            (i - start > 10)) {  // 至少间隔10字节,避免误报
+                            second_header_pos = i;
+                            found_second_header = true;
+                            break;
+                        }
+                    }
+                }
+                
+                // 检查是否是AT命令回显的头(AT+)
+                if (i + 2 < length && buffer[i] == 'A' && buffer[i+1] == 'T' && buffer[i+2] == '+') {
+                    // 检查前面是否有换行符,或者前面是字母/数字/空格/冒号等(说明是URC参数或响应内容)
+                    // 这样可以检测到紧跟在URC后面的命令回显(如+TCPSETUP: 0,O后面直接跟AT+TCPSEND)
+                    // 同时检查这个命令是否在第一个响应的合理范围内(避免误报)
+                    bool is_new_command = false;
+                    if (i == 0) {
+                        is_new_command = true;
+                    } else {
+                        uint8_t prev_char = buffer[i-1];
+                        // 前面是换行符,肯定是新命令
+                        if (prev_char == '\n' || prev_char == '\r') {
+                            is_new_command = true;
+                        }
+                        // 前面是字母/数字/空格/冒号/逗号等,说明是URC参数或响应内容,后面跟AT+应该是新命令
+                        else if ((prev_char >= 'A' && prev_char <= 'Z') || 
+                                 (prev_char >= 'a' && prev_char <= 'z') ||
+                                 (prev_char >= '0' && prev_char <= '9') ||
+                                 prev_char == ' ' || prev_char == ':' || prev_char == ',' || prev_char == 'O') {
+                            // 如果前面是'O',可能是+TCPSETUP: 0,O,后面跟AT+应该是新命令
+                            // 如果前面是其他字符,也可能是URC参数,后面跟AT+应该是新命令
+                            is_new_command = true;
+                        }
+                    }
+                    
+                    if (is_new_command && (i - start > 10)) {  // 至少间隔10字节,避免误报
+                        second_header_pos = i;
+                        found_second_header = true;
+                        break;
+                    }
+                }
+            }
+        }
+        
+        // 如果找到了第二个响应头,且第一个响应没有找到终止符,说明第一个响应不完整
+        // 丢弃第一个头到第二个头之间的数据,从第二个头开始查找尾
+        if (found_second_header && second_header_pos > start && !first_response_has_terminator) {
+            uint32_t dropped_bytes = second_header_pos - start;
+            g_dropped_data_count++;
+            g_dropped_data_bytes += dropped_bytes;
+            
+            // 打印丢弃的数据(前64字节,用于调试)
+            char dropped_preview[129] = {0};
+            uint32_t preview_len = (dropped_bytes < 64) ? dropped_bytes : 64;
+            for (uint32_t j = 0; j < preview_len; j++) {
+                uint8_t c = buffer[start + j];
+                if (c >= 32 && c < 127) {
+                    dropped_preview[j] = c;
+                } else if (c == '\r') {
+                    dropped_preview[j] = '\\';
+                    if (j + 1 < 128) dropped_preview[++j] = 'r';
+                } else if (c == '\n') {
+                    dropped_preview[j] = '\\';
+                    if (j + 1 < 128) dropped_preview[++j] = 'n';
+                } else {
+                    dropped_preview[j] = '.';
+                }
+            }
+            
+            PPP_DEBUG_WARN("[提取完整响应] 检测到连续两个响应头(第一个无终止符),丢弃不完整数据: %u 字节 (累计丢弃: %u 次, %u 字节), 预览: [%.*s]", 
+                          dropped_bytes, g_dropped_data_count, g_dropped_data_bytes, 
+                          (int)preview_len, dropped_preview);
+            
+            // 从第二个头开始查找尾
+            start = second_header_pos;
+            // 跳过第二个头前面的空白字符
+            while (start < length && (buffer[start] == '\r' || buffer[start] == '\n' || buffer[start] == ' ')) {
+                start++;
+            }
+            if (start >= length) {
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                return false;
+            }
+        }
+        
+        for (uint32_t i = start; i < length; i++) {
+            // 优先检查URC格式的终止符(如 +TCPCLOSE: ERROR\r\n 或 +TCPSETUP: 0,OK\r\r)
+            // 这些URC通常以 \r\n 或 \r\r 结尾
+            if (i + 16 <= length) {
+                // 检查 +TCPCLOSE: ERROR
+                if (strncmp(&buffer[i], "+TCPCLOSE: ERROR", 16) == 0) {
+                    if (i + 16 < length && buffer[i+16] == '\r' && i + 17 < length && buffer[i+17] == '\n') {
+                        end = i + 18;
+                        found_end = true;
+                        break;  // 找到第一个终止符,立即返回
+                    } else if (i + 16 == length) {
+                        // ERROR后面没有数据,可能是缓冲区末尾,等待更多数据
+                        // 但如果是完整的ERROR(没有\r\n),也可以认为是结束
+                        end = i + 16;
+                        found_end = true;
+                        break;
+                    }
+                }
+                // 检查 +TCPCLOSE: ERR(缩写形式)
+                if (strncmp(&buffer[i], "+TCPCLOSE: ERR", 14) == 0) {
+                    if (i + 14 < length && buffer[i+14] == '\r') {
+                        // 支持 \r\n 或 \r\r 结尾
+                        if (i + 15 < length && buffer[i+15] == '\n') {
+                            end = i + 16;
+                        } else if (i + 15 < length && buffer[i+15] == '\r') {
+                            end = i + 16;  // \r\r 结尾
+                        } else {
+                            end = i + 15;  // 只有 \r
+                        }
+                        found_end = true;
+                        break;
+                    } else if (i + 14 == length) {
+                        end = i + 14;
+                        found_end = true;
+                        break;
+                    }
+                }
+            }
+            // 检查 +TCPSETUP: 0,OK\r\r 或 +TCPCLOSE: 0,OK\r\r 格式
+            if (i + 15 <= length) {
+                // 检查 +TCPSETUP: 0,OK
+                if (strncmp(&buffer[i], "+TCPSETUP: ", 11) == 0) {
+                    // 查找 OK 或 ERR
+                    uint32_t j = i + 11;
+                    while (j < length && j < i + 20 && buffer[j] != '\r' && buffer[j] != '\n') {
+                        j++;
+                    }
+                    if (j > i + 11) {
+                        // 检查是否包含 OK 或 ERR
+                        const char *urc_data = (const char *)&buffer[i + 11];
+                        uint32_t urc_data_len = j - (i + 11);
+                        if (strstr(urc_data, "OK") != NULL || strstr(urc_data, "ERR") != NULL) {
+                            // 找到 OK 或 ERR,检查结束符
+                            if (j < length && buffer[j] == '\r') {
+                                if (j + 1 < length && buffer[j+1] == '\n') {
+                                    end = j + 2;  // \r\n 结尾(正常格式)
+                                } else if (j + 1 < length && buffer[j+1] == '\r') {
+                                    // ⚠️ \r\r 结尾(异常格式,可能是模组固件问题)
+                                    PPP_DEBUG_WARN("[提取完整响应] 检测到异常格式:响应以 \\r\\r 结尾而不是 \\r\\n(可能是模组固件问题)");
+                                    end = j + 2;  // \r\r 结尾(容错处理)
+                                } else {
+                                    end = j + 1;  // 只有 \r
+                                }
+                                found_end = true;
+                                break;
+                            } else if (j == length) {
+                                // 缓冲区末尾,等待更多数据
+                                break;
+                            }
+                        }
+                    }
+                }
+                // 检查 +TCPCLOSE: <socket_id> 或 +TCPCLOSE: <socket_id>,<reason>
+                // 格式:+TCPCLOSE: 0,Link Closed\r\n 或 +TCPCLOSE: 0,OK\r\n 或 +TCPCLOSE: 0\r\n
+                if (strncmp(&buffer[i], "+TCPCLOSE: ", 11) == 0) {
+                    // 查找结束符 \r\n 或 \n
+                    uint32_t j = i + 11;
+                    // 跳过 socket_id 和可能的逗号及原因字符串
+                    while (j < length && buffer[j] != '\r' && buffer[j] != '\n') {
+                        j++;
+                    }
+                    if (j > i + 11) {
+                        // 找到了结束符,检查格式
+                        if (j < length && buffer[j] == '\r') {
+                            if (j + 1 < length && buffer[j+1] == '\n') {
+                                end = j + 2;  // \r\n 结尾(正常格式)
+                                found_end = true;
+                                break;
+                            } else if (j + 1 < length && buffer[j+1] == '\r') {
+                                // ⚠️ \r\r 结尾(异常格式,可能是模组固件问题)
+                                PPP_DEBUG_WARN("[提取完整响应] 检测到异常格式:响应以 \\r\\r 结尾而不是 \\r\\n(可能是模组固件问题)");
+                                end = j + 2;  // \r\r 结尾(容错处理)
+                                found_end = true;
+                                break;
+                            } else {
+                                end = j + 1;  // 只有 \r
+                                found_end = true;
+                                break;
+                            }
+                        } else if (j < length && buffer[j] == '\n') {
+                            end = j + 1;  // \n 结尾
+                            found_end = true;
+                            break;
+                        } else if (j == length) {
+                            // 缓冲区末尾,等待更多数据
+                            break;
+                        }
+                    }
+                }
+            }
+            if (i + 19 <= length) {
+                // 检查 +SSLTCPCLOSE: ERROR
+                if (strncmp(&buffer[i], "+SSLTCPCLOSE: ERROR", 19) == 0) {
+                    if (i + 19 < length && buffer[i+19] == '\r' && i + 20 < length && buffer[i+20] == '\n') {
+                        end = i + 21;
+                        found_end = true;
+                        break;  // 找到第一个终止符,立即返回
+                    } else if (i + 19 == length) {
+                        end = i + 19;
+                        found_end = true;
+                        break;
+                    }
+                }
+            }
+            
+            // 检查直接以"OK"开头的情况(如果start跳过了开头的\r\n,那么start位置可能就是"OK")
+            // 例如:\r\nOK\r\n 经过跳过空白字符后,start=2,buffer[2]='O',buffer[3]='K'
+            if (i == start && i + 1 < length && buffer[i] == 'O' && buffer[i+1] == 'K') {
+                // 检查OK后面是否是结束符或换行符
+                if (i + 2 == length || (i + 2 < length && buffer[i+2] == '\r')) {
+                    // 找到OK终止符
+                    end = (i + 2 < length && buffer[i+2] == '\r' && i + 3 < length && buffer[i+3] == '\n') ? 
+                          (i + 4) : (i + 2);
+                    found_end = true;
+                    break;  // 立即退出循环,返回第一个完整响应
+                }
+            }
+            
+            // 检查 "\r\nOK\r\n" 或 "\r\nOK"
+            if (i + 1 < length && buffer[i] == '\r' && buffer[i+1] == '\n') {
+                if (i + 3 < length && buffer[i+2] == 'O' && buffer[i+3] == 'K') {
+                    if (i + 4 == length || (i + 4 < length && buffer[i+4] == '\r')) {
+                        // 找到第一个OK终止符,立即返回(流处理:找到第一个完整响应就处理)
+                        end = (i + 4 < length && buffer[i+4] == '\r' && i + 5 < length && buffer[i+5] == '\n') ? 
+                              (i + 6) : (i + 4);
+                        found_end = true;
+                        break;  // 立即退出循环,返回第一个完整响应
+                    }
+                }
+            }
+            
+            // 检查 "\r\nERROR\r\n" 或 "\r\nERROR"(标准格式,不是URC格式)
+            if (i + 1 < length && buffer[i] == '\r' && buffer[i+1] == '\n') {
+                if (i + 6 < length && strncmp(&buffer[i+2], "ERROR", 5) == 0) {
+                    // 检查是否是URC格式的ERROR(如 +TCPCLOSE: ERROR)
+                    // 注意:+TCPCLOSE: ERROR 中的ERROR前面是 ": ",不是 "\r\n"
+                    // 所以如果ERROR前面是 "\r\n",说明是标准的ERROR响应
+                    if (i + 7 == length || (i + 7 < length && buffer[i+7] == '\r')) {
+                        end = (i + 7 < length && buffer[i+7] == '\r' && i + 8 < length && buffer[i+8] == '\n') ? 
+                              (i + 9) : (i + 7);
+                        found_end = true;
+                        break;  // 立即退出循环,返回第一个完整响应
+                    }
+                }
+            }
+            
+            // 检查 ">\r\n" 或 "> \r" 或 "> "(提示符)
+            // 格式可能是:\r\n>\r\n 或 \r\n> \r 或 \r\n> \r\n 等
+            if (buffer[i] == '>') {
+                if (i == 0 || buffer[i-1] == '\n' || buffer[i-1] == '\r' || buffer[i-1] == ' ') {
+                    // 检查">"后面的字符
+                    if (i + 1 == length) {
+                        // ">"是最后一个字符
+                        end = i + 1;
+                        found_end = true;
+                        break;
+                    } else if (buffer[i+1] == '\r') {
+                        // ">\r" 或 ">\r\n"
+                        if (i + 2 < length && buffer[i+2] == '\n') {
+                            end = i + 3;  // ">\r\n"
+                        } else {
+                            end = i + 2;  // ">\r"
+                        }
+                        found_end = true;
+                        break;
+                    } else if (buffer[i+1] == '\n') {
+                        // ">\n"
+                        end = i + 2;
+                        found_end = true;
+                        break;
+                    } else if (buffer[i+1] == ' ') {
+                        // "> " 后面可能有 \r 或 \r\n
+                        if (i + 2 < length && buffer[i+2] == '\r') {
+                            if (i + 3 < length && buffer[i+3] == '\n') {
+                                end = i + 4;  // "> \r\n"
+                            } else {
+                                end = i + 3;  // "> \r"
+                            }
+                        } else {
+                            end = i + 2;  // "> "(后面没有\r)
+                        }
+                        found_end = true;
+                        break;
+                    } else {
+                        // ">"后面是其他字符,也认为是提示符(容错处理)
+                        end = i + 1;
+                        found_end = true;
+                        break;
+                    }
+                }
+            }
+            
+            // 检查 "\r\n+CME ERROR" 或 "\r\n+CMS ERROR"
+            if (i + 1 < length && buffer[i] == '\r' && buffer[i+1] == '\n') {
+                if (i + 11 < length && strncmp(&buffer[i+2], "+CME ERROR", 10) == 0) {
+                    // 查找下一个 \r\n
+                    uint32_t j = i + 12;
+                    while (j + 1 < length && !(buffer[j] == '\r' && buffer[j+1] == '\n')) {
+                        j++;
+                    }
+                    if (j + 1 < length) {
+                        // 找到第一个+CME ERROR终止符,立即返回(流处理:找到第一个完整响应就处理)
+                        end = j + 2;
+                        found_end = true;
+                        break;  // 立即退出循环,返回第一个完整响应
+                    }
+                } else if (i + 11 < length && strncmp(&buffer[i+2], "+CMS ERROR", 10) == 0) {
+                    // 查找下一个 \r\n
+                    uint32_t j = i + 12;
+                    while (j + 1 < length && !(buffer[j] == '\r' && buffer[j+1] == '\n')) {
+                        j++;
+                    }
+                    if (j + 1 < length) {
+                        // 找到第一个+CMS ERROR终止符,立即返回(流处理:找到第一个完整响应就处理)
+                        end = j + 2;
+                        found_end = true;
+                        break;  // 立即退出循环,返回第一个完整响应
+                    }
+                }
+                // 检查 "\r\nCONNECT\r\n" 或 "\r\nCONNECT" (如ATD*99#)
+                else if (i + 8 < length && strncmp(&buffer[i+2], "CONNECT", 7) == 0) {
+                    if (i + 9 == length || (i + 9 < length && buffer[i+9] == '\r')) {
+                        end = (i + 9 < length && buffer[i+9] == '\r' && i + 10 < length && buffer[i+10] == '\n') ? 
+                              (i + 11) : (i + 9);
+                        found_end = true;
+                        break;
+                    }
+                }
+                // 检查 "\r\nNO CARRIER\r\n" 或 "\r\nNO CARRIER"
+                else if (i + 11 < length && strncmp(&buffer[i+2], "NO CARRIER", 10) == 0) {
+                    if (i + 12 == length || (i + 12 < length && buffer[i+12] == '\r')) {
+                        end = (i + 12 < length && buffer[i+12] == '\r' && i + 13 < length && buffer[i+13] == '\n') ? 
+                              (i + 14) : (i + 12);
+                        found_end = true;
+                        break;
+                    }
+                }
+                // 检查 "\r\nBUSY\r\n" 或 "\r\nBUSY"
+                else if (i + 6 < length && strncmp(&buffer[i+2], "BUSY", 4) == 0) {
+                    if (i + 6 == length || (i + 6 < length && buffer[i+6] == '\r')) {
+                        end = (i + 6 < length && buffer[i+6] == '\r' && i + 7 < length && buffer[i+7] == '\n') ? 
+                              (i + 8) : (i + 6);
+                        found_end = true;
+                        break;
+                    }
+                }
+                // 检查 "\r\nNO DIALTONE\r\n" 或 "\r\nNO DIALTONE"
+                else if (i + 12 < length && strncmp(&buffer[i+2], "NO DIALTONE", 11) == 0) {
+                    if (i + 13 == length || (i + 13 < length && buffer[i+13] == '\r')) {
+                        end = (i + 13 < length && buffer[i+13] == '\r' && i + 14 < length && buffer[i+14] == '\n') ? 
+                              (i + 15) : (i + 13);
+                        found_end = true;
+                        break;
+                    }
+                }
+                // 检查 "\r\nGPRS DISCONNECTION\r\n" 或 "\r\nGPRS DISCONNECTION" (如AT+CGATT=0)
+                else if (i + 20 < length && strncmp(&buffer[i+2], "GPRS DISCONNECTION", 18) == 0) {
+                    if (i + 20 == length || (i + 20 < length && buffer[i+20] == '\r')) {
+                        end = (i + 20 < length && buffer[i+20] == '\r' && i + 21 < length && buffer[i+21] == '\n') ? 
+                              (i + 22) : (i + 20);
+                        found_end = true;
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    
+    osal_mutex_unlock(&g_at_accumulate_mutex);
+    
+    if (found_end) {
+        // ⚠️ 关键:验证 end 和 start 的合理性,防止计算错误
+        if (end < start) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:end < start! start=%u, end=%u, length=%u", 
+                          start, end, length);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        
+        if (end > length) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:end > length! start=%u, end=%u, length=%u", 
+                          start, end, length);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        
+        uint32_t calculated_len = end - start;
+        
+        // ⚠️ 关键:验证计算出的长度是否合理(最大64KB)
+        const uint32_t MAX_REASONABLE_RESPONSE_LEN = 65536;
+        if (calculated_len > MAX_REASONABLE_RESPONSE_LEN) {
+            PPP_DEBUG_ERROR("[提取完整响应] ⚠️ 计算错误:response_len异常大!start=%u, end=%u, len=%u (max=%u)", 
+                          start, end, calculated_len, MAX_REASONABLE_RESPONSE_LEN);
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            return false;
+        }
+        
+        *response_start = start;
+        *response_len = calculated_len;
+        PPP_DEBUG_DEBUG("[提取完整响应] 找到完整响应: start=%u (跳过 %u 个\\x00), len=%u", 
+                       start, start, *response_len);
+        /* 调试:检查提取的响应开头是否还有 \x00 */
+        if (start < length && buffer[start] == '\0') {
+            PPP_DEBUG_WARN("[提取完整响应] WARNING: 提取的响应开头仍然是 \\x00! start=%u, buffer[start]=0x%02X", 
+                          start, (unsigned char)buffer[start]);
+        }
+        return true;
+    }
+    
+    // 数据不完整
+    PPP_DEBUG_DEBUG("[提取完整响应] 数据不完整,等待更多数据 (当前长度=%u)", length);
+    return false;
+}
+
+/**
+ * @brief 改进的URC分类和分发(逐个函数判定)
+ * @param response 完整的AT响应
+ * @param response_len 响应长度
+ * @return true=已处理(URC),false=未识别(传给通用处理)
+ * 
+ * 流程:
+ * 1. 调用 n706a_parse_tcprecv_urc() - 如果返回true,处理并返回
+ * 2. 调用 n706a_parse_tcpsetup_urc() - 如果返回true,处理并返回
+ * 3. 调用 n706a_parse_tcpclose_urc() - 如果返回true,处理并返回
+ * 4. 调用 n706a_parse_tcpsend_urc() - 如果返回true,处理并返回
+ * 5. 调用 n706a_parse_httprecv_urc() - 如果返回true,处理并返回
+ * 6. 调用网络状态URC解析 - 如果返回true,处理并返回
+ * 7. 返回false(未识别,传给通用处理)
+ */
+static bool n706a_classify_and_dispatch_urc_improved(const uint8_t *response, uint32_t response_len)
+{
+    if (!response || response_len == 0) {
+        return false;
+    }
+    
+    const char *response_str = (const char *)response;
+    
+    // 类别1:TCP数据接收URC(最高优先级)
+    if (response_len >= 10 && g_tcp_urc_callback) {
+        const char *tcprecv_pos = strstr(response_str, "+TCPRECV: ");
+        const char *ssltcprecv_pos = strstr(response_str, "+SSLTCPRECV: ");
+        
+        if (tcprecv_pos || ssltcprecv_pos) {
+            const char *urc_start = tcprecv_pos ? tcprecv_pos : ssltcprecv_pos;
+            size_t parse_len = response_len - (urc_start - response_str);
+            
+            int socket_id;
+            size_t data_len;
+            const uint8_t *tcp_data;
+            
+            if (n706a_parse_tcprecv_urc(urc_start, parse_len, &socket_id, &data_len, &tcp_data)) {
+                // 记录解析到的 socket_id,用于诊断
+                PPP_DEBUG_INFO("[URC分类] Parsed +TCPRECV URC: socket_id=%d, data_len=%u, response_len=%u", 
+                              socket_id, (unsigned int)data_len, (unsigned int)response_len);
+                
+                // HEX解码处理(与原来的逻辑相同)
+                static uint8_t hex_decode_buffer_0[2048];
+                static uint8_t hex_decode_buffer_1[2048];
+                static volatile int hex_decode_buffer_active = 0;
+                
+                size_t binary_data_len = data_len;
+                const uint8_t *binary_data = tcp_data;
+                bool is_hex_format = false;
+                
+                if (data_len > 0 && g_tcp_recv_hex_mode) {
+                    // HEX模式下,data_len是二进制数据长度,HEX字符串长度 = data_len × 2
+                    size_t hex_str_len = data_len * 2;
+                    
+                    // 检查实际HEX字符串长度(可能被截断)
+                    size_t actual_hex_str_len = 0;
+                    const char *p = (const char *)tcp_data;
+                    while (actual_hex_str_len < hex_str_len && 
+                           ((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'F') || (*p >= 'a' && *p <= 'f'))) {
+                        p++;
+                        actual_hex_str_len++;
+                    }
+                    
+                    // 如果实际HEX字符串长度足够(至少是期望长度,或者接近期望长度),尝试HEX解码
+                    if (actual_hex_str_len >= hex_str_len || (actual_hex_str_len > 0 && actual_hex_str_len >= hex_str_len - 1)) {
+                        // 使用实际可用的HEX字符串长度(如果是奇数,截断最后一个字符)
+                        size_t hex_len_to_decode = (actual_hex_str_len % 2 == 0) ? actual_hex_str_len : (actual_hex_str_len - 1);
+                        
+                        if (hex_len_to_decode > 0 && hex_len_to_decode % 2 == 0) {
+                            is_hex_format = true;
+                            int buffer_idx = hex_decode_buffer_active;
+                            uint8_t *decode_buffer = (buffer_idx == 0) ? hex_decode_buffer_0 : hex_decode_buffer_1;
+                            
+                            binary_data_len = n706a_hex_string_to_binary((const char *)tcp_data, hex_len_to_decode,
+                                                                         decode_buffer, sizeof(hex_decode_buffer_0));
+                            if (binary_data_len > 0) {
+                                binary_data = decode_buffer;
+                                hex_decode_buffer_active = 1 - hex_decode_buffer_active;
+                                if (hex_len_to_decode < hex_str_len) {
+                                    PPP_DEBUG_WARN("[URC分类] HEX data truncated: expected_hex_len=%u, actual_hex_len=%u, decoded=%u bytes", 
+                                                  (unsigned int)hex_str_len, (unsigned int)hex_len_to_decode, (unsigned int)binary_data_len);
+                                }
+                            }
+                        } else {
+                            PPP_DEBUG_WARN("[URC分类] HEX data length is invalid: expected_hex_len=%u, actual_hex_len=%u", 
+                                          (unsigned int)hex_str_len, (unsigned int)actual_hex_str_len);
+                        }
+                    } else {
+                        PPP_DEBUG_WARN("[URC分类] HEX data too short: expected_hex_len=%u, actual_hex_len=%u", 
+                                      (unsigned int)hex_str_len, (unsigned int)actual_hex_str_len);
+                    }
+                }
+                
+                if (binary_data_len > 0) {
+                    // 检查响应中是否包含命令回显(如 "AT+TCPREAD=0,2047")
+                    // 如果包含命令回显,说明这是 AT+TCPREAD 命令的响应,+TCPRECV URC 数据应该保留在响应中
+                    // 供 n706a_tcp_recv 函数提取,而不是通过异步 URC 回调处理
+                    // 这样可以确保即使 socket 未注册,也能从 AT+TCPREAD 响应中提取数据
+                    bool has_command_echo = (strstr(response_str, "AT+TCPREAD") != NULL || 
+                                            strstr(response_str, "AT+SSLTCPREAD") != NULL);
+                    
+                    if (has_command_echo) {
+                        // 这是 AT+TCPREAD 命令的响应,+TCPRECV URC 数据应该保留在响应中
+                        // 不通过异步 URC 回调处理,而是让 n706a_tcp_recv 从响应中提取
+                        PPP_DEBUG_INFO("[URC分类] +TCPRECV URC 在 AT+TCPREAD 响应中,保留在响应中供 n706a_tcp_recv 提取: URC_socket_id=%d, len=%u", 
+                                       socket_id, (unsigned int)binary_data_len);
+                        PPP_DEBUG_INFO("[URC分类] Note: n706a_tcp_recv will verify socket_id match before extracting data");
+                        // 不调用 g_tcp_urc_callback,不返回 true,继续处理响应
+                    } else {
+                        // 这是纯异步 URC,通过回调处理
+                        PPP_DEBUG_INFO("[URC分类] Pure async +TCPRECV URC, calling callback: socket_id=%d, len=%u", 
+                                      socket_id, (unsigned int)binary_data_len);
+                        // 注意:socket 注册检查在 n706a_tcp_on_urc_received 回调函数中进行
+                        // 这里直接调用回调,让回调函数自己处理未注册的情况
+                        g_tcp_urc_callback(socket_id, binary_data, binary_data_len);
+                        PPP_DEBUG_INFO("[URC分类] TCP URC已处理: socket=%d, len=%u", socket_id, (unsigned int)binary_data_len);
+                        return true;  // 已处理,清除数据
+                    }
+                }
+            }
+        }
+    }
+    
+    // 类别2:TCP连接状态URC
+    if (response_len >= 11) {
+        const char *tcpsetup_pos = strstr(response_str, "+TCPSETUP: ");
+        const char *ssltcpsetup_pos = strstr(response_str, "+SSLTCPSETUP: ");
+        
+        if (tcpsetup_pos || ssltcpsetup_pos) {
+            const char *urc_start = tcpsetup_pos ? tcpsetup_pos : ssltcpsetup_pos;
+            const char *urc_end = strstr(urc_start, "\r\n");
+            if (!urc_end) {
+                urc_end = strstr(urc_start, "\n");
+            }
+            if (!urc_end) {
+                urc_end = response_str + response_len;
+            }
+            
+            size_t urc_len = urc_end - urc_start;
+            if (urc_len >= 15) {
+                int socket_id;
+                bool success;
+                if (n706a_parse_tcpsetup_urc(urc_start, urc_len, &socket_id, &success)) {
+                    if (g_tcpsetup_urc_callback) {
+                        g_tcpsetup_urc_callback(socket_id, success);
+                    }
+                    PPP_DEBUG_INFO("[URC分类] +TCPSETUP URC已处理: socket=%d, success=%d", socket_id, success);
+                    return true;  // 已处理,清除数据
+                }
+            }
+        }
+        
+        // +TCPCLOSE
+        const char *tcpclose_pos = strstr(response_str, "+TCPCLOSE: ");
+        const char *ssltcpclose_pos = strstr(response_str, "+SSLTCPCLOSE: ");
+        
+        if (tcpclose_pos || ssltcpclose_pos) {
+            const char *urc_start = tcpclose_pos ? tcpclose_pos : ssltcpclose_pos;
+            const char *urc_end = strstr(urc_start, "\r\n");
+            if (!urc_end) {
+                urc_end = strstr(urc_start, "\n");
+            }
+            if (!urc_end) {
+                urc_end = response_str + response_len;
+            }
+            
+            size_t urc_len = urc_end - urc_start;
+            int socket_id;
+            if (n706a_parse_tcpclose_urc(urc_start, urc_len, &socket_id)) {
+                if (g_tcpclose_urc_callback) {
+                    g_tcpclose_urc_callback(socket_id);
+                }
+                PPP_DEBUG_INFO("[URC分类] +TCPCLOSE URC已处理: socket=%d", socket_id);
+                return true;  // 已处理,清除数据
+            }
+        }
+    }
+    
+    // 类别3:TCP发送结果URC
+    if (response_len >= 10) {
+        const char *tcpsend_pos = strstr(response_str, "+TCPSEND: ");
+        const char *ssltcpend_pos = strstr(response_str, "+SSLTCPEND: ");
+        
+        if (tcpsend_pos || ssltcpend_pos) {
+            const char *urc_start = tcpsend_pos ? tcpsend_pos : ssltcpend_pos;
+            
+            // 关键改进:检查URC前面是否有命令回显(AT+)
+            // 如果URC前面有命令回显,说明这是命令响应的一部分,不是纯异步URC
+            // 纯异步URC应该以 \r\n 开头,或者直接以 + 开头(没有AT+回显)
+            bool is_pure_async_urc = true;
+            if (urc_start > response_str) {
+                // 检查URC前面是否有 "AT+" 字符串(命令回显)
+                const char *before_urc = urc_start - 1;
+                // 向前查找,看是否有 "AT+" 在同一个响应块中
+                while (before_urc >= response_str && (*before_urc == ' ' || *before_urc == '\r' || *before_urc == '\n')) {
+                    before_urc--;
+                }
+                // 检查是否以 "AT+" 结尾(说明前面有命令回显)
+                if (before_urc >= response_str + 2) {
+                    if (before_urc[-2] == 'A' && before_urc[-1] == 'T' && *before_urc == '+') {
+                        // 找到命令回显,这不是纯异步URC
+                        is_pure_async_urc = false;
+                        PPP_DEBUG_DEBUG("[URC分类] +TCPSEND URC前面有命令回显,可能是命令响应的一部分");
+                    }
+                }
+            }
+            
+            const char *urc_end = strstr(urc_start, "\r\n");
+            if (!urc_end) {
+                urc_end = strstr(urc_start, "\n");
+            }
+            if (!urc_end) {
+                // 如果没有找到换行符,尝试查找下一个URC或响应结束
+                // 对于 +TCPSEND: <socket_id>,<sent_bytes> 格式,至少需要找到逗号后的数字
+                const char *colon = strstr(urc_start, ": ");
+                if (colon) {
+                    const char *comma = strstr(colon + 2, ",");
+                    if (comma) {
+                        // 找到逗号,尝试找到数字的结束位置
+                        const char *num_end = comma + 1;
+                        while (num_end < response_str + response_len && 
+                               *num_end >= '0' && *num_end <= '9') {
+                            num_end++;
+                        }
+                        // 如果找到数字结束位置,使用它作为URC结束位置
+                        if (num_end < response_str + response_len) {
+                            urc_end = num_end;
+                        } else {
+                            urc_end = response_str + response_len;
+                        }
+                    } else {
+                        urc_end = response_str + response_len;
+                    }
+                } else {
+                    urc_end = response_str + response_len;
+                }
+            }
+            
+            size_t urc_len = urc_end - urc_start;
+            // 调试:打印URC内容
+            if (urc_len < 100) {
+                char urc_preview[101] = {0};
+                size_t preview_len = (urc_len < 100) ? urc_len : 100;
+                for (size_t i = 0; i < preview_len && i < sizeof(urc_preview) - 1; i++) {
+                    urc_preview[i] = (urc_start[i] >= 32 && urc_start[i] < 127) ? urc_start[i] : '.';
+                }
+                PPP_DEBUG_INFO("[URC分类] Attempting to parse +TCPSEND URC: len=%u, preview=[%.*s]", 
+                              (unsigned int)urc_len, (int)preview_len, urc_preview);
+            }
+            int socket_id;
+            size_t sent_bytes;
+            if (n706a_parse_tcpsend_urc(urc_start, urc_len, &socket_id, &sent_bytes)) {
+                PPP_DEBUG_INFO("[URC分类] +TCPSEND URC已处理: socket=%d, sent=%u (pure_async=%d)", 
+                              socket_id, (unsigned int)sent_bytes, is_pure_async_urc);
+                
+                // 如果是纯异步URC(没有命令回显),立即处理并返回
+                // 如果是命令响应中的URC(有命令回显),需要检查是否还有其他内容
+                if (is_pure_async_urc) {
+                    // 纯异步URC,立即处理
+                    if (g_tcpsend_urc_callback) {
+                        g_tcpsend_urc_callback(socket_id, sent_bytes);
+                    }
+                    // 如果是独立URC(整个响应块就是这个URC),不写入缓冲区
+                    if (urc_start == response_str || (urc_start > response_str && 
+                        (urc_start[-1] == '\r' || urc_start[-1] == '\n'))) {
+                        // URC前面只有空白字符或换行符,是独立的异步URC
+                        if (urc_end >= response_str + response_len - 2) {
+                            return true;  // 独立URC,已处理,不写入缓冲区
+                        }
+                    }
+                } else {
+                    // 命令响应中的URC,也需要处理回调,但继续写入缓冲区
+                    if (g_tcpsend_urc_callback) {
+                        g_tcpsend_urc_callback(socket_id, sent_bytes);
+                    }
+                    // 继续处理,让后续逻辑写入缓冲区
+                }
+            }
+        }
+    }
+    
+    // 类别4:HTTP响应URC
+    if (response_len >= 11) {
+        const char *httprecv_pos = strstr(response_str, "+HTTPRECV: ");
+        const char *httpsrecv_pos = strstr(response_str, "+HTTPSRECV: ");
+        
+        if (httprecv_pos || httpsrecv_pos) {
+            const char *urc_start = httprecv_pos ? httprecv_pos : httpsrecv_pos;
+            bool is_https = (httpsrecv_pos != NULL);
+            
+            size_t data_len;
+            const uint8_t *http_data;
+            
+            size_t declared_len, actual_len;
+            if (n706a_parse_httprecv_urc(urc_start, response_len - (urc_start - response_str), &declared_len, &actual_len, &http_data)) {
+                extern void n706a_http_on_urc_received(const uint8_t *data, size_t length, bool is_https);
+                n706a_http_on_urc_received(http_data, actual_len, is_https);
+                PPP_DEBUG_INFO("[URC分类] HTTP URC已处理: %s, declared_len=%u, actual_len=%u", 
+                              is_https ? "+HTTPSRECV" : "+HTTPRECV", (unsigned int)declared_len, (unsigned int)actual_len);
+                return true;  // 已处理,清除数据
+            }
+        }
+    }
+    
+    // 类别5:网络状态URC
+    if (response_len >= 6 && g_network_status_urc_callback) {
+        const char *creg_pos = strstr(response_str, "+CREG: ");
+        const char *cgreg_pos = strstr(response_str, "+CGREG: ");
+        const char *cgatt_pos = strstr(response_str, "+CGATT: ");
+        
+        const char *urc_start = NULL;
+        const char *urc_type = NULL;
+        
+        if (creg_pos) {
+            urc_start = creg_pos;
+            urc_type = "+CREG";
+        } else if (cgreg_pos) {
+            urc_start = cgreg_pos;
+            urc_type = "+CGREG";
+        } else if (cgatt_pos) {
+            urc_start = cgatt_pos;
+            urc_type = "+CGATT";
+        }
+        
+        if (urc_start) {
+            const char *colon = strchr(urc_start, ':');
+            if (colon) {
+                const char *urc_data_start = colon + 2;
+                const char *urc_end = strstr(urc_data_start, "\r\n");
+                if (!urc_end) {
+                    urc_end = strstr(urc_data_start, "\n");
+                }
+                if (!urc_end) {
+                    urc_end = response_str + response_len;
+                }
+                
+                size_t urc_data_len = urc_end - urc_data_start;
+                g_network_status_urc_callback(urc_type, urc_data_start, urc_data_len);
+                PPP_DEBUG_INFO("[URC分类] 网络状态URC已处理: %s", urc_type);
+                return true;  // 已处理,清除数据
+            }
+        }
+    }
+    
+    // 未识别为URC,返回false,传给通用处理
+    return false;
+}
+
+/**
+ * @brief 写入AT响应数据到环形缓冲区(由ppp_interrupt.c调用)
+ * 
+ * 改进的处理流程:
+ * 1. 将新数据追加到累积缓冲区
+ * 2. 循环提取完整的AT指令响应(有正确的头和尾)
+ * 3. 逐个函数判定,匹配就处理并清除,不匹配就传给下一个
+ * 4. 最后都不匹配,进入通用AT指令处理
+ * 5. 继续处理剩余数据(支持流式处理多个响应)
+ * 
+ * @param data 数据指针
+ * @param length 数据长度
+ */
+void n706a_at_response_buffer_write(const uint8_t *data, uint32_t length)
+{
+    if (!data || length == 0) {
+        return;
+    }
+    
+    PPP_DEBUG_DEBUG("[AT响应缓冲区] 接收到数据块: length=%u", length);
+    
+#if USE_STREAM_PARSER
+    /* 使用流式解析器(新方案) */
+    n706a_stream_parser_process_data(data, length);
+    return;  /* 新方案直接处理,不需要后续的旧逻辑 */
+#else
+    /* 使用旧方案(保持向后兼容) */
+    
+    /* ========== 第一步:将新数据追加到累积缓冲区 ========== */
+    osal_mutex_lock(&g_at_accumulate_mutex);
+    
+    // 检查累积缓冲区超时(如果数据超过5秒还没被识别,清除)
+    unsigned long long current_jiffies = osal_get_jiffies();
+    if (g_at_accumulate_length > 0 && g_at_accumulate_last_update_time > 0) {
+        unsigned long long elapsed_jiffies = current_jiffies - g_at_accumulate_last_update_time;
+        uint32_t elapsed_ms = osal_jiffies_to_msecs((unsigned int)elapsed_jiffies);
+        if (elapsed_ms > N706A_AT_ACCUMULATE_TIMEOUT_MS) {
+            /* 打印最后几个字节的十六进制,帮助分析问题 */
+            uint8_t last_bytes[16] = {0};
+            uint32_t preview_len = (g_at_accumulate_length < 16) ? g_at_accumulate_length : 16;
+            if (g_at_accumulate_length > 0 && preview_len > 0) {
+                uint32_t start_offset = (g_at_accumulate_length > preview_len) ? (g_at_accumulate_length - preview_len) : 0;
+                memcpy(last_bytes, g_at_accumulate_buffer + start_offset, preview_len);
+            }
+            char hex_str[48] = {0};
+            for (uint32_t i = 0; i < preview_len; i++) {
+                snprintf(hex_str + i * 3, 4, "%02X ", last_bytes[i]);
+            }
+            PPP_DEBUG_WARN("[累积缓冲区] 超时清除无效数据: %u 字节 (超时: %u ms), 最后 %u 字节: %s", 
+                          g_at_accumulate_length, elapsed_ms, preview_len, hex_str);
+            g_at_accumulate_length = 0;
+            g_at_accumulate_last_update_time = 0;
+        }
+    }
+    
+    // 检查缓冲区空间
+    uint32_t available_space = N706A_AT_ACCUMULATE_BUFFER_SIZE - g_at_accumulate_length;
+    if (length > available_space) {
+        // 缓冲区空间不足,丢弃最老的数据
+        uint32_t overflow = length - available_space;
+        if (overflow < g_at_accumulate_length) {
+            // 移除最老的数据
+            memmove(g_at_accumulate_buffer, 
+                    g_at_accumulate_buffer + overflow,
+                    g_at_accumulate_length - overflow);
+            g_at_accumulate_length -= overflow;
+            PPP_DEBUG_WARN("[累积缓冲区] 空间不足,丢弃最老的 %u 字节", overflow);
+        } else {
+            // 缓冲区完全清空
+            g_at_accumulate_length = 0;
+            PPP_DEBUG_WARN("[累积缓冲区] 空间不足,清空缓冲区");
+        }
+    }
+    
+    // 追加新数据
+    if (g_at_accumulate_length + length <= N706A_AT_ACCUMULATE_BUFFER_SIZE) {
+        memcpy(g_at_accumulate_buffer + g_at_accumulate_length, data, length);
+        g_at_accumulate_length += length;
+        g_at_accumulate_last_update_time = current_jiffies;  // 更新最后更新时间(tick数)
+        
+        // 打印接收到的数据的前几个字节(用于调试+TCPSEND URC问题)
+        if (length > 0) {
+            char hex_preview[64] = {0};
+            uint32_t preview_len = (length < 16) ? length : 16;
+            for (uint32_t i = 0; i < preview_len; i++) {
+                snprintf(hex_preview + i * 3, 4, "%02X ", data[i]);
+            }
+            // 检查是否包含+TCPSEND相关数据
+            const char *data_str = (const char *)data;
+            if (strstr(data_str, "TCPSEND") != NULL || strstr(data_str, "TCPSETUP") != NULL) {
+                PPP_DEBUG_INFO("[累积缓冲区] 追加 %u 字节,总长度=%u,数据预览: %s (包含TCP相关URC)", 
+                              length, g_at_accumulate_length, hex_preview);
+            } else {
+                PPP_DEBUG_DEBUG("[累积缓冲区] 追加 %u 字节,总长度=%u,数据预览: %s", 
+                              length, g_at_accumulate_length, hex_preview);
+            }
+        } else {
+            PPP_DEBUG_DEBUG("[累积缓冲区] 追加 %u 字节,总长度=%u", length, g_at_accumulate_length);
+        }
+    } else {
+        PPP_DEBUG_ERROR("[累积缓冲区] 追加失败:空间不足 (当前=%u, 需要=%u)", 
+                       g_at_accumulate_length, length);
+    }
+    
+    osal_mutex_unlock(&g_at_accumulate_mutex);
+    
+    /* ========== 第二步:循环提取和处理完整的AT响应 ========== */
+    while (true) {
+        uint32_t response_start = 0;
+        uint32_t response_len = 0;
+        
+        // 提取完整的AT响应
+        if (!n706a_extract_complete_response(&response_start, &response_len)) {
+            // 数据不完整,等待更多数据
+            break;
+        }
+        
+        // 检查是否有无效数据需要清除(用户方法:如果第一个完整响应的起始位置不是0,清除前面的无效数据)
+        if (response_start > 0) {
+            // 检查 response_start 之前是否有非空白字符(无效数据)
+            const char *buffer = (const char *)g_at_accumulate_buffer;
+            bool has_invalid_data = false;
+            for (uint32_t i = 0; i < response_start; i++) {
+                if (buffer[i] != '\r' && buffer[i] != '\n' && buffer[i] != ' ') {
+                    // 发现非空白字符,说明前面有无效数据
+                    has_invalid_data = true;
+                    break;
+                }
+            }
+            
+            if (has_invalid_data) {
+                // 清除前面的无效数据(从0到response_start)
+                PPP_DEBUG_WARN("[累积缓冲区] 检测到开头无效数据,清除 %u 字节", response_start);
+                n706a_remove_processed_data(response_start);
+                // 重新计算 response_start(现在应该是0,因为前面的无效数据已被清除)
+                response_start = 0;
+            }
+        }
+        
+        // 获取响应数据指针
+        const uint8_t *response = g_at_accumulate_buffer + response_start;
+        
+        PPP_DEBUG_DEBUG("[AT响应处理] 提取到完整响应: start=%u, len=%u", response_start, response_len);
+        /* 调试:检查响应开头是否还有 \x00 */
+        if (response_len > 0 && response[0] == '\0') {
+            PPP_DEBUG_WARN("[AT响应处理] WARNING: 响应开头仍然是 \\x00! response_start=%u, response[0]=0x%02X", 
+                          response_start, (unsigned char)response[0]);
+        }
+        
+        /* ========== 第三步:逐个函数判定 ========== */
+        bool is_urc = n706a_classify_and_dispatch_urc_improved(response, response_len);
+        
+        if (is_urc) {
+            // URC已处理,但需要检查响应中是否还包含OK(AT命令响应)
+            // 例如:AT+TCPSETUP的响应可能是:\r\n+TCPSETUP: 0,OK\r\nOK\r\n
+            // 这种情况下,URC被处理了,但OK响应需要写入通用缓冲区
+            // 
+            // 注意:需要区分URC中的"OK"(如+TCPSETUP: 0,OK)和独立的OK响应
+            // 独立的OK响应格式:\r\nOK\r\n 或 \r\nOK(响应结尾)
+            const char *response_str = (const char *)response;
+            bool has_ok = false;
+            bool has_command_echo = false;  // 检查是否包含命令回显(说明是AT命令的响应,不是异步URC)
+            bool is_command_response_urc = false;  // 检查是否是命令响应中的URC(如+TCPCLOSE: ERR或+TCPCLOSE: 0,OK)
+            
+            // 检查是否是命令响应中的URC(这些URC格式通常表示对命令的响应,而不是异步URC)
+            // 例如:+TCPCLOSE: ERR, +TCPCLOSE: 0,OK 等
+            // 注意:+TCPSETUP: 0,OK 不应该作为命令响应处理,因为它是异步URC
+            // 只有 +TCPCLOSE 和 +SSLTCPCLOSE 的响应格式才作为命令响应处理
+            const char *command_response_urc_patterns[] = {
+                "+TCPCLOSE: ERR", "+TCPCLOSE: 0,OK", "+TCPCLOSE: ERROR",
+                "+SSLTCPCLOSE: ERR", "+SSLTCPCLOSE: 0,OK", "+SSLTCPCLOSE: ERROR"
+            };
+            for (size_t k = 0; k < sizeof(command_response_urc_patterns) / sizeof(command_response_urc_patterns[0]); k++) {
+                if (strstr(response_str, command_response_urc_patterns[k]) != NULL) {
+                    is_command_response_urc = true;
+                    PPP_DEBUG_DEBUG("[AT响应处理] 检测到命令响应URC格式: %s,这是AT命令的响应", command_response_urc_patterns[k]);
+                    break;
+                }
+            }
+            
+            // 特殊处理:+TCPSETUP 和 +SSLTCPSETUP 是异步URC,不应该作为命令响应
+            // 这些URC应该通过信号量机制处理,而不是写入通用AT响应缓冲区
+            // 即使响应中包含命令回显(可能是之前命令的回显),也不应该作为命令响应处理
+            bool is_tcpsetup_urc = false;
+            if (strstr(response_str, "+TCPSETUP: ") != NULL || 
+                strstr(response_str, "+SSLTCPSETUP: ") != NULL) {
+                is_tcpsetup_urc = true;
+                // 这些是异步URC,不应该写入通用AT响应缓冲区
+                PPP_DEBUG_DEBUG("[AT响应处理] +TCPSETUP/+SSLTCPSETUP URC是异步URC,不写入通用AT响应缓冲区");
+                is_command_response_urc = false;  // 确保不作为命令响应处理
+                has_command_echo = false;  // 即使有命令回显,也不作为命令响应处理
+            }
+            
+            // 查找所有可能的URC位置(用于排除URC中的OK)
+            const char *urc_markers[] = {
+                "+TCPRECV: ", "+SSLTCPRECV: ",
+                "+TCPSETUP: ", "+SSLTCPSETUP: ",
+                "+TCPCLOSE: ", "+SSLTCPCLOSE: ",
+                "+TCPSEND: ", "+SSLTCPEND: ",
+                "+HTTPRECV: ", "+HTTPSRECV: ",
+                "+CREG: ", "+CGREG: ", "+CGATT: "
+            };
+            const char *urc_start_pos = NULL;
+            const char *urc_end_pos = NULL;
+            
+            // 找到第一个URC的位置
+            for (size_t j = 0; j < sizeof(urc_markers) / sizeof(urc_markers[0]); j++) {
+                const char *pos = strstr(response_str, urc_markers[j]);
+                if (pos && (!urc_start_pos || pos < urc_start_pos)) {
+                    urc_start_pos = pos;
+                    // 找到URC的结束位置(下一个\r\n或\r\r)
+                    urc_end_pos = strstr(urc_start_pos, "\r\n");
+                    if (!urc_end_pos) {
+                        urc_end_pos = strstr(urc_start_pos, "\r\r");
+                    }
+                    if (!urc_end_pos) {
+                        urc_end_pos = strstr(urc_start_pos, "\n");
+                    }
+                    if (!urc_end_pos) {
+                        urc_end_pos = response_str + response_len;
+                    } else {
+                        urc_end_pos += 2;  // 包含\r\n或\r\r
+                    }
+                }
+            }
+            
+            // 检查响应中是否包含命令回显(如 "AT+TCPCLOSE=0\r\n")
+            // 如果包含命令回显,说明这是AT命令的响应,而不是异步URC
+            // 常见的命令回显格式:AT+XXX\r\n 或 AT+XXX=...\r\n
+            // 但是,+TCPSETUP URC是异步的,即使有命令回显也不应该作为命令响应处理
+            if (!is_tcpsetup_urc && strstr(response_str, "AT+") != NULL) {
+                has_command_echo = true;
+                PPP_DEBUG_DEBUG("[AT响应处理] 检测到命令回显,这是AT命令的响应,不是异步URC");
+            }
+            
+            // 检查响应中是否包含独立的OK(不在URC中)
+            // 查找 "\r\nOK\r\n" 或 "\r\nOK"(响应结尾)
+            for (uint32_t i = 0; i + 1 < response_len; i++) {
+                if (response_str[i] == '\r' && response_str[i+1] == '\n') {
+                    if (i + 3 < response_len && 
+                        response_str[i+2] == 'O' && response_str[i+3] == 'K') {
+                        // 检查OK后面是否是结束符或换行符
+                        if (i + 4 >= response_len || 
+                            response_str[i+4] == '\r' || response_str[i+4] == '\n') {
+                            // 检查这个OK是否在URC范围内
+                            const char *ok_pos = response_str + i + 2;  // OK的起始位置
+                            if (!urc_start_pos || ok_pos < urc_start_pos || ok_pos >= urc_end_pos) {
+                                // OK不在URC范围内,是独立的OK响应
+                                has_ok = true;
+                                break;
+                            }
+                            // OK在URC范围内(如+TCPSETUP: 0,OK),跳过
+                        }
+                    }
+                }
+            }
+            
+            // 特殊处理:如果响应包含 +TCPRECV 或 +SSLTCPRECV URC,且响应长度超过环形缓冲区大小
+            // 这种情况下,响应应该直接从累积缓冲区提取(n706a_tcp_recv 会处理),而不是写入环形缓冲区
+            // 因为环形缓冲区只有 4KB,无法容纳包含大量 HEX 数据的响应
+            bool is_tcprecv_urc = (strstr(response_str, "+TCPRECV: ") != NULL || 
+                                   strstr(response_str, "+SSLTCPRECV: ") != NULL);
+            bool response_too_large = (response_len > N706A_AT_RESPONSE_RING_BUFFER_SIZE);
+            
+            // 如果包含命令回显、独立的OK,或者是命令响应中的URC,说明这是AT命令的响应,需要写入通用缓冲区
+            // 但是,+TCPSETUP URC是异步的,即使有命令回显也不应该作为命令响应处理
+            // 如果响应包含 +TCPRECV URC 且响应太大,不应该写入环形缓冲区(会导致溢出),应该直接从累积缓冲区提取
+            if (!is_tcpsetup_urc && (has_ok || has_command_echo || is_command_response_urc)) {
+                // 检查是否是包含 +TCPRECV URC 的大响应
+                if (is_tcprecv_urc && response_too_large) {
+                    // 这是包含 +TCPRECV URC 的大响应(如 AT+TCPREAD 响应),不应该写入环形缓冲区
+                    // n706a_tcp_recv 会直接从累积缓冲区提取数据
+                    PPP_DEBUG_INFO("[AT响应处理] 响应包含 +TCPRECV URC 且响应太大 (%u bytes > %u bytes),不写入环形缓冲区,由 n706a_tcp_recv 直接从累积缓冲区提取", 
+                                  response_len, N706A_AT_RESPONSE_RING_BUFFER_SIZE);
+                } else {
+                    // 响应中包含OK、命令回显或命令响应URC,需要写入通用AT响应缓冲区
+                    if (has_ok) {
+                        PPP_DEBUG_DEBUG("[AT响应处理] URC已处理,但响应中包含OK,写入通用AT响应缓冲区");
+                    } else if (has_command_echo) {
+                        PPP_DEBUG_DEBUG("[AT响应处理] URC已处理,但响应中包含命令回显(AT命令响应),写入通用AT响应缓冲区");
+                    } else if (is_command_response_urc) {
+                        PPP_DEBUG_DEBUG("[AT响应处理] URC已处理,但这是命令响应中的URC(如+TCPCLOSE: ERR),写入通用AT响应缓冲区");
+                    }
+                    
+                    osal_mutex_lock(&g_at_response_mutex);
+                    
+                    // 检查响应大小,如果仍然太大,只写入部分数据(避免溢出)
+                    uint32_t available_space = (g_at_response_head >= g_at_response_tail) ? 
+                                               (N706A_AT_RESPONSE_RING_BUFFER_SIZE - (g_at_response_head - g_at_response_tail) - 1) :
+                                               (g_at_response_tail - g_at_response_head - 1);
+                    
+                    uint32_t write_len = (response_len <= available_space) ? response_len : available_space;
+                    if (write_len < response_len) {
+                        PPP_DEBUG_WARN("[AT响应处理] 响应太大 (%u bytes),环形缓冲区空间不足 (%u bytes available),只写入前 %u 字节", 
+                                      response_len, available_space, write_len);
+                    }
+                    
+                    for (uint32_t i = 0; i < write_len; i++) {
+                        g_at_response_ring_buffer[g_at_response_head] = response[i];
+                        g_at_response_head = (g_at_response_head + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
+                        
+                        // 检查溢出(缓冲区满时丢弃最老的数据)
+                        if (g_at_response_head == g_at_response_tail) {
+                            g_at_response_tail = (g_at_response_tail + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
+                            PPP_DEBUG_WARN("AT response buffer overflow, dropping oldest byte");
+                        }
+                    }
+                    
+                    osal_mutex_unlock(&g_at_response_mutex);
+                    
+                    // 通知等待线程有新数据
+                    osal_sem_up(&g_at_response_sem);
+                }
+            } else {
+                // 纯URC,没有OK响应
+                PPP_DEBUG_DEBUG("[AT响应处理] URC已处理(纯URC,无OK响应)");
+            }
+            
+            // 清除数据
+            n706a_remove_processed_data(response_start + response_len);
+            // 继续处理剩余数据
+            continue;
+        }
+        
+        /* ========== 第四步:通用AT指令处理 ========== */
+        // 未识别为URC,写入环形缓冲区供AT命令响应读取
+        PPP_DEBUG_DEBUG("[AT响应处理] 未识别为URC,写入通用AT响应缓冲区");
+        
+        // 检查响应大小,如果太大,只写入部分数据(避免溢出)
+        osal_mutex_lock(&g_at_response_mutex);
+        
+        uint32_t available_space = (g_at_response_head >= g_at_response_tail) ? 
+                                   (N706A_AT_RESPONSE_RING_BUFFER_SIZE - (g_at_response_head - g_at_response_tail) - 1) :
+                                   (g_at_response_tail - g_at_response_head - 1);
+        
+        uint32_t write_len = (response_len <= available_space) ? response_len : available_space;
+        if (write_len < response_len) {
+            PPP_DEBUG_WARN("[AT响应处理] 响应太大 (%u bytes),环形缓冲区空间不足 (%u bytes available),只写入前 %u 字节", 
+                          response_len, available_space, write_len);
+        }
+        
+        for (uint32_t i = 0; i < write_len; i++) {
+            g_at_response_ring_buffer[g_at_response_head] = response[i];
+            g_at_response_head = (g_at_response_head + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
+            
+            // 检查溢出(缓冲区满时丢弃最老的数据)
+            if (g_at_response_head == g_at_response_tail) {
+                g_at_response_tail = (g_at_response_tail + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
+                PPP_DEBUG_WARN("AT response buffer overflow, dropping oldest byte");
+            }
+        }
+        
+        osal_mutex_unlock(&g_at_response_mutex);
+        
+        // 通知等待线程有新数据
+        osal_sem_up(&g_at_response_sem);
+        
+        // 清除已处理的数据(从0到response_start+response_len)
+        n706a_remove_processed_data(response_start + response_len);
+        
+        // 继续处理剩余数据
+    }
+    
+    PPP_DEBUG_DEBUG("[AT响应缓冲区] 处理完成,累积缓冲区剩余 %u 字节", g_at_accumulate_length);
+#endif  /* USE_STREAM_PARSER */
+}
+
+/**
+ * @brief 初始化AT命令处理模块
+ */
+ppp_error_t ppp_at_handler_init(uint8_t uart_bus, uint32_t current_baudrate, bool auto_baudrate_mode)
+{
+    if (g_at_ctx.initialized) {
+        PPP_DEBUG_WARN("AT handler already initialized");
+        return PPP_ERR_OK;
+    }
+
+    g_at_ctx.uart_bus = uart_bus;
+    g_at_ctx.current_baudrate = current_baudrate;
+    g_at_ctx.auto_baudrate_mode = auto_baudrate_mode;
+
+    /* 创建AT命令互斥锁 */
+    if (osal_mutex_init(&g_at_ctx.at_mutex) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to create AT mutex");
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 创建AT响应缓冲区互斥锁 */
+    if (osal_mutex_init(&g_at_response_mutex) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to create AT response mutex");
+        osal_mutex_destroy(&g_at_ctx.at_mutex);
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 创建AT响应信号量 */
+    if (osal_sem_init(&g_at_response_sem, 0) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to create AT response semaphore");
+        osal_mutex_destroy(&g_at_ctx.at_mutex);
+        osal_mutex_destroy(&g_at_response_mutex);
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 创建累积缓冲区互斥锁 */
+    if (osal_mutex_init(&g_at_accumulate_mutex) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to create AT accumulate mutex");
+        osal_mutex_destroy(&g_at_ctx.at_mutex);
+        osal_mutex_destroy(&g_at_response_mutex);
+        osal_sem_destroy(&g_at_response_sem);
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 初始化环形缓冲区 */
+    g_at_response_head = 0;
+    g_at_response_tail = 0;
+    memset(g_at_response_ring_buffer, 0, sizeof(g_at_response_ring_buffer));
+    
+    /* 初始化累积缓冲区 */
+    g_at_accumulate_length = 0;
+    g_at_accumulate_last_update_time = 0;
+    memset(g_at_accumulate_buffer, 0, sizeof(g_at_accumulate_buffer));
+    
+#if USE_STREAM_PARSER
+    /* 初始化流式解析器(新方案) */
+    n706a_stream_parser_init();
+    PPP_DEBUG_INFO("Stream parser initialized (new scheme)");
+#else
+    PPP_DEBUG_INFO("Using legacy parser (old scheme)");
+#endif
+
+    /* 分配全局AT响应缓存 */
+    g_at_response_buffer = (uint8_t *)malloc(g_at_buffer_size);
+    if (g_at_response_buffer == NULL) {
+        PPP_DEBUG_ERROR("Failed to allocate AT response buffer");
+        osal_mutex_destroy(&g_at_ctx.at_mutex);
+        osal_mutex_destroy(&g_at_response_mutex);
+        osal_sem_destroy(&g_at_response_sem);
+        osal_mutex_destroy(&g_at_accumulate_mutex);
+        return PPP_ERR_MEMORY;
+    }
+    
+    /* 分配AT命令解析临时缓冲区(避免频繁malloc/free,但只在初始化时分配一次) */
+    g_at_cmd_buffer = (char *)malloc(N706A_AT_CMD_MAX_LEN + 4);
+    g_at_printable_buffer = (char *)malloc(PPP_AT_RESPONSE_MAX_LEN + 100);
+    g_at_command_response_buffer = (char *)malloc(512);
+    
+    if (!g_at_cmd_buffer || !g_at_printable_buffer || !g_at_command_response_buffer) {
+        PPP_DEBUG_ERROR("Failed to allocate AT parsing buffers");
+        // 清理已分配的内存
+        if (g_at_cmd_buffer) {
+            free(g_at_cmd_buffer);
+            g_at_cmd_buffer = NULL;
+        }
+        if (g_at_printable_buffer) {
+            free(g_at_printable_buffer);
+            g_at_printable_buffer = NULL;
+        }
+        if (g_at_command_response_buffer) {
+            free(g_at_command_response_buffer);
+            g_at_command_response_buffer = NULL;
+        }
+        if (g_at_response_buffer) {
+            free(g_at_response_buffer);
+            g_at_response_buffer = NULL;
+        }
+        osal_mutex_destroy(&g_at_ctx.at_mutex);
+        osal_mutex_destroy(&g_at_response_mutex);
+        osal_sem_destroy(&g_at_response_sem);
+        osal_mutex_destroy(&g_at_accumulate_mutex);
+        return PPP_ERR_MEMORY;
+    }
+    
+    PPP_DEBUG_INFO("Allocated AT parsing buffers: cmd=%u, printable=%u, response=%u bytes (total ~1.9KB)", 
+                  (unsigned int)(N706A_AT_CMD_MAX_LEN + 4),
+                  (unsigned int)(PPP_AT_RESPONSE_MAX_LEN + 100),
+                  (unsigned int)512);
+
+    g_at_ctx.initialized = true;
+    PPP_DEBUG_INFO("n706a_net AT handler initialized on UART%d with async ring buffer", uart_bus);
+    
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 反初始化AT命令处理模块
+ */
+void ppp_at_handler_deinit(void)
+{
+    if (!g_at_ctx.initialized) {
+        return;
+    }
+
+    /* 销毁互斥锁和信号量 */
+    osal_mutex_destroy(&g_at_ctx.at_mutex);
+    osal_mutex_destroy(&g_at_response_mutex);
+    osal_sem_destroy(&g_at_response_sem);
+    osal_mutex_destroy(&g_at_accumulate_mutex);
+
+    /* 释放全局AT响应缓存 */
+    if (g_at_response_buffer) {
+        free(g_at_response_buffer);
+        g_at_response_buffer = NULL;
+    }
+    
+    /* 释放AT命令解析临时缓冲区 */
+    if (g_at_cmd_buffer) {
+        free(g_at_cmd_buffer);
+        g_at_cmd_buffer = NULL;
+    }
+    if (g_at_printable_buffer) {
+        free(g_at_printable_buffer);
+        g_at_printable_buffer = NULL;
+    }
+    if (g_at_command_response_buffer) {
+        free(g_at_command_response_buffer);
+        g_at_command_response_buffer = NULL;
+    }
+
+    /* 清空环形缓冲区 */
+    g_at_response_head = 0;
+    g_at_response_tail = 0;
+
+    memset_s(&g_at_ctx, sizeof(g_at_ctx), 0, sizeof(g_at_ctx));
+    PPP_DEBUG_INFO("n706a_net AT handler deinitialized");
+}
+
+/**
+ * @brief 执行波特率自适应握手
+ *
+ * 进入自适应波特率,证明1000000没有正常工作,需要先reset,等5秒,开始设置自动握手的第一个波特率,
+ * 读取所有缓存数据丢掉,下达at指令,等待结果,重试3次,如果不成功,就设置第二个波特率,读取所有缓存数据丢掉,
+ * 下达at指令,等结果,重试3次,直至第三个波特率检测。如果都没有成功,则返回失败。
+ * 如果任何一次at指令返回正确ok,直接在当前设置下,下达指 AT+IPR 配置 1000000 波特率,并返回成功。
+ * 设置当前波特率为1000000,再次reset 模组,让其自动工作在 1000000波特率下。
+ */
+ppp_error_t ppp_at_baudrate_handshake(void)
+{
+    if (!g_at_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_INFO("Starting baudrate handshake (prove %d not working, then try auto handshake)",g_at_ctx.current_baudrate);
+
+    // uint8_t dummy_buffer[256]={0};
+
+    // 2. 依次尝试自动波特率列表
+    for (int i = 0; i < PPP_BAUDRATE_AUTO_COUNT; i++) {
+        uint32_t baudrate = g_auto_baudrates[i];
+        PPP_DEBUG_DEBUG("=== Trying Baudrate %u (index %d/%d) ===", baudrate, i + 1, PPP_BAUDRATE_AUTO_COUNT);
+
+        // 配置串口波特率
+        uart_attr_t uart_attr = {
+            .baud_rate = baudrate,
+            .data_bits = UART_DATA_BIT_8,
+            .parity = UART_PARITY_NONE,
+            .stop_bits = UART_STOP_BIT_1,
+        };
+        if (uapi_uart_set_attr(g_at_ctx.uart_bus, &uart_attr) != ERRCODE_SUCC) {
+            PPP_DEBUG_WARN("Failed to set baudrate %u", baudrate);
+            continue;
+        }
+        PPP_DEBUG_DEBUG("UART configured for baudrate %u", baudrate);
+
+        // 清空接收缓冲区
+        // PPP_DEBUG_DEBUG("Clearing UART RX buffer before AT test...");
+        // int clear_cnt = 0;
+        // memset(dummy_buffer,0,sizeof(dummy_buffer));
+        // while (uapi_uart_read(g_at_ctx.uart_bus, dummy_buffer, sizeof(dummy_buffer), 100) > 0) {
+        //     clear_cnt++;
+        // }
+        // if (clear_cnt > 0) {
+        //     PPP_DEBUG_DEBUG("Cleared %d RX buffer rounds", clear_cnt);
+        // }
+
+        // 3次重试AT
+        ppp_error_t ret = PPP_ERR_AT_FAILED;
+        for (int retry = 0; retry < 2; retry++) {
+            PPP_DEBUG_DEBUG("--- AT Test at %u baud, retry %d/3 ---", baudrate, retry + 1);
+
+            // 再次清空缓冲区,防止上次残留
+        // memset(dummy_buffer,0,sizeof(dummy_buffer));
+        // while (uapi_uart_read(g_at_ctx.uart_bus, dummy_buffer, sizeof(dummy_buffer), 100) > 0) {}
+
+            ppp_at_response_t response;
+            ret = ppp_at_send_command("AT", "OK", 1000, &response);
+
+            if (ret == PPP_ERR_OK) {
+                PPP_DEBUG_INFO("AT command successful at baudrate %u", baudrate);
+
+                // 在当前波特率下,设置AT+IPR=1000000
+               ret = ppp_at_set_baudrate(g_at_ctx.current_baudrate);
+               if (ret != PPP_ERR_OK) {
                     ret = ppp_at_set_baudrate(g_at_ctx.current_baudrate);
                     if (ret != PPP_ERR_OK) {
                         PPP_DEBUG_ERROR("Failed to set baudrate %u", g_at_ctx.current_baudrate);
                         return ret;
                     }
                 }
-                // 再次reset模组,让其自动工作在1000000波特率下
-                PPP_DEBUG_DEBUG("Resetting module again to switch to baudrate %d...",g_at_ctx.current_baudrate);
-                // ppp_power_reset_cat1();               
-                // osal_msleep(5000);
-                ret = ppp_at_send_command("AT", "OK", 1000, &response);
-                if(ret != PPP_ERR_OK) {
-                    PPP_DEBUG_INFO("Baudrate handshake failed, now at %d baud",g_at_ctx.current_baudrate);
-                }else{
-                    PPP_DEBUG_INFO("Baudrate handshake successful, now at %d baud",g_at_ctx.current_baudrate);
+                // 再次reset模组,让其自动工作在1000000波特率下
+                PPP_DEBUG_DEBUG("Resetting module again to switch to baudrate %d...",g_at_ctx.current_baudrate);
+                // ppp_power_reset_cat1();               
+                // osal_msleep(5000);
+                ret = ppp_at_send_command("AT", "OK", 1000, &response);
+                if(ret != PPP_ERR_OK) {
+                    PPP_DEBUG_INFO("Baudrate handshake failed, now at %d baud",g_at_ctx.current_baudrate);
+                }else{
+                    PPP_DEBUG_INFO("Baudrate handshake successful, now at %d baud",g_at_ctx.current_baudrate);
+                }
+                return PPP_ERR_OK;
+            } else {
+                PPP_DEBUG_WARN("AT command failed at baudrate %u, retry %d/3 (error: %d)", baudrate, retry + 1, ret);
+                osal_msleep(500);
+            }
+        }
+
+        PPP_DEBUG_WARN("Baudrate %u failed after 3 retries, trying next...", baudrate);
+        osal_msleep(100);
+    }
+
+    PPP_DEBUG_ERROR("Baudrate handshake failed: all baudrates tried, no success");
+    return PPP_ERR_AT_FAILED;
+}
+
+/**
+ * @brief 解析AT+IPR?响应,提取当前波特率
+ * @param response AT+IPR?的响应字符串
+ * @return 解析出的波特率,失败返回0
+ */
+static uint32_t ppp_parse_ipr_response(const char *response)
+{
+    if (!response) {
+        return 0;
+    }
+    
+    PPP_DEBUG_DEBUG("Parsing IPR response: [%s]", response);
+    
+    /* 查找 "+IPR: " 字符串 */
+    const char *ipr_pos = strstr(response, "+IPR: ");
+    if (!ipr_pos) {
+        PPP_DEBUG_WARN("IPR response format error: no '+IPR: ' found in response");
+        return 0;
+    }
+    
+    /* 跳过 "+IPR: " 部分 */
+    ipr_pos += 6;
+    
+    /* 跳过可能的空格 */
+    while (*ipr_pos == ' ' || *ipr_pos == '\t') {
+        ipr_pos++;
+    }
+    
+    /* 解析波特率数字,只读取数字部分 */
+    uint32_t baudrate = 0;
+    const char *num_start = ipr_pos;
+    
+    /* 找到数字的结束位置(遇到非数字字符停止) */
+    while (*ipr_pos >= '0' && *ipr_pos <= '9') {
+        baudrate = baudrate * 10 + (*ipr_pos - '0');
+        ipr_pos++;
+    }
+    
+    /* 检查是否成功解析到数字 */
+    if (ipr_pos == num_start) {
+        PPP_DEBUG_WARN("IPR response format error: no valid number found after '+IPR: '");
+        return 0;
+    }
+    
+    /* 验证波特率是否合理 */
+    if (baudrate < 1200 || baudrate > 4000000) {
+        PPP_DEBUG_WARN("IPR response contains unreasonable baudrate: %u", baudrate);
+        return 0;
+    }
+    
+    PPP_DEBUG_DEBUG("Successfully parsed current baudrate from IPR response: %u", baudrate);
+    return baudrate;
+}
+
+/**
+ * @brief 查询当前modem波特率
+ * @return 当前modem波特率,失败返回0
+ */
+static uint32_t ppp_query_current_modem_baudrate(void)
+{
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command("AT+IPR?", "OK", PPP_AT_CMD_TIMEOUT, &response);
+    
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_WARN("Failed to query current baudrate with AT+IPR?, error: %d", ret);
+        return 0;
+    }
+    
+    return ppp_parse_ipr_response(response.response);
+}
+
+/**
+ * @brief 设置固定波特率(内部函数)
+ */
+static ppp_error_t ppp_at_set_baudrate(uint32_t baudrate)
+{
+    if (!g_at_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    /* 检查波特率是否支持 */
+    bool supported = false;
+    for (int i = 0; i < PPP_BAUDRATE_FIXED_COUNT; i++) {
+        if (g_fixed_baudrates[i] == baudrate) {
+            supported = true;
+            break;
+        }
+    }
+
+    if (!supported) {
+        PPP_DEBUG_ERROR("Unsupported baudrate: %u", baudrate);
+        return PPP_ERR_INVALID_PARAM;
+    }
+#if defined(PPP_DO_SERIAL_TEST) && (PPP_DO_SERIAL_TEST == 1)
+    uint32_t current_modem_baudrate = baudrate; // 测试模式下,直接使用输入的波特率
+#else
+    /* 先查询当前modem波特率 */
+    uint32_t current_modem_baudrate = ppp_query_current_modem_baudrate();
+    if (current_modem_baudrate == baudrate) {
+        PPP_DEBUG_INFO("Modem already at target baudrate %u, skipping AT+IPR command", baudrate);
+        
+        /* 仍需要更新本地UART配置以确保一致性 */
+        uart_attr_t uart_attr = {
+            .baud_rate = baudrate,
+            .data_bits = UART_DATA_BIT_8,
+            .parity = UART_PARITY_NONE,
+            .stop_bits = UART_STOP_BIT_1,
+        };
+        
+        if (uapi_uart_set_attr(g_at_ctx.uart_bus, &uart_attr) != ERRCODE_SUCC) {
+            PPP_DEBUG_ERROR("Failed to update UART baudrate");
+            return PPP_ERR_UART;
+        }
+
+        g_at_ctx.current_baudrate = baudrate;
+        g_at_ctx.auto_baudrate_mode = false;
+        
+        PPP_DEBUG_DEBUG("Baudrate confirmed at %u (no change needed)", baudrate);
+        return PPP_ERR_OK;
+    }
+
+    /* 需要设置新波特率 */
+    PPP_DEBUG_INFO("Changing modem baudrate from %u to %u", current_modem_baudrate, baudrate);
+    
+    /* 发送AT+IPR命令设置波特率 */
+    char cmd[64];
+    snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "AT+IPR=%u", baudrate);
+    
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command(cmd, "OK", PPP_AT_CMD_TIMEOUT, &response);
+    
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to set baudrate to %u", baudrate);
+        return ret;
+    }
+#endif
+    /* 更新串口配置 */
+    uart_attr_t uart_attr = {
+        .baud_rate = baudrate,
+        .data_bits = UART_DATA_BIT_8,
+        .parity = UART_PARITY_NONE,
+        .stop_bits = UART_STOP_BIT_1,
+    };
+    
+    if (uapi_uart_set_attr(g_at_ctx.uart_bus, &uart_attr) != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to update UART baudrate");
+        return PPP_ERR_UART;
+    }
+
+    g_at_ctx.current_baudrate = baudrate;
+    g_at_ctx.auto_baudrate_mode = false;
+    
+    PPP_DEBUG_DEBUG("Baudrate set bus(%d) to %u (changed from %u)", g_at_ctx.uart_bus, baudrate, current_modem_baudrate);
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 发送AT命令(内部函数)
+ * @note 检查内存泄露:所有malloc分配的内存都应在所有分支free
+ */
+static ppp_error_t ppp_at_send_command(const char *command, const char *expected_response, 
+                                uint32_t timeout, ppp_at_response_t *response)
+{
+    /* 优化:使用单个时间戳结构体,减少栈使用 */
+    struct {
+        uint32_t start;           // 开始时间戳
+        uint32_t mutex_acquire;   // 互斥锁获取时间
+        uint32_t mutex_release;   // 互斥锁释放时间
+        uint32_t response_wait;   // 响应等待时间
+        uint32_t mutex_reacquire; // 重新获取互斥锁时间
+        uint32_t uart_write;      // UART写入时间
+        uint32_t mutex_unlock;    // 互斥锁解锁时间
+    } timestamps = {0};
+    
+    timestamps.start = (uint32_t)osal_get_jiffies();
+
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("=== AT COMMAND START ===");
+    PPP_DEBUG_CRITICAL("Timestamp: %u", timestamps.start);
+    PPP_DEBUG_CRITICAL("Command: [%s]", command ? command : "NULL");
+    PPP_DEBUG_CRITICAL("Expected: [%s]", expected_response ? expected_response : "NULL");
+    PPP_DEBUG_CRITICAL("Timeout: %u ms", timeout);
+#else
+    /* Release版本:只记录关键信息 */
+    PPP_DEBUG_CRITICAL("AT CMD: [%s]", command ? command : "NULL");
+#endif
+
+    if (!g_at_ctx.initialized || !command || !response) {
+        PPP_DEBUG_ERROR("Invalid parameters: initialized=%d, command=%p, response=%p", 
+                       g_at_ctx.initialized, command, response);
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    /* 防止系统卡死的安全措施 */
+    const uint32_t MAX_COMMAND_TIMEOUT = 60000;  // 绝对最大超时时间60秒
+    if (timeout > MAX_COMMAND_TIMEOUT) {
+        PPP_DEBUG_WARN("Command timeout %u ms exceeds maximum %u ms, capping it", 
+                       timeout, MAX_COMMAND_TIMEOUT);
+        timeout = MAX_COMMAND_TIMEOUT;
+    }
+
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 1: Attempting to acquire AT mutex...");
+#endif
+    timestamps.mutex_acquire = (uint32_t)osal_get_jiffies();
+
+    /* 使用带超时的互斥锁获取,防止永久阻塞 */
+    int mutex_result = osal_mutex_lock_timeout(&g_at_ctx.at_mutex, timeout + 5000);
+    if (mutex_result != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to acquire AT mutex within timeout, this indicates a serious problem");
+        PPP_DEBUG_ERROR("Possible causes: 1) Previous AT command hung 2) Mutex corruption 3) Deadlock");
+        return PPP_ERR_TIMEOUT;
+    }
+
+    uint32_t mutex_acquire_end = (uint32_t)osal_get_jiffies();
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 1: AT mutex acquired in %u ms", 
+                      osal_jiffies_to_msecs(mutex_acquire_end - timestamps.mutex_acquire));
+#endif
+
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_DEBUG("=== AT Command Send ===");
+    PPP_DEBUG_DEBUG("Command: %s", command);
+    PPP_DEBUG_DEBUG("Expected: %s", expected_response ? expected_response : "None");
+    PPP_DEBUG_DEBUG("Timeout: %u ms", timeout);
+#endif
+
+    /* 清空响应缓冲区 */
+    memset_s(response, sizeof(ppp_at_response_t), 0, sizeof(ppp_at_response_t));
+    
+    /* 清空累积缓冲区(用于组装完整响应的临时缓冲区)
+     * 注意:累积缓冲区只用于组装响应,不存储已处理的URC数据
+     * 纯异步URC(如+TCPSETUP、+TCPRECV等)不会进入响应缓冲区,所以清空不会丢失
+     * 但是,命令响应中的URC(如+TCPCLOSE: ERR)会进入响应缓冲区,需要保留
+     * 因此,我们只清空累积缓冲区,不清空响应环形缓冲区
+     */
+    osal_mutex_lock(&g_at_accumulate_mutex);
+    g_at_accumulate_length = 0;
+    g_at_accumulate_last_update_time = 0;
+    memset(g_at_accumulate_buffer, 0, sizeof(g_at_accumulate_buffer));
+    osal_mutex_unlock(&g_at_accumulate_mutex);
+    
+    /* 读取并丢弃响应环形缓冲区中的旧数据(如果有)
+     * 这样可以避免旧响应干扰新命令,同时不会丢失正在等待的响应
+     * 注意:如果缓冲区中有数据,说明可能有之前的命令响应还在等待读取
+     * 这种情况下,我们应该先读取并丢弃,而不是直接清空
+     */
+    osal_mutex_lock(&g_at_response_mutex);
+    uint32_t buffer_data_count = (g_at_response_head >= g_at_response_tail) ? 
+                                 (g_at_response_head - g_at_response_tail) : 
+                                 (N706A_AT_RESPONSE_RING_BUFFER_SIZE - g_at_response_tail + g_at_response_head);
+    if (buffer_data_count > 0) {
+        PPP_DEBUG_WARN("Response ring buffer has %u bytes of old data, discarding before sending new command", buffer_data_count);
+        /* 清空响应环形缓冲区(丢弃旧数据)
+         * 注意:这些数据可能是之前命令的延迟响应,或者是命令响应中的URC
+         * 由于我们已经获取了互斥锁,说明没有其他线程在等待这些数据
+         * 如果这些数据是重要的,应该在发送新命令前被读取
+         */
+        g_at_response_head = 0;
+        g_at_response_tail = 0;
+    }
+    osal_mutex_unlock(&g_at_response_mutex);
+    
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_DEBUG("Cleared accumulate buffer and old response data before sending new command");
+#endif
+
+    /* 发送命令 */
+    size_t command_len = strlen(command);
+    // 使用动态分配的缓冲区(在初始化时分配,避免栈上分配大缓冲区)
+    if (!g_at_cmd_buffer) {
+        PPP_DEBUG_ERROR("AT command buffer not allocated (module not initialized?)");
+        osal_mutex_unlock(&g_at_ctx.at_mutex);
+        return PPP_ERR_MEMORY;
+    }
+    if (command_len >= (N706A_AT_CMD_MAX_LEN + 4) - 3) {
+        PPP_DEBUG_ERROR("AT command too long: %u bytes (max: %u)", 
+                       (unsigned int)command_len, (unsigned int)(N706A_AT_CMD_MAX_LEN + 1));
+        osal_mutex_unlock(&g_at_ctx.at_mutex);
+        return PPP_ERR_INVALID_PARAM;
+    }
+    snprintf_s(g_at_cmd_buffer, N706A_AT_CMD_MAX_LEN + 4, N706A_AT_CMD_MAX_LEN + 3, "%s\r\n", command);
+
+    uint32_t cmd_len = strlen(g_at_cmd_buffer);
+
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    /* 打印发送的字符串(可见字符)- 使用动态分配的缓冲区 */
+    if (g_at_printable_buffer) {
+        uint32_t j = 0;
+        for (uint32_t i = 0; i < cmd_len && j < (PPP_AT_RESPONSE_MAX_LEN + 100) - 10; i++) {
+            if (g_at_cmd_buffer[i] == '\r') {
+                g_at_printable_buffer[j++] = '\\';
+                g_at_printable_buffer[j++] = 'r';
+            } else if (g_at_cmd_buffer[i] == '\n') {
+                g_at_printable_buffer[j++] = '\\';
+                g_at_printable_buffer[j++] = 'n';
+            } else if (g_at_cmd_buffer[i] >= 32 && g_at_cmd_buffer[i] <= 126) {
+                g_at_printable_buffer[j++] = g_at_cmd_buffer[i];
+            } else {
+                j += snprintf_s(g_at_printable_buffer + j, (PPP_AT_RESPONSE_MAX_LEN + 100) - j, (PPP_AT_RESPONSE_MAX_LEN + 100) - j - 1, "\\x%02X", (uint8_t)g_at_cmd_buffer[i]);
+            }
+        }
+        g_at_printable_buffer[j] = '\0';
+        PPP_DEBUG_DEBUG("Send String: %s", g_at_printable_buffer);
+    }
+#endif
+
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 2: Sending AT command via ppp_serial_write...");
+#endif
+    
+    timestamps.uart_write = (uint32_t)osal_get_jiffies();
+
+    /* 使用 ppp_serial_write 发送 AT 命令(会自动处理 wakeup) */
+    ppp_error_t write_result = ppp_serial_write((const uint8_t *)g_at_cmd_buffer, cmd_len);
+    uint32_t uart_write_end = (uint32_t)osal_get_jiffies();
+
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 2: ppp_serial_write completed in %u ms, result: %d (expected: %u)", 
+                      osal_jiffies_to_msecs(uart_write_end - timestamps.uart_write), write_result, cmd_len);
+#endif
+
+    if (write_result != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to send AT command via ppp_serial_write: error=%d", write_result);
+        osal_mutex_unlock(&g_at_ctx.at_mutex);
+        return write_result;
+    }
+
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 2: AT command sent successfully");
+#endif
+
+    /* ========== 方案1优化:发送后立即释放互斥锁,允许其他命令发送 ========== */
+    /* 释放互斥锁,允许其他AT命令在等待响应期间发送
+     * 这样可以避免长时间阻塞(等待响应可能需要数秒)
+     * URC数据使用独立的 g_at_accumulate_mutex,不受影响
+     */
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 2.5: Releasing AT mutex before waiting for response (optimization: allow other commands)...");
+#endif
+    timestamps.mutex_release = (uint32_t)osal_get_jiffies();
+    osal_mutex_unlock(&g_at_ctx.at_mutex);
+    uint32_t mutex_release_end = (uint32_t)osal_get_jiffies();
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 2.5: AT mutex released in %u ms (will re-acquire after response)", 
+                      osal_jiffies_to_msecs(mutex_release_end - timestamps.mutex_release));
+#endif
+
+    /* 等待响应(不占用互斥锁,使用信号量机制)
+     * 注意:ppp_at_wait_response 使用信号量等待,响应是顺序的
+     * 即使有其他命令发送,响应也不会混淆(因为信号量保证顺序)
+     */
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 3: Starting to wait for AT response (mutex released, using semaphore)...");
+#endif
+    timestamps.response_wait = (uint32_t)osal_get_jiffies();
+    ppp_error_t ret = ppp_at_wait_response(response, timeout);
+    uint32_t response_wait_end = (uint32_t)osal_get_jiffies();
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 3: AT response wait completed in %u ms, result: %d", 
+                      osal_jiffies_to_msecs(response_wait_end - timestamps.response_wait), ret);
+#endif
+
+    /* ========== 方案1优化:响应到达后重新获取互斥锁进行验证 ========== */
+    /* 重新获取互斥锁,用于验证响应(如果需要保护验证过程)
+     * 注意:验证过程通常很快(<10ms),不会长时间阻塞
+     */
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 3.5: Re-acquiring AT mutex for response validation...");
+#endif
+    timestamps.mutex_reacquire = (uint32_t)osal_get_jiffies();
+    int mutex_reacquire_result = osal_mutex_lock_timeout(&g_at_ctx.at_mutex, 1000);
+    if (mutex_reacquire_result != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to re-acquire AT mutex for validation (timeout 1s), but response already received");
+        /* 即使获取互斥锁失败,响应已经收到,可以继续验证(验证过程不需要互斥锁保护) */
+    } else {
+        uint32_t mutex_reacquire_end = (uint32_t)osal_get_jiffies();
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+        PPP_DEBUG_CRITICAL("Step 3.5: AT mutex re-acquired in %u ms", 
+                          osal_jiffies_to_msecs(mutex_reacquire_end - timestamps.mutex_reacquire));
+#endif
+    }
+
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    /* 打印响应结果 */
+    PPP_DEBUG_DEBUG("=== AT Command Response ===");
+    if (ret == PPP_ERR_OK) {
+        PPP_DEBUG_DEBUG("Response received (%u bytes):", response->length);
+
+        /* 打印接收的字符串(可见字符)- 使用动态分配的缓冲区 */
+        if (g_at_printable_buffer) {
+            uint32_t j = 0;
+            for (uint32_t i = 0; i < response->length && j < (PPP_AT_RESPONSE_MAX_LEN + 100) - 10; i++) {
+                if (response->response[i] == '\r') {
+                    g_at_printable_buffer[j++] = '\\';
+                    g_at_printable_buffer[j++] = 'r';
+                } else if (response->response[i] == '\n') {
+                    g_at_printable_buffer[j++] = '\\';
+                    g_at_printable_buffer[j++] = 'n';
+                } else if (response->response[i] >= 32 && response->response[i] <= 126) {
+                    g_at_printable_buffer[j++] = response->response[i];
+                } else {
+                    j += snprintf_s(g_at_printable_buffer + j, (PPP_AT_RESPONSE_MAX_LEN + 100) - j, (PPP_AT_RESPONSE_MAX_LEN + 100) - j - 1, "\\x%02X", (uint8_t)response->response[i]);
+                }
+            }
+            g_at_printable_buffer[j] = '\0';
+            PPP_DEBUG_DEBUG("Response String: %s", g_at_printable_buffer);
+        }
+        PPP_DEBUG_DEBUG("Raw Response: %s", response->response);
+    } else {
+        PPP_DEBUG_ERROR("No response received (error: %d)", ret);
+    }
+#else
+    /* Release版本:只记录错误 */
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("AT command failed: error=%d", ret);
+    }
+#endif
+
+    if (ret == PPP_ERR_OK) {
+        if (expected_response) {
+            if (!ppp_at_parse_response_with_len(response->response, response->length, expected_response)) {
+                PPP_DEBUG_WARN("Response validation failed - Expected: %s", expected_response);
+                PPP_DEBUG_WARN("Response validation failed - Received: %s", response->response);
+                response->success = false;
+                response->error_code = PPP_ERR_AT_FAILED;
+                ret = PPP_ERR_AT_FAILED;
+            } else {
+                PPP_DEBUG_DEBUG("Response validation successful - Expected: %s", expected_response);
+                response->success = true;
+                response->error_code = PPP_ERR_OK;
+            }
+        } else {
+            // 没有期望响应时,不验证特定字符串,但至少要检查是否有错误
+            // 如果响应中包含 ERROR(且不是命令响应URC格式),认为是失败
+            // 否则认为成功(可能是 OK、提示符 >、或其他有效响应)
+            if (!ppp_at_parse_response_with_len(response->response, response->length, NULL)) {
+                // ppp_at_parse_response 返回 false 表示有错误(ERROR/BUSY/NO CARRIER等)
+                PPP_DEBUG_WARN("Response validation failed - Contains error (expected is NULL)");
+                PPP_DEBUG_WARN("Response: %s", response->response);
+                response->success = false;
+                response->error_code = PPP_ERR_AT_FAILED;
+                ret = PPP_ERR_AT_FAILED;
+            } else {
+                // 没有错误,认为成功
+                PPP_DEBUG_DEBUG("Response validation successful - No error found (expected is NULL)");
+                response->success = true;
+                response->error_code = PPP_ERR_OK;
+            }
+        }
+    } else {
+        response->success = false;
+        response->error_code = ret;
+    }
+
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_DEBUG("=== AT Command Complete (Result: %s) ===", ret == PPP_ERR_OK ? "SUCCESS" : "FAILED");
+#endif
+
+    /* 释放互斥锁(如果重新获取成功) */
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 4: Releasing AT mutex...");
+#endif
+    timestamps.mutex_unlock = (uint32_t)osal_get_jiffies();
+    if (mutex_reacquire_result == OSAL_SUCCESS) {
+        osal_mutex_unlock(&g_at_ctx.at_mutex);
+    }
+    uint32_t mutex_unlock_end = (uint32_t)osal_get_jiffies();
+
+    uint32_t total_time = osal_jiffies_to_msecs(mutex_unlock_end - timestamps.start);
+#if PPP_DEBUG_ENABLE && (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG)
+    PPP_DEBUG_CRITICAL("Step 4: AT mutex released in %u ms", 
+                      osal_jiffies_to_msecs(mutex_unlock_end - timestamps.mutex_unlock));
+    PPP_DEBUG_CRITICAL("=== AT COMMAND END (Total: %u ms, Result: %d) ===", total_time, ret);
+#else
+    /* Release版本:只记录总时间和结果(如果失败) */
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_CRITICAL("AT CMD END: %u ms, FAILED (err=%d)", total_time, ret);
+    }
+#endif
+
+    return ret;
+}
+
+/**
+ * @brief 等待 '>' 提示符(用于 AT+TCPSEND/AT+SSLTCPSEND)
+ * @param timeout 超时时间(毫秒)
+ * @return PPP_ERR_OK 成功,其他值失败
+ * 
+ * 根据N706A AT命令手册,AT+TCPSEND发送后会收到 '>' 提示符,
+ * 此时需要延迟50-100ms后再发送数据。
+ */
+static ppp_error_t ppp_at_wait_for_prompt(uint32_t timeout)
+{
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_wait_response(&response, timeout);
+    
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to wait for '>' prompt: error %d", ret);
+        return ret;
+    }
+    
+    /* 检查响应中是否包含 '>' */
+    if (strstr((const char *)response.response, ">") == NULL) {
+        PPP_DEBUG_ERROR("Response does not contain '>' prompt: %s", response.response);
+        return PPP_ERR_AT_FAILED;
+    }
+    
+    PPP_DEBUG_DEBUG("Received '>' prompt, delaying 75ms before sending data...");
+    
+    /* 延迟75ms(在50-100ms范围内,取中间值) */
+    osal_msleep(75);
+    
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 发送原始数据(不带AT命令格式)
+ * @note 用于 TCP/SSL 数据发送,在 AT+TCPSEND/AT+SSLTCPSEND 命令后使用
+ * @note 调用此函数前,必须先调用 ppp_at_send_command 发送 AT+TCPSEND 命令并等待 '>' 提示符
+ */
+ppp_error_t ppp_at_send_raw_data(const void *data, size_t len, uint32_t timeout)
+{
+    if (!g_at_ctx.initialized || !data || len == 0) {
+        PPP_DEBUG_ERROR("Invalid parameters: initialized=%d, data=%p, len=%zu", 
+                       g_at_ctx.initialized, data, len);
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_DEBUG("=== Sending Raw Data (HEX Mode) ===");
+    PPP_DEBUG_DEBUG("Binary length: %zu bytes", len);
+
+    /* 将二进制数据编码为HEX字符串 */
+    size_t hex_str_len = len * 2;  // 每个字节转换为2个HEX字符
+    char *hex_str = (char *)malloc(hex_str_len + 1);  // +1 for null terminator
+    if (!hex_str) {
+        PPP_DEBUG_ERROR("Failed to allocate memory for HEX string");
+        return PPP_ERR_MEMORY;
+    }
+    
+    size_t encoded_len = n706a_binary_to_hex_string((const uint8_t *)data, len, hex_str, hex_str_len + 1);
+    if (encoded_len == 0) {
+        free(hex_str);
+        PPP_DEBUG_ERROR("Failed to encode binary data to HEX string");
+        return PPP_ERR_INVALID_PARAM;
+    }
+    
+    PPP_DEBUG_DEBUG("HEX encoded: binary_len=%zu -> hex_len=%zu", len, encoded_len);
+    
+    /* 使用 ppp_serial_write 发送HEX字符串(ASCII字符),不添加 \r\n(会自动处理 wakeup) */
+    ppp_error_t write_result = ppp_serial_write((const uint8_t *)hex_str, encoded_len);
+
+    free(hex_str);
+
+    if (write_result != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to send HEX data via ppp_serial_write: error=%d", write_result);
+        return write_result;
+    }
+
+    PPP_DEBUG_DEBUG("HEX data sent successfully (%zu hex chars, %zu binary bytes)", 
+                   encoded_len, len);
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 跳过开机日志 (对应chat文件中的 '' \rAT)
+ */
+static ppp_error_t ppp_at_skip_startup_logs(void)
+{
+    PPP_DEBUG_DEBUG("Skipping startup logs...");
+    
+    // 清空接收缓冲区,跳过开机时的各种日志输出
+    uint8_t dummy_buffer[256];
+    uint32_t bytes_read;
+    
+    // 连续读取并丢弃开机日志,直到没有更多数据
+    int retry_count = 0;
+    const int max_retries = 10;
+    
+    do {
+        ppp_error_t ret = ppp_serial_read(dummy_buffer, sizeof(dummy_buffer), &bytes_read);
+        if (ret != PPP_ERR_OK) {
+            break;
+        }
+        
+        if (bytes_read > 0) {
+            PPP_DEBUG_DEBUG("Skipped %u bytes of startup logs", bytes_read);
+            retry_count = 0; // 重置重试计数
+        } else {
+            retry_count++;
+        }
+        
+        osal_msleep(100);
+    } while (retry_count < max_retries);
+    
+    PPP_DEBUG_DEBUG("Startup logs skipped");
+    return PPP_ERR_OK;
+}
+
+/* ========== PPP拨号函数已删除 ========== */
+/* N706A_NET 使用纯AT命令模式,不需要PPP拨号功能 */
+/* 如需PPP功能,请使用 ppp 组件 */
+/**
+ * @brief 发送AT关机指令,支持快速关机和正常关机
+ * @param shutdown_type 关机类型: 0-快速关机, 1-正常流程关机
+ * @return ppp_error_t
+ */
+ppp_error_t ppp_at_poweroff_modem(uint8_t shutdown_type)
+{
+    if (!g_at_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_INFO("Sending AT+NWPWROFF=%d to power off modem", shutdown_type);
+
+    char cmd[32];
+    snprintf(cmd, sizeof(cmd), "AT+NWPWROFF=%d", shutdown_type);
+
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command(cmd, "OK", 10000, &response);
+
+    if (ret == PPP_ERR_OK) {
+        PPP_DEBUG_INFO("Modem power off command sent successfully");
+    } else {
+        PPP_DEBUG_ERROR("Failed to send modem power off command");
+    }
+    ppp_power_off_cat1();
+    return ret;
+}
+
+/* ========== PPP挂断函数已删除 ========== */
+/* N706A_NET 使用纯AT命令模式,不需要PPP挂断功能 */
+
+/**
+ * @brief 获取当前波特率
+ */
+uint32_t ppp_at_get_current_baudrate(void)
+{
+    return g_at_ctx.current_baudrate;
+}
+
+/**
+ * @brief 检查是否为自动波特率模式
+ */
+bool ppp_at_is_auto_baudrate_mode(void)
+{
+    return g_at_ctx.auto_baudrate_mode;
+}
+
+/**
+ * @brief 测试AT通信是否正常
+ */
+ppp_error_t ppp_at_test_communication(void)
+{
+    PPP_DEBUG_INFO("Testing AT communication...");
+    
+    ppp_at_response_t response;
+    
+    // 发送简单的AT命令测试通信
+    ppp_error_t ret = ppp_at_send_command("AT", "OK", 1000, &response);
+    if (ret == PPP_ERR_OK) {
+        PPP_DEBUG_INFO("AT communication test passed");
+    } else {
+        PPP_DEBUG_ERROR("AT communication test failed: %d", ret);
+    }
+    
+    return ret;
+}
+
+/* 内部函数实现 */
+
+/**
+ * @brief 检查AT响应是否完整
+ * @param buffer 响应缓冲区
+ * @param length 缓冲区长度
+ * @return true 如果响应完整,false 否则
+ */
+static bool ppp_at_check_response_complete(const char *buffer, uint32_t length)
+{
+    if (!buffer || length == 0) {
+        return false;
+    }
+    
+    /* 搜索">"提示符(用于AT+TCPSEND/AT+SSLTCPSEND等命令) */
+    /* 这些命令的响应格式是:AT+TCPSEND=<n>,<len>\r\n\r\n> */
+    /* 注意:">"可能单独在一行,后面可能有\r\n,也可能没有(如果响应被截断) */
+    if (length >= 1) {
+        for (uint32_t i = 0; i < length; i++) {
+            if (buffer[i] == '>') {
+                /* 检查">"前面是否有换行符或空格(避免误匹配) */
+                if (i == 0 || buffer[i-1] == '\n' || buffer[i-1] == '\r' || buffer[i-1] == ' ') {
+                    /* 检查">"后面是否有换行符或字符串结束(确保是独立的提示符) */
+                    /* 注意:如果">"是最后一个字符,也认为是有效的提示符(响应可能被截断) */
+                    if (i == length - 1 || buffer[i+1] == '\n' || buffer[i+1] == '\r' || buffer[i+1] == '\0' || buffer[i+1] == ' ') {
+                        PPP_DEBUG_DEBUG("Found '>' prompt at position %u (length=%u, next_char=0x%02x)", 
+                                       i, length, (i < length - 1) ? (unsigned char)buffer[i+1] : 0);
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+    
+    /* 检查命令响应URC格式的结束符(如 +TCPSETUP: 0,OK\r\r 或 +TCPCLOSE: 0,OK\r\r) */
+    /* 这些格式可能以 \r\r 结尾(异常格式,但需要容错处理) */
+    if (length >= 11) {
+        // 检查 +TCPSETUP: 或 +TCPCLOSE: 格式
+        if (strstr(buffer, "+TCPSETUP: ") || strstr(buffer, "+TCPCLOSE: ") ||
+            strstr(buffer, "+SSLTCPSETUP: ") || strstr(buffer, "+SSLTCPCLOSE: ")) {
+            // 检查是否包含 OK 或 ERR,并且以 \r\n 或 \r\r 结尾
+            const char *ok_pos = strstr(buffer, "OK");
+            const char *err_pos = strstr(buffer, "ERR");
+            if (ok_pos || err_pos) {
+                // 检查响应是否以 \r\n 或 \r\r 结尾
+                if (length >= 2) {
+                    if (buffer[length - 2] == '\r' && (buffer[length - 1] == '\n' || buffer[length - 1] == '\r')) {
+                        PPP_DEBUG_DEBUG("Found command response URC terminator (ends with \\r\\%c)", 
+                                       buffer[length - 1] == '\n' ? 'n' : 'r');
+                        return true;
+                    }
                 }
-                return PPP_ERR_OK;
-            } else {
-                PPP_DEBUG_WARN("AT command failed at baudrate %u, retry %d/3 (error: %d)", baudrate, retry + 1, ret);
-                osal_msleep(500);
             }
         }
-
-        PPP_DEBUG_WARN("Baudrate %u failed after 3 retries, trying next...", baudrate);
-        osal_msleep(100);
     }
-
-    PPP_DEBUG_ERROR("Baudrate handshake failed: all baudrates tried, no success");
-    return PPP_ERR_AT_FAILED;
+    
+    /* 搜索OK终止符 - 修复无符号整数下溢问题 */
+    if (length >= 2) {
+        for (uint32_t i = 0; i <= length - 2; i++) {
+            if (buffer[i] == 'O' && buffer[i+1] == 'K') {
+                /* 检查OK前面是否有换行符(避免误匹配) */
+                if (i == 0 || buffer[i-1] == '\n' || buffer[i-1] == '\r') {
+                    PPP_DEBUG_DEBUG("Found OK terminator at position %u", i);
+                    return true;
+                }
+            }
+        }
+    }
+    
+    /* 搜索ERROR终止符 - 已经有长度检查,但确保安全 */
+    if (length >= 5) {
+        for (uint32_t i = 0; i <= length - 5; i++) {
+            if (strncmp(&buffer[i], "ERROR", 5) == 0) {
+                /* 检查ERROR前面是否有换行符 */
+                if (i == 0 || buffer[i-1] == '\n' || buffer[i-1] == '\r') {
+                    PPP_DEBUG_DEBUG("Found ERROR terminator at position %u", i);
+                    return true;
+                }
+            }
+        }
+    }
+    
+    /* 检查其他终止符 */
+    if (strstr(buffer, "CONNECT") ||
+        strstr(buffer, "NO CARRIER") ||
+        strstr(buffer, "BUSY") ||
+        strstr(buffer, "NO DIALTONE") ||
+        strstr(buffer, "+CME ERROR") ||
+        strstr(buffer, "+CMS ERROR")) {
+        PPP_DEBUG_DEBUG("Found other terminator in response");
+        return true;
+    }
+    
+    return false;
 }
 
 /**
- * @brief 解析AT+IPR?响应,提取当前波特率
- * @param response AT+IPR?的响应字符串
- * @return 解析出的波特率,失败返回0
+ * @brief 跳过命令回显行,返回实际响应内容
+ * @param response 原始响应(可能包含回显)
+ * @param cleaned_response 输出缓冲区(用于存储清理后的响应)
+ * @param cleaned_size 输出缓冲区大小
+ * @return 清理后的响应指针(指向cleaned_response或原始response)
+ * 
+ * 启用回显后,响应格式为:AT+XXX\r\n\r\n响应内容\r\nOK\r\n
+ * 此函数会跳过回显行(AT+XXX\r\n),只保留实际响应内容
  */
-static uint32_t ppp_parse_ipr_response(const char *response)
+static const char *ppp_at_skip_echo(const char *response, size_t response_len, char *cleaned_response, size_t cleaned_size)
 {
     if (!response) {
-        return 0;
+        return NULL;
+    }
+    
+    // 如果 response_len 为 0,尝试使用 strlen(处理正常情况)
+    if (response_len == 0) {
+        response_len = strlen(response);
+        if (response_len == 0) {
+            return response;
+        }
+    }
+    
+    // 检查是否包含命令回显(AT+开头)
+    // 注意:响应已经过 trim 处理,不再包含开头的 \x00 字节,可以直接使用 strstr
+    const char *at_pos = strstr(response, "AT+");
+    
+    if (!at_pos) {
+        // 没有回显,直接返回原始响应
+        return response;
+    }
+    
+    // 查找回显行的结束位置(\r\n)
+    // 注意:响应已经过 trim 处理,可以直接使用 strstr
+    const char *echo_end = strstr(at_pos, "\r\n");
+    
+    if (!echo_end) {
+        // 没有找到回显结束符,返回原始响应
+        return response;
+    }
+    
+    // 跳过回显行和后续的空白行(\r\n\r\n)
+    const char *actual_response = echo_end + 2;  // 跳过第一个\r\n
+    while (actual_response < response + response_len && 
+           (*actual_response == '\r' || *actual_response == '\n' || *actual_response == '\0')) {
+        actual_response++;
+    }
+    
+    // 如果实际响应为空或超出范围,返回原始响应
+    if (actual_response >= response + response_len || *actual_response == '\0') {
+        return response;
+    }
+    
+    // 将清理后的响应复制到输出缓冲区
+    if (cleaned_response && cleaned_size > 0) {
+        // 计算实际响应长度(到字符串结束或缓冲区结束)
+        size_t remaining_len = response_len - (actual_response - response);
+        size_t copy_len = remaining_len;
+        
+        // 注意:响应已经过 trim 处理,但可能中间仍包含 \x00(虽然不应该)
+        // 为了安全,仍然检查第一个 \x00 的位置
+        for (size_t i = 0; i < remaining_len; i++) {
+            if (actual_response[i] == '\0') {
+                copy_len = i;
+                break;
+            }
+        }
+        
+        if (copy_len >= cleaned_size) {
+            copy_len = cleaned_size - 1;
+        }
+        memcpy(cleaned_response, actual_response, copy_len);
+        cleaned_response[copy_len] = '\0';
+        return cleaned_response;
+    }
+    
+    // 如果没有提供输出缓冲区,返回实际响应的指针(注意:这要求原始response不会被修改)
+    return actual_response;
+}
+
+/**
+ * @brief 去除响应字符串两头的 \x00 字节(保留其他空白字符如 \r\n、空格等)
+ * @param response 原始响应字符串
+ * @param response_len 原始响应长度
+ * @param trimmed_start 输出:trim后的起始位置(相对于原始response的偏移)
+ * @param trimmed_len 输出:trim后的长度
+ * @return true=成功,false=失败
+ * 
+ * 此函数只去除响应字符串两头的 \x00 字节,保留其他字符(包括 \r、\n、空格等)。
+ * 
+ * 原因:
+ * 1. \x00 字节会导致 strstr 等函数无法正常工作(遇到 \x00 会停止)
+ * 2. \r\n 是 AT 响应的重要分隔符,用于识别行边界(如 "\r\nOK\r\n")
+ * 3. 空格在某些情况下也是有效的(如 "> " 提示符)
+ * 
+ * 这样可以解决响应开头包含 \x00 字节的问题,同时不影响 AT 响应的正常解析。
+ */
+static bool ppp_at_trim_response(const char *response, size_t response_len, 
+                                 size_t *trimmed_start, size_t *trimmed_len)
+{
+    if (!response || response_len == 0 || !trimmed_start || !trimmed_len) {
+        return false;
+    }
+    
+    // 从开头跳过 \x00 字节(只跳过 \x00,保留其他字符)
+    size_t start = 0;
+    while (start < response_len && response[start] == '\0') {
+        start++;
+    }
+    
+    // 如果全部是 \x00 字节
+    if (start >= response_len) {
+        *trimmed_start = 0;
+        *trimmed_len = 0;
+        return false;
+    }
+    
+    // 从结尾跳过 \x00 字节(只跳过 \x00,保留其他字符)
+    size_t end = response_len;
+    while (end > start && response[end - 1] == '\0') {
+        end--;
+    }
+    
+    *trimmed_start = start;
+    *trimmed_len = end - start;
+    return true;
+}
+
+static bool ppp_at_parse_response(const char *response, const char *expected)
+{
+    return ppp_at_parse_response_with_len(response, response ? strlen(response) : 0, expected);
+}
+
+static bool ppp_at_parse_response_with_len(const char *response, size_t response_len, const char *expected)
+{
+    if (!response || response_len == 0) {
+        // 如果 response_len 为 0,尝试使用 strlen(处理正常情况)
+        if (response) {
+            response_len = strlen(response);
+            if (response_len == 0) {
+                return false;
+            }
+        } else {
+            return false;
+        }
     }
     
-    PPP_DEBUG_DEBUG("Parsing IPR response: [%s]", response);
+    // 第一步:trim 响应字符串,去除两头的 \x00 和空白字符
+    size_t trimmed_start = 0;
+    size_t trimmed_len = 0;
+    const char *trimmed_response = response;
+    size_t trimmed_response_len = response_len;
     
-    /* 查找 "+IPR: " 字符串 */
-    const char *ipr_pos = strstr(response, "+IPR: ");
-    if (!ipr_pos) {
-        PPP_DEBUG_WARN("IPR response format error: no '+IPR: ' found in response");
-        return 0;
+    if (ppp_at_trim_response(response, response_len, &trimmed_start, &trimmed_len)) {
+        if (trimmed_len > 0) {
+            trimmed_response = response + trimmed_start;
+            trimmed_response_len = trimmed_len;
+            PPP_DEBUG_DEBUG("[响应解析] Trimmed response: original_len=%u, trimmed_start=%u, trimmed_len=%u", 
+                          (unsigned int)response_len, (unsigned int)trimmed_start, (unsigned int)trimmed_len);
+        } else {
+            // 全部是无效字符
+            return false;
+        }
     }
     
-    /* 跳过 "+IPR: " 部分 */
-    ipr_pos += 6;
-    
-    /* 跳过可能的空格 */
-    while (*ipr_pos == ' ' || *ipr_pos == '\t') {
-        ipr_pos++;
+    // 第二步:跳过命令回显,获取实际响应内容
+    // 注意:现在使用 trim 后的响应,不再需要手动跳过 \x00 字节
+    char cleaned_response[PPP_AT_RESPONSE_MAX_LEN] = {0};
+    const char *actual_response = ppp_at_skip_echo(trimmed_response, trimmed_response_len, cleaned_response, sizeof(cleaned_response));
+    const char *search_response = trimmed_response;  // 使用 trim 后的响应来查找 URC
+    size_t search_response_len = trimmed_response_len;  // 使用 trim 后的响应长度
+    if (!actual_response) {
+        actual_response = trimmed_response;  // 如果清理失败,使用 trim 后的响应
     }
+
+    // 检查是否是命令响应中的URC格式(这些格式表示对命令的响应,即使包含ERROR也是有效的)
+    // 例如:+TCPCLOSE: ERROR, +TCPCLOSE: 0,OK, +TCPSETUP: 0,OK 等
+    // 这些格式通常出现在发送AT命令后立即收到的响应中,即使包含ERROR也应该认为是有效的响应
     
-    /* 解析波特率数字,只读取数字部分 */
-    uint32_t baudrate = 0;
-    const char *num_start = ipr_pos;
+    // 特殊处理:+TCPRECV 和 +SSLTCPRECV URC 是异步数据接收,不应该作为命令响应来验证
+    // 这些URC包含十六进制数据,可能会被误判为包含"ERROR"字符串
+    // 在验证响应之前,需要排除这些URC,只验证命令本身的响应部分
+    // 注意:+TCPRECV URC中的数据(如HTTP响应)是重要的协议内容,需要在应用层处理,不应该在这里验证
+    // 注意:响应已经过 trim 处理,不再包含开头的 \x00 字节,可以直接使用 strstr
+    const char *tcprecv_pos = strstr(search_response, "+TCPRECV: ");
+    const char *ssltcprecv_pos = strstr(search_response, "+SSLTCPRECV: ");
     
-    /* 找到数字的结束位置(遇到非数字字符停止) */
-    while (*ipr_pos >= '0' && *ipr_pos <= '9') {
-        baudrate = baudrate * 10 + (*ipr_pos - '0');
-        ipr_pos++;
+    if (tcprecv_pos || ssltcprecv_pos) {
+        // 找到 +TCPRECV 或 +SSLTCPRECV URC,只验证URC之前的部分(命令响应部分)
+        const char *urc_start = tcprecv_pos ? tcprecv_pos : ssltcprecv_pos;
+        // 计算命令响应部分的长度(从原始响应开头到URC开始位置)
+        size_t command_response_len = urc_start - search_response;
+        if (command_response_len > 0) {
+            // 使用动态分配的缓冲区(在初始化时分配,避免栈上分配大缓冲区)
+            if (!g_at_command_response_buffer) {
+                // 缓冲区未分配,跳过验证(不影响功能,但可能影响错误检测)
+                PPP_DEBUG_WARN("[响应解析] Command response buffer not allocated, skipping validation");
+            } else {
+                // 如果超过缓冲区大小,截断处理(这种情况很少见)
+                size_t max_copy_len = (command_response_len < 511) ? command_response_len : 511;
+                
+                // 从原始响应复制,跳过开头的 \x00 字节
+                size_t copy_idx = 0;
+                for (size_t i = 0; i < command_response_len && copy_idx < max_copy_len; i++) {
+                    if (search_response[i] != '\0') {
+                        g_at_command_response_buffer[copy_idx++] = search_response[i];
+                    }
+                }
+                g_at_command_response_buffer[copy_idx] = '\0';
+                
+                // 如果数据被截断,记录警告
+                if (copy_idx >= max_copy_len && command_response_len > max_copy_len) {
+                    PPP_DEBUG_WARN("[响应解析] Command response part truncated: original_len=%u, copied=%u", 
+                                  (unsigned int)command_response_len, (unsigned int)copy_idx);
+                }
+                
+                // 递归调用,只验证命令响应部分
+                bool result = ppp_at_parse_response(g_at_command_response_buffer, expected);
+                if (result) {
+                    PPP_DEBUG_DEBUG("Response contains +TCPRECV/+SSLTCPRECV URC, validated command response part only (URC data will be processed by application layer)");
+                    return true;
+                }
+                // 如果命令响应部分验证失败,继续下面的错误检查(但排除URC部分)
+                // 注意:这里不直接返回false,而是继续检查,因为URC可能包含数据
+            }
+        }
+        // 如果无法分离命令响应部分,继续下面的检查,但需要排除URC中的错误检查
     }
     
-    /* 检查是否成功解析到数字 */
-    if (ipr_pos == num_start) {
-        PPP_DEBUG_WARN("IPR response format error: no valid number found after '+IPR: '");
-        return 0;
+    const char *command_response_urc_patterns[] = {
+        "+TCPCLOSE: ERR", "+TCPCLOSE: 0,OK", "+TCPCLOSE: ERROR",
+        "+SSLTCPCLOSE: ERR", "+SSLTCPCLOSE: 0,OK", "+SSLTCPCLOSE: ERROR",
+        "+TCPSETUP: 0,OK", "+TCPSETUP: 0,ERR",
+        "+SSLTCPSETUP: 0,OK", "+SSLTCPSETUP: 0,ERR"
+    };
+    for (size_t i = 0; i < sizeof(command_response_urc_patterns) / sizeof(command_response_urc_patterns[0]); i++) {
+        if (strstr(actual_response, command_response_urc_patterns[i]) != NULL) {
+            // 命令响应URC格式(如+TCPCLOSE: ERROR)表示命令已确认,无论expected是什么都应该接受
+            // 因为对于某些命令(如AT+TCPCLOSE),ERROR表示socket已经关闭或不存在,这是正常的
+            PPP_DEBUG_DEBUG("Response contains command response URC format: %s, accepting as valid response", command_response_urc_patterns[i]);
+            return true;
+        }
+    }
+
+    // 如果期望响应是 NULL(表示不验证特定字符串,但至少要检查是否有错误)
+    if (!expected) {
+        // 检查是否是提示符响应(如 AT+TCPSEND 命令返回的 ">" 提示符)
+        if (strstr(actual_response, ">") != NULL) {
+            PPP_DEBUG_DEBUG("Response contains '>' prompt (expected is NULL), accepting as valid response");
+            return true;
+        }
+        // 检查是否包含 OK(表示成功)
+        if (strstr(actual_response, "OK") != NULL) {
+            PPP_DEBUG_DEBUG("Response contains 'OK' (expected is NULL), accepting as valid response");
+            return true;
+        }
+        // 如果既没有 > 也没有 OK,继续检查是否有错误(ERROR/BUSY/NO CARRIER)
+        // 这些错误检查在下面的代码中处理,如果都没有,最后返回 false
+    }
+
+    // 检查期望响应,优先检测期望响应
+    // 特殊处理:如果期望是">"提示符,需要更精确的匹配(避免误匹配)
+    if (expected) {
+        if (strcmp(expected, ">") == 0) {
+            // 期望">"提示符,检查响应中是否包含独立的">"提示符
+            // 注意:">"前面应该有换行符或空格,后面应该有换行符或字符串结束
+            const char *prompt_pos = strstr(actual_response, ">");
+            if (prompt_pos != NULL) {
+                // 检查">"前面是否有换行符或空格(避免误匹配)
+                if (prompt_pos == actual_response || 
+                    prompt_pos[-1] == '\n' || prompt_pos[-1] == '\r' || prompt_pos[-1] == ' ') {
+                    // 检查">"后面是否有换行符或字符串结束(确保是独立的提示符)
+                    if (prompt_pos[1] == '\0' || prompt_pos[1] == '\n' || 
+                        prompt_pos[1] == '\r' || prompt_pos[1] == ' ') {
+                        PPP_DEBUG_DEBUG("Response contains '>' prompt (expected: >), accepting as valid response");
+                        return true;
+                    }
+                }
+            }
+        } else {
+            // 普通字符串匹配
+            if (strstr(actual_response, expected)) {
+                return true;
+            }
+        }
     }
+
+    // 检查ABORT条件 (对应chat文件中的ABORT指令)
+    // 注意:如果响应包含命令响应URC格式,即使包含ERROR也不应该认为是ABORT
+    // 因为命令响应URC格式已经在上面处理了
+    // 同时,需要排除 +TCPRECV/+SSLTCPRECV URC 部分,因为它们是异步数据,不应该影响命令响应验证
     
-    /* 验证波特率是否合理 */
-    if (baudrate < 1200 || baudrate > 4000000) {
-        PPP_DEBUG_WARN("IPR response contains unreasonable baudrate: %u", baudrate);
-        return 0;
+    // 确定要检查的响应范围(排除 +TCPRECV/+SSLTCPRECV URC)
+    const char *check_start = actual_response;
+    size_t check_len = search_response_len;  // 使用原始响应长度
+    const char *tcprecv_check = NULL;
+    const char *ssltcprecv_check = NULL;
+    
+    // 手动查找 +TCPRECV 或 +SSLTCPRECV,跳过 \x00 字节
+    // 注意:在原始响应中查找,因为清理后的响应可能不包含 \x00 字节
+    for (size_t i = 0; i < check_len; i++) {
+        if (search_response[i] == '\0') {
+            continue;  // 跳过 \x00 字节
+        }
+        if (i + 10 <= check_len && strncmp(search_response + i, "+TCPRECV: ", 10) == 0) {
+            tcprecv_check = search_response + i;
+            break;
+        }
+        if (i + 13 <= check_len && strncmp(search_response + i, "+SSLTCPRECV: ", 13) == 0) {
+            ssltcprecv_check = search_response + i;
+            break;
+        }
     }
     
-    PPP_DEBUG_DEBUG("Successfully parsed current baudrate from IPR response: %u", baudrate);
-    return baudrate;
-}
-
-/**
- * @brief 查询当前modem波特率
- * @return 当前modem波特率,失败返回0
- */
-static uint32_t ppp_query_current_modem_baudrate(void)
-{
-    ppp_at_response_t response;
-    ppp_error_t ret = ppp_at_send_command("AT+IPR?", "OK", PPP_AT_CMD_TIMEOUT, &response);
+    // 如果手动查找没找到,使用 strstr 作为后备(处理正常情况)
+    // 注意:strstr 在遇到 \x00 时会停止,所以可能找不到
+    if (!tcprecv_check && !ssltcprecv_check) {
+        tcprecv_check = strstr(actual_response, "+TCPRECV: ");
+        ssltcprecv_check = strstr(actual_response, "+SSLTCPRECV: ");
+    }
     
-    if (ret != PPP_ERR_OK) {
-        PPP_DEBUG_WARN("Failed to query current baudrate with AT+IPR?, error: %d", ret);
-        return 0;
+    if (tcprecv_check || ssltcprecv_check) {
+        // 如果响应中包含 +TCPRECV/+SSLTCPRECV URC,只检查URC之前的部分
+        const char *urc_check_start = tcprecv_check ? tcprecv_check : ssltcprecv_check;
+        check_len = urc_check_start - search_response;  // 使用原始响应位置
     }
     
-    return ppp_parse_ipr_response(response.response);
+    // 在排除URC后的响应范围内检查错误
+    if (check_len > 0) {
+        // 检查 BUSY 和 NO CARRIER(只在命令响应部分检查,不包括URC)
+        if ((strstr(actual_response, "BUSY") && 
+             (strstr(actual_response, "BUSY") - actual_response) < (int)check_len) ||
+            (strstr(actual_response, "NO CARRIER") && 
+             (strstr(actual_response, "NO CARRIER") - actual_response) < (int)check_len)) {
+            PPP_DEBUG_ERROR("AT command aborted: %.*s", (int)check_len, actual_response);
+            return false;
+        }
+        
+        // 对于普通的ERROR(不是命令响应URC格式),认为是ABORT
+        const char *error_pos = strstr(actual_response, "ERROR");
+        if (error_pos && (error_pos - actual_response) < (int)check_len) {
+            // 检查是否是命令响应URC格式中的ERROR(已经在上面处理了)
+            bool is_command_response_error = false;
+            for (size_t i = 0; i < sizeof(command_response_urc_patterns) / sizeof(command_response_urc_patterns[0]); i++) {
+                const char *pattern_pos = strstr(actual_response, command_response_urc_patterns[i]);
+                if (pattern_pos && (pattern_pos - actual_response) < (int)check_len) {
+                    is_command_response_error = true;
+                    break;
+                }
+            }
+            if (!is_command_response_error) {
+                PPP_DEBUG_ERROR("AT command aborted: %.*s", (int)check_len, actual_response);
+                return false;
+            }
+        }
+    }
+
+    return false;
 }
 
 /**
- * @brief 设置固定波特率(内部函数)
+ * @brief 等待AT命令响应 - 异步版本(从环形缓冲区读取)
+ * 
+ * 该函数从ppp_interrupt.c填充的环形缓冲区中异步读取AT响应数据
+ * 提供同步的函数接口,但底层是异步的高速串口接收
  */
-static ppp_error_t ppp_at_set_baudrate(uint32_t baudrate)
+static ppp_error_t ppp_at_wait_response(ppp_at_response_t *response, uint32_t timeout)
 {
-    if (!g_at_ctx.initialized) {
+    PPP_DEBUG_DEBUG(">>> N706A AT WAIT_RESPONSE START (async mode, timeout: %u ms) <<<", timeout);
+    
+    if (!response || !g_at_response_buffer) {
+        PPP_DEBUG_ERROR("Invalid parameters: response=%p, buffer=%p", response, g_at_response_buffer);
         return PPP_ERR_INVALID_PARAM;
     }
 
-    /* 检查波特率是否支持 */
-    bool supported = false;
-    for (int i = 0; i < PPP_BAUDRATE_FIXED_COUNT; i++) {
-        if (g_fixed_baudrates[i] == baudrate) {
-            supported = true;
+    uint32_t start_time = (uint32_t)osal_get_jiffies();
+    uint32_t bytes_received = 0;
+    bool response_complete = false;
+    uint32_t last_accumulate_check_elapsed = 0;  // 上次检查累积缓冲区的时间(相对于start_time)
+    uint32_t last_accumulate_length = 0;  // 上次检查时的累积缓冲区长度(用于减少重复日志)
+    uint32_t accumulate_stuck_start_elapsed = 0;  // 累积缓冲区开始卡住的时间(相对于start_time)
+    bool has_checked_accumulate_buffer_on_timeout = false;  // 标记是否在超时时已检查过累积缓冲区
+    
+    /* 清空全局缓存 */
+    memset(g_at_response_buffer, 0, g_at_buffer_size);
+    
+    PPP_DEBUG_DEBUG("N706A AT: Waiting for response from async ring buffer (timeout: %u ms)", timeout);
+    
+    /* 循环从环形缓冲区读取数据直到完整响应或超时 */
+    while (!response_complete) {
+        uint32_t current_time = (uint32_t)osal_get_jiffies();
+        uint32_t elapsed = osal_jiffies_to_msecs(current_time - start_time);
+        
+        /* 检查超时 */
+        if (elapsed >= timeout) {
+            /* 在超时前,检查累积缓冲区是否还有未处理的数据 */
+            /* 如果有,说明响应可能还在处理中,再等待一段时间 */
+            if (!has_checked_accumulate_buffer_on_timeout) {
+                osal_mutex_lock(&g_at_accumulate_mutex);
+                uint32_t accumulate_length = g_at_accumulate_length;
+                osal_mutex_unlock(&g_at_accumulate_mutex);
+                
+                if (accumulate_length > 0) {
+                    /* 累积缓冲区还有数据,可能是响应还在处理中,再等待200ms */
+                    PPP_DEBUG_WARN("N706A AT: Timeout reached but accumulate buffer has %u bytes, waiting 200ms more", accumulate_length);
+                    has_checked_accumulate_buffer_on_timeout = true;
+                    timeout += 200;  // 延长超时时间200ms
+                    continue;  // 继续等待
+                }
+                has_checked_accumulate_buffer_on_timeout = true;
+            }
+            
+            PPP_DEBUG_WARN("N706A AT: Response timeout after %u ms", elapsed);
             break;
         }
-    }
-
-    if (!supported) {
-        PPP_DEBUG_ERROR("Unsupported baudrate: %u", baudrate);
-        return PPP_ERR_INVALID_PARAM;
-    }
-#if defined(PPP_DO_SERIAL_TEST) && (PPP_DO_SERIAL_TEST == 1)
-    uint32_t current_modem_baudrate = baudrate; // 测试模式下,直接使用输入的波特率
-#else
-    /* 先查询当前modem波特率 */
-    uint32_t current_modem_baudrate = ppp_query_current_modem_baudrate();
-    if (current_modem_baudrate == baudrate) {
-        PPP_DEBUG_INFO("Modem already at target baudrate %u, skipping AT+IPR command", baudrate);
         
-        /* 仍需要更新本地UART配置以确保一致性 */
-        uart_attr_t uart_attr = {
-            .baud_rate = baudrate,
-            .data_bits = UART_DATA_BIT_8,
-            .parity = UART_PARITY_NONE,
-            .stop_bits = UART_STOP_BIT_1,
-        };
+        /* 定期检查累积缓冲区是否有数据(每1秒检查一次,减少日志输出) */
+        /* 如果累积缓冲区有数据但环形缓冲区没有,说明响应可能还在处理中 */
+        uint32_t time_since_last_check = elapsed - last_accumulate_check_elapsed;
+        if (time_since_last_check >= 1000) {
+            osal_mutex_lock(&g_at_accumulate_mutex);
+            uint32_t accumulate_length = g_at_accumulate_length;
+            /* 获取最后几个字节用于分析(最多16字节) */
+            uint8_t last_bytes[16] = {0};
+            uint32_t preview_len = (accumulate_length < 16) ? accumulate_length : 16;
+            if (accumulate_length > 0 && preview_len > 0) {
+                uint32_t start_offset = (accumulate_length > preview_len) ? (accumulate_length - preview_len) : 0;
+                memcpy(last_bytes, g_at_accumulate_buffer + start_offset, preview_len);
+            }
+            osal_mutex_unlock(&g_at_accumulate_mutex);
+            
+            /* 只在累积缓冲区大小变化时打印日志,减少重复输出 */
+            if (accumulate_length > 0 && accumulate_length != last_accumulate_length) {
+                /* 累积缓冲区大小变化,重置卡住计时 */
+                accumulate_stuck_start_elapsed = elapsed;
+                /* 打印最后几个字节的十六进制,帮助分析问题(使用静态缓冲区避免栈溢出) */
+                static char hex_str[48] = {0};  // 静态缓冲区,避免栈分配
+                memset(hex_str, 0, sizeof(hex_str));
+                /* 使用查表法替代snprintf,减少栈使用 */
+                static const char hex_chars[] = "0123456789ABCDEF";
+                for (uint32_t i = 0; i < preview_len && i * 3 + 3 < sizeof(hex_str); i++) {
+                    hex_str[i * 3] = hex_chars[(last_bytes[i] >> 4) & 0x0F];
+                    hex_str[i * 3 + 1] = hex_chars[last_bytes[i] & 0x0F];
+                    hex_str[i * 3 + 2] = ' ';
+                }
+                PPP_DEBUG_DEBUG("N706A AT: Accumulate buffer has %u bytes, response may be processing, last %u bytes: %s", 
+                              accumulate_length, preview_len, hex_str);
+                last_accumulate_length = accumulate_length;
+            } else if (accumulate_length == 0 && last_accumulate_length > 0) {
+                /* 累积缓冲区清空时也打印一次 */
+                accumulate_stuck_start_elapsed = 0;
+                PPP_DEBUG_DEBUG("N706A AT: Accumulate buffer cleared");
+                last_accumulate_length = 0;
+            } else if (accumulate_length > 0 && accumulate_length == last_accumulate_length) {
+                /* 累积缓冲区大小不变,检查是否卡住超过5秒 */
+                if (accumulate_stuck_start_elapsed == 0) {
+                    accumulate_stuck_start_elapsed = elapsed;  // 第一次检测到卡住
+                } else {
+                    uint32_t stuck_duration = elapsed - accumulate_stuck_start_elapsed;
+                    if (stuck_duration >= 5000) {
+                        /* 累积缓冲区卡住超过5秒,打印警告和最后几个字节(使用静态缓冲区避免栈溢出) */
+                        static char hex_str[48] = {0};  // 静态缓冲区,避免栈分配
+                        memset(hex_str, 0, sizeof(hex_str));
+                        /* 使用查表法替代snprintf,减少栈使用 */
+                        static const char hex_chars[] = "0123456789ABCDEF";
+                        for (uint32_t i = 0; i < preview_len && i * 3 + 3 < sizeof(hex_str); i++) {
+                            hex_str[i * 3] = hex_chars[(last_bytes[i] >> 4) & 0x0F];
+                            hex_str[i * 3 + 1] = hex_chars[last_bytes[i] & 0x0F];
+                            hex_str[i * 3 + 2] = ' ';
+                        }
+                        PPP_DEBUG_WARN("N706A AT: Accumulate buffer stuck at %u bytes for %u ms, last %u bytes: %s", 
+                                     accumulate_length, stuck_duration, preview_len, hex_str);
+                        accumulate_stuck_start_elapsed = elapsed;  // 重置计时,避免重复打印
+                    }
+                }
+            }
+            last_accumulate_check_elapsed = elapsed;
+        }
         
-        if (uapi_uart_set_attr(g_at_ctx.uart_bus, &uart_attr) != ERRCODE_SUCC) {
-            PPP_DEBUG_ERROR("Failed to update UART baudrate");
-            return PPP_ERR_UART;
+        /* 等待信号量(有新数据到达),使用剩余超时时间 */
+        uint32_t remaining_timeout = timeout - elapsed;
+        if (remaining_timeout > 100) {
+            remaining_timeout = 100;  // 最多等100ms,然后检查一次
         }
-
-        g_at_ctx.current_baudrate = baudrate;
-        g_at_ctx.auto_baudrate_mode = false;
         
-        PPP_DEBUG_DEBUG("Baudrate confirmed at %u (no change needed)", baudrate);
-        return PPP_ERR_OK;
-    }
-
-    /* 需要设置新波特率 */
-    PPP_DEBUG_INFO("Changing modem baudrate from %u to %u", current_modem_baudrate, baudrate);
-    
-    /* 发送AT+IPR命令设置波特率 */
-    char cmd[64];
-    snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "AT+IPR=%u", baudrate);
-    
-    ppp_at_response_t response;
-    ppp_error_t ret = ppp_at_send_command(cmd, "OK", PPP_AT_CMD_TIMEOUT, &response);
-    
-    if (ret != PPP_ERR_OK) {
-        PPP_DEBUG_ERROR("Failed to set baudrate to %u", baudrate);
-        return ret;
+        int sem_result = osal_sem_down_timeout(&g_at_response_sem, remaining_timeout);
+        if (sem_result == OSAL_SUCCESS || sem_result == OSAL_FAILURE) {
+            /* 有新数据到达或信号量操作失败,尝试读取环形缓冲区 */
+            osal_mutex_lock(&g_at_response_mutex);
+            
+            /* 从环形缓冲区读取数据到响应缓冲区 */
+            while (g_at_response_tail != g_at_response_head && 
+                   bytes_received < (g_at_buffer_size - 1)) {
+                g_at_response_buffer[bytes_received] = g_at_response_ring_buffer[g_at_response_tail];
+                g_at_response_tail = (g_at_response_tail + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
+                bytes_received++;
+            }
+            
+            osal_mutex_unlock(&g_at_response_mutex);
+            
+            /* 设置字符串结束符 */
+            if (bytes_received > 0) {
+                g_at_response_buffer[bytes_received] = '\0';
+                
+                PPP_DEBUG_DEBUG("N706A AT: Read %u bytes from ring buffer", bytes_received);
+                
+                /* 检查是否收到完整响应 */
+                if (ppp_at_check_response_complete((char*)g_at_response_buffer, bytes_received)) {
+                    PPP_DEBUG_DEBUG("N706A AT: Complete response detected");
+                    response_complete = true;
+                    break;
+                }
+            }
+        }
+        
+        /* 短暂延迟避免CPU占用过高 */
+        if (!response_complete) {
+            osal_msleep(10);
+        }
     }
-#endif
-    /* 更新串口配置 */
-    uart_attr_t uart_attr = {
-        .baud_rate = baudrate,
-        .data_bits = UART_DATA_BIT_8,
-        .parity = UART_PARITY_NONE,
-        .stop_bits = UART_STOP_BIT_1,
-    };
     
-    if (uapi_uart_set_attr(g_at_ctx.uart_bus, &uart_attr) != ERRCODE_SUCC) {
-        PPP_DEBUG_ERROR("Failed to update UART baudrate");
-        return PPP_ERR_UART;
+    /* 处理结果 */
+    if (bytes_received > 0) {
+        uint32_t copy_len = (bytes_received < PPP_AT_RESPONSE_MAX_LEN - 1) ? 
+                           bytes_received : PPP_AT_RESPONSE_MAX_LEN - 1;
+        
+        memcpy_s(response->response, PPP_AT_RESPONSE_MAX_LEN, 
+                g_at_response_buffer, copy_len);
+        response->response[copy_len] = '\0';
+        response->length = copy_len;
+        
+        uint32_t final_time = (uint32_t)osal_get_jiffies();
+        uint32_t total_elapsed = osal_jiffies_to_msecs(final_time - start_time);
+        
+        PPP_DEBUG_DEBUG("N706A AT: SUCCESS - %u bytes in %u ms", bytes_received, total_elapsed);
+        PPP_DEBUG_DEBUG(">>> N706A AT WAIT_RESPONSE END (SUCCESS) <<<");
+        return PPP_ERR_OK;
+    } else {
+        uint32_t final_time = (uint32_t)osal_get_jiffies();
+        uint32_t total_elapsed = osal_jiffies_to_msecs(final_time - start_time);
+        
+        PPP_DEBUG_WARN("N706A AT: FAILED - No response after %u ms", total_elapsed);
+        PPP_DEBUG_DEBUG(">>> N706A AT WAIT_RESPONSE END (FAILED) <<<");
+        response->length = 0;
+        response->response[0] = '\0';
+        return PPP_ERR_TIMEOUT;
     }
-
-    g_at_ctx.current_baudrate = baudrate;
-    g_at_ctx.auto_baudrate_mode = false;
-    
-    PPP_DEBUG_DEBUG("Baudrate set bus(%d) to %u (changed from %u)", g_at_ctx.uart_bus, baudrate, current_modem_baudrate);
-    return PPP_ERR_OK;
 }
 
 /**
- * @brief 发送AT命令(内部函数)
- * @note 检查内存泄露:所有malloc分配的内存都应在所有分支free
+ * @brief 解析AT+CESQ响应
+ * 参考N706A AT命令手册: +CESQ: <rxlev>,<ber>,<rscp>,<ecno>,<rsrq>,<rsrp>
  */
-static ppp_error_t ppp_at_send_command(const char *command, const char *expected_response, 
-                                uint32_t timeout, ppp_at_response_t *response)
+static ppp_error_t ppp_parse_cesq_response(const char *response, n706a_network_signal_info_t *info)
 {
-    uint32_t start_timestamp = (uint32_t)osal_get_jiffies();
-
-    PPP_DEBUG_CRITICAL("=== AT COMMAND START ===");
-    PPP_DEBUG_CRITICAL("Timestamp: %u", start_timestamp);
-    PPP_DEBUG_CRITICAL("Command: [%s]", command ? command : "NULL");
-    PPP_DEBUG_CRITICAL("Expected: [%s]", expected_response ? expected_response : "NULL");
-    PPP_DEBUG_CRITICAL("Timeout: %u ms", timeout);
-
-    if (!g_at_ctx.initialized || !command || !response) {
-        PPP_DEBUG_ERROR("Invalid parameters: initialized=%d, command=%p, response=%p", 
-                       g_at_ctx.initialized, command, response);
+    if (!response || !info) {
         return PPP_ERR_INVALID_PARAM;
     }
 
-    /* 防止系统卡死的安全措施 */
-    const uint32_t MAX_COMMAND_TIMEOUT = 60000;  // 绝对最大超时时间60秒
-    if (timeout > MAX_COMMAND_TIMEOUT) {
-        PPP_DEBUG_WARN("Command timeout %u ms exceeds maximum %u ms, capping it", 
-                       timeout, MAX_COMMAND_TIMEOUT);
-        timeout = MAX_COMMAND_TIMEOUT;
+    memset_s(info, sizeof(n706a_network_signal_info_t), 0, sizeof(n706a_network_signal_info_t));
+
+    // 查找+CESQ:开头的行
+    const char *cesq_line = strstr(response, "+CESQ:");
+    if (!cesq_line) {
+        PPP_DEBUG_WARN("No +CESQ: found in response");
+        return PPP_ERR_AT_FAILED;
     }
 
-    PPP_DEBUG_CRITICAL("Step 1: Attempting to acquire AT mutex...");
-    uint32_t mutex_start = (uint32_t)osal_get_jiffies();
+    // 跳过"+CESQ:"
+    cesq_line += 6;
+    while (*cesq_line == ' ' || *cesq_line == '\t') cesq_line++;
 
-    /* 使用带超时的互斥锁获取,防止永久阻塞 */
-    int mutex_result = osal_mutex_lock_timeout(&g_at_ctx.at_mutex, timeout + 5000);
-    if (mutex_result != OSAL_SUCCESS) {
-        PPP_DEBUG_ERROR("Failed to acquire AT mutex within timeout, this indicates a serious problem");
-        PPP_DEBUG_ERROR("Possible causes: 1) Previous AT command hung 2) Mutex corruption 3) Deadlock");
-        return PPP_ERR_TIMEOUT;
+    // +CESQ: <rxlev>,<ber>,<rscp>,<ecno>,<rsrq>,<rsrp>
+    int rxlev = 99, ber = 99, rscp = 255, ecno = 255, rsrq = 255, rsrp = 255;
+    int parsed = sscanf(cesq_line, "%d,%d,%d,%d,%d,%d", &rxlev, &ber, &rscp, &ecno, &rsrq, &rsrp);
+
+    if (parsed == 6) {
+        info->valid = true;
+        info->rxlev = rxlev;
+        info->ber = ber;
+        info->rscp = rscp;
+        info->ecno = ecno;
+        info->rsrq = rsrq;
+        info->rsrp = rsrp;
+        PPP_DEBUG_INFO("CESQ parsed successfully: rxlev=%d, ber=%d, rscp=%d, ecno=%d, rsrq=%d, rsrp=%d",
+            rxlev, ber, rscp, ecno, rsrq, rsrp);
+        return PPP_ERR_OK;
+    } else {
+        PPP_DEBUG_WARN("Failed to parse CESQ response, parsed: %d", parsed);
+        return PPP_ERR_AT_FAILED;
     }
+}
 
-    uint32_t mutex_end = (uint32_t)osal_get_jiffies();
-    PPP_DEBUG_CRITICAL("Step 1: AT mutex acquired in %u ms", osal_jiffies_to_msecs(mutex_end - mutex_start));
+/**
+ * @brief 获取网络信号信息 (AT+CESQ)
+ */
+ppp_error_t ppp_at_get_network_signal_info(n706a_network_signal_info_t *info)
+{
+    if (!g_at_ctx.initialized || !info) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+    // N706A_NET模式下始终处于AT模式,无需检查
+    // (PPP模式需要检查是否成功切换到AT模式)
+    
+    PPP_DEBUG_INFO("Getting network signal information (AT+CESQ)...");
 
-    PPP_DEBUG_DEBUG("=== AT Command Send ===");
-    PPP_DEBUG_DEBUG("Command: %s", command);
-    PPP_DEBUG_DEBUG("Expected: %s", expected_response ? expected_response : "None");
-    PPP_DEBUG_DEBUG("Timeout: %u ms", timeout);
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command("AT+CESQ", "OK", 5000, &response);
 
-    /* 清空响应缓冲区 */
-    memset_s(response, sizeof(ppp_at_response_t), 0, sizeof(ppp_at_response_t));
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("AT+CESQ command failed: %d", ret);
+        return ret;
+    }
 
-    /* 发送命令 */
-    size_t command_len = strlen(command);
-    char *cmd_with_crlf = (char *)malloc(command_len + 4);
-    if (!cmd_with_crlf) {
-        PPP_DEBUG_ERROR("Failed to allocate memory for cmd_with_crlf");
-        osal_mutex_unlock(&g_at_ctx.at_mutex);
-        return PPP_ERR_MEMORY;
+    // 解析响应
+    ret = ppp_parse_cesq_response(response.response, info);
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to parse CESQ response");
+        return ret;
     }
-    snprintf_s(cmd_with_crlf, command_len + 4, command_len + 3, "%s\r\n", command);
 
-    uint32_t cmd_len = strlen(cmd_with_crlf);
+    // 打印详细信息
+    PPP_DEBUG_INFO("=== Network Signal Information (AT+CESQ) ===");
+    PPP_DEBUG_INFO("rxlev: %d", info->rxlev);
+    PPP_DEBUG_INFO("ber: %d", info->ber);
+    PPP_DEBUG_INFO("rscp: %d", info->rscp);
+    PPP_DEBUG_INFO("ecno: %d", info->ecno);
+    PPP_DEBUG_INFO("rsrq: %d", info->rsrq);
+    PPP_DEBUG_INFO("rsrp: %d", info->rsrp);
 
-    /* 打印发送的字符串(可见字符) */
-    char *printable_cmd = (char *)malloc(PPP_AT_CMD_MAX_LEN + 20);
-    if (printable_cmd == NULL) {
-        PPP_DEBUG_ERROR("Failed to allocate memory for printable command");
-        free(cmd_with_crlf); // 防止内存泄露
-        osal_mutex_unlock(&g_at_ctx.at_mutex);
-        return PPP_ERR_MEMORY;
+    // LTE信号质量评估 - 仅关注RSRQ和RSRP
+    PPP_DEBUG_INFO("=== LTE Signal Quality Analysis ===");
+    
+    // RSRQ评估 (0-49有效,255无效)
+    if (info->rsrq == 255) {
+        PPP_DEBUG_INFO("RSRQ: 无法获取LTE信号质量");
+    } else if (info->rsrq >= 0 && info->rsrq <= 49) {
+        // RSRQ转换公式: 实际值(dB) = (rsrq - 94)/10
+        float rsrq_db = (info->rsrq - 94) / 10.0f;
+        PPP_DEBUG_INFO("RSRQ: %d (%.1f dB)", info->rsrq, rsrq_db);
+        
+        // RSRQ评估标准 (范围: -19.5dB ~ -3dB,数值越大质量越好)
+        if (rsrq_db >= -6.0) {
+            PPP_DEBUG_INFO("RSRQ评级: 极佳 (信号质量优秀)");
+        } else if (rsrq_db >= -9.0) {
+            PPP_DEBUG_INFO("RSRQ评级: 良好 (信号质量稳定)");
+        } else if (rsrq_db >= -12.0) {
+            PPP_DEBUG_INFO("RSRQ评级: 一般 (信号质量可用)");
+        } else {
+            PPP_DEBUG_INFO("RSRQ评级: 较差 (信号质量不佳)");
+        }
+    } else {
+        PPP_DEBUG_INFO("RSRQ: %d (超出有效范围0-49)", info->rsrq);
     }
-
-    uint32_t j = 0;
-    for (uint32_t i = 0; i < cmd_len && j < (PPP_AT_CMD_MAX_LEN + 20) - 10; i++) {
-        if (cmd_with_crlf[i] == '\r') {
-            printable_cmd[j++] = '\\';
-            printable_cmd[j++] = 'r';
-        } else if (cmd_with_crlf[i] == '\n') {
-            printable_cmd[j++] = '\\';
-            printable_cmd[j++] = 'n';
-        } else if (cmd_with_crlf[i] >= 32 && cmd_with_crlf[i] <= 126) {
-            printable_cmd[j++] = cmd_with_crlf[i];
+    
+    // RSRP评估 (0-97有效,255无效)
+    if (info->rsrp == 255) {
+        PPP_DEBUG_INFO("RSRP: 无法获取LTE信号强度");
+    } else if (info->rsrp >= 0 && info->rsrp <= 97) {
+        // RSRP转换公式: 实际值(dBm) = rsrp - 140
+        float rsrp_dbm = info->rsrp - 140.0f;
+        PPP_DEBUG_INFO("RSRP: %d (%.0f dBm)", info->rsrp, rsrp_dbm);
+        
+        // RSRP评估标准 (范围: -140dBm ~ -43dBm,数值越大信号越强)
+        if (rsrp_dbm >= -80) {
+            PPP_DEBUG_INFO("RSRP评级: 极佳 (信号强度优秀)");
+        } else if (rsrp_dbm >= -90) {
+            PPP_DEBUG_INFO("RSRP评级: 良好 (信号强度稳定)");
+        } else if (rsrp_dbm >= -100) {
+            PPP_DEBUG_INFO("RSRP评级: 一般 (信号强度可用)");
         } else {
-            j += snprintf_s(printable_cmd + j, (PPP_AT_CMD_MAX_LEN + 20) - j, (PPP_AT_CMD_MAX_LEN + 20) - j - 1, "\\x%02X", (uint8_t)cmd_with_crlf[i]);
+            PPP_DEBUG_INFO("RSRP评级: 较差 (信号强度不足)");
         }
+    } else {
+        PPP_DEBUG_INFO("RSRP: %d (超出有效范围0-97)", info->rsrp);
     }
-    printable_cmd[j] = '\0';
-    PPP_DEBUG_DEBUG("Send String: %s", printable_cmd);
-    free(printable_cmd);
-
-    PPP_DEBUG_CRITICAL("Step 2: Sending AT command via UART...");
-    uint32_t uart_write_start = (uint32_t)osal_get_jiffies();
-
-    int32_t write_result = uapi_uart_write(g_at_ctx.uart_bus, (const uint8_t *)cmd_with_crlf, cmd_len, 0);
-    uint32_t uart_write_end = (uint32_t)osal_get_jiffies();
-
-    free(cmd_with_crlf);
-
-    PPP_DEBUG_CRITICAL("Step 2: UART write completed in %u ms, result: %d (expected: %u)", 
-                      osal_jiffies_to_msecs(uart_write_end - uart_write_start), write_result, cmd_len);
-
-    if (write_result != cmd_len) {
-        PPP_DEBUG_ERROR("Failed to send AT command: wrote %d bytes, expected %u", write_result, cmd_len);
-        osal_mutex_unlock(&g_at_ctx.at_mutex);
-        return PPP_ERR_UART;
+    
+    // 综合LTE信号质量评估
+    if (info->rsrq != 255 && info->rsrp != 255 && 
+        info->rsrq >= 0 && info->rsrq <= 49 && 
+        info->rsrp >= 0 && info->rsrp <= 97) {
+        
+        float rsrq_db = (info->rsrq - 94) / 10.0f;
+        float rsrp_dbm = info->rsrp - 140.0f;
+        
+        const char *overall_quality = "未知";
+        if (rsrq_db >= -6.0 && rsrp_dbm >= -80) {
+            overall_quality = "LTE信号极佳 (质量和强度都优秀)";
+        } else if (rsrq_db >= -9.0 && rsrp_dbm >= -90) {
+            overall_quality = "LTE信号良好 (质量和强度都稳定)";
+        } else if (rsrq_db >= -12.0 && rsrp_dbm >= -100) {
+            overall_quality = "LTE信号一般 (质量和强度可用)";
+        } else {
+            overall_quality = "LTE信号较差 (需要改善网络环境)";
+        }
+        
+        PPP_DEBUG_INFO("=== 综合评估: %s ===", overall_quality);
+    } else {
+        PPP_DEBUG_INFO("=== 综合评估: LTE参数无效,无法评估 ===");
     }
 
-    PPP_DEBUG_CRITICAL("Step 2: AT command sent successfully on bus %d", g_at_ctx.uart_bus);
+    PPP_DEBUG_INFO("=== End Network Signal Information ===");
 
-    /* 等待响应 */
-    PPP_DEBUG_CRITICAL("Step 3: Starting to wait for AT response...");
-    uint32_t response_wait_start = (uint32_t)osal_get_jiffies();
-    ppp_error_t ret = ppp_at_wait_response(response, timeout);
-    uint32_t response_wait_end = (uint32_t)osal_get_jiffies();
-    PPP_DEBUG_CRITICAL("Step 3: AT response wait completed in %u ms, result: %d", 
-                      osal_jiffies_to_msecs(response_wait_end - response_wait_start), ret);
+    return PPP_ERR_OK;
+}
 
-    /* 打印响应结果 */
-    PPP_DEBUG_DEBUG("=== AT Command Response ===");
-    if (ret == PPP_ERR_OK) {
-        PPP_DEBUG_DEBUG("Response received (%u bytes):", response->length);
+/* ========== 导出的N706A API包装函数(用于n706a_tcp_client和n706a_net_at_cmd) ========== */
 
-        /* 打印接收的字符串(可见字符) */
-        char *printable_resp = (char *)malloc(PPP_AT_RESPONSE_MAX_LEN + 100);
-        if (printable_resp != NULL) {
-            uint32_t j = 0;
-            for (uint32_t i = 0; i < response->length && j < (PPP_AT_RESPONSE_MAX_LEN + 100) - 10; i++) {
-                if (response->response[i] == '\r') {
-                    printable_resp[j++] = '\\';
-                    printable_resp[j++] = 'r';
-                } else if (response->response[i] == '\n') {
-                    printable_resp[j++] = '\\';
-                    printable_resp[j++] = 'n';
-                } else if (response->response[i] >= 32 && response->response[i] <= 126) {
-                    printable_resp[j++] = response->response[i];
-                } else {
-                    j += snprintf_s(printable_resp + j, (PPP_AT_RESPONSE_MAX_LEN + 100) - j, (PPP_AT_RESPONSE_MAX_LEN + 100) - j - 1, "\\x%02X", (uint8_t)response->response[i]);
-                }
+/**
+ * @brief N706A对外导出:发送AT命令并等待响应
+ */
+n706a_at_error_t n706a_at_send_command(const char *command, 
+                                       const char *expected_response, 
+                                       uint32_t timeout, 
+                                       n706a_at_response_t *response)
+{
+    ppp_at_response_t ppp_resp;
+    ppp_error_t ret = ppp_at_send_command(command, expected_response, timeout, &ppp_resp);
+    
+    if (response) {
+        /* 转换ppp_at_response_t到n706a_at_response_t */
+        memcpy_s(response->response, sizeof(response->response), ppp_resp.response, sizeof(ppp_resp.response));
+        response->length = ppp_resp.length;
+        response->success = ppp_resp.success;
+        
+        /* 检查响应中是否包含ERROR字符串(即使ppp_at_send_command返回成功) */
+        bool has_error = (strstr((const char *)response->response, "ERROR") != NULL);
+        
+        if (ret == PPP_ERR_OK && !has_error && ppp_resp.success) {
+            response->error_code = N706A_AT_OK;
+        } else {
+            response->error_code = N706A_AT_ERR_AT_FAILED;
+            response->success = false;
+            if (has_error) {
+                PPP_DEBUG_ERROR("[AT] Response contains ERROR: %s", response->response);
             }
-            printable_resp[j] = '\0';
-            PPP_DEBUG_DEBUG("Response String: %s", printable_resp);
-            free(printable_resp);
         }
-        PPP_DEBUG_DEBUG("Raw Response: %s", response->response);
+    }
+    
+    /* 如果响应中包含ERROR,即使ppp_at_send_command返回成功,也应该返回失败 */
+    bool has_error = (response && strstr((const char *)response->response, "ERROR") != NULL);
+    if (ret == PPP_ERR_OK && !has_error) {
+        return N706A_AT_OK;
     } else {
-        PPP_DEBUG_ERROR("No response received (error: %d)", ret);
+        return N706A_AT_ERR_AT_FAILED;
+    }
+}
+
+/**
+ * @brief N706A对外导出:测试AT通信是否正常
+ */
+n706a_at_error_t n706a_at_test_communication(void)
+{
+    if (!g_at_ctx.initialized) {
+        return N706A_AT_ERR_INVALID_PARAM;
+    }
+    
+    n706a_at_response_t response;
+    n706a_at_error_t ret = n706a_at_send_command("AT", "OK", 3000, &response);
+    
+    if (ret == N706A_AT_OK && response.success) {
+        PPP_DEBUG_INFO("AT communication test passed\n");
+        return N706A_AT_OK;
+    } else {
+        PPP_DEBUG_ERROR("AT communication test failed\n");
+        return N706A_AT_ERR_AT_FAILED;
     }
+}
 
+/**
+ * @brief N706A对外导出:获取网络信号信息
+ */
+n706a_at_error_t n706a_at_get_network_signal_info(n706a_network_signal_info_t *signal_info)
+{
+    if (!signal_info) {
+        return N706A_AT_ERR_INVALID_PARAM;
+    }
+    
+    /* 调用内部的ppp_at_get_network_signal_info */
+    n706a_network_signal_info_t ppp_signal;
+    ppp_error_t ret = ppp_at_get_network_signal_info(&ppp_signal);
+    
     if (ret == PPP_ERR_OK) {
-        if (expected_response) {
-            if (!ppp_at_parse_response(response->response, expected_response)) {
-                PPP_DEBUG_WARN("Response validation failed - Expected: %s", expected_response);
-                PPP_DEBUG_WARN("Response validation failed - Received: %s", response->response);
-                response->success = false;
-                response->error_code = PPP_ERR_AT_FAILED;
-                ret = PPP_ERR_AT_FAILED;
-            } else {
-                PPP_DEBUG_DEBUG("Response validation successful - Expected: %s", expected_response);
-                response->success = true;
-                response->error_code = PPP_ERR_OK;
-            }
-        } else {
-            // 没有期望响应时,只要收到响应就认为成功
-            response->success = true;
-            response->error_code = PPP_ERR_OK;
-        }
+        /* 转换数据结构 */
+        signal_info->rxlev = ppp_signal.rxlev;
+        signal_info->ber = ppp_signal.ber;
+        signal_info->rscp = ppp_signal.rscp;
+        signal_info->ecno = ppp_signal.ecno;
+        signal_info->rsrq = ppp_signal.rsrq;
+        signal_info->rsrp = ppp_signal.rsrp;
+        signal_info->valid = ppp_signal.valid;
+        return N706A_AT_OK;
     } else {
-        response->success = false;
-        response->error_code = ret;
+        signal_info->valid = false;
+        return N706A_AT_ERR_AT_FAILED;
     }
+}
 
-    PPP_DEBUG_DEBUG("=== AT Command Complete (Result: %s) ===", ret == PPP_ERR_OK ? "SUCCESS" : "FAILED");
-
-    PPP_DEBUG_CRITICAL("Step 4: Releasing AT mutex...");
-    uint32_t mutex_unlock_start = (uint32_t)osal_get_jiffies();
-    osal_mutex_unlock(&g_at_ctx.at_mutex);
-    uint32_t mutex_unlock_end = (uint32_t)osal_get_jiffies();
+/**
+ * @brief N706A对外导出:设置波特率
+ */
+n706a_at_error_t n706a_at_set_baudrate(uint32_t baudrate)
+{
+    ppp_error_t ret = ppp_at_set_baudrate(baudrate);
+    return (ret == PPP_ERR_OK) ? N706A_AT_OK : N706A_AT_ERR_UART;
+}
 
-    uint32_t total_time = osal_jiffies_to_msecs(mutex_unlock_end - start_timestamp);
-    PPP_DEBUG_CRITICAL("Step 4: AT mutex released in %u ms", osal_jiffies_to_msecs(mutex_unlock_end - mutex_unlock_start));
-    PPP_DEBUG_CRITICAL("=== AT COMMAND END (Total: %u ms, Result: %d) ===", total_time, ret);
+/**
+ * @brief N706A对外导出:获取当前波特率
+ */
+uint32_t n706a_at_get_current_baudrate(void)
+{
+    return g_at_ctx.current_baudrate;
+}
 
-    return ret;
+/**
+ * @brief N706A对外导出:发送原始数据(不带AT命令格式)
+ * @param data 要发送的数据
+ * @param len 数据长度
+ * @param timeout 超时时间(ms)
+ * @return 错误码
+ * @note 用于 HTTP/HTTPS POST/PUT/DELETE/HEAD 请求,在 AT+HTTPACTION=99 命令后使用
+ */
+n706a_at_error_t n706a_at_send_raw_data(const void *data, size_t len, uint32_t timeout)
+{
+    ppp_error_t ret = ppp_at_send_raw_data(data, len, timeout);
+    if (ret == PPP_ERR_OK) {
+        return N706A_AT_OK;
+    } else if (ret == PPP_ERR_TIMEOUT) {
+        return N706A_AT_ERR_TIMEOUT;
+    } else if (ret == PPP_ERR_MEMORY) {
+        return N706A_AT_ERR_MEMORY;
+    } else {
+        return N706A_AT_ERR_UART;
+    }
 }
 
 /**
- * @brief 等待 '>' 提示符(用于 AT+TCPSEND/AT+SSLTCPSEND)
- * @param timeout 超时时间(毫秒)
- * @return PPP_ERR_OK 成功,其他值失败
+ * @brief 解析 +TCPRECV URC
+ * @param urc_data URC数据(以 "+TCPRECV: " 开头)
+ * @param urc_len URC数据长度
+ * @param socket_id 输出:socket ID
+ * @param data_len 输出:数据长度
+ *   - ASCII模式:二进制数据长度(字节数)
+ *   - HEX模式:二进制数据长度(字节数),HEX字符串长度 = data_len * 2
+ * @param data 输出:数据指针(指向 urc_data 内部)
+ *   - ASCII模式:指向ASCII数据
+ *   - HEX模式:指向HEX字符串
+ * @return true=解析成功,false=失败
  * 
- * 根据N706A AT命令手册,AT+TCPSEND发送后会收到 '>' 提示符,
- * 此时需要延迟50-100ms后再发送数据。
+ * URC格式(ASCII模式):+TCPRECV: <socket_id>,<binary_len>,<ascii_data>\r\n
+ * 例如:+TCPRECV: 0,10,1234567890\r\n (10字节ASCII数据)
+ * 注意:<length>是二进制数据长度(10)
+ * 
+ * URC格式(HEX模式):+TCPRECV: <socket_id>,<binary_len>,<hex_data>\r\n
+ * 例如:+TCPRECV: 0,3,313233\r\n (表示3字节二进制数据:0x31, 0x32, 0x33,HEX字符串长度为6)
+ * 注意:<length>是二进制数据长度(3),HEX字符串长度 = binary_len × 2 = 6
+ * 
+ * 与AT+TCPSEND的对应关系:
+ * - AT+TCPSEND的length参数:二进制数据长度(字节数)
+ * - +TCPRECV的length参数:二进制数据长度(字节数),HEX字符串长度 = length × 2
  */
-static ppp_error_t ppp_at_wait_for_prompt(uint32_t timeout)
+static bool n706a_parse_tcprecv_urc(const char *urc_data, size_t urc_len, 
+                                    int *socket_id, size_t *data_len, const uint8_t **data)
 {
-    ppp_at_response_t response;
-    ppp_error_t ret = ppp_at_wait_response(&response, timeout);
+    if (!urc_data || !socket_id || !data_len || !data || urc_len < 15) {
+        return false;
+    }
     
-    if (ret != PPP_ERR_OK) {
-        PPP_DEBUG_ERROR("Failed to wait for '>' prompt: error %d", ret);
-        return ret;
+    /* ⚠️ 关键:正确识别标识符类型,只跳过标识符本身,不跳过标识符后的任何字符 */
+    const char *p = urc_data;
+    if (memcmp(p, "+TCPRECV: ", 10) == 0) {
+        p += 10;  /* 跳过 "+TCPRECV: " (10字节) */
+    } else if (memcmp(p, "+SSLTCPRECV: ", 13) == 0) {
+        p += 13;  /* 跳过 "+SSLTCPRECV: " (13字节) */
+    } else {
+        PPP_DEBUG_ERROR("URC parse error: invalid identifier, expected '+TCPRECV: ' or '+SSLTCPRECV: '");
+        return false;
     }
     
-    /* 检查响应中是否包含 '>' */
-    if (strstr((const char *)response.response, ">") == NULL) {
-        PPP_DEBUG_ERROR("Response does not contain '>' prompt: %s", response.response);
-        return PPP_ERR_AT_FAILED;
+    const char *end = urc_data + urc_len;
+    
+    // 解析 socket_id
+    *socket_id = 0;
+    while (p < end && *p >= '0' && *p <= '9') {
+        *socket_id = *socket_id * 10 + (*p - '0');
+        p++;
     }
     
-    PPP_DEBUG_DEBUG("Received '>' prompt, delaying 75ms before sending data...");
+    if (p >= end || *p != ',') {
+        PPP_DEBUG_ERROR("URC parse error: no comma after socket_id");
+        return false;
+    }
+    p++;  // 跳过逗号
     
-    /* 延迟75ms(在50-100ms范围内,取中间值) */
-    osal_msleep(75);
+    // 解析 length(HEX模式下这是二进制数据的长度,HEX字符串长度 = length × 2)
+    // ⚠️ 关键:添加溢出检查,防止 data_len 解析错误导致后续计算错误
+    *data_len = 0;
+    const size_t MAX_REASONABLE_DATA_LEN = 65536;  // 最大合理的数据长度(64KB)
+    while (p < end && *p >= '0' && *p <= '9') {
+        size_t old_data_len = *data_len;
+        *data_len = *data_len * 10 + (*p - '0');
+        // 检查溢出:如果新值小于旧值,说明溢出
+        if (*data_len < old_data_len || *data_len > MAX_REASONABLE_DATA_LEN) {
+            PPP_DEBUG_ERROR("URC parse error: data_len overflow or too large! value=%u (max=%u), stopping parse at position %u", 
+                          (unsigned int)*data_len, (unsigned int)MAX_REASONABLE_DATA_LEN, (unsigned int)(p - urc_data));
+            // 截断到最大值,避免后续计算错误
+            *data_len = MAX_REASONABLE_DATA_LEN;
+            // 跳过剩余的数字
+            while (p < end && *p >= '0' && *p <= '9') {
+                p++;
+            }
+            break;
+        }
+        p++;
+    }
     
-    return PPP_ERR_OK;
+    if (p >= end || *p != ',') {
+        PPP_DEBUG_ERROR("URC parse error: no comma after length (parsed data_len=%u)", (unsigned int)*data_len);
+        return false;
+    }
+    p++;  // 跳过逗号
+    
+    // 数据起始位置
+    *data = (const uint8_t *)p;
+    
+    // 验证数据长度是否合理
+    // 注意:data_len是二进制数据长度,在HEX模式下,实际需要的HEX字符串长度 = data_len × 2
+    // 这里只做基本验证(检查是否有至少 data_len 字节的数据),具体模式判断由调用者处理
+    size_t remaining = end - p;
+    // 在HEX模式下,实际需要的数据长度是 data_len * 2,但这里只检查是否有至少 data_len 字节
+    // 如果数据不足,会在后续的HEX解码中处理(通过检查实际HEX字符串长度)
+    if (*data_len > remaining) {
+        PPP_DEBUG_WARN("URC data_len=%u (binary) exceeds remaining=%u, truncating (hex_mode may need %u bytes)", 
+                      (unsigned int)*data_len, (unsigned int)remaining, (unsigned int)(*data_len * 2));
+        *data_len = remaining;
+        /* ⚠️ 关键:不移除尾部的 \r\n,因为它们是URC数据的一部分 */
+        /* 如果移除,会导致数据丢失,特别是TCP数据以换行符结尾的情况 */
+    }
+    
+    PPP_DEBUG_DEBUG("Parsed URC: socket=%d, data_len=%u", *socket_id, (unsigned int)*data_len);
+    return true;
 }
 
 /**
- * @brief 发送原始数据(不带AT命令格式)
- * @note 用于 TCP/SSL 数据发送,在 AT+TCPSEND/AT+SSLTCPSEND 命令后使用
- * @note 调用此函数前,必须先调用 ppp_at_send_command 发送 AT+TCPSEND 命令并等待 '>' 提示符
+ * @brief 解析 +TCPRECV URC(公共版本,供其他模块使用)
  */
-ppp_error_t ppp_at_send_raw_data(const void *data, size_t len, uint32_t timeout)
+bool n706a_parse_tcprecv_urc_public(const char *urc_data, size_t urc_len, 
+                                    int *socket_id, size_t *data_len, const uint8_t **data)
 {
-    if (!g_at_ctx.initialized || !data || len == 0) {
-        PPP_DEBUG_ERROR("Invalid parameters: initialized=%d, data=%p, len=%zu", 
-                       g_at_ctx.initialized, data, len);
-        return PPP_ERR_INVALID_PARAM;
-    }
-
-    PPP_DEBUG_DEBUG("=== Sending Raw Data (HEX Mode) ===");
-    PPP_DEBUG_DEBUG("Binary length: %zu bytes", len);
+    return n706a_parse_tcprecv_urc(urc_data, urc_len, socket_id, data_len, data);
+}
 
-    /* 将二进制数据编码为HEX字符串 */
-    size_t hex_str_len = len * 2;  // 每个字节转换为2个HEX字符
-    char *hex_str = (char *)malloc(hex_str_len + 1);  // +1 for null terminator
-    if (!hex_str) {
-        PPP_DEBUG_ERROR("Failed to allocate memory for HEX string");
-        return PPP_ERR_MEMORY;
+/**
+ * @brief 解析 +TCPCLOSE URC
+ * @param urc_data URC数据(以 "+TCPCLOSE: " 或 "+SSLTCPCLOSE: " 开头)
+ * @param urc_len URC数据长度
+ * @param socket_id 输出:socket ID(如果格式是ERROR,socket_id设为-1)
+ * @return true=解析成功,false=失败
+ * 
+ * URC格式:
+ *   +TCPCLOSE: <socket_id>
+ *   +TCPCLOSE: <socket_id>,Link Closed
+ *   +TCPCLOSE: ERROR(错误格式,socket_id设为-1)
+ *     或:+SSLTCPCLOSE: <socket_id>
+ */
+static bool n706a_parse_tcpclose_urc(const char *urc_data, size_t urc_len, int *socket_id)
+{
+    if (!urc_data || !socket_id || urc_len < 12) {
+        return false;
     }
     
-    size_t encoded_len = n706a_binary_to_hex_string((const uint8_t *)data, len, hex_str, hex_str_len + 1);
-    if (encoded_len == 0) {
-        free(hex_str);
-        PPP_DEBUG_ERROR("Failed to encode binary data to HEX string");
-        return PPP_ERR_INVALID_PARAM;
+    /* ⚠️ 关键:正确识别标识符类型,只跳过标识符本身,不跳过标识符后的任何字符 */
+    const char *p = urc_data;
+    if (memcmp(p, "+TCPCLOSE: ", 11) == 0) {
+        p += 11;  /* 跳过 "+TCPCLOSE: " (11字节) */
+    } else if (memcmp(p, "+SSLTCPCLOSE: ", 14) == 0) {
+        p += 14;  /* 跳过 "+SSLTCPCLOSE: " (14字节) */
+    } else {
+        /* 尝试使用strstr作为后备(兼容性) */
+        const char *colon_space = strstr(urc_data, ": ");
+        if (!colon_space) {
+            return false;
+        }
+        p = colon_space + 2;  /* 跳过 ": " (2字节) */
     }
     
-    PPP_DEBUG_DEBUG("HEX encoded: binary_len=%zu -> hex_len=%zu", len, encoded_len);
+    const char *end = urc_data + urc_len;
     
-    /* 通过 UART 发送HEX字符串(ASCII字符),不添加 \r\n */
-    int32_t write_result = uapi_uart_write(g_at_ctx.uart_bus, (const uint8_t *)hex_str, encoded_len, 0);
-
-    free(hex_str);
-
-    if (write_result != (int32_t)encoded_len) {
-        PPP_DEBUG_ERROR("Failed to send HEX data: wrote %d bytes, expected %zu", write_result, encoded_len);
-        return PPP_ERR_UART;
+    // 检查是否是ERROR格式
+    if (p + 5 <= end && memcmp(p, "ERROR", 5) == 0 &&
+        (p[5] == '\r' || p[5] == '\n' || p[5] == '\0' || p[5] == ' ')) {
+        *socket_id = -1;  // ERROR格式,socket_id设为-1表示错误
+        return true;
     }
-
-    PPP_DEBUG_DEBUG("HEX data sent successfully (%zu hex chars, %zu binary bytes) on bus %d", 
-                   encoded_len, len, g_at_ctx.uart_bus);
-    return PPP_ERR_OK;
+    
+    // 解析 socket_id
+    *socket_id = 0;
+    while (p < end && *p >= '0' && *p <= '9') {
+        *socket_id = *socket_id * 10 + (*p - '0');
+        p++;
+    }
+    
+    // 可能后面有 ",Link Closed" 或其他信息,但socket_id已经解析完成
+    return (*socket_id >= 0);  // 确保至少解析到一个有效的socket_id或ERROR
 }
 
 /**
- * @brief 跳过开机日志 (对应chat文件中的 '' \rAT)
+ * @brief 解析 +TCPSEND URC
+ * @param urc_data URC数据(以 "+TCPSEND: " 或 "+SSLTCPEND: " 开头)
+ * @param urc_len URC数据长度
+ * @param socket_id 输出:socket ID
+ * @param sent_bytes 输出:实际发送的字节数
+ * @return true=解析成功,false=失败
+ * 
+ * URC格式:+TCPSEND: <socket_id>,<sent_bytes>
+ *     或:+SSLTCPEND: <socket_id>,<sent_bytes>
  */
-static ppp_error_t ppp_at_skip_startup_logs(void)
+static bool n706a_parse_tcpsend_urc(const char *urc_data, size_t urc_len, 
+                                    int *socket_id, size_t *sent_bytes)
 {
-    PPP_DEBUG_DEBUG("Skipping startup logs...");
+    if (!urc_data || !socket_id || !sent_bytes || urc_len < 15) {
+        return false;
+    }
     
-    // 清空接收缓冲区,跳过开机时的各种日志输出
-    uint8_t dummy_buffer[256];
-    uint32_t bytes_read;
+    /* ⚠️ 关键:正确识别标识符类型,只跳过标识符本身,不跳过标识符后的任何字符 */
+    const char *p = urc_data;
+    if (memcmp(p, "+TCPSEND: ", 10) == 0) {
+        p += 10;  /* 跳过 "+TCPSEND: " (10字节) */
+    } else if (memcmp(p, "+SSLTCPEND: ", 12) == 0) {
+        p += 12;  /* 跳过 "+SSLTCPEND: " (12字节) */
+    } else {
+        /* 尝试使用strstr作为后备(兼容性) */
+        const char *colon_space = strstr(urc_data, ": ");
+        if (!colon_space) {
+            return false;
+        }
+        p = colon_space + 2;  /* 跳过 ": " (2字节) */
+    }
     
-    // 连续读取并丢弃开机日志,直到没有更多数据
-    int retry_count = 0;
-    const int max_retries = 10;
+    const char *end = urc_data + urc_len;
     
-    do {
-        ppp_error_t ret = ppp_serial_read(dummy_buffer, sizeof(dummy_buffer), &bytes_read);
-        if (ret != PPP_ERR_OK) {
-            break;
-        }
-        
-        if (bytes_read > 0) {
-            PPP_DEBUG_DEBUG("Skipped %u bytes of startup logs", bytes_read);
-            retry_count = 0; // 重置重试计数
-        } else {
-            retry_count++;
+    // 解析 socket_id
+    *socket_id = 0;
+    while (p < end && *p >= '0' && *p <= '9') {
+        *socket_id = *socket_id * 10 + (*p - '0');
+        p++;
+    }
+    
+    if (p >= end || *p != ',') {
+        PPP_DEBUG_ERROR("TCPSEND URC parse error: no comma after socket_id");
+        return false;
+    }
+    p++;  // 跳过逗号
+    
+    // 解析 sent_bytes(可能是数字,也可能是错误消息如 "OPERATION EXPIRED")
+    *sent_bytes = 0;
+    if (p < end && *p >= '0' && *p <= '9') {
+        // 数字格式:正常情况
+        while (p < end && *p >= '0' && *p <= '9') {
+            *sent_bytes = *sent_bytes * 10 + (*p - '0');
+            p++;
         }
-        
-        osal_msleep(100);
-    } while (retry_count < max_retries);
+    } else {
+        // 文本格式:错误消息(如 "OPERATION EXPIRED")
+        // 这种情况下,sent_bytes 保持为 0,表示发送失败
+        PPP_DEBUG_WARN("TCPSEND URC contains error message instead of sent_bytes: [%.*s]", 
+                      (int)(end - p < 64 ? end - p : 64), p);
+    }
     
-    PPP_DEBUG_DEBUG("Startup logs skipped");
-    return PPP_ERR_OK;
+    return true;
 }
 
-/* ========== PPP拨号函数已删除 ========== */
-/* N706A_NET 使用纯AT命令模式,不需要PPP拨号功能 */
-/* 如需PPP功能,请使用 ppp 组件 */
 /**
- * @brief 发送AT关机指令,支持快速关机和正常关机
- * @param shutdown_type 关机类型: 0-快速关机, 1-正常流程关机
- * @return ppp_error_t
+ * @brief 解析 +TCPSETUP URC
+ * @param urc_data URC数据(以 "+TCPSETUP: " 或 "+SSLTCPSETUP: " 开头)
+ * @param urc_len URC数据长度
+ * @param socket_id 输出:socket ID
+ * @param success 输出:true表示OK,false表示FAIL
+ * @return true=解析成功,false=失败
+ * 
+ * URC格式:+TCPSETUP: <socket_id>,OK 或 +TCPSETUP: <socket_id>,FAIL
+ *     或:+SSLTCPSETUP: <socket_id>,OK 或 +SSLTCPSETUP: <socket_id>,FAIL
+ * 例如:+TCPSETUP: 0,OK 或 +TCPSETUP: 0,FAIL
  */
-ppp_error_t ppp_at_poweroff_modem(uint8_t shutdown_type)
+static bool n706a_parse_tcpsetup_urc(const char *urc_data, size_t urc_len,
+                                     int *socket_id, bool *success)
 {
-    if (!g_at_ctx.initialized) {
-        return PPP_ERR_INVALID_PARAM;
+    if (!urc_data || !socket_id || !success || urc_len < 15) {
+        return false;
     }
-
-    PPP_DEBUG_INFO("Sending AT+NWPWROFF=%d to power off modem", shutdown_type);
-
-    char cmd[32];
-    snprintf(cmd, sizeof(cmd), "AT+NWPWROFF=%d", shutdown_type);
-
-    ppp_at_response_t response;
-    ppp_error_t ret = ppp_at_send_command(cmd, "OK", 10000, &response);
-
-    if (ret == PPP_ERR_OK) {
-        PPP_DEBUG_INFO("Modem power off command sent successfully");
+    
+    /* ⚠️ 关键:正确识别标识符类型,只跳过标识符本身,不跳过标识符后的任何字符 */
+    const char *p = urc_data;
+    if (memcmp(p, "+TCPSETUP: ", 11) == 0) {
+        p += 11;  /* 跳过 "+TCPSETUP: " (11字节) */
+    } else if (memcmp(p, "+SSLTCPSETUP: ", 14) == 0) {
+        p += 14;  /* 跳过 "+SSLTCPSETUP: " (14字节) */
     } else {
-        PPP_DEBUG_ERROR("Failed to send modem power off command");
+        /* 尝试使用strstr作为后备(兼容性) */
+        const char *colon_space = strstr(urc_data, ": ");
+        if (!colon_space) {
+            return false;
+        }
+        p = colon_space + 2;  /* 跳过 ": " (2字节) */
     }
-    ppp_power_off_cat1();
-    return ret;
-}
-
-/* ========== PPP挂断函数已删除 ========== */
-/* N706A_NET 使用纯AT命令模式,不需要PPP挂断功能 */
-
-/**
- * @brief 获取当前波特率
- */
-uint32_t ppp_at_get_current_baudrate(void)
-{
-    return g_at_ctx.current_baudrate;
+    
+    const char *end = urc_data + urc_len;
+    
+    /* 注意:对于TCPSETUP,参数之间可能有空格(如 "+TCPSETUP: 0, OK"),
+     * 这些空格是参数分隔符,跳过它们是合理的,因为它们不是数据的一部分 */
+    /* 跳过可能的空格(参数分隔符) */
+    while (p < end && (*p == ' ' || *p == '\t')) {
+        p++;
+    }
+    
+    // 解析 socket_id
+    *socket_id = 0;
+    while (p < end && *p >= '0' && *p <= '9') {
+        *socket_id = *socket_id * 10 + (*p - '0');
+        p++;
+    }
+    
+    if (p >= end || *p != ',') {
+        PPP_DEBUG_ERROR("TCPSETUP URC parse error: no comma after socket_id (found: 0x%02x)", 
+                       p < end ? (unsigned char)*p : 0);
+        return false;
+    }
+    p++;  // 跳过逗号
+    
+    // 跳过可能的空格
+    while (p < end && (*p == ' ' || *p == '\t')) {
+        p++;
+    }
+    
+    // 解析结果(OK或FAIL),忽略前后空白字符
+    if (p < end) {
+        // 检查OK
+        if (p + 2 <= end && (p[0] == 'O' || p[0] == 'o') && (p[1] == 'K' || p[1] == 'k')) {
+            // 检查OK后面是否是结束符或空白字符
+            if (p + 2 >= end || p[2] == '\r' || p[2] == '\n' || p[2] == '\0' || 
+                p[2] == ' ' || p[2] == '\t') {
+                *success = true;
+                return true;
+            }
+        }
+        
+        // 检查FAIL
+        if (p + 4 <= end && 
+            (p[0] == 'F' || p[0] == 'f') && 
+            (p[1] == 'A' || p[1] == 'a') && 
+            (p[2] == 'I' || p[2] == 'i') && 
+            (p[3] == 'L' || p[3] == 'l')) {
+            // 检查FAIL后面是否是结束符或空白字符
+            if (p + 4 >= end || p[4] == '\r' || p[4] == '\n' || p[4] == '\0' || 
+                p[4] == ' ' || p[4] == '\t') {
+                *success = false;
+                return true;
+            }
+        }
+    }
+    
+    // 如果都不匹配,输出详细的调试信息
+    char debug_buf[64] = {0};
+    size_t debug_len = (end - p < sizeof(debug_buf) - 1) ? (end - p) : (sizeof(debug_buf) - 1);
+    memcpy(debug_buf, p, debug_len);
+    PPP_DEBUG_ERROR("TCPSETUP URC parse error: invalid result (expected OK/FAIL), found: [%.*s] (len=%u)", 
+                   (int)debug_len, debug_buf, (unsigned int)(end - p));
+    return false;
 }
 
 /**
- * @brief 检查是否为自动波特率模式
+ * @brief 将单个HEX字符转换为数值
+ * @param c HEX字符('0'-'9', 'A'-'F', 'a'-'f')
+ * @param value 输出的数值
+ * @return true=成功,false=失败
  */
-bool ppp_at_is_auto_baudrate_mode(void)
+static bool n706a_hex_char_to_value(char c, uint8_t *value)
 {
-    return g_at_ctx.auto_baudrate_mode;
+    if (c >= '0' && c <= '9') {
+        *value = c - '0';
+        return true;
+    } else if (c >= 'A' && c <= 'F') {
+        *value = c - 'A' + 10;
+        return true;
+    } else if (c >= 'a' && c <= 'f') {
+        *value = c - 'a' + 10;
+        return true;
+    }
+    return false;
 }
 
 /**
- * @brief 测试AT通信是否正常
+ * @brief 将二进制数据转换为HEX字符串
+ * @param binary_data 二进制数据
+ * @param binary_len 二进制数据长度
+ * @param hex_str 输出的HEX字符串缓冲区
+ * @param hex_buf_size HEX缓冲区大小(至少需要binary_len*2+1)
+ * @return 转换后的HEX字符串长度(不含结束符),0表示失败
  */
-ppp_error_t ppp_at_test_communication(void)
+static size_t n706a_binary_to_hex_string(const uint8_t *binary_data, size_t binary_len,
+                                         char *hex_str, size_t hex_buf_size)
 {
-    PPP_DEBUG_INFO("Testing AT communication...");
+    if (!binary_data || !hex_str || binary_len == 0) {
+        return 0;
+    }
     
-    ppp_at_response_t response;
+    // 需要binary_len * 2字节(每个字节转换为2个HEX字符),再加1个结束符
+    if (hex_buf_size < binary_len * 2 + 1) {
+        PPP_DEBUG_ERROR("HEX encode: buffer too small (need %u, got %u)", 
+                       (unsigned int)(binary_len * 2 + 1), (unsigned int)hex_buf_size);
+        return 0;
+    }
     
-    // 发送简单的AT命令测试通信
-    ppp_error_t ret = ppp_at_send_command("AT", "OK", 1000, &response);
-    if (ret == PPP_ERR_OK) {
-        PPP_DEBUG_INFO("AT communication test passed");
-    } else {
-        PPP_DEBUG_ERROR("AT communication test failed: %d", ret);
+    static const char hex_chars[] = "0123456789ABCDEF";
+    
+    for (size_t i = 0; i < binary_len; i++) {
+        hex_str[i * 2] = hex_chars[(binary_data[i] >> 4) & 0x0F];
+        hex_str[i * 2 + 1] = hex_chars[binary_data[i] & 0x0F];
     }
     
-    return ret;
+    hex_str[binary_len * 2] = '\0';
+    return binary_len * 2;
 }
 
-/* 内部函数实现 */
-
 /**
- * @brief 检查AT响应是否完整
- * @param buffer 响应缓冲区
- * @param length 缓冲区长度
- * @return true 如果响应完整,false 否则
+ * @brief 将HEX字符串转换为二进制数据
+ * @param hex_str HEX字符串(如 "313233")
+ * @param hex_len HEX字符串长度(字节数,必须是偶数)
+ * @param binary_buf 输出的二进制缓冲区
+ * @param binary_buf_size 二进制缓冲区大小
+ * @return 转换后的二进制数据长度(字节数),0表示失败
  */
-static bool ppp_at_check_response_complete(const char *buffer, uint32_t length)
+static size_t n706a_hex_string_to_binary(const char *hex_str, size_t hex_len, 
+                                         uint8_t *binary_buf, size_t binary_buf_size)
 {
-    if (!buffer || length == 0) {
-        return false;
+    if (!hex_str || !binary_buf || hex_len == 0 || hex_len % 2 != 0) {
+        return 0;
     }
     
-    /* 搜索OK终止符 - 修复无符号整数下溢问题 */
-    if (length >= 2) {
-        for (uint32_t i = 0; i <= length - 2; i++) {
-            if (buffer[i] == 'O' && buffer[i+1] == 'K') {
-                /* 检查OK前面是否有换行符(避免误匹配) */
-                if (i == 0 || buffer[i-1] == '\n' || buffer[i-1] == '\r') {
-                    PPP_DEBUG_DEBUG("Found OK terminator at position %u", i);
-                    return true;
-                }
-            }
-        }
+    size_t binary_len = hex_len / 2;
+    if (binary_len > binary_buf_size) {
+        PPP_DEBUG_ERROR("HEX decode: binary_len=%u exceeds buffer size=%u", 
+                       (unsigned int)binary_len, (unsigned int)binary_buf_size);
+        return 0;
     }
     
-    /* 搜索ERROR终止符 - 已经有长度检查,但确保安全 */
-    if (length >= 5) {
-        for (uint32_t i = 0; i <= length - 5; i++) {
-            if (strncmp(&buffer[i], "ERROR", 5) == 0) {
-                /* 检查ERROR前面是否有换行符 */
-                if (i == 0 || buffer[i-1] == '\n' || buffer[i-1] == '\r') {
-                    PPP_DEBUG_DEBUG("Found ERROR terminator at position %u", i);
-                    return true;
-                }
-            }
+    for (size_t i = 0; i < binary_len; i++) {
+        uint8_t high_nibble, low_nibble;
+        
+        if (!n706a_hex_char_to_value(hex_str[i * 2], &high_nibble) ||
+            !n706a_hex_char_to_value(hex_str[i * 2 + 1], &low_nibble)) {
+            PPP_DEBUG_ERROR("Invalid HEX character at position %u", (unsigned int)(i * 2));
+            return 0;
         }
+        
+        binary_buf[i] = (high_nibble << 4) | low_nibble;
     }
     
-    /* 检查其他终止符 */
-    if (strstr(buffer, "CONNECT") ||
-        strstr(buffer, "NO CARRIER") ||
-        strstr(buffer, "BUSY") ||
-        strstr(buffer, "NO DIALTONE") ||
-        strstr(buffer, "+CME ERROR") ||
-        strstr(buffer, "+CMS ERROR")) {
-        PPP_DEBUG_DEBUG("Found other terminator in response");
-        return true;
-    }
-    
-    return false;
+    return binary_len;
+}
+
+/**
+ * @brief 注册TCP URC接收回调
+ * @param callback 回调函数指针,NULL表示取消注册
+ */
+void n706a_at_register_tcp_urc_callback(n706a_tcp_urc_callback_t callback)
+{
+    g_tcp_urc_callback = callback;
+    PPP_DEBUG_INFO("TCP URC callback %s", callback ? "registered" : "unregistered");
+}
+
+/**
+ * @brief 注册+TCPSETUP URC回调
+ * @param callback 回调函数指针,NULL表示取消注册
+ */
+void n706a_at_register_tcpsetup_urc_callback(n706a_tcpsetup_urc_callback_t callback)
+{
+    g_tcpsetup_urc_callback = callback;
+    PPP_DEBUG_INFO("+TCPSETUP URC callback %s", callback ? "registered" : "unregistered");
 }
 
 /**
- * @brief 解析AT命令响应
+ * @brief 注册+TCPCLOSE URC回调
  */
-static bool ppp_at_parse_response(const char *response, const char *expected)
+void n706a_at_register_tcpclose_urc_callback(n706a_tcpclose_urc_callback_t callback)
 {
-    if (!response) {
-        return false;
-    }
-
+    g_tcpclose_urc_callback = callback;
+    PPP_DEBUG_INFO("+TCPCLOSE URC callback %s", callback ? "registered" : "unregistered");
+}
 
-     // 检查期望响应 ,优先检测期望响应
-    if (expected && strstr(response, expected)) {
-        return true;
-    }
+/**
+ * @brief 注册+TCPSEND URC回调
+ */
+void n706a_at_register_tcpsend_urc_callback(n706a_tcpsend_urc_callback_t callback)
+{
+    g_tcpsend_urc_callback = callback;
+    PPP_DEBUG_INFO("+TCPSEND URC callback %s", callback ? "registered" : "unregistered");
+}
 
-    // 检查ABORT条件 (对应chat文件中的ABORT指令)
-    if (strstr(response, "BUSY") || 
-        strstr(response, "ERROR") || 
-        strstr(response, "NO CARRIER")) {
-        PPP_DEBUG_ERROR("AT command aborted: %s", response);
-        return false;
-    }
+/**
+ * @brief 注册网络状态URC回调
+ */
+void n706a_at_register_network_status_urc_callback(n706a_network_status_urc_callback_t callback)
+{
+    g_network_status_urc_callback = callback;
+    PPP_DEBUG_INFO("Network status URC callback %s", callback ? "registered" : "unregistered");
+}
 
-   
+/**
+ * @brief 设置TCP接收模式(HEX模式或ASCII模式)
+ */
+void n706a_at_set_tcp_recv_hex_mode(bool hex_mode)
+{
+    g_tcp_recv_hex_mode = hex_mode;
+    PPP_DEBUG_INFO("TCP receive mode set to %s", hex_mode ? "HEX" : "ASCII");
+}
 
-    return false;
+/**
+ * @brief 获取TCP接收模式(HEX模式或ASCII模式)
+ */
+bool n706a_at_get_tcp_recv_hex_mode(void)
+{
+    return g_tcp_recv_hex_mode;
 }
 
 /**
- * @brief 等待AT命令响应 - 异步版本(从环形缓冲区读取)
+ * @brief 解析 +HTTPRECV: / +HTTPSRECV: URC
+ * @param urc_data URC数据(以 "+HTTPRECV: " 或 "+HTTPSRECV: " 开头)
+ * @param urc_len URC数据长度
+ * @param declared_len 输出:URC中声明的数据长度(从 <len> 字段解析,二进制长度)
+ * @param actual_len 输出:实际可用的数据长度(从数据开始位置到URC结束位置)
+ * @param data 输出:数据指针(指向 urc_data 内部)
+ * @return true=解析成功,false=失败
  * 
- * 该函数从ppp_interrupt.c填充的环形缓冲区中异步读取AT响应数据
- * 提供同步的函数接口,但底层是异步的高速串口接收
+ * URC格式(两种可能,由 AT+HTTPPARA=recvmode 决定):
+ * 1. recvmode=1:+HTTPRECV: <len>,<data> 或 +HTTPSRECV: <len>,<data> (带长度字段)
+ * 2. recvmode=0(默认):+HTTPRECV: <data> 或 +HTTPSRECV: <data> (无长度字段,直接是数据)
+ * 
+ * 例如:
+ *   +HTTPSRECV: 1024,HTTP/1.1 200 OK\r\n...  (格式1,recvmode=1)
+ *   +HTTPSRECV: HTTP/1.1 404 Not Found\r\n... (格式2,recvmode=0)
+ * 
+ * 重要说明:
+ * - HTTPRECV 不支持分片,每个 URC 是独立的
+ * - declared_len:URC中声明的 <len> 字段,表示当前URC的数据长度(二进制长度)
+ *   - 如果是 HEX 模式,实际 HEX 字符串长度 = declared_len × 2
+ *   - 如果是 ASCII 模式,实际 ASCII 字符串长度 = declared_len
+ * - actual_len:实际可用的数据长度(从数据开始位置到URC结束位置)
+ * - 正常情况下,declared_len 应该等于 actual_len(如果数据完整)
+ * - 如果 declared_len != actual_len,说明数据不完整(URC 被截断)
+ * - 如果 declared_len > 2048,这是正常的,因为模块不会自动分片
  */
-static ppp_error_t ppp_at_wait_response(ppp_at_response_t *response, uint32_t timeout)
+static bool n706a_parse_httprecv_urc(const char *urc_data, size_t urc_len,
+                                     size_t *declared_len, size_t *actual_len, const uint8_t **data)
 {
-    PPP_DEBUG_DEBUG(">>> N706A AT WAIT_RESPONSE START (async mode, timeout: %u ms) <<<", timeout);
+    if (!urc_data || !declared_len || !actual_len || !data || urc_len < 12) {
+        return false;
+    }
     
-    if (!response || !g_at_response_buffer) {
-        PPP_DEBUG_ERROR("Invalid parameters: response=%p, buffer=%p", response, g_at_response_buffer);
-        return PPP_ERR_INVALID_PARAM;
+    /* 调试:打印原始URC数据(前256字节) */
+    size_t debug_len = (urc_len < 256) ? urc_len : 256;
+    char debug_buf[257] = {0};
+    for (size_t i = 0; i < debug_len; i++) {
+        if (urc_data[i] >= 32 && urc_data[i] < 127) {
+            debug_buf[i] = urc_data[i];
+        } else if (urc_data[i] == '\r') {
+            debug_buf[i] = '\\';
+            if (i + 1 < 256) debug_buf[++i] = 'r';
+        } else if (urc_data[i] == '\n') {
+            debug_buf[i] = '\\';
+            if (i + 1 < 256) debug_buf[++i] = 'n';
+        } else {
+            debug_buf[i] = '.';
+        }
     }
-
-    uint32_t start_time = (uint32_t)osal_get_jiffies();
-    uint32_t bytes_received = 0;
-    bool response_complete = false;
+    PPP_DEBUG_INFO("[HTTPRECV URC] Raw URC data (first %u bytes): %s", (unsigned int)debug_len, debug_buf);
     
-    /* 清空全局缓存 */
-    memset(g_at_response_buffer, 0, g_at_buffer_size);
+    /* 跳过 "+HTTPRECV: " 或 "+HTTPSRECV: " */
+    const char *p = urc_data;
+    if (memcmp(p, "+HTTPRECV: ", 11) == 0) {
+        p += 11;
+    } else if (memcmp(p, "+HTTPSRECV: ", 12) == 0) {
+        p += 12;
+    } else {
+        return false;
+    }
     
-    PPP_DEBUG_DEBUG("N706A AT: Waiting for response from async ring buffer (timeout: %u ms)", timeout);
+    const char *end = urc_data + urc_len;
+    const char *data_start = p;
     
-    /* 循环从环形缓冲区读取数据直到完整响应或超时 */
-    while (!response_complete) {
-        uint32_t current_time = (uint32_t)osal_get_jiffies();
-        uint32_t elapsed = osal_jiffies_to_msecs(current_time - start_time);
+    /* 尝试解析格式1:+HTTPSRECV: <len>,<data> */
+    size_t parsed_len = 0;
+    const char *len_start = p;
+    
+    /* 解析数据长度(如果存在) */
+    while (p < end && *p >= '0' && *p <= '9') {
+        parsed_len = parsed_len * 10 + (*p - '0');
+        p++;
+    }
+    
+    /* 检查是否有逗号(格式1) */
+    if (p < end && *p == ',' && parsed_len > 0) {
+        /* 格式1:+HTTPSRECV: <len>,<data> */
+        PPP_DEBUG_INFO("[HTTPRECV URC] Format 1 detected: len=%u, comma found", (unsigned int)parsed_len);
+        p++;  /* 跳过逗号 */
+        data_start = p;
         
-        /* 检查超时 */
-        if (elapsed >= timeout) {
-            PPP_DEBUG_WARN("N706A AT: Response timeout after %u ms", elapsed);
-            break;
+        /* ⚠️ 关键:使用URC中声明的 <len> 作为 declared_len(这是最准确的) */
+        /* 根据用户确认:HTTPRECV 不支持分片,<length> 字段就是当前URC的数据长度(二进制长度) */
+        /* 如果是 HEX 模式,实际 HEX 字符串长度 = declared_len × 2 */
+        /* 如果是 ASCII 模式,实际 ASCII 字符串长度 = declared_len */
+        *declared_len = parsed_len;
+        
+        /* ⚠️ remaining 的作用:计算实际可用的数据长度,防止内存越界 */
+        /* remaining = end - p,表示从数据开始位置到URC结束位置之间的实际可用字节数 */
+        /* 正常情况下,declared_len 应该等于 actual_len(如果数据完整) */
+        /* 如果 declared_len != actual_len,说明数据不完整(URC 被截断) */
+        size_t remaining = end - p;
+        *actual_len = remaining;  /* 实际可用的数据长度 */
+        
+        /* 验证数据完整性:正常情况下 declared_len 应该等于 actual_len */
+        if (*declared_len != *actual_len) {
+            PPP_DEBUG_WARN("HTTPRECV URC: declared len=%u != actual len=%u (data may be incomplete/truncated)", 
+                          (unsigned int)*declared_len, (unsigned int)*actual_len);
         }
         
-        /* 等待信号量(有新数据到达),使用剩余超时时间 */
-        uint32_t remaining_timeout = timeout - elapsed;
-        if (remaining_timeout > 100) {
-            remaining_timeout = 100;  // 最多等100ms,然后检查一次
+        /* 注意:HTTPRECV 不支持分片,所以 declared_len > 2048 是正常的 */
+        /* 这与 TCPRECV 不同,TCPRECV 有明确的 2048 字节限制并会自动分片 */
+        if (*declared_len > 2048) {
+            PPP_DEBUG_INFO("HTTPRECV URC: declared len=%u exceeds 2048 bytes (normal, HTTPRECV doesn't auto-fragment)", 
+                          (unsigned int)*declared_len);
         }
         
-        int sem_result = osal_sem_down_timeout(&g_at_response_sem, remaining_timeout);
-        if (sem_result == OSAL_SUCCESS || sem_result == OSAL_FAILURE) {
-            /* 有新数据到达或信号量操作失败,尝试读取环形缓冲区 */
-            osal_mutex_lock(&g_at_response_mutex);
-            
-            /* 从环形缓冲区读取数据到响应缓冲区 */
-            while (g_at_response_tail != g_at_response_head && 
-                   bytes_received < (g_at_buffer_size - 1)) {
-                g_at_response_buffer[bytes_received] = g_at_response_ring_buffer[g_at_response_tail];
-                g_at_response_tail = (g_at_response_tail + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
-                bytes_received++;
-            }
-            
-            osal_mutex_unlock(&g_at_response_mutex);
-            
-            /* 设置字符串结束符 */
-            if (bytes_received > 0) {
-                g_at_response_buffer[bytes_received] = '\0';
-                
-                PPP_DEBUG_DEBUG("N706A AT: Read %u bytes from ring buffer", bytes_received);
-                
-                /* 检查是否收到完整响应 */
-                if (ppp_at_check_response_complete((char*)g_at_response_buffer, bytes_received)) {
-                    PPP_DEBUG_DEBUG("N706A AT: Complete response detected");
-                    response_complete = true;
-                    break;
-                }
+        /* 调试:打印解析后的数据起始位置(前128字节) */
+        size_t preview_len = (*actual_len < 128) ? *actual_len : 128;
+        char preview_buf[129] = {0};
+        for (size_t i = 0; i < preview_len && i < 128; i++) {
+            if (data_start[i] >= 32 && data_start[i] < 127) {
+                preview_buf[i] = data_start[i];
+            } else if (data_start[i] == '\r') {
+                preview_buf[i] = '\\';
+                if (i + 1 < 128) preview_buf[++i] = 'r';
+            } else if (data_start[i] == '\n') {
+                preview_buf[i] = '\\';
+                if (i + 1 < 128) preview_buf[++i] = 'n';
+            } else {
+                preview_buf[i] = '.';
             }
         }
+        PPP_DEBUG_INFO("[HTTPRECV URC] Parsed data start (first %u bytes): %s", (unsigned int)preview_len, preview_buf);
+    } else {
+        /* 格式2:+HTTPSRECV: <data> (无长度字段) */
+        PPP_DEBUG_INFO("[HTTPRECV URC] Format 2 detected: no comma, parsed_len=%u (may be part of data)", (unsigned int)parsed_len);
+        /* 数据从URC标识符后直接开始,直到遇到 +HTTPSCLOSED 或 +HTTPCLOSED 或数据结束 */
+        data_start = len_start;  /* 回退到长度解析开始位置(实际是数据开始位置) */
         
-        /* 短暂延迟避免CPU占用过高 */
-        if (!response_complete) {
-            osal_msleep(10);
+        /* 查找数据结束位置(可能是 +HTTPSCLOSED、+HTTPCLOSED 或缓冲区结束) */
+        const char *data_end = end;
+        const char *closed_pos = strstr(data_start, "+HTTPSCLOSED:");
+        if (!closed_pos) {
+            closed_pos = strstr(data_start, "+HTTPCLOSED:");
+        }
+        if (closed_pos && closed_pos < end) {
+            data_end = closed_pos;
+            PPP_DEBUG_INFO("[HTTPRECV URC] Found +HTTPSCLOSED/+HTTPCLOSED at offset %u", 
+                          (unsigned int)(closed_pos - data_start));
         }
-    }
-    
-    /* 处理结果 */
-    if (bytes_received > 0) {
-        uint32_t copy_len = (bytes_received < PPP_AT_RESPONSE_MAX_LEN - 1) ? 
-                           bytes_received : PPP_AT_RESPONSE_MAX_LEN - 1;
         
-        memcpy_s(response->response, PPP_AT_RESPONSE_MAX_LEN, 
-                g_at_response_buffer, copy_len);
-        response->response[copy_len] = '\0';
-        response->length = copy_len;
+        /* 计算数据长度 */
+        /* ⚠️ 关键:不移除尾部的换行符,因为它们是HTTP响应数据的一部分 */
+        /* 如果移除,会导致数据丢失,特别是HTTP响应体以换行符结尾的情况 */
+        size_t calculated_len = data_end - data_start;
         
-        uint32_t final_time = (uint32_t)osal_get_jiffies();
-        uint32_t total_elapsed = osal_jiffies_to_msecs(final_time - start_time);
+        /* 格式2没有声明的长度,所以 declared_len = actual_len = 计算出的长度 */
+        *declared_len = calculated_len;
+        *actual_len = calculated_len;
         
-        PPP_DEBUG_DEBUG("N706A AT: SUCCESS - %u bytes in %u ms", bytes_received, total_elapsed);
-        PPP_DEBUG_DEBUG(">>> N706A AT WAIT_RESPONSE END (SUCCESS) <<<");
-        return PPP_ERR_OK;
-    } else {
-        uint32_t final_time = (uint32_t)osal_get_jiffies();
-        uint32_t total_elapsed = osal_jiffies_to_msecs(final_time - start_time);
+        /* 注意:HTTPRECV 不支持分片,所以 declared_len > 2048 是正常的 */
+        /* 这与 TCPRECV 不同,TCPRECV 有明确的 2048 字节限制并会自动分片 */
+        if (*declared_len > 2048) {
+            PPP_DEBUG_INFO("HTTPRECV URC: declared len=%u exceeds 2048 bytes (normal, HTTPRECV doesn't auto-fragment)", 
+                          (unsigned int)*declared_len);
+        }
         
-        PPP_DEBUG_WARN("N706A AT: FAILED - No response after %u ms", total_elapsed);
-        PPP_DEBUG_DEBUG(">>> N706A AT WAIT_RESPONSE END (FAILED) <<<");
-        response->length = 0;
-        response->response[0] = '\0';
-        return PPP_ERR_TIMEOUT;
+        PPP_DEBUG_INFO("[HTTPRECV URC] Format 2: declared_len=%u, actual_len=%u", 
+                      (unsigned int)*declared_len, (unsigned int)*actual_len);
+        
+        /* 调试:打印解析后的数据起始位置(前128字节) */
+        size_t preview_len = (*actual_len < 128) ? *actual_len : 128;
+        char preview_buf[129] = {0};
+        for (size_t i = 0; i < preview_len && i < 128; i++) {
+            if (data_start[i] >= 32 && data_start[i] < 127) {
+                preview_buf[i] = data_start[i];
+            } else if (data_start[i] == '\r') {
+                preview_buf[i] = '\\';
+                if (i + 1 < 128) preview_buf[++i] = 'r';
+            } else if (data_start[i] == '\n') {
+                preview_buf[i] = '\\';
+                if (i + 1 < 128) preview_buf[++i] = 'n';
+            } else {
+                preview_buf[i] = '.';
+            }
+        }
+        PPP_DEBUG_INFO("[HTTPRECV URC] Parsed data start (first %u bytes): %s", (unsigned int)preview_len, preview_buf);
     }
+    
+    /* 数据起始位置 */
+    *data = (const uint8_t *)data_start;
+    
+    if (*actual_len == 0) {
+        PPP_DEBUG_WARN("HTTPRECV URC actual_len is 0");
+        return false;
+    }
+    
+    PPP_DEBUG_INFO("[HTTPRECV URC] Final parsed: declared_len=%u, actual_len=%u, data_start offset=%u", 
+                   (unsigned int)*declared_len, (unsigned int)*actual_len, (unsigned int)(data_start - urc_data));
+    return true;
 }
 
 /**
- * @brief è§£æ\9e\90AT+CESQå\93\8dåº\94
- * 参考N706A AT命令手册: +CESQ: <rxlev>,<ber>,<rscp>,<ecno>,<rsrq>,<rsrp>
+ * @brief è\8e·å\8f\96累积ç¼\93å\86²å\8cºé\95¿åº¦ï¼\88ç\94¨äº\8eè°\83è¯\95ï¼\89
+ * @return 累积缓冲区当前长度
  */
-static ppp_error_t ppp_parse_cesq_response(const char *response, n706a_network_signal_info_t *info)
+uint32_t n706a_at_get_accumulate_buffer_length(void)
 {
-    if (!response || !info) {
-        return PPP_ERR_INVALID_PARAM;
-    }
-
-    memset_s(info, sizeof(n706a_network_signal_info_t), 0, sizeof(n706a_network_signal_info_t));
-
-    // 查找+CESQ:开头的行
-    const char *cesq_line = strstr(response, "+CESQ:");
-    if (!cesq_line) {
-        PPP_DEBUG_WARN("No +CESQ: found in response");
-        return PPP_ERR_AT_FAILED;
-    }
-
-    // 跳过"+CESQ:"
-    cesq_line += 6;
-    while (*cesq_line == ' ' || *cesq_line == '\t') cesq_line++;
-
-    // +CESQ: <rxlev>,<ber>,<rscp>,<ecno>,<rsrq>,<rsrp>
-    int rxlev = 99, ber = 99, rscp = 255, ecno = 255, rsrq = 255, rsrp = 255;
-    int parsed = sscanf(cesq_line, "%d,%d,%d,%d,%d,%d", &rxlev, &ber, &rscp, &ecno, &rsrq, &rsrp);
-
-    if (parsed == 6) {
-        info->valid = true;
-        info->rxlev = rxlev;
-        info->ber = ber;
-        info->rscp = rscp;
-        info->ecno = ecno;
-        info->rsrq = rsrq;
-        info->rsrp = rsrp;
-        PPP_DEBUG_INFO("CESQ parsed successfully: rxlev=%d, ber=%d, rscp=%d, ecno=%d, rsrq=%d, rsrp=%d",
-            rxlev, ber, rscp, ecno, rsrq, rsrp);
-        return PPP_ERR_OK;
-    } else {
-        PPP_DEBUG_WARN("Failed to parse CESQ response, parsed: %d", parsed);
-        return PPP_ERR_AT_FAILED;
-    }
+    osal_mutex_lock(&g_at_accumulate_mutex);
+    uint32_t len = g_at_accumulate_length;
+    osal_mutex_unlock(&g_at_accumulate_mutex);
+    return len;
 }
 
 /**
- * @brief 获取网络信号信息 (AT+CESQ)
+ * @brief 获取累积缓冲区的最后几个字节(用于调试)
+ * @param buffer 输出缓冲区
+ * @param max_len 最大长度
+ * @param actual_len 实际长度(输出)
+ * @return true=成功,false=失败
  */
-ppp_error_t ppp_at_get_network_signal_info(n706a_network_signal_info_t *info)
+bool n706a_at_get_accumulate_buffer_preview(uint8_t *buffer, uint32_t max_len, uint32_t *actual_len)
 {
-    if (!g_at_ctx.initialized || !info) {
-        return PPP_ERR_INVALID_PARAM;
+    if (!buffer || !actual_len || max_len == 0) {
+        return false;
     }
-    // N706A_NET模式下始终处于AT模式,无需检查
-    // (PPP模式需要检查是否成功切换到AT模式)
     
-    PPP_DEBUG_INFO("Getting network signal information (AT+CESQ)...");
-
-    ppp_at_response_t response;
-    ppp_error_t ret = ppp_at_send_command("AT+CESQ", "OK", 5000, &response);
-
-    if (ret != PPP_ERR_OK) {
-        PPP_DEBUG_ERROR("AT+CESQ command failed: %d", ret);
-        return ret;
-    }
-
-    // 解析响应
-    ret = ppp_parse_cesq_response(response.response, info);
-    if (ret != PPP_ERR_OK) {
-        PPP_DEBUG_ERROR("Failed to parse CESQ response");
-        return ret;
+    osal_mutex_lock(&g_at_accumulate_mutex);
+    uint32_t len = g_at_accumulate_length;
+    if (len == 0) {
+        *actual_len = 0;
+        osal_mutex_unlock(&g_at_accumulate_mutex);
+        return true;
     }
-
-    // 打印详细信息
-    PPP_DEBUG_INFO("=== Network Signal Information (AT+CESQ) ===");
-    PPP_DEBUG_INFO("rxlev: %d", info->rxlev);
-    PPP_DEBUG_INFO("ber: %d", info->ber);
-    PPP_DEBUG_INFO("rscp: %d", info->rscp);
-    PPP_DEBUG_INFO("ecno: %d", info->ecno);
-    PPP_DEBUG_INFO("rsrq: %d", info->rsrq);
-    PPP_DEBUG_INFO("rsrp: %d", info->rsrp);
-
-    // LTE信号质量评估 - 仅关注RSRQ和RSRP
-    PPP_DEBUG_INFO("=== LTE Signal Quality Analysis ===");
     
-    // RSRQ评估 (0-49有效,255无效)
-    if (info->rsrq == 255) {
-        PPP_DEBUG_INFO("RSRQ: 无法获取LTE信号质量");
-    } else if (info->rsrq >= 0 && info->rsrq <= 49) {
-        // RSRQ转换公式: 实际值(dB) = (rsrq - 94)/10
-        float rsrq_db = (info->rsrq - 94) / 10.0f;
-        PPP_DEBUG_INFO("RSRQ: %d (%.1f dB)", info->rsrq, rsrq_db);
-        
-        // RSRQ评估标准 (范围: -19.5dB ~ -3dB,数值越大质量越好)
-        if (rsrq_db >= -6.0) {
-            PPP_DEBUG_INFO("RSRQ评级: 极佳 (信号质量优秀)");
-        } else if (rsrq_db >= -9.0) {
-            PPP_DEBUG_INFO("RSRQ评级: 良好 (信号质量稳定)");
-        } else if (rsrq_db >= -12.0) {
-            PPP_DEBUG_INFO("RSRQ评级: 一般 (信号质量可用)");
-        } else {
-            PPP_DEBUG_INFO("RSRQ评级: 较差 (信号质量不佳)");
-        }
-    } else {
-        PPP_DEBUG_INFO("RSRQ: %d (超出有效范围0-49)", info->rsrq);
+    uint32_t preview_len = (len < max_len) ? len : max_len;
+    uint32_t start_offset = (len > preview_len) ? (len - preview_len) : 0;
+    memcpy(buffer, g_at_accumulate_buffer + start_offset, preview_len);
+    *actual_len = preview_len;
+    osal_mutex_unlock(&g_at_accumulate_mutex);
+    return true;
+}
+
+/**
+ * @brief 获取累积缓冲区的完整内容(从开头开始,用于调试)
+ * @param buffer 输出缓冲区
+ * @param max_len 最大长度
+ * @param actual_len 实际长度(输出)
+ * @return true=成功,false=失败
+ */
+bool n706a_at_get_accumulate_buffer_all(uint8_t *buffer, uint32_t max_len, uint32_t *actual_len)
+{
+    if (!buffer || !actual_len || max_len == 0) {
+        return false;
     }
     
-    // RSRP评估 (0-97有效,255无效)
-    if (info->rsrp == 255) {
-        PPP_DEBUG_INFO("RSRP: 无法获取LTE信号强度");
-    } else if (info->rsrp >= 0 && info->rsrp <= 97) {
-        // RSRP转换公式: 实际值(dBm) = rsrp - 140
-        float rsrp_dbm = info->rsrp - 140.0f;
-        PPP_DEBUG_INFO("RSRP: %d (%.0f dBm)", info->rsrp, rsrp_dbm);
-        
-        // RSRP评估标准 (范围: -140dBm ~ -43dBm,数值越大信号越强)
-        if (rsrp_dbm >= -80) {
-            PPP_DEBUG_INFO("RSRP评级: 极佳 (信号强度优秀)");
-        } else if (rsrp_dbm >= -90) {
-            PPP_DEBUG_INFO("RSRP评级: 良好 (信号强度稳定)");
-        } else if (rsrp_dbm >= -100) {
-            PPP_DEBUG_INFO("RSRP评级: 一般 (信号强度可用)");
-        } else {
-            PPP_DEBUG_INFO("RSRP评级: 较差 (信号强度不足)");
-        }
-    } else {
-        PPP_DEBUG_INFO("RSRP: %d (超出有效范围0-97)", info->rsrp);
+    osal_mutex_lock(&g_at_accumulate_mutex);
+    uint32_t len = g_at_accumulate_length;
+    if (len == 0) {
+        *actual_len = 0;
+        osal_mutex_unlock(&g_at_accumulate_mutex);
+        return true;
     }
     
-    // 综合LTE信号质量评估
-    if (info->rsrq != 255 && info->rsrp != 255 && 
-        info->rsrq >= 0 && info->rsrq <= 49 && 
-        info->rsrp >= 0 && info->rsrp <= 97) {
-        
-        float rsrq_db = (info->rsrq - 94) / 10.0f;
-        float rsrp_dbm = info->rsrp - 140.0f;
-        
-        const char *overall_quality = "未知";
-        if (rsrq_db >= -6.0 && rsrp_dbm >= -80) {
-            overall_quality = "LTE信号极佳 (质量和强度都优秀)";
-        } else if (rsrq_db >= -9.0 && rsrp_dbm >= -90) {
-            overall_quality = "LTE信号良好 (质量和强度都稳定)";
-        } else if (rsrq_db >= -12.0 && rsrp_dbm >= -100) {
-            overall_quality = "LTE信号一般 (质量和强度可用)";
-        } else {
-            overall_quality = "LTE信号较差 (需要改善网络环境)";
-        }
-        
-        PPP_DEBUG_INFO("=== 综合评估: %s ===", overall_quality);
-    } else {
-        PPP_DEBUG_INFO("=== 综合评估: LTE参数无效,无法评估 ===");
-    }
+    uint32_t copy_len = (len < max_len) ? len : max_len;
+    memcpy(buffer, g_at_accumulate_buffer, copy_len);
+    *actual_len = copy_len;
+    osal_mutex_unlock(&g_at_accumulate_mutex);
+    return true;
+}
 
-    PPP_DEBUG_INFO("=== End Network Signal Information ===");
+/* ========== 流式解析器实现(新方案) ========== */
 
-    return PPP_ERR_OK;
+/**
+ * @brief 初始化流式解析器
+ */
+static void n706a_stream_parser_init(void)
+{
+    memset(&g_parser_ctx, 0, sizeof(g_parser_ctx));
+    g_parser_ctx.state = AT_PARSER_STATE_IDLE;
+    g_parser_ctx.urc_type = URC_TYPE_NONE;
+    g_parser_ctx.tcp_socket_id = -1;
+    g_parser_ctx.state_transition_pending = false;
+    PPP_DEBUG_INFO("[StreamParser] Initialized");
 }
 
-/* ========== 导出的N706A API包装函数(用于n706a_tcp_client和n706a_net_at_cmd) ========== */
+/**
+ * @brief 重置流式解析器状态
+ */
+static void n706a_stream_parser_reset(void)
+{
+    /* 重置到空闲状态 */
+    g_parser_ctx.state = AT_PARSER_STATE_IDLE;
+    g_parser_ctx.response_start_pos = 0;
+    g_parser_ctx.response_expected_len = 0;
+    g_parser_ctx.response_received_len = 0;
+    g_parser_ctx.urc_type = URC_TYPE_NONE;
+    g_parser_ctx.is_https = false;
+    g_parser_ctx.tcp_socket_id = -1;
+    g_parser_ctx.partial_buffer_len = 0;
+    g_parser_ctx.remaining_data_len = 0;
+    g_parser_ctx.received_data_len = 0;
+    g_parser_ctx.urc_header_len = 0;
+    g_parser_ctx.state_transition_pending = false;  // 方案2:清除状态转换标记
+    g_parser_ctx.urc_match_pos = 0;
+    g_parser_ctx.urc_pattern = NULL;
+    g_parser_ctx.urc_pattern_len = 0;
+    
+    memset(g_parser_ctx.partial_buffer, 0, sizeof(g_parser_ctx.partial_buffer));
+    memset(g_parser_ctx.urc_header_buf, 0, sizeof(g_parser_ctx.urc_header_buf));
+}
 
 /**
- * @brief N706A对外导出:发送AT命令并等待响应
+ * @brief URC标识符模式表
  */
-n706a_at_error_t n706a_at_send_command(const char *command, 
-                                       const char *expected_response, 
-                                       uint32_t timeout, 
-                                       n706a_at_response_t *response)
+static const struct {
+    const char *pattern;
+    uint32_t pattern_len;
+    urc_type_t type;
+} urc_patterns[] = {
+    {"+HTTPSRECV: ", 12, URC_TYPE_HTTPSRECV},
+    {"+HTTPRECV: ", 11, URC_TYPE_HTTPRECV},
+    {"+SSLTCPRECV: ", 13, URC_TYPE_SSLTCPRECV},
+    {"+TCPRECV: ", 10, URC_TYPE_TCPRECV},
+    {"+SSLTCPSETUP: ", 14, URC_TYPE_SSLTCPSETUP},
+    {"+TCPSETUP: ", 11, URC_TYPE_TCPSETUP},
+    {"+SSLTCPCLOSE: ", 14, URC_TYPE_SSLTCPCLOSE},
+    {"+TCPCLOSE: ", 11, URC_TYPE_TCPCLOSE},
+    {"+SSLTCPEND: ", 12, URC_TYPE_SSLTCPEND},
+    {"+TCPSEND: ", 10, URC_TYPE_TCPSEND},
+    {"+CGATT: ", 8, URC_TYPE_CGATT},
+    {"+CGREG: ", 8, URC_TYPE_CGREG},
+    {"+CREG: ", 7, URC_TYPE_CREG},
+    {NULL, 0, URC_TYPE_NONE}
+};
+
+/**
+ * @brief 匹配URC标识符(支持跨数据块)
+ * @param byte 当前字节
+ * @param type 输出:匹配到的URC类型
+ * @param pattern 输出:匹配到的URC模式
+ * @param pattern_len 输出:URC模式长度
+ * @return true=匹配成功,false=未匹配
+ */
+static bool n706a_stream_parser_match_urc_identifier(uint8_t byte, urc_type_t *type, const char **pattern, uint32_t *pattern_len)
 {
-    ppp_at_response_t ppp_resp;
-    ppp_error_t ret = ppp_at_send_command(command, expected_response, timeout, &ppp_resp);
+    /* 原始设计:支持跨数据块匹配URC标识符 */
     
-    if (response) {
-        /* 转换ppp_at_response_t到n706a_at_response_t */
-        memcpy_s(response->response, sizeof(response->response), ppp_resp.response, sizeof(ppp_resp.response));
-        response->length = ppp_resp.length;
-        response->success = ppp_resp.success;
-        response->error_code = (ret == PPP_ERR_OK) ? N706A_AT_OK : N706A_AT_ERR_AT_FAILED;
+    if (g_parser_ctx.urc_match_pos == 0 && byte != '+') {
+        /* 不是URC开始字符 */
+        *type = URC_TYPE_NONE;
+        *pattern = NULL;
+        *pattern_len = 0;
+        return false;
+    }
+    
+    /* 检查所有URC模式 */
+    for (int i = 0; urc_patterns[i].pattern != NULL; i++) {
+        const char *p = urc_patterns[i].pattern;
+        uint32_t len = urc_patterns[i].pattern_len;
+        
+        /* 如果当前匹配位置为0,检查是否匹配第一个字符 */
+        if (g_parser_ctx.urc_match_pos == 0) {
+            if (byte == p[0]) {
+                /* 匹配第一个字符,开始匹配 */
+                g_parser_ctx.urc_match_pos = 1;
+                g_parser_ctx.urc_pattern = p;
+                g_parser_ctx.urc_pattern_len = len;
+                
+                if (len == 1) {
+                    /* 单字符模式,匹配成功 */
+                    *type = urc_patterns[i].type;
+                    *pattern = p;
+                    *pattern_len = len;
+                    g_parser_ctx.urc_match_pos = 0;  /* 重置 */
+                    return true;
+                }
+                /* 继续匹配 */
+                return false;
+            }
+        } else if (g_parser_ctx.urc_pattern == p) {
+            /* 正在匹配这个模式 */
+            if (g_parser_ctx.urc_match_pos < len && byte == p[g_parser_ctx.urc_match_pos]) {
+                g_parser_ctx.urc_match_pos++;
+                if (g_parser_ctx.urc_match_pos >= len) {
+                    /* 匹配成功 */
+                    *type = urc_patterns[i].type;
+                    *pattern = p;
+                    *pattern_len = len;
+                    g_parser_ctx.urc_match_pos = 0;  /* 重置 */
+                    return true;
+                }
+                /* 继续匹配 */
+                return false;
+            } else {
+                /* 匹配失败,重置 */
+                g_parser_ctx.urc_match_pos = 0;
+                g_parser_ctx.urc_pattern = NULL;
+                g_parser_ctx.urc_pattern_len = 0;
+                /* 如果当前字节是'+',重新开始匹配 */
+                if (byte == '+') {
+                    return n706a_stream_parser_match_urc_identifier(byte, type, pattern, pattern_len);
+                }
+            }
+        }
+    }
+    
+    /* 没有匹配到 */
+    if (g_parser_ctx.urc_match_pos > 0) {
+        /* 部分匹配失败,重置 */
+                    g_parser_ctx.urc_match_pos = 0;
+                    g_parser_ctx.urc_pattern = NULL;
+                    g_parser_ctx.urc_pattern_len = 0;
     }
     
-    return (ret == PPP_ERR_OK) ? N706A_AT_OK : N706A_AT_ERR_AT_FAILED;
+    *type = URC_TYPE_NONE;
+    *pattern = NULL;
+    *pattern_len = 0;
+    return false;
 }
 
 /**
- * @brief N706A对外导出:测试AT通信是否正常
+ * @brief 处理URC头部(解析参数)
+ * @param data 数据指针
+ * @param length 数据长度
+ * @param consumed 输出:已消费的字节数
  */
-n706a_at_error_t n706a_at_test_communication(void)
+static void n706a_stream_parser_process_urc_header(const uint8_t *data, uint32_t length, uint32_t *consumed)
 {
-    if (!g_at_ctx.initialized) {
-        return N706A_AT_ERR_INVALID_PARAM;
-    }
+    *consumed = 0;
     
-    n706a_at_response_t response;
-    n706a_at_error_t ret = n706a_at_send_command("AT", "OK", 3000, &response);
+    /* TCPRECV/SSLTCPRECV 特殊处理:格式为 +TCPRECV: <socket_id>,<data_len>,<data>\r\n */
+    /* 数据直接跟在长度后面,没有换行符,所以需要解析长度字段,然后直接进入URC_DATA状态 */
+    if (g_parser_ctx.urc_type == URC_TYPE_TCPRECV || g_parser_ctx.urc_type == URC_TYPE_SSLTCPRECV) {
+        /* 检查是否已经跳过标识符 */
+        if (g_parser_ctx.urc_header_len == 0) {
+            /* 第一次处理,需要跳过标识符部分 */
+            /* 标识符长度:+SSLTCPRECV:  = 13字节,+TCPRECV:  = 10字节 */
+            uint32_t identifier_len = (g_parser_ctx.urc_type == URC_TYPE_SSLTCPRECV) ? 13 : 10;
+            
+            if (length > identifier_len) {
+                /* 当前数据块包含标识符和参数,解析socket_id和data_len */
+                const char *header_str = (const char *)(data + identifier_len);
+                uint32_t header_remaining = length - identifier_len;
+                
+                /* 解析格式:<socket_id>,<data_len>,<data> */
+                int socket_id = 0;  /* 修复:初始化为0,而不是-1 */
+                bool socket_id_parsed = false;  /* 标记是否开始解析socket_id */
+                uint32_t data_len = 0;
+                const char *p = header_str;
+                const char *end = header_str + header_remaining;
+                
+                /* 解析socket_id */
+                while (p < end && *p >= '0' && *p <= '9') {
+                    if (!socket_id_parsed) {
+                        socket_id_parsed = true;  /* 标记开始解析 */
+                    }
+                    socket_id = socket_id * 10 + (*p - '0');
+                    p++;
+                }
+                
+                /* 验证socket_id是否成功解析 */
+                if (!socket_id_parsed) {
+                    PPP_DEBUG_ERROR("[StreamParser] Invalid TCPRECV header: socket_id not found");
+                    *consumed = 0;
+                    return;
+                }
+                
+                /* 检查逗号 */
+                if (p < end && *p == ',') {
+                    p++;  /* 跳过逗号 */
+                    
+                    /* 解析data_len */
+                    while (p < end && *p >= '0' && *p <= '9') {
+                        data_len = data_len * 10 + (*p - '0');
+                        p++;
+                    }
+                    
+                    /* 检查逗号 */
+                    if (p < end && *p == ',') {
+                        p++;  /* 跳过逗号,p现在指向数据开始位置 */
+                        
+                        /* 保存解析结果 */
+                        g_parser_ctx.tcp_socket_id = socket_id;
+                        /* 修复:HEX模式下,remaining_data_len应该是data_len * 2 */
+                        g_parser_ctx.remaining_data_len = g_tcp_recv_hex_mode ? (data_len * 2) : data_len;
+                        g_parser_ctx.received_data_len = 0;
+                        
+                        /* 计算已消费的字节数(标识符 + socket_id + 逗号 + data_len + 逗号) */
+                        uint32_t header_consumed = p - (const char *)data;
+                        
+                        /* 如果有数据,转换到URC_DATA状态,让URC_DATA处理函数处理数据(包括HEX解码) */
+                        if (p < end) {
+                            uint32_t data_offset = p - (const char *)data;
+                            /* 转换到URC_DATA状态,继续接收剩余数据 */
+                            /* 注意:不在这里传递数据,让URC_DATA状态处理(包括HEX解码) */
+                            g_parser_ctx.state = AT_PARSER_STATE_URC_DATA;
+                            *consumed = header_consumed;  /* 只消费头部,数据部分由URC_DATA状态处理 */
+                            
+                            PPP_DEBUG_INFO("[StreamParser] TCPRECV header parsed: socket=%d, data_len=%u (binary), remaining_hex_len=%u, data_offset=%u, hex_mode=%d, entering URC_DATA state", 
+                                          socket_id, data_len, g_parser_ctx.remaining_data_len, data_offset, g_tcp_recv_hex_mode);
+                        } else {
+                            /* 没有数据,只消费头部 */
+                            g_parser_ctx.state = AT_PARSER_STATE_URC_DATA;
+                            *consumed = header_consumed;
+                            
+                            PPP_DEBUG_INFO("[StreamParser] TCPRECV header parsed: socket=%d, data_len=%u (binary), remaining_hex_len=%u, no data yet, hex_mode=%d, entering URC_DATA state", 
+                                          socket_id, data_len, g_parser_ctx.remaining_data_len, g_tcp_recv_hex_mode);
+                        }
+                    } else {
+                        /* 没有找到第二个逗号,数据不完整,等待更多数据 */
+                        *consumed = 0;
+                        PPP_DEBUG_DEBUG("[StreamParser] TCPRECV header incomplete: waiting for second comma");
+                    }
+                } else {
+                    /* 没有找到第一个逗号,数据不完整,等待更多数据 */
+                    *consumed = 0;
+                    PPP_DEBUG_DEBUG("[StreamParser] TCPRECV header incomplete: waiting for first comma");
+                }
+            } else {
+                /* 当前数据块只包含部分标识符,跳过已接收的部分 */
+                *consumed = length;
+                g_parser_ctx.urc_header_len = length;  /* 记录已跳过的字节数 */
+                PPP_DEBUG_DEBUG("[StreamParser] TCPRECV identifier partially received: %u/%u bytes", 
+                               (unsigned int)length, identifier_len);
+            }
+        } else {
+            /* 已经跳过部分标识符,继续跳过剩余部分 */
+            uint32_t identifier_len = (g_parser_ctx.urc_type == URC_TYPE_SSLTCPRECV) ? 13 : 10;
+            uint32_t remaining_identifier = identifier_len - g_parser_ctx.urc_header_len;
+            
+            if (length >= remaining_identifier) {
+                /* 当前数据块包含剩余标识符和参数,重新解析 */
+                const char *header_str = (const char *)(data + remaining_identifier);
+                uint32_t header_remaining = length - remaining_identifier;
+                
+                /* 解析格式:<socket_id>,<data_len>,<data> */
+                int socket_id = 0;  /* 修复:初始化为0,而不是-1 */
+                bool socket_id_parsed = false;  /* 标记是否开始解析socket_id */
+                uint32_t data_len = 0;
+                const char *p = header_str;
+                const char *end = header_str + header_remaining;
+                
+                /* 解析socket_id */
+                while (p < end && *p >= '0' && *p <= '9') {
+                    if (!socket_id_parsed) {
+                        socket_id_parsed = true;  /* 标记开始解析 */
+                    }
+                    socket_id = socket_id * 10 + (*p - '0');
+                    p++;
+                }
+                
+                /* 验证socket_id是否成功解析 */
+                if (!socket_id_parsed) {
+                    PPP_DEBUG_ERROR("[StreamParser] Invalid TCPRECV header: socket_id not found");
+                    *consumed = 0;
+                    return;
+                }
+                
+                /* 检查逗号 */
+                if (p < end && *p == ',') {
+                    p++;  /* 跳过逗号 */
+                    
+                    /* 解析data_len */
+                    while (p < end && *p >= '0' && *p <= '9') {
+                        data_len = data_len * 10 + (*p - '0');
+                        p++;
+                    }
+                    
+                    /* 检查逗号 */
+                    if (p < end && *p == ',') {
+                        p++;  /* 跳过逗号,p现在指向数据开始位置 */
+                        
+                        /* 保存解析结果 */
+                        g_parser_ctx.tcp_socket_id = socket_id;
+                        /* 修复:HEX模式下,remaining_data_len应该是data_len * 2 */
+                        g_parser_ctx.remaining_data_len = g_tcp_recv_hex_mode ? (data_len * 2) : data_len;
+                        g_parser_ctx.received_data_len = 0;
+                        g_parser_ctx.urc_header_len = 0;  /* 标识符已完全跳过,重置 */
+                        
+                        /* 计算已消费的字节数 */
+                        uint32_t header_consumed = p - (const char *)data;
+                        
+                        /* 如果有数据,转换到URC_DATA状态,让URC_DATA处理函数处理数据(包括HEX解码) */
+                        if (p < end) {
+                            uint32_t data_offset = p - (const char *)data;
+                            /* 转换到URC_DATA状态,继续接收剩余数据 */
+                            /* 注意:不在这里传递数据,让URC_DATA状态处理(包括HEX解码) */
+                            g_parser_ctx.state = AT_PARSER_STATE_URC_DATA;
+                            *consumed = header_consumed;  /* 只消费头部,数据部分由URC_DATA状态处理 */
+                            
+                            PPP_DEBUG_INFO("[StreamParser] TCPRECV header fully parsed: socket=%d, data_len=%u (binary), remaining_hex_len=%u, data_offset=%u, hex_mode=%d, entering URC_DATA state", 
+                                          socket_id, data_len, g_parser_ctx.remaining_data_len, data_offset, g_tcp_recv_hex_mode);
+                        } else {
+                            /* 没有数据,只消费头部 */
+                            g_parser_ctx.state = AT_PARSER_STATE_URC_DATA;
+                            *consumed = header_consumed;
+                            
+                            PPP_DEBUG_INFO("[StreamParser] TCPRECV header fully parsed: socket=%d, data_len=%u (binary), remaining_hex_len=%u, no data yet, hex_mode=%d, entering URC_DATA state", 
+                                          socket_id, data_len, g_parser_ctx.remaining_data_len, g_tcp_recv_hex_mode);
+                        }
+                    } else {
+                        /* 没有找到第二个逗号,数据不完整,等待更多数据 */
+                        *consumed = 0;
+                        PPP_DEBUG_DEBUG("[StreamParser] TCPRECV header incomplete: waiting for second comma");
+                    }
+                } else {
+                    /* 没有找到第一个逗号,数据不完整,等待更多数据 */
+                    *consumed = 0;
+                    PPP_DEBUG_DEBUG("[StreamParser] TCPRECV header incomplete: waiting for first comma");
+                }
+            } else {
+                /* 当前数据块只包含部分剩余标识符,继续跳过 */
+                *consumed = length;
+                g_parser_ctx.urc_header_len += length;
+                PPP_DEBUG_DEBUG("[StreamParser] TCPRECV identifier partially skipped: %u/%u bytes", 
+                               g_parser_ctx.urc_header_len, identifier_len);
+            }
+        }
+        
+        return;
+    }
     
-    if (ret == N706A_AT_OK && response.success) {
-        PPP_DEBUG_INFO("AT communication test passed\n");
-        return N706A_AT_OK;
+    if (g_parser_ctx.urc_type == URC_TYPE_HTTPSRECV || g_parser_ctx.urc_type == URC_TYPE_HTTPRECV) {
+        /* HTTPRECV/HTTPSRECV 特殊处理:格式2(无长度字段),数据直接跟在标识符后面 */
+        /* 优化:一旦识别到标识符,立即跳过标识符,直接传递数据,避免缓冲区溢出 */
+        /* ⚠️ 注意:标识符后面可能还有空格或 \r\n,这些应该保留,不能跳过 */
+        
+        /* 检查是否已经跳过标识符 */
+        if (g_parser_ctx.urc_header_len == 0) {
+            /* 第一次处理,需要跳过标识符部分 */
+            /* 标识符长度:+HTTPSRECV:  = 12字节,+HTTPRECV:  = 11字节 */
+            uint32_t identifier_len = (g_parser_ctx.urc_type == URC_TYPE_HTTPSRECV) ? 12 : 11;
+            
+            if (length > identifier_len) {
+                /* 当前数据块包含标识符和数据,跳过标识符,直接传递数据 */
+                /* ⚠️ 关键:只跳过标识符本身,不跳过标识符后的空格或 \r\n */
+                /* 这些字符是HTTP响应数据的一部分,必须保留 */
+                uint32_t data_offset = identifier_len;
+                uint32_t data_len = length - data_offset;
+                
+                /* 调试:打印跳过标识符后的数据开头(前128字节) */
+                size_t debug_len = (data_len < 128) ? data_len : 128;
+                char debug_buf[129] = {0};
+                for (size_t i = 0; i < debug_len && i < 128; i++) {
+                    uint8_t b = data[data_offset + i];
+                    if (b >= 32 && b < 127) {
+                        debug_buf[i] = b;
+                    } else if (b == '\r') {
+                        debug_buf[i] = '\\';
+                        if (i + 1 < 128) debug_buf[++i] = 'r';
+                    } else if (b == '\n') {
+                        debug_buf[i] = '\\';
+                        if (i + 1 < 128) debug_buf[++i] = 'n';
+                    } else {
+                        debug_buf[i] = '.';
+                    }
+                }
+                PPP_DEBUG_INFO("[StreamParser] HTTPRECV data after identifier (first %u bytes): %s", 
+                              (unsigned int)debug_len, debug_buf);
+                
+                /* 检查数据开头是否是HTTP状态行,如果不是,记录警告 */
+                if (data_len >= 5 && memcmp(data + data_offset, "HTTP/", 5) != 0) {
+                    /* 数据开头不是 "HTTP/",可能是空格、\r\n 或其他字符 */
+                    /* 这些字符应该保留,因为它们是HTTP响应数据的一部分 */
+                    PPP_DEBUG_WARN("[StreamParser] HTTPRECV data does not start with 'HTTP/' after identifier. "
+                                  "First bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", 
+                                  data[data_offset], 
+                                  data_len > 1 ? data[data_offset + 1] : 0,
+                                  data_len > 2 ? data[data_offset + 2] : 0,
+                                  data_len > 3 ? data[data_offset + 3] : 0,
+                                  data_len > 4 ? data[data_offset + 4] : 0);
+                }
+                
+                extern void n706a_http_on_urc_received(const uint8_t *data, size_t length, bool is_https);
+                n706a_http_on_urc_received(data + data_offset, data_len, 
+                                          g_parser_ctx.urc_type == URC_TYPE_HTTPSRECV);
+                g_parser_ctx.received_data_len += data_len;
+                g_parser_ctx.is_https = (g_parser_ctx.urc_type == URC_TYPE_HTTPSRECV);
+                g_parser_ctx.remaining_data_len = 0;  /* 格式2没有长度字段,使用0表示未知长度 */
+                g_parser_ctx.state = AT_PARSER_STATE_URC_DATA;  /* 转换到URC_DATA状态,继续接收数据 */
+                *consumed = length;
+                
+                PPP_DEBUG_INFO("[StreamParser] HTTPRECV identifier skipped, data passed: %u bytes, entering URC_DATA state", 
+                              (unsigned int)data_len);
+            } else {
+                /* 当前数据块只包含部分标识符,跳过已接收的部分 */
+                *consumed = length;
+                g_parser_ctx.urc_header_len = length;  /* 记录已跳过的字节数 */
+                PPP_DEBUG_DEBUG("[StreamParser] HTTPRECV identifier partially received: %u/%u bytes", 
+                               (unsigned int)length, identifier_len);
+            }
+        } else {
+            /* 已经跳过部分标识符,继续跳过剩余部分 */
+            uint32_t identifier_len = (g_parser_ctx.urc_type == URC_TYPE_HTTPSRECV) ? 12 : 11;
+            uint32_t remaining_identifier = identifier_len - g_parser_ctx.urc_header_len;
+            
+            if (length >= remaining_identifier) {
+                /* 当前数据块包含剩余标识符和数据,跳过标识符,传递数据 */
+                uint32_t data_offset = remaining_identifier;
+                uint32_t data_len = length - data_offset;
+                
+                extern void n706a_http_on_urc_received(const uint8_t *data, size_t length, bool is_https);
+                n706a_http_on_urc_received(data + data_offset, data_len, 
+                                          g_parser_ctx.urc_type == URC_TYPE_HTTPSRECV);
+                g_parser_ctx.received_data_len += data_len;
+                g_parser_ctx.is_https = (g_parser_ctx.urc_type == URC_TYPE_HTTPSRECV);
+                g_parser_ctx.remaining_data_len = 0;  /* 格式2没有长度字段,使用0表示未知长度 */
+                g_parser_ctx.urc_header_len = 0;  /* 标识符已完全跳过,重置 */
+                g_parser_ctx.state = AT_PARSER_STATE_URC_DATA;  /* 转换到URC_DATA状态,继续接收数据 */
+                *consumed = length;
+                
+                PPP_DEBUG_INFO("[StreamParser] HTTPRECV identifier fully skipped, data passed: %u bytes, entering URC_DATA state", 
+                              (unsigned int)data_len);
+            } else {
+                /* 当前数据块只包含部分剩余标识符,继续跳过 */
+                *consumed = length;
+                g_parser_ctx.urc_header_len += length;
+                PPP_DEBUG_DEBUG("[StreamParser] HTTPRECV identifier partially skipped: %u/%u bytes", 
+                               g_parser_ctx.urc_header_len, identifier_len);
+            }
+        }
+        
+        return;
     } else {
-        PPP_DEBUG_ERROR("AT communication test failed\n");
-        return N706A_AT_ERR_AT_FAILED;
+        /* 其他URC类型,使用简单的头部解析 */
+        /* 将数据追加到URC头部缓冲区,直到遇到换行符 */
+        uint32_t consumed_before_loop = *consumed;  /* 记录循环前的consumed值 */
+        
+        while (*consumed < length && g_parser_ctx.urc_header_len < sizeof(g_parser_ctx.urc_header_buf) - 1) {
+            uint8_t byte = data[*consumed];
+            g_parser_ctx.urc_header_buf[g_parser_ctx.urc_header_len++] = byte;
+            (*consumed)++;
+            
+            /* 检查是否到达URC头部结束(遇到换行符)
+             * 支持多种格式:
+             * - \r\n (标准格式)
+             * - \n   (只有\n)
+             * - \r   (只有\r,可能跨数据块)
+             * - \r\r (双\r,某些模块的特殊格式)
+             */
+            if (byte == '\n') {
+                /* \n 结尾 */
+                g_parser_ctx.urc_header_buf[g_parser_ctx.urc_header_len] = '\0';
+                /* 解析并处理URC */
+                n706a_stream_parser_dispatch_urc();
+                n706a_stream_parser_reset();
+                return;
+            } else if (byte == '\r') {
+                /* \r 结尾,检查下一个字节 */
+                if (*consumed < length) {
+                    uint8_t next_byte = data[*consumed];
+                    if (next_byte == '\n') {
+                        /* \r\n 结尾 */
+                        g_parser_ctx.urc_header_buf[g_parser_ctx.urc_header_len] = '\0';
+                        /* 解析并处理URC */
+                        n706a_stream_parser_dispatch_urc();
+                        n706a_stream_parser_reset();
+                        return;
+                    } else if (next_byte == '\r') {
+                        /* \r\r 结尾 */
+                        g_parser_ctx.urc_header_buf[g_parser_ctx.urc_header_len] = '\0';
+                        /* 解析并处理URC */
+                        n706a_stream_parser_dispatch_urc();
+                        n706a_stream_parser_reset();
+                        return;
+                    }
+                    /* 只有 \r,可能跨数据块,继续累积 */
+                } else {
+                    /* \r 在数据块末尾,可能跨数据块,继续累积 */
+                    /* 注意:这里不重置,等待下一个数据块 */
+                }
+            }
+        }
+        
+        /* 修复:确保consumed只反映实际消费的字节数,而不是缓冲区大小 */
+        /* 如果while循环因为缓冲区满而退出,consumed应该等于实际累积的字节数 */
+        /* 如果while循环因为数据用完而退出,consumed应该等于length */
+        /* 如果while循环没有执行(缓冲区已满且consumed=0),至少消费1个字节 */
+        if (*consumed == 0 && length > 0) {
+            /* 缓冲区已满,无法继续累积 */
+            /* 这种情况不应该发生,因为URC头部通常不会超过256字节 */
+            /* 但如果真的发生了,我们需要处理:累积当前字符到缓冲区(如果还有空间) */
+            if (g_parser_ctx.urc_header_len < sizeof(g_parser_ctx.urc_header_buf) - 1) {
+                /* 还有空间,累积当前字符 */
+                g_parser_ctx.urc_header_buf[g_parser_ctx.urc_header_len++] = data[0];
+                *consumed = 1;
+                PPP_DEBUG_WARN("[StreamParser] URC header buffer接近满,累积字符: 0x%02X", data[0]);
+            } else {
+                /* 缓冲区真的满了,这是异常情况 */
+                PPP_DEBUG_ERROR("[StreamParser] URC header buffer已满,无法继续处理!重置状态");
+                n706a_stream_parser_reset();
+                *consumed = 1;  /* 跳过当前字符,避免死循环 */
+            }
+        } else {
+            /* consumed > 0,说明while循环执行了 */
+            /* 确保consumed不超过length,这是安全的 */
+            if (*consumed > length) {
+                PPP_DEBUG_ERROR("[StreamParser] URC header consumed (%u) > length (%u), 修复为length", 
+                              *consumed, length);
+                *consumed = length;
+            }
+            
+            /* 关键修复:如果缓冲区满了但没有遇到换行符,说明URC头部异常长 */
+            /* 这种情况可能是数据错误,或者URC头部真的非常长 */
+            /* 如果urc_header_len达到255(缓冲区满),但没有遇到换行符,应该重置状态 */
+            if (g_parser_ctx.urc_header_len >= sizeof(g_parser_ctx.urc_header_buf) - 1) {
+                /* 缓冲区满了,但没有遇到换行符,这是异常情况 */
+                /* 可能的原因:
+                 * 1. URC头部真的非常长(超过255字节)
+                 * 2. 数据错误,没有换行符
+                 * 3. 数据被截断,或者这不是URC头部而是数据
+                 */
+                PPP_DEBUG_ERROR("[StreamParser] URC header buffer满但没有换行符!urc_header_len=%u, consumed=%u, 重置状态并消费数据", 
+                              g_parser_ctx.urc_header_len, *consumed);
+                /* 关键修复:必须消费已累积的数据,否则会死循环 */
+                /* 如果consumed=0,说明while循环没有执行,这不应该发生 */
+                /* 如果consumed>0,说明while循环执行了,应该保留consumed的值 */
+                /* 这样pos会前进,不会死循环 */
+                if (*consumed == 0) {
+                    /* 这种情况不应该发生,但如果发生了,至少消费1个字节 */
+                    PPP_DEBUG_ERROR("[StreamParser] 异常:缓冲区满但consumed=0,强制消费1字节");
+                    *consumed = 1;
+                }
+                /* 重置状态,让解析器重新开始 */
+                n706a_stream_parser_reset();
+                /* 保留consumed的值,让pos前进,避免死循环 */
+                /* 这样虽然会丢失一些数据,但至少不会死循环 */
+            } else {
+                /* 缓冲区未满,正常情况,继续等待更多数据 */
+                /* 添加调试信息:如果consumed很大,记录详细信息 */
+                if (*consumed > 100) {
+                    PPP_DEBUG_WARN("[StreamParser] URC header consumed很大: %u, length=%u, urc_header_len=%u, buffer_size=%zu", 
+                                  *consumed, length, g_parser_ctx.urc_header_len, sizeof(g_parser_ctx.urc_header_buf));
+                }
+            }
+        }
     }
 }
 
 /**
- * @brief N706A对外导出:获取网络信号信息
+ * @brief 分发URC(根据URC类型调用相应的回调函数)
  */
-n706a_at_error_t n706a_at_get_network_signal_info(n706a_network_signal_info_t *signal_info)
+static void n706a_stream_parser_dispatch_urc(void)
 {
-    if (!signal_info) {
-        return N706A_AT_ERR_INVALID_PARAM;
-    }
+    const char *urc_header = (const char *)g_parser_ctx.urc_header_buf;
+    uint32_t urc_header_len = g_parser_ctx.urc_header_len;
     
-    /* 调用内部的ppp_at_get_network_signal_info */
-    n706a_network_signal_info_t ppp_signal;
-    ppp_error_t ret = ppp_at_get_network_signal_info(&ppp_signal);
+    PPP_DEBUG_INFO("[StreamParser] Dispatching URC: type=%d, header=[%.*s]", 
+                   g_parser_ctx.urc_type, (int)urc_header_len, urc_header);
     
-    if (ret == PPP_ERR_OK) {
-        /* 转换数据结构 */
-        signal_info->rxlev = ppp_signal.rxlev;
-        signal_info->ber = ppp_signal.ber;
-        signal_info->rscp = ppp_signal.rscp;
-        signal_info->ecno = ppp_signal.ecno;
-        signal_info->rsrq = ppp_signal.rsrq;
-        signal_info->rsrp = ppp_signal.rsrp;
-        signal_info->valid = ppp_signal.valid;
-        return N706A_AT_OK;
-    } else {
-        signal_info->valid = false;
-        return N706A_AT_ERR_AT_FAILED;
+    switch (g_parser_ctx.urc_type) {
+        case URC_TYPE_TCPRECV:
+        case URC_TYPE_SSLTCPRECV: {
+            /* +TCPRECV: <socket_id>,<data_len>,<data> 或 +SSLTCPRECV: <socket_id>,<data_len>,<data> */
+            if (g_tcp_urc_callback) {
+                int socket_id;
+                size_t data_len;
+                const uint8_t *tcp_data;
+                
+                if (n706a_parse_tcprecv_urc(urc_header, urc_header_len, &socket_id, &data_len, &tcp_data)) {
+                    /* 注意:TCPRECV的数据部分可能跨数据块,需要在URC_DATA状态中处理 */
+                    /* 这里只处理头部,数据部分在URC_DATA状态中处理 */
+                    PPP_DEBUG_INFO("[StreamParser] TCPRECV URC parsed: socket=%d, data_len=%u", 
+                                  socket_id, (unsigned int)data_len);
+                    /* 保存socket_id到上下文 */
+                    g_parser_ctx.tcp_socket_id = socket_id;
+                    /* 计算数据在头部缓冲区中的偏移量 */
+                    uint32_t data_offset = (const uint8_t *)tcp_data - (const uint8_t *)urc_header;
+                    if (data_offset < urc_header_len) {
+                        /* 数据在头部缓冲区中,立即传递这部分数据 */
+                        uint32_t header_data_len = urc_header_len - data_offset;
+                        if (header_data_len > 0 && header_data_len <= data_len) {
+                            g_tcp_urc_callback(socket_id, (const uint8_t *)tcp_data, header_data_len);
+                            g_parser_ctx.received_data_len = header_data_len;
+                            g_parser_ctx.remaining_data_len = data_len - header_data_len;
+                        } else {
+                            g_parser_ctx.received_data_len = 0;
+                            g_parser_ctx.remaining_data_len = data_len;
+                        }
+                    } else {
+                        /* 数据不在头部缓冲区中,等待后续数据块 */
+                        g_parser_ctx.received_data_len = 0;
+                        g_parser_ctx.remaining_data_len = data_len;
+                    }
+                    /* 转换到URC_DATA状态 */
+                    g_parser_ctx.state = AT_PARSER_STATE_URC_DATA;
+                } else {
+                    PPP_DEBUG_WARN("[StreamParser] Failed to parse TCPRECV URC");
+                }
+            }
+            break;
+        }
+        
+        case URC_TYPE_TCPSETUP:
+        case URC_TYPE_SSLTCPSETUP: {
+            /* +TCPSETUP: <socket_id>,OK 或 +TCPSETUP: <socket_id>,FAIL */
+            if (g_tcpsetup_urc_callback) {
+                int socket_id;
+                bool success;
+                
+                if (n706a_parse_tcpsetup_urc(urc_header, urc_header_len, &socket_id, &success)) {
+                    PPP_DEBUG_INFO("[StreamParser] TCPSETUP URC parsed: socket=%d, success=%d", socket_id, success);
+                    g_tcpsetup_urc_callback(socket_id, success);
+                } else {
+                    PPP_DEBUG_WARN("[StreamParser] Failed to parse TCPSETUP URC");
+                }
+            }
+            break;
+        }
+        
+        case URC_TYPE_TCPCLOSE:
+        case URC_TYPE_SSLTCPCLOSE: {
+            /* +TCPCLOSE: <socket_id> 或 +SSLTCPCLOSE: <socket_id> */
+            if (g_tcpclose_urc_callback) {
+                int socket_id;
+                
+                if (n706a_parse_tcpclose_urc(urc_header, urc_header_len, &socket_id)) {
+                    PPP_DEBUG_INFO("[StreamParser] TCPCLOSE URC parsed: socket=%d", socket_id);
+                    g_tcpclose_urc_callback(socket_id);
+                } else {
+                    PPP_DEBUG_WARN("[StreamParser] Failed to parse TCPCLOSE URC");
+                }
+            }
+            break;
+        }
+        
+        case URC_TYPE_TCPSEND:
+        case URC_TYPE_SSLTCPEND: {
+            /* +TCPSEND: <socket_id>,<sent_bytes> 或 +SSLTCPEND: <socket_id>,<sent_bytes> */
+            if (g_tcpsend_urc_callback) {
+                int socket_id;
+                size_t sent_bytes;
+                
+                if (n706a_parse_tcpsend_urc(urc_header, urc_header_len, &socket_id, &sent_bytes)) {
+                    PPP_DEBUG_INFO("[StreamParser] TCPSEND/SSLTCPEND URC parsed: socket=%d, sent=%u", 
+                                  socket_id, (unsigned int)sent_bytes);
+                    g_tcpsend_urc_callback(socket_id, sent_bytes);
+                } else {
+                    PPP_DEBUG_WARN("[StreamParser] Failed to parse TCPSEND/SSLTCPEND URC");
+                }
+            }
+            break;
+        }
+        
+        default:
+            PPP_DEBUG_DEBUG("[StreamParser] URC type %d not handled in dispatch", g_parser_ctx.urc_type);
+            break;
     }
 }
 
 /**
- * @brief N706A对外导出:设置波特率
+ * @brief 检测HTTPCLOSED标识符(支持跨数据块)
+ * @param data 数据指针
+ * @param length 数据长度
+ * @param closed_pos 输出:HTTPCLOSED标识符位置(如果找到)
+ * @return true=找到,false=未找到
  */
-n706a_at_error_t n706a_at_set_baudrate(uint32_t baudrate)
+static bool n706a_stream_parser_detect_httpclosed(const uint8_t *data, uint32_t length, uint32_t *closed_pos)
 {
-    ppp_error_t ret = ppp_at_set_baudrate(baudrate);
-    return (ret == PPP_ERR_OK) ? N706A_AT_OK : N706A_AT_ERR_UART;
+    static const char *closed_patterns[] = {"+HTTPSCLOSED:", "+HTTPCLOSED:"};
+    static uint32_t closed_match_pos[2] = {0, 0};  // 两个模式的匹配位置
+    
+    for (uint32_t i = 0; i < length; i++) {
+        uint8_t byte = data[i];
+        
+        /* 检查两个模式 */
+        for (int p = 0; p < 2; p++) {
+            const char *pattern = closed_patterns[p];
+            uint32_t pattern_len = strlen(pattern);
+            
+            if (closed_match_pos[p] < pattern_len) {
+                if (byte == pattern[closed_match_pos[p]]) {
+                    closed_match_pos[p]++;
+                    if (closed_match_pos[p] >= pattern_len) {
+                        /* 匹配成功 */
+                        *closed_pos = i + 1 - pattern_len;
+                        /* 重置所有匹配位置 */
+                        closed_match_pos[0] = 0;
+                        closed_match_pos[1] = 0;
+                        return true;
+                    }
+                } else {
+                    /* 匹配失败,重置 */
+                    closed_match_pos[p] = 0;
+                    /* 如果当前字节是 '+',重新开始匹配 */
+                    if (byte == '+') {
+                        closed_match_pos[p] = 1;
+                    }
+                }
+            }
+        }
+    }
+    
+    return false;
 }
 
 /**
- * @brief N706A对外导出:获取当前波特率
+ * @brief 处理URC数据(直接传递给回调)
+ * @param data 数据指针
+ * @param length 数据长度
+ * @param consumed 输出:已消费的字节数
  */
-uint32_t n706a_at_get_current_baudrate(void)
+static void n706a_stream_parser_process_urc_data(const uint8_t *data, uint32_t length, uint32_t *consumed)
 {
-    return g_at_ctx.current_baudrate;
+    *consumed = 0;
+    
+    /* 方案2修复:检测数据是否真的是URC数据,还是新的AT响应 */
+    /* 如果检测到AT响应开始(AT+),说明URC数据已结束,应该重置状态 */
+    if (length >= 2 && data[0] == 'A' && data[1] == 'T') {
+        PPP_DEBUG_WARN("[StreamParser] 在URC_DATA状态检测到AT响应开始,重置状态");
+        n706a_stream_parser_reset();
+        /* 不消费字符,让IDLE状态处理 */
+        return;
+    }
+    
+    if (g_parser_ctx.urc_type == URC_TYPE_HTTPSRECV || g_parser_ctx.urc_type == URC_TYPE_HTTPRECV) {
+        /* HTTPRECV/HTTPSRECV 特殊处理:格式2(无长度字段),需要检测 +HTTPSCLOSED */
+        uint32_t closed_pos = 0;
+        bool found_closed = n706a_stream_parser_detect_httpclosed(data, length, &closed_pos);
+        
+        uint32_t to_consume = 0;
+        if (found_closed) {
+            /* 找到 +HTTPSCLOSED,数据结束 */
+            to_consume = closed_pos;
+            PPP_DEBUG_INFO("[StreamParser] Found +HTTPSCLOSED at offset %u, URC data complete: total=%u bytes", 
+                          closed_pos, (unsigned int)(g_parser_ctx.received_data_len + to_consume));
+        } else if (g_parser_ctx.remaining_data_len > 0) {
+            /* 有剩余数据长度,使用剩余长度 */
+            to_consume = (length < g_parser_ctx.remaining_data_len) ? length : g_parser_ctx.remaining_data_len;
+        } else {
+            /* 格式2且没有剩余长度,传递所有数据(直到找到 +HTTPSCLOSED) */
+            /* 但需要检查数据中是否包含AT响应开始 */
+            to_consume = length;
+            for (uint32_t i = 0; i < length - 1; i++) {
+                if (data[i] == 'A' && data[i+1] == 'T') {
+                    /* 找到AT响应开始,只处理到此处 */
+                    to_consume = i;
+                    PPP_DEBUG_WARN("[StreamParser] 在HTTPRECV数据中检测到AT响应开始,提前结束URC数据");
+                    break;
+                }
+            }
+        }
+        
+        if (to_consume > 0) {
+            extern void n706a_http_on_urc_received(const uint8_t *data, size_t length, bool is_https);
+            extern void n706a_http_mark_connection_closed(void);
+            
+            n706a_http_on_urc_received(data, to_consume, g_parser_ctx.is_https);
+            g_parser_ctx.received_data_len += to_consume;
+            *consumed = to_consume;
+            
+            if (found_closed) {
+                /* 检测到+HTTPSCLOSED,标记连接已关闭 */
+                n706a_http_mark_connection_closed();
+                /* 数据已完整,重置状态 */
+                g_parser_ctx.urc_count++;
+                n706a_stream_parser_reset();
+            } else if (g_parser_ctx.remaining_data_len > 0) {
+                /* 更新剩余数据长度 */
+                g_parser_ctx.remaining_data_len -= to_consume;
+                if (g_parser_ctx.remaining_data_len == 0) {
+                    PPP_DEBUG_INFO("[StreamParser] URC data complete: total=%u bytes", 
+                                  (unsigned int)g_parser_ctx.received_data_len);
+                    g_parser_ctx.urc_count++;
+                    n706a_stream_parser_reset();
+                }
+            } else if (to_consume < length) {
+                /* 因为检测到AT响应而提前结束,重置状态 */
+                PPP_DEBUG_INFO("[StreamParser] URC data ended early due to AT response, resetting state");
+                n706a_stream_parser_reset();
+            }
+        } else if (length > 0) {
+            /* 没有数据要处理,但至少消费1个字节,避免死循环 */
+            /* 这种情况不应该发生,但如果发生了,至少消费1个字节 */
+            *consumed = 1;
+            PPP_DEBUG_WARN("[StreamParser] URC_DATA: no data to process but length>0, consuming 1 byte");
+        }
+        } else if (g_parser_ctx.remaining_data_len > 0) {
+        /* 其他URC类型,使用剩余数据长度 */
+        /* 方案2修复:检测数据中是否包含AT响应开始 */
+        uint32_t to_consume = (length < g_parser_ctx.remaining_data_len) ? length : g_parser_ctx.remaining_data_len;
+        
+        /* 检查数据中是否包含AT响应开始 */
+        for (uint32_t i = 0; i < to_consume - 1; i++) {
+            if (data[i] == 'A' && data[i+1] == 'T') {
+                /* 找到AT响应开始,只处理到此处 */
+                to_consume = i;
+                PPP_DEBUG_WARN("[StreamParser] 在URC数据中检测到AT响应开始,提前结束URC数据");
+                break;
+            }
+        }
+        
+        if (g_parser_ctx.urc_type == URC_TYPE_TCPRECV || g_parser_ctx.urc_type == URC_TYPE_SSLTCPRECV) {
+            /* TCPRECV数据传递 */
+            if (g_tcp_urc_callback && to_consume > 0) {
+                if (g_tcp_recv_hex_mode) {
+                    /* HEX模式:需要解码HEX字符串为二进制数据 */
+                    /* 处理跨数据块的不完整HEX对 */
+                    uint32_t hex_data_start = 0;
+                    uint32_t hex_data_len = to_consume;
+                    
+                    /* 如果有部分数据,先合并 */
+                    if (g_parser_ctx.partial_buffer_len > 0) {
+                        /* 合并部分数据和当前数据 */
+                        if (g_parser_ctx.partial_buffer_len + hex_data_len > sizeof(g_parser_ctx.partial_buffer)) {
+                            PPP_DEBUG_ERROR("[StreamParser] Partial buffer overflow: partial_len=%u, new_len=%u, max=%u", 
+                                          g_parser_ctx.partial_buffer_len, hex_data_len, (unsigned int)sizeof(g_parser_ctx.partial_buffer));
+                            /* 清空部分缓冲区,只处理当前数据 */
+                            g_parser_ctx.partial_buffer_len = 0;
+                        } else {
+                            memcpy(g_parser_ctx.partial_buffer + g_parser_ctx.partial_buffer_len, data, hex_data_len);
+                            hex_data_len += g_parser_ctx.partial_buffer_len;
+                            hex_data_start = 0;  /* 使用partial_buffer */
+                        }
+                    }
+                    
+                    /* 确保HEX数据长度是偶数(HEX对) */
+                    uint32_t hex_pairs_len = (hex_data_len / 2) * 2;
+                    uint32_t remaining_hex = hex_data_len - hex_pairs_len;
+                    
+                    /* 解码HEX数据 */
+                    const char *hex_str = (hex_data_start == 0 && g_parser_ctx.partial_buffer_len > 0) ? 
+                                         (const char *)g_parser_ctx.partial_buffer : (const char *)data;
+                    /* 优化:使用静态缓冲区,避免在栈上分配2048字节(这是导致栈溢出的主要原因) */
+                    /* 注意:这个函数在单线程(ppp_irq_proc)中调用,所以静态缓冲区是安全的 */
+                    static uint8_t binary_buffer[2048];  /* 静态二进制缓冲区,避免栈溢出 */
+                    size_t binary_len = 0;
+                    
+                    if (hex_pairs_len > 0) {
+                        /* 限制解码长度,避免缓冲区溢出 */
+                        size_t max_binary_len = hex_pairs_len / 2;
+                        if (max_binary_len > sizeof(binary_buffer)) {
+                            max_binary_len = sizeof(binary_buffer);
+                            hex_pairs_len = max_binary_len * 2;  /* 调整HEX长度以匹配缓冲区 */
+                        }
+                        
+                        binary_len = n706a_hex_string_to_binary(hex_str, hex_pairs_len, binary_buffer, sizeof(binary_buffer));
+                        if (binary_len > 0) {
+                            g_tcp_urc_callback(g_parser_ctx.tcp_socket_id, binary_buffer, binary_len);
+                            PPP_DEBUG_DEBUG("[StreamParser] TCPRECV HEX data decoded: socket=%d, hex_len=%u, binary_len=%u", 
+                                           g_parser_ctx.tcp_socket_id, hex_pairs_len, (unsigned int)binary_len);
+                        } else {
+                            PPP_DEBUG_ERROR("[StreamParser] Failed to decode HEX data: hex_len=%u", hex_pairs_len);
+                        }
+                    }
+                    
+                    /* 保存不完整的HEX字符到partial_buffer */
+                    if (remaining_hex > 0) {
+                        const char *remaining_hex_str = (hex_data_start == 0 && g_parser_ctx.partial_buffer_len > 0) ? 
+                                                       (const char *)(g_parser_ctx.partial_buffer + hex_pairs_len) : 
+                                                       (const char *)(data + hex_pairs_len);
+                        memcpy(g_parser_ctx.partial_buffer, remaining_hex_str, remaining_hex);
+                        g_parser_ctx.partial_buffer_len = remaining_hex;
+                        PPP_DEBUG_DEBUG("[StreamParser] Saved %u incomplete HEX characters to partial buffer", remaining_hex);
+                    } else {
+                        g_parser_ctx.partial_buffer_len = 0;  /* 没有不完整的数据 */
+                    }
+                    
+                    /* 更新consumed:HEX模式下,consumed应该是HEX字符串的长度 */
+                    /* 注意:to_consume已经是HEX字符串的长度,所以不需要修改 */
+                    /* 但是,我们需要更新remaining_data_len,减去已处理的HEX字符数 */
+                    /* 注意:这里to_consume是HEX字符串的长度,remaining_data_len也是HEX字符串的长度 */
+                } else {
+                    /* 非HEX模式:直接传递二进制数据 */
+                    g_tcp_urc_callback(g_parser_ctx.tcp_socket_id, data, to_consume);
+                    PPP_DEBUG_DEBUG("[StreamParser] TCPRECV binary data passed: socket=%d, len=%u", 
+                                   g_parser_ctx.tcp_socket_id, (unsigned int)to_consume);
+                }
+            }
+        }
+        
+        /* 更新remaining_data_len和received_data_len */
+        g_parser_ctx.remaining_data_len -= to_consume;
+        g_parser_ctx.received_data_len += to_consume;
+        *consumed = to_consume;
+        
+        if (g_parser_ctx.remaining_data_len == 0 || to_consume < length) {
+            /* 数据已完整,或因为检测到AT响应而提前结束 */
+            if (to_consume < length) {
+                PPP_DEBUG_INFO("[StreamParser] URC data ended early due to AT response, resetting state");
+            } else {
+            PPP_DEBUG_INFO("[StreamParser] URC data complete: total=%u bytes", 
+                          (unsigned int)g_parser_ctx.received_data_len);
+            }
+            g_parser_ctx.urc_count++;
+            n706a_stream_parser_reset();
+        }
+    } else {
+        /* 没有剩余数据长度,重置状态 */
+        PPP_DEBUG_WARN("[StreamParser] URC_DATA状态但没有剩余数据长度,重置状态");
+        n706a_stream_parser_reset();
+        *consumed = 1;
+    }
 }
 
 /**
- * @brief 解析 +TCPRECV URC
- * @param urc_data URC数据(以 "+TCPRECV: " 开头)
- * @param urc_len URC数据长度
- * @param socket_id 输出:socket ID
- * @param data_len 输出:数据长度
- *   - ASCII模式:二进制数据长度(字节数)
- *   - HEX模式:HEX字符串长度(字节数),需要除以2得到二进制数据长度
- * @param data 输出:数据指针(指向 urc_data 内部)
- *   - ASCII模式:指向ASCII数据
- *   - HEX模式:指向HEX字符串
- * @return true=解析成功,false=失败
- * 
- * URC格式(ASCII模式):+TCPRECV: <socket_id>,<binary_len>,<ascii_data>\r\n
- * 例如:+TCPRECV: 0,10,1234567890\r\n (10字节ASCII数据)
- * 注意:<length>是二进制数据长度(10)
- * 
- * URC格式(HEX模式):+TCPRECV: <socket_id>,<hex_str_len>,<hex_data>\r\n
- * 例如:+TCPRECV: 0,6,313233\r\n (表示3字节二进制数据:0x31, 0x32, 0x33)
- * 注意:<length>是HEX字符串长度(6),不是二进制数据长度(3)
+ * @brief 检测AT响应结束(支持多种换行符格式)
+ * @param data 数据指针
+ * @param length 数据长度
+ * @param terminator_pos 输出:终止符位置(如果找到)
+ * @return true=找到终止符,false=未找到
  * 
- * 与AT+TCPSEND的对应关系:
- * - AT+TCPSEND的length参数:二进制数据长度(字节数)
- * - +TCPRECV的length参数:HEX模式下是HEX字符串长度(字节数)= 二进制数据长度 × 2
+ * 支持的格式:
+ * - \r\nOK\r\n  (标准格式)
+ * - \r\nOK\r    (只有\r结尾)
+ * - \r\nOK\n    (只有\n结尾)
+ * - \r\nOK      (没有结尾换行符,在数据块末尾)
+ * - \r\nERROR\r\n
+ * - \r\nERROR\r
+ * - \r\nERROR\n
+ * - \r\nERROR
+ * - \rOK\r\n    (只有\r开头)
+ * - \nOK\r\n    (只有\n开头)
+ * - \r\r        (双\r结尾,某些模块的特殊格式)
  */
-static bool n706a_parse_tcprecv_urc(const char *urc_data, size_t urc_len, 
-                                    int *socket_id, size_t *data_len, const uint8_t **data)
+static bool n706a_stream_parser_detect_at_response_end(const uint8_t *data, uint32_t length, uint32_t *terminator_pos)
 {
-    if (!urc_data || !socket_id || !data_len || !data || urc_len < 15) {
-        return false;
-    }
-    
-    // 跳过 "+TCPRECV: "
-    const char *p = urc_data + 10;
-    const char *end = urc_data + urc_len;
-    
-    // 解析 socket_id
-    *socket_id = 0;
-    while (p < end && *p >= '0' && *p <= '9') {
-        *socket_id = *socket_id * 10 + (*p - '0');
-        p++;
-    }
-    
-    if (p >= end || *p != ',') {
-        PPP_DEBUG_ERROR("URC parse error: no comma after socket_id");
-        return false;
-    }
-    p++;  // 跳过逗号
-    
-    // 解析 length(HEX模式下这是HEX字符串的长度)
-    *data_len = 0;
-    while (p < end && *p >= '0' && *p <= '9') {
-        *data_len = *data_len * 10 + (*p - '0');
-        p++;
-    }
-    
-    if (p >= end || *p != ',') {
-        PPP_DEBUG_ERROR("URC parse error: no comma after length");
+    if (!data || length == 0) {
         return false;
     }
-    p++;  // 跳过逗号
     
-    // 数据起始位置
-    *data = (const uint8_t *)p;
+    /* 方案2:增强检测 - 支持多种AT响应终止符 */
+    /* 支持的终止符:OK, ERROR, CONNECT, +COMMAND: params, NO CARRIER 等 */
     
-    // 验证数据长度是否合理
-    size_t remaining = end - p;
-    if (*data_len > remaining) {
-        PPP_DEBUG_WARN("URC data_len=%u exceeds remaining=%u, truncating", 
-                      (unsigned int)*data_len, (unsigned int)remaining);
-        *data_len = remaining;
+    for (uint32_t i = 0; i < length; i++) {
+        /* 检查是否是 OK */
+        if (i + 2 <= length && data[i] == 'O' && data[i+1] == 'K') {
+            /* 检查OK前面的分隔符 */
+            bool valid_prefix = false;
+            uint32_t ok_start = i;
+            
+            /* 检查 \r\n 或 \r 或 \n 或开头 */
+            if (ok_start >= 2 && data[ok_start-2] == '\r' && data[ok_start-1] == '\n') {
+                valid_prefix = true;  /* \r\nOK */
+            } else if (ok_start >= 1 && data[ok_start-1] == '\r') {
+                valid_prefix = true;  /* \rOK */
+            } else if (ok_start >= 1 && data[ok_start-1] == '\n') {
+                valid_prefix = true;  /* \nOK */
+            } else if (ok_start == 0) {
+                valid_prefix = true;  /* OK在开头 */
+            }
+            
+            if (valid_prefix) {
+                /* 检查OK后面的终止符 */
+                uint32_t ok_end = i + 2;  /* OK结束位置 */
+                
+                /* 情况1: \r\nOK\r\n (标准格式) */
+                if (ok_end + 2 <= length && data[ok_end] == '\r' && data[ok_end+1] == '\n') {
+                    *terminator_pos = ok_end + 2;
+                    return true;
+                }
+                /* 情况2: \r\nOK\r (只有\r结尾) */
+                if (ok_end + 1 <= length && data[ok_end] == '\r') {
+                    /* 检查后面是否还有\n(跨数据块的情况) */
+                    *terminator_pos = ok_end + 1;
+                    return true;
+                }
+                /* 情况3: \r\nOK\n (只有\n结尾) */
+                if (ok_end + 1 <= length && data[ok_end] == '\n') {
+                    *terminator_pos = ok_end + 1;
+                    return true;
+                }
+                /* 情况4: \r\nOK (没有结尾换行符,在数据块末尾) */
+                if (ok_end >= length) {
+                    *terminator_pos = ok_end;
+                    return true;
+                }
+                /* 情况5: \r\nOK\r\r (双\r结尾) */
+                if (ok_end + 2 <= length && data[ok_end] == '\r' && data[ok_end+1] == '\r') {
+                    *terminator_pos = ok_end + 2;
+                    return true;
+                }
+            }
+        }
+        
+        /* 检查是否是 ERROR */
+        if (i + 5 <= length && 
+            data[i] == 'E' && data[i+1] == 'R' && data[i+2] == 'R' &&
+            data[i+3] == 'O' && data[i+4] == 'R') {
+            /* 检查ERROR前面的分隔符 */
+            bool valid_prefix = false;
+            uint32_t error_start = i;
+            
+            /* 检查 \r\n 或 \r 或 \n 或开头 */
+            if (error_start >= 2 && data[error_start-2] == '\r' && data[error_start-1] == '\n') {
+                valid_prefix = true;  /* \r\nERROR */
+            } else if (error_start >= 1 && data[error_start-1] == '\r') {
+                valid_prefix = true;  /* \rERROR */
+            } else if (error_start >= 1 && data[error_start-1] == '\n') {
+                valid_prefix = true;  /* \nERROR */
+            } else if (error_start == 0) {
+                valid_prefix = true;  /* ERROR在开头 */
+            }
+            
+            if (valid_prefix) {
+                /* 检查ERROR后面的终止符 */
+                uint32_t error_end = i + 5;  /* ERROR结束位置 */
+                
+                /* 情况1: \r\nERROR\r\n (标准格式) */
+                if (error_end + 2 <= length && data[error_end] == '\r' && data[error_end+1] == '\n') {
+                    *terminator_pos = error_end + 2;
+                    return true;
+                }
+                /* 情况2: \r\nERROR\r (只有\r结尾) */
+                if (error_end + 1 <= length && data[error_end] == '\r') {
+                    *terminator_pos = error_end + 1;
+                    return true;
+                }
+                /* 情况3: \r\nERROR\n (只有\n结尾) */
+                if (error_end + 1 <= length && data[error_end] == '\n') {
+                    *terminator_pos = error_end + 1;
+                    return true;
+                }
+                /* 情况4: \r\nERROR (没有结尾换行符,在数据块末尾) */
+                if (error_end >= length) {
+                    *terminator_pos = error_end;
+                    return true;
+                }
+                /* 情况5: \r\nERROR\r\r (双\r结尾) */
+                if (error_end + 2 <= length && data[error_end] == '\r' && data[error_end+1] == '\r') {
+                    *terminator_pos = error_end + 2;
+                    return true;
+                }
+            }
+        }
+        
+        /* 方案2:检查是否是 CONNECT(用于AT+CERTADD等) */
+        if (i + 7 <= length && 
+            data[i] == 'C' && data[i+1] == 'O' && data[i+2] == 'N' &&
+            data[i+3] == 'N' && data[i+4] == 'E' && data[i+5] == 'C' && data[i+6] == 'T') {
+            /* 检查CONNECT前面的分隔符 */
+            bool valid_prefix = false;
+            uint32_t connect_start = i;
+            
+            if (connect_start >= 2 && data[connect_start-2] == '\r' && data[connect_start-1] == '\n') {
+                valid_prefix = true;  /* \r\nCONNECT */
+            } else if (connect_start >= 1 && (data[connect_start-1] == '\r' || data[connect_start-1] == '\n')) {
+                valid_prefix = true;
+            } else if (connect_start == 0) {
+                valid_prefix = true;
+            }
+            
+            if (valid_prefix) {
+                uint32_t connect_end = i + 7;  /* CONNECT结束位置 */
+                
+                /* 检查CONNECT后面的终止符 */
+                if (connect_end + 2 <= length && data[connect_end] == '\r' && data[connect_end+1] == '\n') {
+                    *terminator_pos = connect_end + 2;
+                    return true;
+                }
+                if (connect_end + 1 <= length && (data[connect_end] == '\r' || data[connect_end] == '\n')) {
+                    *terminator_pos = connect_end + 1;
+                    return true;
+                }
+                if (connect_end >= length) {
+                    *terminator_pos = connect_end;
+                    return true;
+                }
+            }
+        }
         
-        // 移除尾部的 \r\n(如果存在)
-        if (*data_len >= 2 && (*data)[*data_len - 2] == '\r' && (*data)[*data_len - 1] == '\n') {
-            *data_len -= 2;
+        /* 方案2:检查带参数的响应(如+CERTCHECK: xxx) */
+        /* 模式:\r\n+COMMAND: params\r\n */
+        if (i + 1 < length && data[i] == '\r' && data[i+1] == '\n') {
+            /* 查找下一个\r\n作为结束 */
+            for (uint32_t j = i + 2; j < length - 1; j++) {
+                if (data[j] == '\r' && data[j+1] == '\n') {
+                    /* 检查中间是否有OK/ERROR/CONNECT */
+                    bool has_terminator = false;
+                    for (uint32_t k = i + 2; k < j; k++) {
+                        if (k + 2 <= j && data[k] == 'O' && data[k+1] == 'K') {
+                            has_terminator = true;
+                            break;
+                        }
+                        if (k + 5 <= j && strncmp((char*)&data[k], "ERROR", 5) == 0) {
+                            has_terminator = true;
+                            break;
+                        }
+                        if (k + 7 <= j && strncmp((char*)&data[k], "CONNECT", 7) == 0) {
+                            has_terminator = true;
+                            break;
+                        }
+                    }
+                    if (!has_terminator) {
+                        /* 没有标准终止符,可能是带参数的响应 */
+                        /* 检查是否以+开头(如+CERTCHECK:) */
+                        if (i + 2 < length && data[i+2] == '+') {
+                            *terminator_pos = j + 2;
+                            return true;
+                        }
+                    }
+                    break;
+                }
+            }
         }
     }
     
-    PPP_DEBUG_DEBUG("Parsed URC: socket=%d, data_len=%u", *socket_id, (unsigned int)*data_len);
-    return true;
+    return false;
 }
 
 /**
- * @brief 解析 +TCPCLOSE URC
- * @param urc_data URC数据(以 "+TCPCLOSE: " 或 "+SSLTCPCLOSE: " 开头)
- * @param urc_len URC数据长度
- * @param socket_id 输出:socket ID(如果格式是ERROR,socket_id设为-1)
- * @return true=解析成功,false=失败
- * 
- * URC格式:
- *   +TCPCLOSE: <socket_id>
- *   +TCPCLOSE: <socket_id>,Link Closed
- *   +TCPCLOSE: ERROR(错误格式,socket_id设为-1)
- *     或:+SSLTCPCLOSE: <socket_id>
+ * @brief 处理AT命令响应(写入环形缓冲区)
+ * @param data 数据指针
+ * @param length 数据长度
+ * @param consumed 输出:已消费的字节数
  */
-static bool n706a_parse_tcpclose_urc(const char *urc_data, size_t urc_len, int *socket_id)
+static void n706a_stream_parser_process_at_response(const uint8_t *data, uint32_t length, uint32_t *consumed)
 {
-    if (!urc_data || !socket_id || urc_len < 12) {
-        return false;
-    }
+    *consumed = 0;
     
-    // 查找 ": " 位置
-    const char *colon_space = strstr(urc_data, ": ");
-    if (!colon_space) {
-        return false;
+    if (length == 0) {
+        /* 没有数据,返回0是正常的 */
+        return;
     }
     
-    const char *p = colon_space + 2;  // 跳过 ": "
-    const char *end = urc_data + urc_len;
+    /* 检测响应结束 */
+    uint32_t terminator_pos = 0;
+    bool response_complete = n706a_stream_parser_detect_at_response_end(data, length, &terminator_pos);
     
-    // 检查是否是ERROR格式
-    if (p + 5 <= end && memcmp(p, "ERROR", 5) == 0 &&
-        (p[5] == '\r' || p[5] == '\n' || p[5] == '\0' || p[5] == ' ')) {
-        *socket_id = -1;  // ERROR格式,socket_id设为-1表示错误
-        return true;
-    }
+    uint32_t to_write = response_complete ? terminator_pos : length;
     
-    // 解析 socket_id
-    *socket_id = 0;
-    while (p < end && *p >= '0' && *p <= '9') {
-        *socket_id = *socket_id * 10 + (*p - '0');
-        p++;
-    }
+    /* 将数据写入环形缓冲区 */
+    osal_mutex_lock(&g_at_response_mutex);
     
-    // 可能后面有 ",Link Closed" 或其他信息,但socket_id已经解析完成
-    return (*socket_id >= 0);  // 确保至少解析到一个有效的socket_id或ERROR
-}
-
-/**
- * @brief 解析 +TCPSEND URC
- * @param urc_data URC数据(以 "+TCPSEND: " 或 "+SSLTCPEND: " 开头)
- * @param urc_len URC数据长度
- * @param socket_id 输出:socket ID
- * @param sent_bytes 输出:实际发送的字节数
- * @return true=解析成功,false=失败
- * 
- * URC格式:+TCPSEND: <socket_id>,<sent_bytes>
- *     或:+SSLTCPEND: <socket_id>,<sent_bytes>
- */
-static bool n706a_parse_tcpsend_urc(const char *urc_data, size_t urc_len, 
-                                    int *socket_id, size_t *sent_bytes)
-{
-    if (!urc_data || !socket_id || !sent_bytes || urc_len < 15) {
-        return false;
-    }
+    uint32_t available_space = (g_at_response_head >= g_at_response_tail) ? 
+                               (N706A_AT_RESPONSE_RING_BUFFER_SIZE - (g_at_response_head - g_at_response_tail) - 1) :
+                               (g_at_response_tail - g_at_response_head - 1);
     
-    // 查找 ": " 位置
-    const char *colon_space = strstr(urc_data, ": ");
-    if (!colon_space) {
-        return false;
+    if (to_write > available_space) {
+        to_write = available_space;
     }
     
-    const char *p = colon_space + 2;  // 跳过 ": "
-    const char *end = urc_data + urc_len;
+    /* 方案2修复:确保至少消费1个字节,避免死循环 */
+    /* 如果缓冲区满了,至少消费1个字节,这样数据不会丢失,只是会覆盖最旧的数据 */
+    static uint32_t overflow_warn_count = 0;  // 静态计数器,减少警告频率
+    static uint32_t last_overflow_warn_time = 0;  // 上次警告时间
     
-    // 解析 socket_id
-    *socket_id = 0;
-    while (p < end && *p >= '0' && *p <= '9') {
-        *socket_id = *socket_id * 10 + (*p - '0');
-        p++;
+    if (to_write == 0 && length > 0) {
+        to_write = 1;
+        /* 减少警告频率:每100次或每10秒才打印一次警告 */
+        uint32_t current_time = osal_jiffies_to_msecs(osal_get_jiffies());
+        if (overflow_warn_count == 0 || (current_time - last_overflow_warn_time) > 10000 || 
+            (last_overflow_warn_time > current_time)) {  // 处理时间回绕
+            PPP_DEBUG_WARN("[StreamParser] AT response buffer full, consuming 1 byte (will overwrite oldest)");
+            overflow_warn_count = 0;
+            last_overflow_warn_time = current_time;
+        }
+        overflow_warn_count++;
+    }
+    
+    /* 批量写入,减少溢出检查次数 */
+    uint32_t overflow_count = 0;
+    for (uint32_t i = 0; i < to_write; i++) {
+        g_at_response_ring_buffer[g_at_response_head] = data[i];
+        g_at_response_head = (g_at_response_head + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
+        
+        if (g_at_response_head == g_at_response_tail) {
+            g_at_response_tail = (g_at_response_tail + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
+            overflow_count++;
+        }
     }
     
-    if (p >= end || *p != ',') {
-        PPP_DEBUG_ERROR("TCPSEND URC parse error: no comma after socket_id");
-        return false;
+    /* 只在有溢出时打印警告,且减少频率 */
+    if (overflow_count > 0) {
+        static uint32_t last_overflow_log_count = 0;
+        static uint32_t last_overflow_log_time = 0;
+        uint32_t current_time = osal_jiffies_to_msecs(osal_get_jiffies());
+        
+        /* 每100次溢出或每10秒才打印一次警告 */
+        if (overflow_count - last_overflow_log_count >= 100 || 
+            (current_time - last_overflow_log_time) > 10000 ||
+            (last_overflow_log_time > current_time)) {  // 处理时间回绕
+            PPP_DEBUG_WARN("[StreamParser] AT response buffer overflow, dropped %u bytes (total dropped: %u)", 
+                          overflow_count, overflow_count);
+            last_overflow_log_count = overflow_count;
+            last_overflow_log_time = current_time;
+        }
     }
-    p++;  // 跳过逗号
     
-    // 解析 sent_bytes
-    *sent_bytes = 0;
-    while (p < end && *p >= '0' && *p <= '9') {
-        *sent_bytes = *sent_bytes * 10 + (*p - '0');
-        p++;
+    osal_mutex_unlock(&g_at_response_mutex);
+    
+    /* 通知等待线程 */
+    if (to_write > 0) {
+        osal_sem_up(&g_at_response_sem);
     }
     
-    return true;
+    *consumed = to_write;
+    
+    /* 如果响应完成,重置状态 */
+    if (response_complete) {
+        PPP_DEBUG_DEBUG("[StreamParser] AT response complete: %u bytes", to_write);
+        g_parser_ctx.at_response_count++;
+        n706a_stream_parser_reset();
+    }
 }
 
+/* ========== 重构后的解析器辅助函数 ========== */
+
 /**
- * @brief 解析 +TCPSETUP URC
- * @param urc_data URC数据(以 "+TCPSETUP: " 或 "+SSLTCPSETUP: " 开头)
- * @param urc_len URC数据长度
- * @param socket_id 输出:socket ID
- * @param success 输出:true表示OK,false表示FAIL
- * @return true=解析成功,false=失败
- * 
- * URC格式:+TCPSETUP: <socket_id>,OK 或 +TCPSETUP: <socket_id>,FAIL
- *     或:+SSLTCPSETUP: <socket_id>,OK 或 +SSLTCPSETUP: <socket_id>,FAIL
- * 例如:+TCPSETUP: 0,OK 或 +TCPSETUP: 0,FAIL
+ * @brief 检查是否是AT响应开始
  */
-static bool n706a_parse_tcpsetup_urc(const char *urc_data, size_t urc_len,
-                                     int *socket_id, bool *success)
-{
-    if (!urc_data || !socket_id || !success || urc_len < 15) {
-        return false;
-    }
+static bool is_at_response_start(const uint8_t *data, uint32_t length, uint32_t pos) {
+    if (pos >= length) return false;
     
-    // 查找 ": " 位置(跳过 "+TCPSETUP" 或 "+SSLTCPSETUP")
-    const char *colon_space = strstr(urc_data, ": ");
-    if (!colon_space) {
-        return false;
+    // AT命令回显:AT+...
+    if (pos + 2 < length && data[pos] == 'A' && data[pos+1] == 'T' && data[pos+2] == '+') {
+        return true;
     }
     
-    const char *p = colon_space + 2;  // 跳过 ": "
-    const char *end = urc_data + urc_len;
-    
-    // 跳过可能的空格
-    while (p < end && (*p == ' ' || *p == '\t')) {
-        p++;
+    // 直接的响应:+CGATT: 1 或 OK 或 ERROR
+    if (data[pos] == '+') {
+        return true;  // 可能是响应中的+参数
     }
     
-    // 解析 socket_id
-    *socket_id = 0;
-    while (p < end && *p >= '0' && *p <= '9') {
-        *socket_id = *socket_id * 10 + (*p - '0');
-        p++;
+    if (pos + 1 < length && data[pos] == 'O' && data[pos+1] == 'K') {
+        return true;
     }
     
-    if (p >= end || *p != ',') {
-        PPP_DEBUG_ERROR("TCPSETUP URC parse error: no comma after socket_id (found: 0x%02x)", 
-                       p < end ? (unsigned char)*p : 0);
-        return false;
+    if (pos + 4 < length && strncmp((char*)&data[pos], "ERROR", 5) == 0) {
+        return true;
     }
-    p++;  // 跳过逗号
     
-    // 跳过可能的空格
-    while (p < end && (*p == ' ' || *p == '\t')) {
-        p++;
-    }
+    return false;
+}
+
+/**
+ * @brief 检查是否是URC开始
+ */
+static bool is_urc_start(const uint8_t *data, uint32_t length, uint32_t pos) {
+    if (pos >= length || data[pos] != '+') return false;
     
-    // 解析结果(OK或FAIL),忽略前后空白字符
-    if (p < end) {
-        // 检查OK
-        if (p + 2 <= end && (p[0] == 'O' || p[0] == 'o') && (p[1] == 'K' || p[1] == 'k')) {
-            // 检查OK后面是否是结束符或空白字符
-            if (p + 2 >= end || p[2] == '\r' || p[2] == '\n' || p[2] == '\0' || 
-                p[2] == ' ' || p[2] == '\t') {
-                *success = true;
-                return true;
-            }
-        }
-        
-        // 检查FAIL
-        if (p + 4 <= end && 
-            (p[0] == 'F' || p[0] == 'f') && 
-            (p[1] == 'A' || p[1] == 'a') && 
-            (p[2] == 'I' || p[2] == 'i') && 
-            (p[3] == 'L' || p[3] == 'l')) {
-            // 检查FAIL后面是否是结束符或空白字符
-            if (p + 4 >= end || p[4] == '\r' || p[4] == '\n' || p[4] == '\0' || 
-                p[4] == ' ' || p[4] == '\t') {
-                *success = false;
-                return true;
-            }
+    // 检查URC模式
+    for (int i = 0; urc_patterns[i].pattern != NULL; i++) {
+        uint32_t pattern_len = urc_patterns[i].pattern_len;
+        if (pos + pattern_len <= length && 
+            strncmp((char*)&data[pos], urc_patterns[i].pattern, pattern_len) == 0) {
+            return true;
         }
     }
-    
-    // 如果都不匹配,输出详细的调试信息
-    char debug_buf[64] = {0};
-    size_t debug_len = (end - p < sizeof(debug_buf) - 1) ? (end - p) : (sizeof(debug_buf) - 1);
-    memcpy(debug_buf, p, debug_len);
-    PPP_DEBUG_ERROR("TCPSETUP URC parse error: invalid result (expected OK/FAIL), found: [%.*s] (len=%u)", 
-                   (int)debug_len, debug_buf, (unsigned int)(end - p));
     return false;
 }
 
 /**
- * @brief 将单个HEX字符转换为数值
- * @param c HEX字符('0'-'9', 'A'-'F', 'a'-'f')
- * @param value 输出的数值
- * @return true=成功,false=失败
+ * @brief 查找AT响应结束位置
  */
-static bool n706a_hex_char_to_value(char c, uint8_t *value)
-{
-    if (c >= '0' && c <= '9') {
-        *value = c - '0';
-        return true;
-    } else if (c >= 'A' && c <= 'F') {
-        *value = c - 'A' + 10;
-        return true;
-    } else if (c >= 'a' && c <= 'f') {
-        *value = c - 'a' + 10;
-        return true;
+static bool find_at_response_end(const uint8_t *data, uint32_t length, 
+                                 uint32_t start_pos, uint32_t *end_pos) {
+    // 查找 \r\nOK\r\n 或 \r\nERROR\r\n
+    for (uint32_t i = start_pos; i < length - 3; i++) {
+        if (data[i] == '\r' && data[i+1] == '\n') {
+            // 检查OK
+            if (i + 5 < length && data[i+2] == 'O' && data[i+3] == 'K' && 
+                data[i+4] == '\r' && data[i+5] == '\n') {
+                *end_pos = i + 6;
+                return true;
+            }
+            // 检查ERROR
+            if (i + 8 < length && strncmp((char*)&data[i+2], "ERROR", 5) == 0 &&
+                data[i+7] == '\r' && data[i+8] == '\n') {
+                *end_pos = i + 9;
+                return true;
+            }
+        }
     }
     return false;
 }
 
 /**
- * @brief 将二进制数据转换为HEX字符串
- * @param binary_data 二进制数据
- * @param binary_len 二进制数据长度
- * @param hex_str 输出的HEX字符串缓冲区
- * @param hex_buf_size HEX缓冲区大小(至少需要binary_len*2+1)
- * @return 转换后的HEX字符串长度(不含结束符),0表示失败
+ * @brief 处理完整的AT响应
  */
-static size_t n706a_binary_to_hex_string(const uint8_t *binary_data, size_t binary_len,
-                                         char *hex_str, size_t hex_buf_size)
-{
-    if (!binary_data || !hex_str || binary_len == 0) {
-        return 0;
-    }
+static void process_complete_at_response(const uint8_t *data, uint32_t length) {
+    PPP_DEBUG_DEBUG("[StreamParser] 处理完整AT响应: %u字节", length);
     
-    // 需要binary_len * 2字节(每个字节转换为2个HEX字符),再加1个结束符
-    if (hex_buf_size < binary_len * 2 + 1) {
-        PPP_DEBUG_ERROR("HEX encode: buffer too small (need %u, got %u)", 
-                       (unsigned int)(binary_len * 2 + 1), (unsigned int)hex_buf_size);
-        return 0;
+    // 写入环形缓冲区
+    osal_mutex_lock(&g_at_response_mutex);
+    
+    uint32_t available_space = (g_at_response_head >= g_at_response_tail) ? 
+                               (N706A_AT_RESPONSE_RING_BUFFER_SIZE - (g_at_response_head - g_at_response_tail) - 1) :
+                               (g_at_response_tail - g_at_response_head - 1);
+    
+    uint32_t to_write = (length <= available_space) ? length : available_space;
+    
+    for (uint32_t i = 0; i < to_write; i++) {
+        g_at_response_ring_buffer[g_at_response_head] = data[i];
+        g_at_response_head = (g_at_response_head + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
+        
+        if (g_at_response_head == g_at_response_tail) {
+            g_at_response_tail = (g_at_response_tail + 1) % N706A_AT_RESPONSE_RING_BUFFER_SIZE;
+            PPP_DEBUG_WARN("[StreamParser] AT response buffer overflow, dropping oldest byte");
+        }
     }
     
-    static const char hex_chars[] = "0123456789ABCDEF";
+    osal_mutex_unlock(&g_at_response_mutex);
     
-    for (size_t i = 0; i < binary_len; i++) {
-        hex_str[i * 2] = hex_chars[(binary_data[i] >> 4) & 0x0F];
-        hex_str[i * 2 + 1] = hex_chars[binary_data[i] & 0x0F];
+    // 通知等待线程
+    if (to_write > 0) {
+        osal_sem_up(&g_at_response_sem);
     }
     
-    hex_str[binary_len * 2] = '\0';
-    return binary_len * 2;
+    g_parser_ctx.at_response_count++;
 }
 
 /**
- * @brief 将HEX字符串转换为二进制数据
- * @param hex_str HEX字符串(如 "313233")
- * @param hex_len HEX字符串长度(字节数,必须是偶数)
- * @param binary_buf 输出的二进制缓冲区
- * @param binary_buf_size 二进制缓冲区大小
- * @return 转换后的二进制数据长度(字节数),0表示失败
+ * @brief 流式解析器主处理函数(原始设计 + 方案2修复)
+ * @param data 数据指针
+ * @param length 数据长度
  */
-static size_t n706a_hex_string_to_binary(const char *hex_str, size_t hex_len, 
-                                         uint8_t *binary_buf, size_t binary_buf_size)
+static void n706a_stream_parser_process_data(const uint8_t *data, uint32_t length)
 {
-    if (!hex_str || !binary_buf || hex_len == 0 || hex_len % 2 != 0) {
-        return 0;
+    if (!data || length == 0) {
+        return;
     }
     
-    size_t binary_len = hex_len / 2;
-    if (binary_len > binary_buf_size) {
-        PPP_DEBUG_ERROR("HEX decode: binary_len=%u exceeds buffer size=%u", 
-                       (unsigned int)binary_len, (unsigned int)binary_buf_size);
-        return 0;
-    }
+    PPP_DEBUG_DEBUG("[StreamParser] 处理数据: %u字节, 当前状态=%d", length, g_parser_ctx.state);
     
-    for (size_t i = 0; i < binary_len; i++) {
-        uint8_t high_nibble, low_nibble;
+    uint32_t pos = 0;
+    
+    while (pos < length) {
+        uint32_t consumed = 0;
         
-        if (!n706a_hex_char_to_value(hex_str[i * 2], &high_nibble) ||
-            !n706a_hex_char_to_value(hex_str[i * 2 + 1], &low_nibble)) {
-            PPP_DEBUG_ERROR("Invalid HEX character at position %u", (unsigned int)(i * 2));
-            return 0;
+        switch (g_parser_ctx.state) {
+            case AT_PARSER_STATE_IDLE: {
+                /* 空闲状态:扫描数据,寻找响应开始 */
+                uint8_t byte = data[pos];
+                urc_type_t urc_type;
+                const char *urc_pattern;
+                uint32_t urc_pattern_len;
+                
+                /* 尝试匹配URC标识符 */
+                if (n706a_stream_parser_match_urc_identifier(byte, &urc_type, &urc_pattern, &urc_pattern_len)) {
+                    /* 匹配到URC标识符 */
+                        g_parser_ctx.urc_type = urc_type;
+                    g_parser_ctx.urc_pattern = urc_pattern;
+                    g_parser_ctx.urc_pattern_len = urc_pattern_len;
+                    g_parser_ctx.state = AT_PARSER_STATE_URC_HEADER;
+                    g_parser_ctx.response_start_pos = pos;
+                    g_parser_ctx.state_transition_pending = true;  // 方案2:标记状态转换
+                    /* 不消费字符,让URC_HEADER状态处理 */
+                } else if (byte == 'A' && pos + 1 < length && data[pos + 1] == 'T') {
+                    /* 检测到AT响应开始(AT命令回显) */
+                    /* 改进:检查AT后面是否有+或空格,避免误判二进制数据 */
+                    bool is_valid_at = false;
+                    if (pos + 2 < length) {
+                        uint8_t next_byte = data[pos + 2];
+                        /* AT命令格式:AT+... 或 AT ... 或 AT\r\n */
+                        if (next_byte == '+' || next_byte == ' ' || next_byte == '\r' || next_byte == '\n') {
+                            is_valid_at = true;
+                        }
+                    } else if (pos + 2 == length) {
+                        /* 数据块末尾,可能是AT,等待更多数据 */
+                        is_valid_at = true;
+                    }
+                    
+                    if (is_valid_at) {
+                        PPP_DEBUG_INFO("[StreamParser] 检测到AT响应开始");
+                        g_parser_ctx.state = AT_PARSER_STATE_AT_RESPONSE;
+                        g_parser_ctx.response_start_pos = pos;
+                        g_parser_ctx.state_transition_pending = true;  // 方案2:标记状态转换
+                        /* 不消费字符,让AT_RESPONSE状态处理 */
+                    } else {
+                        /* 不是有效的AT命令,可能是二进制数据,跳过 */
+                        PPP_DEBUG_DEBUG("[StreamParser] 跳过疑似AT的二进制数据: 0x%02X 0x%02X 0x%02X", 
+                                       byte, data[pos + 1], (pos + 2 < length) ? data[pos + 2] : 0);
+                        consumed = 1;
+                    }
+                } else if (byte == '+' || byte == 'O' || byte == 'E') {
+                    /* 可能是AT响应(+参数、OK、ERROR) */
+                    /* 检查是否是URC(优先) */
+                    bool is_urc = false;
+                    for (int i = 0; urc_patterns[i].pattern != NULL; i++) {
+                        if (pos + urc_patterns[i].pattern_len <= length &&
+                            strncmp((char*)&data[pos], urc_patterns[i].pattern, urc_patterns[i].pattern_len) == 0) {
+                            /* 是URC */
+                            g_parser_ctx.urc_type = urc_patterns[i].type;
+                            g_parser_ctx.urc_pattern = urc_patterns[i].pattern;
+                            g_parser_ctx.urc_pattern_len = urc_patterns[i].pattern_len;
+                            g_parser_ctx.state = AT_PARSER_STATE_URC_HEADER;
+                            g_parser_ctx.response_start_pos = pos;
+                            g_parser_ctx.state_transition_pending = true;  // 方案2:标记状态转换
+                            is_urc = true;
+                            break;
+                        }
+                    }
+                    if (!is_urc) {
+                        /* 不是URC,可能是AT响应 */
+                                    g_parser_ctx.state = AT_PARSER_STATE_AT_RESPONSE;
+                        g_parser_ctx.response_start_pos = pos;
+                        g_parser_ctx.state_transition_pending = true;  // 方案2:标记状态转换
+                                }
+                    /* 不消费字符,让相应状态处理 */
+                            } else {
+                    /* 跳过无关字符 */
+                    if (byte != '\r' && byte != '\n' && byte != '\0') {
+                        PPP_DEBUG_DEBUG("[StreamParser] 跳过未识别字符: 0x%02X", byte);
+                    }
+                            consumed = 1;
+                }
+                break;
+            }
+            
+            case AT_PARSER_STATE_AT_RESPONSE: {
+                /* AT响应处理 */
+                /* 方案2修复:状态转换标记应该在进入新状态后立即清除,而不是等到处理完成 */
+                bool was_transition_pending = g_parser_ctx.state_transition_pending;
+                g_parser_ctx.state_transition_pending = false;  // 立即清除标记,状态转换已完成
+                
+                n706a_stream_parser_process_at_response(data + pos, length - pos, &consumed);
+                
+                /* 如果处理函数返回consumed=0且这是状态转换后的第一次调用,这是正常的 */
+                if (consumed == 0 && was_transition_pending) {
+                    PPP_DEBUG_DEBUG("[StreamParser] AT_RESPONSE状态转换后首次调用,consumed=0(可能数据不完整)");
+                }
+                break;
+            }
+            
+            case AT_PARSER_STATE_URC_HEADER: {
+                /* URC头部处理 */
+                /* 方案2修复:状态转换标记应该在进入新状态后立即清除,而不是等到处理完成 */
+                bool was_transition_pending = g_parser_ctx.state_transition_pending;
+                g_parser_ctx.state_transition_pending = false;  // 立即清除标记,状态转换已完成
+                
+                uint32_t consumed_before = consumed;
+                n706a_stream_parser_process_urc_header(data + pos, length - pos, &consumed);
+                
+                /* 添加调试信息,帮助定位pos跳转问题 */
+                if (consumed > 100) {
+                    PPP_DEBUG_WARN("[StreamParser] URC_HEADER consumed异常大: %u (pos=%u, length=%u, urc_header_len=%u)", 
+                                  consumed, pos, length, g_parser_ctx.urc_header_len);
+                }
+                
+                /* 如果处理函数返回consumed=0且这是状态转换后的第一次调用,这是正常的 */
+                /* 但如果连续多次都是consumed=0,说明有问题 */
+                if (consumed == 0 && was_transition_pending) {
+                    /* 状态转换后的第一次调用返回consumed=0,这是正常的(比如数据不完整) */
+                    /* 但需要确保下次循环时能继续处理 */
+                    PPP_DEBUG_DEBUG("[StreamParser] URC_HEADER状态转换后首次调用,consumed=0(可能数据不完整)");
+                }
+                break;
+            }
+            
+            case AT_PARSER_STATE_URC_DATA: {
+                /* URC数据处理 */
+                /* 方案2修复:状态转换标记应该在进入新状态后立即清除,而不是等到处理完成 */
+                bool was_transition_pending = g_parser_ctx.state_transition_pending;
+                g_parser_ctx.state_transition_pending = false;  // 立即清除标记,状态转换已完成
+                
+                n706a_stream_parser_process_urc_data(data + pos, length - pos, &consumed);
+                
+                /* 如果处理函数返回consumed=0且这是状态转换后的第一次调用,这是正常的 */
+                if (consumed == 0 && was_transition_pending) {
+                    PPP_DEBUG_DEBUG("[StreamParser] URC_DATA状态转换后首次调用,consumed=0(可能数据不完整)");
+                }
+                break;
+            }
+                
+            default:
+                /* 未知状态,重置并跳过字符 */
+                PPP_DEBUG_ERROR("[StreamParser] 未知状态: %d,重置解析器", g_parser_ctx.state);
+                n706a_stream_parser_reset();
+                consumed = 1;
+                break;
         }
         
-        binary_buf[i] = (high_nibble << 4) | low_nibble;
+        /* 方案2:检测consumed=0的情况 */
+        /* 正常情况下,处理函数应该总是消费至少一些数据(如果length > 0) */
+        /* 如果consumed=0,说明处理函数认为需要等待更多数据,这是正常的 */
+        /* 但如果连续多次都是consumed=0且状态没变,可能是死循环 */
+        /* 优化:使用全局静态变量,避免在栈上分配 */
+        static uint32_t last_consumed_zero_pos = UINT32_MAX;
+        static at_parser_state_t last_consumed_zero_state = AT_PARSER_STATE_IDLE;
+        static uint32_t consumed_zero_count = 0;
+        
+        if (consumed == 0) {
+            if (pos == last_consumed_zero_pos && g_parser_ctx.state == last_consumed_zero_state) {
+                /* 同一位置同一状态连续出现consumed=0 */
+                consumed_zero_count++;
+                if (consumed_zero_count >= 10) {
+                    /* 连续10次都是consumed=0且状态没变,这是死循环 */
+                    /* 重置状态,让解析器重新开始扫描 */
+                    PPP_DEBUG_ERROR("[StreamParser] 死循环检测:pos=%u, state=%d, 连续%u次consumed=0,重置状态", 
+                                  pos, g_parser_ctx.state, consumed_zero_count);
+                    consumed_zero_count = 0;  /* 重置计数 */
+                    /* 重置状态,让解析器重新开始扫描 */
+                    n706a_stream_parser_reset();
+                    /* 不修改consumed,让IDLE状态重新处理当前字符 */
+                    /* 这样不会丢失数据,只是重新开始解析 */
+                } else {
+                    PPP_DEBUG_DEBUG("[StreamParser] consumed=0,pos=%u, state=%d (第%u次,等待更多数据)", 
+                                  pos, g_parser_ctx.state, consumed_zero_count);
+                }
+            } else {
+                /* 位置或状态发生变化,重置计数 */
+                last_consumed_zero_pos = pos;
+                last_consumed_zero_state = g_parser_ctx.state;
+                consumed_zero_count = 1;
+                PPP_DEBUG_DEBUG("[StreamParser] consumed=0,pos=%u, state=%d (可能是数据不完整,等待更多数据)", 
+                              pos, g_parser_ctx.state);
+            }
+        } else {
+            /* consumed > 0,重置计数 */
+            last_consumed_zero_pos = UINT32_MAX;
+            last_consumed_zero_state = AT_PARSER_STATE_IDLE;
+            consumed_zero_count = 0;
+        }
+        
+        pos += consumed;
+        g_parser_ctx.total_bytes_processed += consumed;
     }
     
-    return binary_len;
-}
-
-/**
- * @brief 注册TCP URC接收回调
- * @param callback 回调函数指针,NULL表示取消注册
- */
-void n706a_at_register_tcp_urc_callback(n706a_tcp_urc_callback_t callback)
-{
-    g_tcp_urc_callback = callback;
-    PPP_DEBUG_INFO("TCP URC callback %s", callback ? "registered" : "unregistered");
-}
-
-/**
- * @brief 注册+TCPSETUP URC回调
- * @param callback 回调函数指针,NULL表示取消注册
- */
-void n706a_at_register_tcpsetup_urc_callback(n706a_tcpsetup_urc_callback_t callback)
-{
-    g_tcpsetup_urc_callback = callback;
-    PPP_DEBUG_INFO("+TCPSETUP URC callback %s", callback ? "registered" : "unregistered");
-}
-
-/**
- * @brief 注册+TCPCLOSE URC回调
- */
-void n706a_at_register_tcpclose_urc_callback(n706a_tcpclose_urc_callback_t callback)
-{
-    g_tcpclose_urc_callback = callback;
-    PPP_DEBUG_INFO("+TCPCLOSE URC callback %s", callback ? "registered" : "unregistered");
-}
-
-/**
- * @brief 注册+TCPSEND URC回调
- */
-void n706a_at_register_tcpsend_urc_callback(n706a_tcpsend_urc_callback_t callback)
-{
-    g_tcpsend_urc_callback = callback;
-    PPP_DEBUG_INFO("+TCPSEND URC callback %s", callback ? "registered" : "unregistered");
-}
-
-/**
- * @brief 注册网络状态URC回调
- */
-void n706a_at_register_network_status_urc_callback(n706a_network_status_urc_callback_t callback)
-{
-    g_network_status_urc_callback = callback;
-    PPP_DEBUG_INFO("Network status URC callback %s", callback ? "registered" : "unregistered");
+    PPP_DEBUG_DEBUG("[StreamParser] 数据处理完成,最终状态=%d", g_parser_ctx.state);
 }
\ No newline at end of file
index 2e2cc4b..9cbc182 100644 (file)
 /**
  * @file n706a_http_client.c
- * @brief N706A HTTP/HTTPS客户端实现(基于TCP)
+ * @brief N706A HTTP/HTTPS客户端实现(基于HTTP专用AT指令)
  * 
- * 基于TCP手动构造HTTP协议,支持:
- * - HTTP (普通TCP)
- * - HTTPS (SSL TCP)
- * - 6个并发连接
- * - 所有标准HTTP方法
+ * 使用N706A模块的HTTP专用AT指令,与Python脚本实现逻辑一致:
+ * - HTTP: AT+HTTPPARA, AT+HTTPSETUP, AT+HTTPACTION
+ * - HTTPS: AT+HTTPSPARA, AT+HTTPSSETUP, AT+HTTPSACTION
+ * - 接收: +HTTPRECV: / +HTTPSRECV: URC
  */
 
+#define _GNU_SOURCE  /* 用于 strcasestr */
 #include "n706a_http_client.h"
-#include "n706a_tcp_client.h"
+#include "n706a_at_handler.h"
+#include "ppp_serial.h"
 #include "ppp_debug.h"
+#include "lock/osal_mutex.h"
+#include "semaphore/osal_semaphore.h"
+#include "soc_osal.h"
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 
+/* ========== CA证书链(通用HTTPS证书) ========== */
+/* 证书文件来源:leiniao_python_test/certs/ca_chain.pem */
+/* 包含完整的证书链:服务器证书 -> 中间CA -> 根CA */
+#if 0
+static const char N706A_ROOT_CA_CERT_NAME[] = "ca_chain.pem";
+static const char N706A_ROOT_CA_CERT_DATA[] =
+"-----BEGIN CERTIFICATE-----\n"
+"MIIF4jCCA8qgAwIBAgIRANVuJGyU7WOrsUbvwZa2T7AwDQYJKoZIhvcNAQEMBQAw\n"
+"gYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtK\n"
+"ZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYD\n"
+"VQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTIw\n"
+"MDEwODAwMDAwMFoXDTMwMDEwNzIzNTk1OVowXDELMAkGA1UEBhMCQ04xGjAYBgNV\n"
+"BAoTEVdvVHJ1cyBDQSBMaW1pdGVkMTEwLwYDVQQDDChXb1RydXMgRFYgU2VydmVy\n"
+"IENBICBbUnVuIGJ5IHRoZSBJc3N1ZXJdMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n"
+"MIIBCgKCAQEA7IE0rmVTVdRdNOzK1jsR/VppyukZ/XbQgakJHOhg6XGDsiHe/l5B\n"
+"3PxyXw18jEdN+7YxP0qsGz+HlQbsQh6XlwIyjpz/2gFMiqa7y1v+dHOgj6xNOF5a\n"
+"oaPm/Qhb0N+JYQidgaC+1Zp6W+YeC736rzCMr9vL1Usa3QLzRoQEo0DzbG4sPeP1\n"
+"US0Ia/i8o6szArH+DAcvrzCZ2kkpTScQ9QfOsvkMBP1W2otICdKUyZHaBc+ztTAd\n"
+"ovSlOR+GPf29dYfGQkZAp0tffIRw/na3WB86WGZPpNFfo2QxxsHYoL3oSWKfSWTY\n"
+"FgW22J8eA03TFHYowm/NqYuJ7GW553HppQIDAQABo4IBcDCCAWwwHwYDVR0jBBgw\n"
+"FoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFJmbLfaL8KPbidSe++V0\n"
+"L2jSkE/kMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1Ud\n"
+"JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAiBgNVHSAEGzAZMA0GCysGAQQBsjEB\n"
+"AgIWMAgGBmeBDAECATBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLnVzZXJ0\n"
+"cnVzdC5jb20vVVNFUlRydXN0UlNBQ2VydGlmaWNhdGlvbkF1dGhvcml0eS5jcmww\n"
+"cQYIKwYBBQUHAQEEZTBjMDoGCCsGAQUFBzAChi5odHRwOi8vY3J0LnVzZXJ0cnVz\n"
+"dC5jb20vVVNFUlRydXN0UlNBQUFBQ0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8v\n"
+"b2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBDAUAA4ICAQB5t8v3uYzHa4EL\n"
+"0rOb9g/YAmptUbILcBMKk1x188ucsGVPaG1DG9bpVamxbmCtFA1MlrA7iUC8SGop\n"
+"KBnuWFsNKiC7jCbRoahT1/FSwFsSuDlDmOjr1MqDXE+or08UkXsJB57XxXxdVOPl\n"
+"DcZHII4qHi1XKK4iurMqb+kbdpAWadyfidRRCGPopYCVYLLYhRJgpFGtfr6Gk8N0\n"
+"j81jq/7QbN0dRSDzMNdadKTc7c3+i9fIrXj79lV5Wvva+OL7nh8MxQhG1Ekek7Rv\n"
+"en++jSZvaEhCrMsSedFTA/aIy7oJg85tfglF2ybK61HsobjYzdDNICKJlIm4chlA\n"
+"XIDDqw2mw0Kz2snrkp9dpvMBqahF/Uy1kHzPcrq1/w5OqZWAuDKxZ68PuZ/ME2hI\n"
+"YbIDG9dWT6Y7eqtjQ2TmAQbOqdAG2LeikPMl2DMrPEa4lcKJzsFbHfHAW3hVgPSQ\n"
+"hRfS4TtbNnxijbsp8GguMHxP2R7dpAAYybwfZdXP7WYAnwEr1mzIf0Y3J0m7GDyX\n"
+"JhaflN3G2wIm2HzRd39NvnDRmFEraqui/YYO9ym0pwq1d0S+bGG6876QCto0u3Cg\n"
+"ItFh3Za2ZeIY+g5mWrejSaDs9LT7eu44iCyebfgekdMRqFeCuGAsJdsun3LOHHJo\n"
+"tCVPRjyFg9NDeJeMa4Z8QuXAXLd9cw==\n"
+"-----END CERTIFICATE-----\n"
+"-----BEGIN CERTIFICATE-----\n"
+"MIIFgTCCBGmgAwIBAgIQOXJEOvkit1HX02wQ3TE1lTANBgkqhkiG9w0BAQwFADB7\n"
+"MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD\n"
+"VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE\n"
+"AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4\n"
+"MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5\n"
+"MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO\n"
+"ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0\n"
+"aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgBJlFzYOw9sI\n"
+"s9CsVw127c0n00ytUINh4qogTQktZAnczomfzD2p7PbPwdzx07HWezcoEStH2jnG\n"
+"vDoZtF+mvX2do2NCtnbyqTsrkfjib9DsFiCQCT7i6HTJGLSR1GJk23+jBvGIGGqQ\n"
+"Ijy8/hPwhxR79uQfjtTkUcYRZ0YIUcuGFFQ/vDP+fmyc/xadGL1RjjWmp2bIcmfb\n"
+"IWax1Jt4A8BQOujM8Ny8nkz+rwWWNR9XWrf/zvk9tyy29lTdyOcSOk2uTIq3XJq0\n"
+"tyA9yn8iNK5+O2hmAUTnAU5GU5szYPeUvlM3kHND8zLDU+/bqv50TmnHa4xgk97E\n"
+"xwzf4TKuzJM7UXiVZ4vuPVb+DNBpDxsP8yUmazNt925H+nND5X4OpWaxKXwyhGNV\n"
+"icQNwZNUMBkTrNN9N6frXTpsNVzbQdcS2qlJC9/YgIoJk2KOtWbPJYjNhLixP6Q5\n"
+"D9kCnusSTJV882sFqV4Wg8y4Z+LoE53MW4LTTLPtW//e5XOsIzstAL81VXQJSdhJ\n"
+"WBp/kjbmUZIO8yZ9HE0XvMnsQybQv0FfQKlERPSZ51eHnlAfV1SoPv10Yy+xUGUJ\n"
+"5lhCLkMaTLTwJUdZ+gQek9QmRkpQgbLevni3/GcV4clXhB4PY9bpYrrWX1Uu6lzG\n"
+"KAgEJTm4Diup8kyXHAc/DVL17e8vgg8CAwEAAaOB8jCB7zAfBgNVHSMEGDAWgBSg\n"
+"EQojPpbxB+zirynvgqV/0DCktDAdBgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rID\n"
+"ZsswDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAG\n"
+"BgRVHSAAMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29t\n"
+"L0FBQUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggr\n"
+"BgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUA\n"
+"A4IBAQAYh1HcdCE9nIrgJ7cz0C7M7PDmy14R3iJvm3WOnnL+5Nb+qh+cli3vA0p+\n"
+"rvSNb3I8QzvAP+u431yqqcau8vzY7qN7Q/aGNnwU4M309z/+3ri0ivCRlv79Q2R+\n"
+"/czSAaF9ffgZGclCKxO/WIu6pKJmBHaIkU4MiRTOok3JMrO66BQavHHxW/BBC5gA\n"
+"CiIDEOUMsfnNkjcZ7Tvx5Dq2+UUTJnWvu6rvP3t3O9LEApE9GQDTF1w52z97GA1F\n"
+"zZOFli9d31kWTz9RvdVFGD/tSo7oBmF0Ixa1DVBzJ0RHfxBdiSprhTEUxOipakyA\n"
+"vGp4z7h/jnZymQyd/teRCBaho1+V\n"
+"-----END CERTIFICATE-----\n"
+"-----BEGIN CERTIFICATE-----\n"
+"MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb\n"
+"MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow\n"
+"GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj\n"
+"YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL\n"
+"MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n"
+"BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM\n"
+"GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP\n"
+"ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua\n"
+"BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n"
+"3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4\n"
+"YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR\n"
+"rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm\n"
+"ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU\n"
+"oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\n"
+"MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v\n"
+"QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t\n"
+"b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF\n"
+"AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\n"
+"GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\n"
+"Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2\n"
+"G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi\n"
+"l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3\n"
+"smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n"
+"-----END CERTIFICATE-----\n";
+#else
+static const char N706A_ROOT_CA_CERT_NAME[] = "root_ca.pem";
+static const char N706A_ROOT_CA_CERT_DATA[] =
+"-----BEGIN CERTIFICATE-----\n"
+"MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb\n"
+"MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow\n"
+"GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj\n"
+"YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL\n"
+"MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n"
+"BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM\n"
+"GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP\n"
+"ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua\n"
+"BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n"
+"3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4\n"
+"YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR\n"
+"rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm\n"
+"ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU\n"
+"oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\n"
+"MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v\n"
+"QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t\n"
+"b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF\n"
+"AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\n"
+"GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\n"
+"Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2\n"
+"G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi\n"
+"l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3\n"
+"smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n"
+"-----END CERTIFICATE-----\n";
+
+#endif
+
+
+
+#define N706A_ROOT_CA_CERT_LEN (sizeof(N706A_ROOT_CA_CERT_DATA) - 1)
+
+/* 证书初始化标志(开机执行一次后设置为true,后续不再执行) */
+static bool g_cert_initialized = false;
+
 /* Base64编码表(用于Basic Auth) */
 static const char base64_table[] = 
     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-/* ========== 私有函数声明 ========== */
-
-static int http_build_request(n706a_http_client_t *client,
-                              const char *method,
-                              const char *path,
-                              const char *body,
-                              size_t body_len,
-                              const char *content_type,
-                              char *request_buf,
-                              size_t buf_size);
+/* ========== 全局HTTP客户端实例(HTTP专用AT指令只支持单连接) ========== */
+static n706a_http_client_t *g_http_client = NULL;
+static osal_mutex g_http_client_mutex;
+static bool g_http_client_mutex_initialized = false;
 
-static n706a_httpc_result_t http_parse_response(const char *response,
-                                                size_t response_len,
-                                                n706a_http_client_t *client,
-                                                n706a_http_client_data_t *data);
-
-static void http_base64_encode(const char *input, size_t len, char *output);
+/* ========== 私有函数声明 ========== */
 
 static int http_parse_url(const char *url,
                           char *protocol,
@@ -44,50 +172,494 @@ static int http_parse_url(const char *url,
                           uint16_t *port,
                           char *path);
 
+static n706a_httpc_result_t http_receive_response(n706a_http_client_t *client,
+                                                  n706a_http_client_data_t *client_data,
+                                                  uint32_t timeout_ms);
+
+/**
+ * @brief 流式接收HTTP响应数据(公开接口,用于在发送请求后继续接收后续数据块)
+ */
+n706a_httpc_result_t n706a_http_client_recv_response_stream(n706a_http_client_t *client,
+                                                            n706a_http_client_data_t *client_data,
+                                                            uint32_t timeout_ms)
+{
+    if (!client || !client_data) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->stream_mode_enabled) {
+        PPP_DEBUG_ERROR("[HTTP] Stream mode not enabled");
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->response_mutex || !client->response_sem) {
+        PPP_DEBUG_ERROR("[HTTP] Response buffer not initialized");
+        return N706A_HTTP_EARG;
+    }
+    
+    osal_mutex *mutex = (osal_mutex *)client->response_mutex;
+    osal_semaphore *sem = (osal_semaphore *)client->response_sem;
+    
+    /* 等待后续的URC数据到达 */
+    int ret = osal_sem_down_timeout(sem, timeout_ms);
+    
+    if (ret != OSAL_SUCCESS) {
+        /* 超时:检查是否已经接收完成 */
+        osal_mutex_lock(mutex);
+        bool has_more = false;
+        if (client->stream_content_length > 0) {
+            has_more = (client->stream_body_received < (size_t)client->stream_content_length);
+        } else if (client->stream_is_chunked) {
+            has_more = true;  /* Chunked编码,假设还有更多数据 */
+        }
+        client_data->isMore = has_more;
+        osal_mutex_unlock(mutex);
+        
+        if (has_more) {
+            return N706A_HTTP_EAGAIN;  /* 还有更多数据,但超时了 */
+        } else {
+            return N706A_HTTP_SUCCESS;  /* 接收完成 */
+        }
+    }
+    
+    /* URC数据到达,更新状态 */
+    osal_mutex_lock(mutex);
+    bool has_more = false;
+    if (client->stream_content_length > 0) {
+        has_more = (client->stream_body_received < (size_t)client->stream_content_length);
+    } else if (client->stream_is_chunked) {
+        has_more = true;  /* Chunked编码,假设还有更多数据 */
+    }
+    client_data->isMore = has_more;
+    client_data->responseContentLen = client->stream_content_length;
+    osal_mutex_unlock(mutex);
+    
+    return has_more ? N706A_HTTP_EAGAIN : N706A_HTTP_SUCCESS;
+}
+
 /* ========== 公共函数实现 ========== */
 
 /**
  * @brief 初始化HTTP客户端
  * @param client HTTP客户端结构体指针
- * @param socket_id socket ID (0-5),如果传入-1则自动分配空闲的socket_id
  * @return N706A_HTTP_SUCCESS 成功,其他值失败
+ * @note HTTP专用AT指令不占用TCP socket,无需socket_id
  */
-n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client, int socket_id)
+n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client)
 {
     if (!client) {
         return N706A_HTTP_EARG;
     }
     
-    memset(client, 0, sizeof(n706a_http_client_t));
-    
-    /* 如果socket_id为-1,自动分配空闲的socket_id(避免与WebSocket冲突) */
-    if (socket_id == -1) {
-        socket_id = n706a_tcp_find_free_socket_id();
-        if (socket_id < 0 || socket_id > 5) {
-            PPP_DEBUG_ERROR("[HTTP] No free socket ID available for HTTP client");
-            return N706A_HTTP_ENOBUFS;
+    /* 初始化互斥锁(首次调用时) */
+    if (!g_http_client_mutex_initialized) {
+        if (osal_mutex_init(&g_http_client_mutex) != OSAL_SUCCESS) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to init mutex");
+            return N706A_HTTP_ERROR;
+        }
+        g_http_client_mutex_initialized = true;
+    }
+    
+    osal_mutex_lock(&g_http_client_mutex);
+    
+    /* HTTP专用AT指令只支持单连接,检查是否已有客户端在使用 */
+    if (g_http_client != NULL && g_http_client != client) {
+        /* 检查旧的客户端是否仍然有效(已连接或正在使用) */
+        /* 注意:如果 g_http_client 指向的客户端已经被 free(),访问其字段是未定义行为
+         * 但是,由于我们使用互斥锁保护,且客户端通常不会被外部 free(),
+         * 这里假设 g_http_client 指向的客户端仍然有效
+         * 
+         * 但是,为了安全起见,我们首先检查客户端是否已被完全清理(所有关键字段为NULL)
+         * 如果已被完全清理,说明客户端已被关闭但 g_http_client 指针未清空,直接清空指针
+         */
+        bool is_active = false;
+        n706a_http_client_t *old_client = g_http_client;  /* 保存指针,避免在检查过程中被修改 */
+        
+        /* 首先检查客户端是否已被完全清理(所有关键字段为NULL)
+         * 如果已被完全清理,说明客户端已被关闭但 g_http_client 指针未清空,直接清空指针
+         * 注意:这里访问 old_client 的字段是安全的,因为:
+         * 1. 如果 old_client 已被 free(),但 g_http_client 仍指向它,这是 bug,但我们可以通过检查字段来检测
+         * 2. 如果 old_client 未被 free(),访问字段是安全的
+         * 3. 如果 old_client 已被部分清理(字段为NULL),访问字段也是安全的(只是读取NULL值)
+         */
+        if (old_client->response_mutex == NULL && old_client->response_sem == NULL && 
+            old_client->response_buffer == NULL) {
+            /* 客户端已被完全清理,但 g_http_client 指针未清空,直接清空指针 */
+            PPP_DEBUG_WARN("[HTTP] Found stale HTTP client pointer (all resources NULL), clearing...");
+            g_http_client = NULL;
+            /* 注意:此时 old_client 可能指向已释放的内存,不能再访问其字段 */
+        } else if (old_client->connected) {
+            /* 客户端仍然连接,说明正在使用中 */
+            is_active = true;
+            PPP_DEBUG_WARN("[HTTP] Another HTTP client is already active (connected=true)");
+        } else if (old_client->response_buffer != NULL) {
+            /* 检查 response_buffer 是否真的在使用中(通过检查 response_mutex 是否存在) */
+            if (old_client->response_mutex != NULL) {
+                /* response_buffer 和 response_mutex 都存在,说明客户端正在使用中 */
+                is_active = true;
+                PPP_DEBUG_WARN("[HTTP] Another HTTP client is already active (response_buffer=%p, response_mutex=%p)", 
+                              old_client->response_buffer, old_client->response_mutex);
+            } else {
+                /* response_buffer 存在但 response_mutex 不存在,说明客户端已被部分清理,可以安全清理 */
+                PPP_DEBUG_WARN("[HTTP] Found stale HTTP client (response_buffer=%p but response_mutex=NULL), cleaning up...", 
+                              old_client->response_buffer);
+            }
+        }
+        
+        if (is_active) {
+            osal_mutex_unlock(&g_http_client_mutex);
+            return N706A_HTTP_ECONN;
+        }
+        
+        /* 旧的客户端已经关闭但未清理,清理它 */
+        /* 注意:此时 g_http_client 可能已被设置为 NULL(第281行),所以需要检查 */
+        if (g_http_client != NULL && g_http_client == old_client) {
+            PPP_DEBUG_WARN("[HTTP] Found stale HTTP client (connected=%d, response_buffer=%p), cleaning up...", 
+                          old_client->connected, old_client->response_buffer);
+            
+            /* 清理旧的客户端资源 */
+            if (old_client->response_buffer) {
+                free(old_client->response_buffer);
+                old_client->response_buffer = NULL;
+            }
+            if (old_client->response_mutex) {
+                osal_mutex *mutex = (osal_mutex *)old_client->response_mutex;
+                osal_mutex_destroy(mutex);
+                free(mutex);
+                old_client->response_mutex = NULL;
+            }
+            if (old_client->response_sem) {
+                osal_semaphore *sem = (osal_semaphore *)old_client->response_sem;
+                osal_sem_destroy(sem);
+                free(sem);
+                old_client->response_sem = NULL;
+            }
+            /* 清理其他资源 */
+            if (old_client->header) {
+                free(old_client->header);
+                old_client->header = NULL;
+            }
+            if (old_client->auth_user) {
+                free(old_client->auth_user);
+                old_client->auth_user = NULL;
+            }
+            if (old_client->auth_password) {
+                free(old_client->auth_password);
+                old_client->auth_password = NULL;
+            }
+            /* host 和 url_path 是数组,不需要释放,只需清零 */
+            memset(old_client->host, 0, sizeof(old_client->host));
+            memset(old_client->url_path, 0, sizeof(old_client->url_path));
+            
+            /* 清除全局引用(必须在清理资源之后) */
+            g_http_client = NULL;
+            PPP_DEBUG_INFO("[HTTP] Stale HTTP client cleaned up");
         }
-        PPP_DEBUG_INFO("[HTTP] Auto-allocated socket ID: %d", socket_id);
-    } else if (socket_id < 0 || socket_id > 5) {
-        return N706A_HTTP_EARG;
     }
     
-    client->socket_id = socket_id;
+    memset(client, 0, sizeof(n706a_http_client_t));
+    
     client->is_https = false;
     client->remote_port = 0;
     client->response_code = 0;
     client->connected = false;
     client->keepalive = false;
-    client->timeout_ms = 10000;  // 默认10秒
+    client->timeout_ms = 60000;  // 默认60秒(HTTP响应可能需要更长时间)
+    
+    /* 初始化响应缓冲区(默认8KB) */
+    client->response_buffer_size = 8192;
+    client->response_buffer = (uint8_t *)malloc(client->response_buffer_size);
+    if (!client->response_buffer) {
+        osal_mutex_unlock(&g_http_client_mutex);
+        return N706A_HTTP_ENOMEM;
+    }
+    
+    client->response_received = 0;
+    client->response_complete = false;
+    
+    /* 初始化流式接收相关字段 */
+    client->stream_callback = NULL;
+    client->stream_user_data = NULL;
+    client->stream_mode_enabled = false;
+    client->stream_body_received = 0;
+    client->stream_content_length = 0;
+    client->stream_header_parsed = false;
+    client->stream_connection_closed = false;
+    client->stream_is_chunked = false;
+    
+    /* 初始化响应缓冲区互斥锁和信号量 */
+    osal_mutex *mutex = (osal_mutex *)malloc(sizeof(osal_mutex));
+    osal_semaphore *sem = (osal_semaphore *)malloc(sizeof(osal_semaphore));
+    if (!mutex || !sem) {
+        free(client->response_buffer);
+        if (mutex) free(mutex);
+        if (sem) free(sem);
+        osal_mutex_unlock(&g_http_client_mutex);
+        return N706A_HTTP_ENOMEM;
+    }
+    
+    /* 清零结构体,确保mutex->mutex和sem->sem字段为0(LiteOS要求) */
+    memset(mutex, 0, sizeof(osal_mutex));
+    memset(sem, 0, sizeof(osal_semaphore));
+    
+    if (osal_mutex_init(mutex) != OSAL_SUCCESS || 
+        osal_sem_init(sem, 0) != OSAL_SUCCESS) {
+        free(client->response_buffer);
+        free(mutex);
+        free(sem);
+        osal_mutex_unlock(&g_http_client_mutex);
+        return N706A_HTTP_ERROR;
+    }
+    
+    client->response_mutex = mutex;
+    client->response_sem = sem;
+    
+    /* 注册为全局客户端 */
+    g_http_client = client;
+    
+    osal_mutex_unlock(&g_http_client_mutex);
+    
+    PPP_DEBUG_INFO("[HTTP] HTTP client initialized (using HTTP AT commands)");
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 设置HTTP/HTTPS请求URL(自动判断协议类型)
+ * @param client HTTP客户端结构指针
+ * @param url 完整URL(如"http://example.com/path"或"https://example.com/path")
+ * @param keepalive 是否启用长连接,0=关闭(默认),1=启用
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_set_url(n706a_http_client_t *client,
+                                               const char *url,
+                                               int keepalive)
+{
+    if (!client || !url) {
+        return N706A_HTTP_EARG;
+    }
+    
+    /* 动态分配大缓冲区,避免栈溢出 */
+    char *protocol = (char *)malloc(8);
+    char *host = (char *)malloc(256);
+    char *path = (char *)malloc(512);
+    char *url_param = (char *)malloc(768);
+    char *at_cmd = (char *)malloc(1024);
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    
+    if (!protocol || !host || !path || !url_param || !at_cmd || !response) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to allocate memory for URL parsing");
+        if (protocol) free(protocol);
+        if (host) free(host);
+        if (path) free(path);
+        if (url_param) free(url_param);
+        if (at_cmd) free(at_cmd);
+        if (response) free(response);
+        return N706A_HTTP_ENOMEM;
+    }
+    
+    memset(response, 0, sizeof(n706a_at_response_t));
+    
+    uint16_t port;
+    
+    /* 解析URL */
+    if (http_parse_url(url, protocol, host, &port, path) != 0) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to parse URL: %s", url);
+        free(protocol);
+        free(host);
+        free(path);
+        free(url_param);
+        free(at_cmd);
+        free(response);
+        return N706A_HTTP_EPARSE;
+    }
+    
+    /* 判断协议类型 */
+    client->is_https = (strcmp(protocol, "https") == 0);
+    strncpy(client->host, host, sizeof(client->host) - 1);
+    client->remote_port = port;
+    strncpy(client->url_path, path, sizeof(client->url_path) - 1);
+    client->keepalive = (keepalive != 0);
+    
+    PPP_DEBUG_INFO("[HTTP] Setting URL: %s (protocol=%s, host=%s, port=%d, path=%s)", 
+                   url, protocol, host, port, path);
+    
+    /* 构建URL参数(host+path) */
+    snprintf(url_param, 768, "%s%s", host, path);
+    
+    n706a_at_error_t ret;
+    
+    if (client->is_https) {
+        /* HTTPS: 使用AT+HTTPSPARA */
+        snprintf(at_cmd, 1024, "AT+HTTPSPARA=url,\"%s\"", url_param);
+        ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to set HTTPS URL: %s", response->response);
+            free(protocol);
+            free(host);
+            free(path);
+            free(url_param);
+            free(at_cmd);
+            free(response);
+            return N706A_HTTP_EPARSE;
+        }
+        
+        snprintf(at_cmd, 1024, "AT+HTTPSPARA=port,%d", port);
+        ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to set HTTPS port: %s", response->response);
+            free(protocol);
+            free(host);
+            free(path);
+            free(url_param);
+            free(at_cmd);
+            free(response);
+            return N706A_HTTP_EPARSE;
+        }
+        
+        if (keepalive >= 0 && keepalive <= 1) {
+            snprintf(at_cmd, 1024, "AT+HTTPSPARA=keepalive,%d", keepalive);
+            ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+            if (ret != N706A_AT_OK) {
+                PPP_DEBUG_WARN("[HTTP] Failed to set HTTPS keepalive: %s", response->response);
+            }
+        }
+    } else {
+        /* HTTP: 使用AT+HTTPPARA */
+        snprintf(at_cmd, 1024, "AT+HTTPPARA=url,\"%s\"", url_param);
+        ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to set HTTP URL: %s", response->response);
+            free(protocol);
+            free(host);
+            free(path);
+            free(url_param);
+            free(at_cmd);
+            free(response);
+            return N706A_HTTP_EPARSE;
+        }
+        
+        snprintf(at_cmd, 1024, "AT+HTTPPARA=port,%d", port);
+        ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to set HTTP port: %s", response->response);
+            free(protocol);
+            free(host);
+            free(path);
+            free(url_param);
+            free(at_cmd);
+            free(response);
+            return N706A_HTTP_EPARSE;
+        }
+        
+        if (keepalive >= 0 && keepalive <= 1) {
+            snprintf(at_cmd, 1024, "AT+HTTPPARA=keepalive,%d", keepalive);
+            ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+            if (ret != N706A_AT_OK) {
+                PPP_DEBUG_WARN("[HTTP] Failed to set HTTP keepalive: %s", response->response);
+            }
+        }
+    }
+    
+    /* 释放所有动态分配的内存 */
+    free(protocol);
+    free(host);
+    free(path);
+    free(url_param);
+    free(at_cmd);
+    free(response);
+    
+    PPP_DEBUG_INFO("[HTTP] URL set successfully");
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 建立HTTP/HTTPS连接(根据URL自动选择)
+ * @param client HTTP客户端结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_setup_connection(n706a_http_client_t *client)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    /* 如果是 HTTPS,自动配置 SSL 证书和参数 */
+    if (client->is_https) {
+        /* 自动初始化证书(如果尚未初始化) */
+        n706a_httpc_result_t cert_ret = n706a_http_client_init_certificate();
+        if (cert_ret != N706A_HTTP_SUCCESS) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to initialize certificate for HTTPS");
+            return cert_ret;
+        }
+        
+        /* 自动配置 HTTPS SSL 参数(默认:TLS1.2,单向认证) */
+        n706a_httpc_result_t https_ret = n706a_http_client_config_https(
+            3,  /* SSL版本:3=TLS1.2 */
+            1,  /* 认证模式:1=单向认证 */
+            NULL,  /* CA证书文件名(NULL表示使用默认证书) */
+            NULL,  /* 客户端证书(单向认证为NULL) */
+            NULL   /* 客户端密钥(单向认证为NULL) */
+        );
+        if (https_ret != N706A_HTTP_SUCCESS) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to configure HTTPS SSL parameters");
+            return https_ret;
+        }
+        
+        PPP_DEBUG_INFO("[HTTP] HTTPS SSL configured automatically (TLS1.2, one-way auth)");
+        
+        /* 等待配置生效(参考Python脚本:配置后可能需要短暂延迟) */
+        osal_msleep(100);
+    }
+    
+    /* 动态分配response结构体,避免栈溢出 */
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to allocate memory for response");
+        return N706A_HTTP_ENOMEM;
+    }
+    memset(response, 0, sizeof(n706a_at_response_t));
+    
+    n706a_at_error_t ret;
     
-    /* 初始化TCP socket */
-    n706a_tcp_socket_create(&client->tcp_sock, socket_id);
+    if (client->is_https) {
+        ret = n706a_at_send_command("AT+HTTPSSETUP", "OK", 15000, response);
+        if (ret != N706A_AT_OK || strstr(response->response, "ERROR") != NULL) {
+            PPP_DEBUG_ERROR("[HTTP] HTTPS connection setup failed: %s", response->response);
+            free(response);
+            return N706A_HTTP_ECONN;
+        }
+        
+        /* 检查响应中是否包含 +HTTPSSETUP: AUTHFAIL URC */
+        if (strstr(response->response, "+HTTPSSETUP:") != NULL && 
+            strstr(response->response, "AUTHFAIL") != NULL) {
+            PPP_DEBUG_ERROR("[HTTP] HTTPS certificate authentication failed (AUTHFAIL)");
+            PPP_DEBUG_ERROR("[HTTP] Possible causes:");
+            PPP_DEBUG_ERROR("[HTTP]   1. Certificate is not the correct root CA certificate");
+            PPP_DEBUG_ERROR("[HTTP]   2. Server certificate is not signed by this root CA");
+            PPP_DEBUG_ERROR("[HTTP]   3. Certificate chain is incomplete");
+            PPP_DEBUG_ERROR("[HTTP]   4. Certificate format is incorrect");
+            free(response);
+            return N706A_HTTP_ECONN;
+        }
+    } else {
+        ret = n706a_at_send_command("AT+HTTPSETUP", "OK", 15000, response);
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] HTTP connection setup failed: %s", response->response);
+            free(response);
+            return N706A_HTTP_ECONN;
+        }
+    }
+    
+    free(response);
+    client->connected = true;
+    PPP_DEBUG_INFO("[HTTP] Connection setup successfully (%s)", client->is_https ? "HTTPS" : "HTTP");
     
     return N706A_HTTP_SUCCESS;
 }
 
 /**
- * @brief 连接到HTTP/HTTPS服务器
+ * @brief 连接到HTTP/HTTPS服务器(保留兼容性,内部调用set_url和setup_connection)
  */
 n706a_httpc_result_t n706a_http_client_connect(n706a_http_client_t *client,
                                                const char *host,
@@ -103,23 +675,25 @@ n706a_httpc_result_t n706a_http_client_connect(n706a_http_client_t *client,
     client->remote_port = port;
     client->is_https = use_ssl;
     
-    /* 建立TCP连接 */
-    n706a_tcp_result_t ret = n706a_tcp_connect(&client->tcp_sock,
-                                               host,
-                                               port,
-                                               use_ssl,
-                                               30000);  // SSL握手需要更长时间
+    /* 构建URL并调用set_url和setup_connection */
+    char *url = (char *)malloc(512);
+    if (!url) {
+        return N706A_HTTP_ENOMEM;
+    }
+    
+    const char *scheme = use_ssl ? "https://" : "http://";
+    snprintf(url, 512, "%s%s:%d/", scheme, host, port);
     
-    if (ret == N706A_TCP_SUCCESS) {
-        client->connected = true;
-        return N706A_HTTP_SUCCESS;
-    } else if (ret == N706A_TCP_ETIMEDOUT) {
-        return N706A_HTTP_ETIMEOUT;
-    } else {
-        return N706A_HTTP_ECONN;
+    n706a_httpc_result_t ret = n706a_http_client_set_url(client, url, 0);
+    free(url);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return ret;
     }
+    
+    return n706a_http_client_setup_connection(client);
 }
 
+
 /**
  * @brief 发送HTTP GET请求
  */
@@ -127,7 +701,7 @@ n706a_httpc_result_t n706a_http_client_get(n706a_http_client_t *client,
                                            const char *path,
                                            n706a_http_client_data_t *client_data)
 {
-    if (!client || !path || !client_data) {
+    if (!client || !client_data) {
         return N706A_HTTP_EARG;
     }
     
@@ -135,59 +709,151 @@ n706a_httpc_result_t n706a_http_client_get(n706a_http_client_t *client,
         return N706A_HTTP_ECONN;
     }
     
-    /* 动态分配请求缓冲区 */
-    char *request_buf = (char *)malloc(2048);
+    /* 如果path不为NULL且与当前URL path不同,更新URL */
+    if (path != NULL && strcmp(client->url_path, path) != 0) {
+        char *url = (char *)malloc(768);
+        if (!url) {
+        return N706A_HTTP_ENOMEM;
+    }
+        const char *scheme = client->is_https ? "https://" : "http://";
+        snprintf(url, 768, "%s%s:%d%s", scheme, client->host, client->remote_port, path);
+        n706a_httpc_result_t ret = n706a_http_client_set_url(client, url, client->keepalive ? 1 : 0);
+        free(url);
+        if (ret != N706A_HTTP_SUCCESS) {
+            return ret;
+        }
+    }
+    
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
+        return N706A_HTTP_ENOMEM;
+    }
+    memset(response, 0, sizeof(n706a_at_response_t));
+    n706a_at_error_t ret;
+    
+    /* 如果有自定义headers,使用OPEN_MODE (mode=99) 发送自定义HTTP报文 */
+    if (client->header && strlen(client->header) > 0) {
+        /* 构建HTTP请求报文 */
+        char *request_buf = (char *)malloc(4096);
     if (!request_buf) {
+            free(response);
         return N706A_HTTP_ENOMEM;
     }
     
-    int request_len = http_build_request(client, "GET", path,
-                                        NULL, 0, NULL,
-                                        request_buf, 2048);
+        /* 使用path参数(如果为NULL则使用client->url_path) */
+        const char *request_path = (path != NULL) ? path : client->url_path;
     
-    if (request_len < 0) {
+        int request_len = snprintf(request_buf, 4096,
+                                   "GET %s HTTP/1.1\r\n"
+                                   "Host: %s\r\n"
+                                   "%s"
+                                   "Connection: close\r\n"
+                                   "\r\n",
+                                   request_path,
+                                   client->host,
+                                   client->header);
+        
+        if (request_len < 0 || request_len >= 4096) {
         free(request_buf);
+            free(response);
         return N706A_HTTP_ENOBUFS;
     }
     
-    /* 发送请求 */
-    size_t sent;
-    n706a_tcp_result_t ret = n706a_tcp_send(&client->tcp_sock,
-                                            (uint8_t*)request_buf,
-                                            request_len,
-                                            &sent);
-    
-    free(request_buf);
+        /* 检查请求长度是否超过AT指令限制 */
+        if (request_len > N706A_HTTP_MAX_REQUEST_LEN) {
+            free(request_buf);
+            free(response);
+            PPP_DEBUG_ERROR("[HTTP] GET request length (%d bytes) exceeds AT+HTTPACTION max (%d bytes)", 
+                          request_len, N706A_HTTP_MAX_REQUEST_LEN);
+            return N706A_HTTP_ENOBUFS;
+        }
+        
+        /* 调试:打印构建的HTTP请求(完整内容,使用ERROR级别确保输出) */
+        PPP_DEBUG_ERROR("[HTTP] ========== Built GET request (%d bytes) ==========", request_len);
+        if (request_len <= 1024) {
+            /* 如果请求小于1KB,完整打印 */
+            PPP_DEBUG_ERROR("[HTTP] Request content:\n%.*s", request_len, request_buf);
+        } else {
+            /* 如果请求大于1KB,打印前512字节和后128字节 */
+            PPP_DEBUG_ERROR("[HTTP] Request content (first 512 bytes):\n%.512s", request_buf);
+            PPP_DEBUG_ERROR("[HTTP] Request content (last 128 bytes):\n%.128s", request_buf + request_len - 128);
+        }
+        PPP_DEBUG_ERROR("[HTTP] ========== End of GET request ==========");
     
-    if (ret != N706A_TCP_SUCCESS || sent != (size_t)request_len) {
-        return N706A_HTTP_ESEND;
-    }
+        /* 发送AT+HTTPSACTION=99,<length> 或 AT+HTTPACTION=99,<length> */
+        char at_cmd[64];
+        if (client->is_https) {
+            snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSACTION=99,%d", request_len);
+        } else {
+            snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPACTION=99,%d", request_len);
+        }
+        
+        ret = n706a_at_send_command(at_cmd, NULL, 5000, response);
+        if (ret != N706A_AT_OK || !response->success || strstr((const char *)response->response, "ERROR") != NULL) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to send GET request (mode=99): ret=%d, success=%d, response=%s", 
+                          ret, response ? response->success : 0, response ? response->response : "NULL");
+            free(request_buf);
+            free(response);
+            return N706A_HTTP_ESEND;
+        }
     
-    /* 动态分配响应缓冲区 */
-    uint8_t *response_buf = (uint8_t *)malloc(8192);
-    if (!response_buf) {
-        return N706A_HTTP_ENOMEM;
+        /* 检查响应中是否包含 '>' 提示符 */
+        if (strstr((const char *)response->response, ">") == NULL) {
+            free(request_buf);
+            free(response);
+            PPP_DEBUG_ERROR("[HTTP] Response does not contain '>' prompt: %s", response->response);
+            return N706A_HTTP_ESEND;
+        }
+        
+        free(response);
+        
+        /* 延迟75ms(根据AT命令手册要求) */
+        osal_msleep(75);
+        
+        /* 发送HTTP请求报文(ASCII文本,不是HEX编码)
+         * 注意:AT+HTTPSACTION=99/AT+HTTPACTION=99期望接收ASCII文本,不是HEX编码!
+         * Python脚本使用 serial_conn.write(http_request.encode('utf-8')) 直接发送原始字节
+         * 而 n706a_at_send_raw_data 会将数据转换为HEX字符串,这是错误的!
+         * 因此这里使用 ppp_serial_write 发送原始数据(它会自动处理wakeup)
+         */
+        PPP_DEBUG_INFO("[HTTP] Sending HTTP request as ASCII text (%d bytes)...", request_len);
+        ppp_error_t write_result = ppp_serial_write((const uint8_t *)request_buf, request_len);
+        free(request_buf);
+        if (write_result != PPP_ERR_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to send GET request data via ppp_serial_write: error=%d", write_result);
+            return N706A_HTTP_ESEND;
+        }
+        PPP_DEBUG_INFO("[HTTP] HTTP request sent successfully (%d bytes as ASCII text)", request_len);
+    } else {
+        /* 没有自定义headers,使用标准GET模式 (mode=0) */
+        if (client->is_https) {
+            ret = n706a_at_send_command("AT+HTTPSACTION=0", "OK", 5000, response);
+        } else {
+            ret = n706a_at_send_command("AT+HTTPACTION=0", "OK", 5000, response);
+        }
+        
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to send GET request: %s", response->response);
+            free(response);
+            return N706A_HTTP_ESEND;
+        }
     }
     
-    size_t received;
-    ret = n706a_tcp_recv(&client->tcp_sock,
-                        response_buf,
-                        8192 - 1,
-                        &received,
-                        client->timeout_ms);
+    free(response);
+    PPP_DEBUG_INFO("[HTTP] GET request sent, waiting for response...");
     
-    if (ret != N706A_TCP_SUCCESS) {
-        free(response_buf);
-        return N706A_HTTP_ERECV;
+    /* 重置流式接收状态(如果启用了流式模式) */
+    if (client->stream_mode_enabled) {
+        client->stream_body_received = 0;
+        client->stream_content_length = 0;
+        client->stream_header_parsed = false;
+        client->stream_is_chunked = false;
+        client->stream_connection_closed = false;
+        client->response_received = 0;  /* 清空缓冲区,准备接收新的响应头 */
     }
     
-    response_buf[received] = '\0';
-    
-    /* 解析响应 */
-    n706a_httpc_result_t result = http_parse_response((char*)response_buf, received, client, client_data);
-    free(response_buf);
-    
-    return result;
+    /* 等待并接收响应(通过URC) */
+    return http_receive_response(client, client_data, client->timeout_ms);
 }
 
 /**
@@ -208,62 +874,132 @@ n706a_httpc_result_t n706a_http_client_post(n706a_http_client_t *client,
         return N706A_HTTP_ECONN;
     }
     
-    /* 动态分配请求缓冲区 */
+    /* 如果path与当前URL path不同,更新URL */
+    if (strcmp(client->url_path, path) != 0) {
+        char *url = (char *)malloc(768);
+        if (!url) {
+            return N706A_HTTP_ENOMEM;
+        }
+        const char *scheme = client->is_https ? "https://" : "http://";
+        snprintf(url, 768, "%s%s:%d%s", scheme, client->host, client->remote_port, path);
+        n706a_httpc_result_t ret = n706a_http_client_set_url(client, url, client->keepalive ? 1 : 0);
+        free(url);
+        if (ret != N706A_HTTP_SUCCESS) {
+            return ret;
+        }
+    }
+    
+    /* POST请求:使用OPEN_MODE (mode=99) 发送自定义HTTP报文 */
+    /* 构建HTTP请求报文 */
     char *request_buf = (char *)malloc(4096);
     if (!request_buf) {
         return N706A_HTTP_ENOMEM;
     }
     
-    int request_len = http_build_request(client, "POST", path,
-                                        body, body_len, content_type,
-                                        request_buf, 4096);
+    int request_len = snprintf(request_buf, 4096,
+                               "POST %s HTTP/1.1\r\n"
+                               "Host: %s\r\n"
+                               "Content-Type: %s\r\n"
+                               "Content-Length: %zu\r\n"
+                               "Connection: close\r\n"
+                               "\r\n",
+                               path,
+                               client->host,
+                               content_type ? content_type : "application/octet-stream",
+                               body_len);
+    
+    if (request_len < 0 || request_len >= 4096) {
+        free(request_buf);
+        return N706A_HTTP_ENOBUFS;
+    }
+    
+    /* 追加请求体 */
+    if (body && body_len > 0) {
+        if (request_len + body_len < 4096) {
+            memcpy(request_buf + request_len, body, body_len);
+            request_len += body_len;
+        } else {
+            free(request_buf);
+            return N706A_HTTP_ENOBUFS;
+        }
+    }
     
-    if (request_len < 0) {
+    /* 检查请求长度是否超过AT指令限制 */
+    if (request_len > N706A_HTTP_MAX_REQUEST_LEN) {
         free(request_buf);
+        PPP_DEBUG_ERROR("[HTTP] POST request length (%d bytes) exceeds AT+HTTPACTION max (%d bytes)", 
+                      request_len, N706A_HTTP_MAX_REQUEST_LEN);
         return N706A_HTTP_ENOBUFS;
     }
     
-    /* 发送请求 */
-    size_t sent;
-    n706a_tcp_result_t ret = n706a_tcp_send(&client->tcp_sock,
-                                            (uint8_t*)request_buf,
-                                            request_len,
-                                            &sent);
+    /* 发送AT+HTTPACTION=99,<length> 或 AT+HTTPSACTION=99,<length> */
+    char at_cmd[64];
+    if (client->is_https) {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSACTION=99,%d", request_len);
+    } else {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPACTION=99,%d", request_len);
+    }
     
-    free(request_buf);
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
+        free(request_buf);
+        return N706A_HTTP_ENOMEM;
+    }
+    memset(response, 0, sizeof(n706a_at_response_t));
     
-    if (ret != N706A_TCP_SUCCESS) {
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 5000, response);
+    if (ret != N706A_AT_OK) {
+        free(request_buf);
+        free(response);
+        PPP_DEBUG_ERROR("[HTTP] Failed to send POST request command: %s", response->response);
         return N706A_HTTP_ESEND;
     }
     
-    /* 动态分配响应缓冲区 */
-    uint8_t *response_buf = (uint8_t *)malloc(8192);
-    if (!response_buf) {
-        return N706A_HTTP_ENOMEM;
+    /* 检查响应中是否包含 '>' 提示符 */
+    if (strstr((const char *)response->response, ">") == NULL) {
+        free(request_buf);
+        free(response);
+        PPP_DEBUG_ERROR("[HTTP] Response does not contain '>' prompt: %s", response->response);
+        return N706A_HTTP_ESEND;
     }
     
-    size_t received;
-    ret = n706a_tcp_recv(&client->tcp_sock,
-                        response_buf,
-                        8192 - 1,
-                        &received,
-                        client->timeout_ms);
+    free(response);
     
-    if (ret != N706A_TCP_SUCCESS) {
-        free(response_buf);
-        return N706A_HTTP_ERECV;
+    /* 延迟75ms(根据AT命令手册要求) */
+    osal_msleep(75);
+    
+    /* 发送HTTP请求报文(ASCII文本,不是HEX编码)
+     * 注意:AT+HTTPSACTION=99/AT+HTTPACTION=99期望接收ASCII文本,不是HEX编码!
+     * 因此这里使用 ppp_serial_write 发送原始数据(它会自动处理wakeup)
+     */
+    PPP_DEBUG_INFO("[HTTP] Sending POST request as ASCII text (%d bytes)...", request_len);
+    ppp_error_t write_result = ppp_serial_write((const uint8_t *)request_buf, request_len);
+    free(request_buf);
+    if (write_result != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to send POST request data via ppp_serial_write: error=%d", write_result);
+        return N706A_HTTP_ESEND;
     }
+    PPP_DEBUG_INFO("[HTTP] POST request sent successfully (%d bytes as ASCII text)", request_len);
     
-    response_buf[received] = '\0';
+    PPP_DEBUG_INFO("[HTTP] POST request sent, waiting for response...");
     
-    n706a_httpc_result_t result = http_parse_response((char*)response_buf, received, client, client_data);
-    free(response_buf);
+    /* 重置流式接收状态(如果启用了流式模式) */
+    if (client->stream_mode_enabled) {
+        client->stream_body_received = 0;
+        client->stream_content_length = 0;
+        client->stream_header_parsed = false;
+        client->stream_is_chunked = false;
+        client->stream_connection_closed = false;
+        client->response_received = 0;  /* 清空缓冲区,准备接收新的响应头 */
+    }
     
-    return result;
+    /* 等待并接收响应(通过URC) */
+    return http_receive_response(client, client_data, client->timeout_ms);
 }
 
 /**
  * @brief 发送HTTP PUT请求
+ * @note 使用HTTP专用AT指令时,PUT请求需要使用OPEN_MODE (mode=99) 发送自定义HTTP报文
  */
 n706a_httpc_result_t n706a_http_client_put(n706a_http_client_t *client,
                                            const char *path,
@@ -280,45 +1016,122 @@ n706a_httpc_result_t n706a_http_client_put(n706a_http_client_t *client,
         return N706A_HTTP_ECONN;
     }
     
-    /* 动态分配请求缓冲区 */
+    /* PUT请求与POST类似,使用OPEN_MODE (mode=99) 发送自定义HTTP报文 */
+    /* 如果path与当前URL path不同,更新URL */
+    if (strcmp(client->url_path, path) != 0) {
+        char *url = (char *)malloc(768);
+        if (!url) {
+            return N706A_HTTP_ENOMEM;
+        }
+        const char *scheme = client->is_https ? "https://" : "http://";
+        snprintf(url, 768, "%s%s:%d%s", scheme, client->host, client->remote_port, path);
+        n706a_httpc_result_t ret = n706a_http_client_set_url(client, url, client->keepalive ? 1 : 0);
+        free(url);
+        if (ret != N706A_HTTP_SUCCESS) {
+            return ret;
+        }
+    }
+    
+    /* 构建HTTP请求报文 */
     char *request_buf = (char *)malloc(4096);
     if (!request_buf) {
         return N706A_HTTP_ENOMEM;
     }
     
-    int request_len = http_build_request(client, "PUT", path,
-                                        body, body_len, content_type,
-                                        request_buf, 4096);
+    int request_len = snprintf(request_buf, 4096,
+                               "PUT %s HTTP/1.1\r\n"
+                               "Host: %s\r\n"
+                               "Content-Type: %s\r\n"
+                               "Content-Length: %zu\r\n"
+                               "Connection: close\r\n"
+                               "\r\n",
+                               path,
+                               client->host,
+                               content_type ? content_type : "application/octet-stream",
+                               body_len);
     
-    if (request_len < 0) {
+    if (request_len < 0 || request_len >= 4096) {
         free(request_buf);
         return N706A_HTTP_ENOBUFS;
     }
     
-    size_t sent;
-    n706a_tcp_send(&client->tcp_sock, (uint8_t*)request_buf, request_len, &sent);
-    free(request_buf);
+    /* 追加请求体 */
+    if (body && body_len > 0) {
+        if (request_len + body_len < 4096) {
+            memcpy(request_buf + request_len, body, body_len);
+            request_len += body_len;
+        } else {
+            free(request_buf);
+            return N706A_HTTP_ENOBUFS;
+        }
+    }
     
-    /* 动态分配响应缓冲区 */
-    uint8_t *response_buf = (uint8_t *)malloc(8192);
-    if (!response_buf) {
+    /* 检查请求长度是否超过AT指令限制 */
+    if (request_len > N706A_HTTP_MAX_REQUEST_LEN) {
+        free(request_buf);
+        PPP_DEBUG_ERROR("[HTTP] PUT request length (%d bytes) exceeds AT+HTTPACTION max (%d bytes)", 
+                      request_len, N706A_HTTP_MAX_REQUEST_LEN);
+        return N706A_HTTP_ENOBUFS;
+    }
+    
+    /* 发送AT+HTTPACTION=99,<length> 或 AT+HTTPSACTION=99,<length> */
+    char at_cmd[64];
+    if (client->is_https) {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSACTION=99,%d", request_len);
+    } else {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPACTION=99,%d", request_len);
+    }
+    
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
+        free(request_buf);
         return N706A_HTTP_ENOMEM;
     }
+    memset(response, 0, sizeof(n706a_at_response_t));
+    
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 5000, response);
+    if (ret != N706A_AT_OK) {
+        free(request_buf);
+        free(response);
+        PPP_DEBUG_ERROR("[HTTP] Failed to send PUT request command: %s", response->response);
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 检查响应中是否包含 '>' 提示符 */
+    if (strstr((const char *)response->response, ">") == NULL) {
+        free(request_buf);
+        free(response);
+        PPP_DEBUG_ERROR("[HTTP] Response does not contain '>' prompt: %s", response->response);
+        return N706A_HTTP_ESEND;
+    }
     
-    size_t received;
-    n706a_tcp_recv(&client->tcp_sock, response_buf, 8192 - 1,
-                   &received, client->timeout_ms);
+    free(response);
     
-    response_buf[received] = '\0';
+    /* 延迟75ms(根据AT命令手册要求) */
+    osal_msleep(75);
     
-    n706a_httpc_result_t result = http_parse_response((char*)response_buf, received, client, client_data);
-    free(response_buf);
+    /* 发送HTTP请求报文(ASCII文本,不是HEX编码)
+     * 注意:AT+HTTPSACTION=99/AT+HTTPACTION=99期望接收ASCII文本,不是HEX编码!
+     * 因此这里使用 ppp_serial_write 发送原始数据(它会自动处理wakeup)
+     */
+    PPP_DEBUG_INFO("[HTTP] Sending PUT request as ASCII text (%d bytes)...", request_len);
+    ppp_error_t write_result = ppp_serial_write((const uint8_t *)request_buf, request_len);
+    free(request_buf);
+    if (write_result != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to send PUT request data via ppp_serial_write: error=%d", write_result);
+        return N706A_HTTP_ESEND;
+    }
+    PPP_DEBUG_INFO("[HTTP] PUT request sent successfully (%d bytes as ASCII text)", request_len);
+    
+    PPP_DEBUG_INFO("[HTTP] PUT request sent, waiting for response...");
     
-    return result;
+    /* 等待并接收响应(通过URC) */
+    return http_receive_response(client, client_data, client->timeout_ms);
 }
 
 /**
  * @brief 发送HTTP DELETE请求
+ * @note 使用HTTP专用AT指令时,DELETE请求需要使用OPEN_MODE (mode=99) 发送自定义HTTP报文
  */
 n706a_httpc_result_t n706a_http_client_delete(n706a_http_client_t *client,
                                               const char *path,
@@ -332,45 +1145,107 @@ n706a_httpc_result_t n706a_http_client_delete(n706a_http_client_t *client,
         return N706A_HTTP_ECONN;
     }
     
-    /* 动态分配请求缓冲区 */
+    /* DELETE请求使用OPEN_MODE (mode=99) 发送自定义HTTP报文 */
+    /* 如果path与当前URL path不同,更新URL */
+    if (strcmp(client->url_path, path) != 0) {
+        char *url = (char *)malloc(768);
+        if (!url) {
+            return N706A_HTTP_ENOMEM;
+        }
+        const char *scheme = client->is_https ? "https://" : "http://";
+        snprintf(url, 768, "%s%s:%d%s", scheme, client->host, client->remote_port, path);
+        n706a_httpc_result_t ret = n706a_http_client_set_url(client, url, client->keepalive ? 1 : 0);
+        free(url);
+        if (ret != N706A_HTTP_SUCCESS) {
+            return ret;
+        }
+    }
+    
+    /* 构建HTTP请求报文 */
     char *request_buf = (char *)malloc(2048);
     if (!request_buf) {
         return N706A_HTTP_ENOMEM;
     }
     
-    int request_len = http_build_request(client, "DELETE", path,
-                                        NULL, 0, NULL,
-                                        request_buf, 2048);
+    int request_len = snprintf(request_buf, 2048,
+                               "DELETE %s HTTP/1.1\r\n"
+                               "Host: %s\r\n"
+                               "Connection: close\r\n"
+                               "\r\n",
+                               path,
+                               client->host);
     
-    if (request_len < 0) {
+    if (request_len < 0 || request_len >= 2048) {
         free(request_buf);
         return N706A_HTTP_ENOBUFS;
     }
     
-    size_t sent;
-    n706a_tcp_send(&client->tcp_sock, (uint8_t*)request_buf, request_len, &sent);
-    free(request_buf);
+    /* 检查请求长度是否超过AT指令限制 */
+    if (request_len > N706A_HTTP_MAX_REQUEST_LEN) {
+        free(request_buf);
+        PPP_DEBUG_ERROR("[HTTP] DELETE request length (%d bytes) exceeds AT+HTTPACTION max (%d bytes)", 
+                      request_len, N706A_HTTP_MAX_REQUEST_LEN);
+        return N706A_HTTP_ENOBUFS;
+    }
     
-    /* 动态分配响应缓冲区 */
-    uint8_t *response_buf = (uint8_t *)malloc(8192);
-    if (!response_buf) {
+    /* 发送AT+HTTPACTION=99,<length> 或 AT+HTTPSACTION=99,<length> */
+    char at_cmd[64];
+    if (client->is_https) {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSACTION=99,%d", request_len);
+    } else {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPACTION=99,%d", request_len);
+    }
+    
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
+        free(request_buf);
         return N706A_HTTP_ENOMEM;
     }
+    memset(response, 0, sizeof(n706a_at_response_t));
     
-    size_t received;
-    n706a_tcp_recv(&client->tcp_sock, response_buf, 8192 - 1,
-                   &received, client->timeout_ms);
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 5000, response);
+    if (ret != N706A_AT_OK) {
+        free(request_buf);
+        free(response);
+        PPP_DEBUG_ERROR("[HTTP] Failed to send DELETE request command: %s", response->response);
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 检查响应中是否包含 '>' 提示符 */
+    if (strstr((const char *)response->response, ">") == NULL) {
+        free(request_buf);
+        free(response);
+        PPP_DEBUG_ERROR("[HTTP] Response does not contain '>' prompt: %s", response->response);
+        return N706A_HTTP_ESEND;
+    }
     
-    response_buf[received] = '\0';
+    free(response);
     
-    n706a_httpc_result_t result = http_parse_response((char*)response_buf, received, client, client_data);
-    free(response_buf);
+    /* 延迟75ms(根据AT命令手册要求) */
+    osal_msleep(75);
     
-    return result;
+    /* 发送HTTP请求报文(ASCII文本,不是HEX编码)
+     * 注意:AT+HTTPSACTION=99/AT+HTTPACTION=99期望接收ASCII文本,不是HEX编码!
+     * 因此这里使用 ppp_serial_write 发送原始数据(它会自动处理wakeup)
+     */
+    PPP_DEBUG_INFO("[HTTP] Sending DELETE request as ASCII text (%d bytes)...", request_len);
+    ppp_error_t write_result = ppp_serial_write((const uint8_t *)request_buf, request_len);
+    free(request_buf);
+    if (write_result != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to send DELETE request data via ppp_serial_write: error=%d", write_result);
+        return N706A_HTTP_ESEND;
+    }
+    PPP_DEBUG_INFO("[HTTP] DELETE request sent successfully (%d bytes as ASCII text)", request_len);
+    
+    PPP_DEBUG_INFO("[HTTP] DELETE request sent, waiting for response...");
+    
+    /* 等待并接收响应(通过URC) */
+    return http_receive_response(client, client_data, client->timeout_ms);
 }
 
 /**
  * @brief 发送HTTP HEAD请求
+ * @note 使用HTTP专用AT指令时,HEAD请求需要使用OPEN_MODE (mode=99) 发送自定义HTTP报文
  */
 n706a_httpc_result_t n706a_http_client_head(n706a_http_client_t *client,
                                             const char *path,
@@ -384,41 +1259,398 @@ n706a_httpc_result_t n706a_http_client_head(n706a_http_client_t *client,
         return N706A_HTTP_ECONN;
     }
     
-    /* 动态分配请求缓冲区 */
+    /* HEAD请求使用OPEN_MODE (mode=99) 发送自定义HTTP报文 */
+    /* 如果path与当前URL path不同,更新URL */
+    if (strcmp(client->url_path, path) != 0) {
+        char *url = (char *)malloc(768);
+        if (!url) {
+            return N706A_HTTP_ENOMEM;
+        }
+        const char *scheme = client->is_https ? "https://" : "http://";
+        snprintf(url, 768, "%s%s:%d%s", scheme, client->host, client->remote_port, path);
+        n706a_httpc_result_t ret = n706a_http_client_set_url(client, url, client->keepalive ? 1 : 0);
+        free(url);
+        if (ret != N706A_HTTP_SUCCESS) {
+            return ret;
+        }
+    }
+    
+    /* 构建HTTP请求报文 */
     char *request_buf = (char *)malloc(2048);
     if (!request_buf) {
         return N706A_HTTP_ENOMEM;
     }
     
-    int request_len = http_build_request(client, "HEAD", path,
-                                        NULL, 0, NULL,
-                                        request_buf, 2048);
+    int request_len = snprintf(request_buf, 2048,
+                               "HEAD %s HTTP/1.1\r\n"
+                               "Host: %s\r\n"
+                               "Connection: close\r\n"
+                               "\r\n",
+                               path,
+                               client->host);
     
-    if (request_len < 0) {
+    if (request_len < 0 || request_len >= 2048) {
         free(request_buf);
         return N706A_HTTP_ENOBUFS;
     }
     
-    size_t sent;
-    n706a_tcp_send(&client->tcp_sock, (uint8_t*)request_buf, request_len, &sent);
+    /* 检查请求长度是否超过AT指令限制 */
+    if (request_len > N706A_HTTP_MAX_REQUEST_LEN) {
+        free(request_buf);
+        PPP_DEBUG_ERROR("[HTTP] HEAD request length (%d bytes) exceeds AT+HTTPACTION max (%d bytes)", 
+                      request_len, N706A_HTTP_MAX_REQUEST_LEN);
+        return N706A_HTTP_ENOBUFS;
+    }
+    
+    /* 发送AT+HTTPACTION=99,<length> 或 AT+HTTPSACTION=99,<length> */
+    char at_cmd[64];
+    if (client->is_https) {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSACTION=99,%d", request_len);
+    } else {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPACTION=99,%d", request_len);
+    }
+    
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
+        free(request_buf);
+        return N706A_HTTP_ENOMEM;
+    }
+    memset(response, 0, sizeof(n706a_at_response_t));
+    
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 5000, response);
+    if (ret != N706A_AT_OK) {
+        free(request_buf);
+        free(response);
+        PPP_DEBUG_ERROR("[HTTP] Failed to send HEAD request command: %s", response->response);
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 检查响应中是否包含 '>' 提示符 */
+    if (strstr((const char *)response->response, ">") == NULL) {
+        free(request_buf);
+        free(response);
+        PPP_DEBUG_ERROR("[HTTP] Response does not contain '>' prompt: %s", response->response);
+        return N706A_HTTP_ESEND;
+    }
+    
+    free(response);
+    
+    /* 延迟75ms(根据AT命令手册要求) */
+    osal_msleep(75);
+    
+    /* 发送HTTP请求报文(ASCII文本,不是HEX编码)
+     * 注意:AT+HTTPSACTION=99/AT+HTTPACTION=99期望接收ASCII文本,不是HEX编码!
+     * 因此这里使用 ppp_serial_write 发送原始数据(它会自动处理wakeup)
+     */
+    PPP_DEBUG_INFO("[HTTP] Sending HEAD request as ASCII text (%d bytes)...", request_len);
+    ppp_error_t write_result = ppp_serial_write((const uint8_t *)request_buf, request_len);
     free(request_buf);
+    if (write_result != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to send HEAD request data via ppp_serial_write: error=%d", write_result);
+        return N706A_HTTP_ESEND;
+    }
+    PPP_DEBUG_INFO("[HTTP] HEAD request sent successfully (%d bytes as ASCII text)", request_len);
+    
+    PPP_DEBUG_INFO("[HTTP] HEAD request sent, waiting for response...");
+    
+    /* 等待并接收响应(通过URC) */
+    return http_receive_response(client, client_data, client->timeout_ms);
+}
+
+/**
+ * @brief 添加SSL证书到N706A模块(仅HTTPS需要)
+ * @note 如果证书已存在,会先删除再重新添加,确保使用最新的证书内容
+ */
+n706a_httpc_result_t n706a_http_client_add_certificate(const char *cert_name,
+                                                       const uint8_t *cert_data,
+                                                       size_t cert_len)
+{
+    if (!cert_name || !cert_data || cert_len == 0) {
+        return N706A_HTTP_EARG;
+    }
+    
+    /* 发送AT+CERTADD命令 */
+    char at_cmd[128];
+    snprintf(at_cmd, sizeof(at_cmd), "AT+CERTADD=%s,%zu", cert_name, cert_len);
+    
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
+        return N706A_HTTP_ENOMEM;
+    }
+    memset(response, 0, sizeof(n706a_at_response_t));
+    
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, "CONNECT", 5000, response);
+    if (ret != N706A_AT_OK) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to send CERTADD command: %s", response->response);
+        free(response);
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 检查响应中是否包含 'CONNECT' 提示符 */
+    if (strstr((const char *)response->response, "CONNECT") == NULL) {
+        free(response);
+        PPP_DEBUG_ERROR("[HTTP] Response does not contain 'CONNECT' prompt");
+        return N706A_HTTP_ESEND;
+    }
+    
+    free(response);
+    
+    /* 延迟75ms(根据AT命令手册要求) */
+    osal_msleep(75);
+    
+    /* 发送证书数据(ASCII文本,PEM格式)
+     * 注意:AT+CERTADD期望接收ASCII文本,不是HEX编码!
+     * Python脚本使用 serial_conn.write(cert_data) 直接发送原始字节
+     * 而 n706a_at_send_raw_data 会将数据转换为HEX字符串,这是错误的!
+     * 因此这里使用 ppp_serial_write 发送原始数据(它会自动处理wakeup)
+     */
+    PPP_DEBUG_INFO("[HTTP] Sending certificate data as ASCII text (%u bytes)...", (unsigned int)cert_len);
+    
+    /* 使用 ppp_serial_write 发送原始证书数据(ASCII文本,PEM格式)
+     * 该函数会自动处理:
+     * 1. CAT1模块唤醒(低功耗模式下)
+     * 2. UART总线选择
+     * 3. 错误处理
+     */
+    ppp_error_t write_result = ppp_serial_write(cert_data, cert_len);
+    if (write_result != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to send certificate data via ppp_serial_write: error=%d", write_result);
+        return N706A_HTTP_ESEND;
+    }
+    
+    PPP_DEBUG_INFO("[HTTP] Certificate data sent successfully (%u bytes as ASCII text)", (unsigned int)cert_len);
+    
+    /* 等待证书添加完成(模块会返回+CERTADD: <len>,OK URC,但不需要主动检查)
+     * 给模块一些时间处理证书数据(通常500ms足够)
+     * 如果证书添加失败,后续HTTPS连接时会返回AUTHFAIL错误,那时再处理
+     */
+    osal_msleep(500);
+    
+    PPP_DEBUG_INFO("[HTTP] Certificate %s add completed (waiting for module to process)", cert_name);
+    
+    /* 注意:不进行证书验证,如果证书添加失败,后续HTTPS连接时会返回AUTHFAIL错误 */
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 检查证书是否已存在于设备中
+ */
+bool n706a_http_client_certificate_exists(const char *cert_name)
+{
+    if (!cert_name) {
+        return false;
+    }
+    
+    char at_cmd[64];
+    snprintf(at_cmd, sizeof(at_cmd), "AT+CERTCHECK=%s", cert_name);
+    
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
+        return false;
+    }
+    memset(response, 0, sizeof(n706a_at_response_t));
+    
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+    bool exists = (ret == N706A_AT_OK && 
+                   strstr(response->response, cert_name) != NULL && 
+                   strstr(response->response, "OK") != NULL);
+    
+    free(response);
+    return exists;
+}
+
+/**
+ * @brief 删除SSL证书
+ * @param cert_name 证书文件名
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_delete_certificate(const char *cert_name)
+{
+    if (!cert_name) {
+        return N706A_HTTP_EARG;
+    }
+    
+    char at_cmd[64];
+    snprintf(at_cmd, sizeof(at_cmd), "AT+CERTDEL=%s", cert_name);
     
-    /* 动态分配响应缓冲区 */
-    uint8_t *response_buf = (uint8_t *)malloc(8192);
-    if (!response_buf) {
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
         return N706A_HTTP_ENOMEM;
     }
+    memset(response, 0, sizeof(n706a_at_response_t));
+
     
-    size_t received;
-    n706a_tcp_recv(&client->tcp_sock, response_buf, 8192 - 1,
-                   &received, client->timeout_ms);
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+    if (ret != N706A_AT_OK) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to delete certificate: %s", response->response);
+        free(response);
+        return N706A_HTTP_ESEND;
+    }
     
-    response_buf[received] = '\0';
+    /* 检查响应中是否包含OK */
+    if (strstr(response->response, "OK") == NULL) {
+        PPP_DEBUG_WARN("[HTTP] Certificate delete response may be abnormal: %s", response->response);
+        /* 即使响应异常,也可能删除成功(如果证书不存在) */
+        if (strstr(response->response, "ERROR") != NULL) {
+            free(response);
+            return N706A_HTTP_ESEND;
+        }
+    }
     
-    n706a_httpc_result_t result = http_parse_response((char*)response_buf, received, client, client_data);
-    free(response_buf);
+    free(response);
+    PPP_DEBUG_INFO("[HTTP] Certificate %s deleted successfully", cert_name);
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 初始化HTTPS通用证书(开机执行一次,检查有证书就跳过,无证书就添加)
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 此函数只在开机第一次调用时执行,检查证书是否存在
+ *       如果证书已存在,跳过添加;如果不存在,添加证书
+ *       后续调用直接返回成功,不再执行任何操作
+ */
+n706a_httpc_result_t n706a_http_client_init_certificate()
+{
+    /* 如果已经初始化过,直接返回成功 */
+    if (g_cert_initialized) {
+        PPP_DEBUG_DEBUG("[HTTP] Certificate already initialized, skipping...");
+        return N706A_HTTP_SUCCESS;
+    }
+    
+    /* 开机第一次调用:检查证书是否存在 */
+    if (n706a_http_client_certificate_exists(N706A_ROOT_CA_CERT_NAME)) {
+        PPP_DEBUG_INFO("[HTTP] Certificate %s already exists, skipping add...", N706A_ROOT_CA_CERT_NAME);
+        g_cert_initialized = true;  /* 标记已初始化,后续不再执行 */
+        return N706A_HTTP_SUCCESS;
+    }
+    
+    /* 证书不存在,添加证书 */
+    PPP_DEBUG_INFO("[HTTP] Certificate %s not found, adding (size: %u bytes)...", 
+                   N706A_ROOT_CA_CERT_NAME, (unsigned int)N706A_ROOT_CA_CERT_LEN);
+    n706a_httpc_result_t ret = n706a_http_client_add_certificate(
+        N706A_ROOT_CA_CERT_NAME,
+        (const uint8_t *)N706A_ROOT_CA_CERT_DATA,
+        N706A_ROOT_CA_CERT_LEN
+    );
+    
+    if (ret == N706A_HTTP_SUCCESS) {
+        PPP_DEBUG_INFO("[HTTP] Certificate %s added successfully", N706A_ROOT_CA_CERT_NAME);
+        g_cert_initialized = true;  /* 标记已初始化,后续不再执行 */
+    } else {
+        PPP_DEBUG_ERROR("[HTTP] Failed to add certificate %s", N706A_ROOT_CA_CERT_NAME);
+        /* 即使添加失败,也标记为已初始化,避免重复尝试 */
+        g_cert_initialized = true;
+    }
     
-    return result;
+    return ret;
+}
+
+/**
+ * @brief 配置HTTPS SSL参数(仅HTTPS需要)
+ * @param ssl_version SSL版本,0=SSL3.0, 1=TLS1.0, 2=TLS1.1, 3=TLS1.2, 4=TLS1.0/1.1/1.2, 5=TLS1.3
+ * @param auth_mode 认证模式,0=不认证, 1=单向认证, 2=双向认证
+ * @param ca_cert CA证书文件名(单向/双向认证需要,如果为NULL则使用默认证书)
+ * @param client_cert 客户端证书文件名(双向认证需要,可为NULL)
+ * @param client_key 客户端密钥文件名(双向认证需要,可为NULL)
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 使用AT+HTTPSCFG命令配置SSL参数
+ *       如果ca_cert为NULL且auth_mode=1,则使用默认的root_ca.pem证书
+ */
+n706a_httpc_result_t n706a_http_client_config_https(int ssl_version,
+                                                    int auth_mode,
+                                                    const char *ca_cert,
+                                                    const char *client_cert,
+                                                    const char *client_key)
+{
+    char at_cmd[256];
+    n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+    if (!response) {
+        return N706A_HTTP_ENOMEM;
+    }
+    memset(response, 0, sizeof(n706a_at_response_t));
+    
+    /* 设置SSL版本 */
+    snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSCFG=\"sslversion\",%d", ssl_version);
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+    if (ret != N706A_AT_OK) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to set SSL version: %s", response->response);
+        free(response);
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 设置认证模式 */
+    snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSCFG=\"authmode\",%d", auth_mode);
+    ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+    if (ret != N706A_AT_OK) {
+        PPP_DEBUG_ERROR("[HTTP] Failed to set auth mode: %s", response->response);
+        free(response);
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 单向认证:配置CA证书 */
+    if (auth_mode == 1) {
+        
+        /* 如果证书未初始化,尝试初始化(通常应该在开机时已初始化) */
+        if (!g_cert_initialized) {
+            n706a_httpc_result_t cert_init_ret = n706a_http_client_init_certificate();
+            if (cert_init_ret != N706A_HTTP_SUCCESS) {
+                PPP_DEBUG_WARN("[HTTP] Certificate not initialized, attempting to initialize now...");
+                /* 即使初始化失败,也继续配置,可能证书已存在 */
+            }
+        }
+        
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSCFG=\"cacert\",%s", N706A_ROOT_CA_CERT_NAME);
+        ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to set CA certificate: %s", response->response);
+            free(response);
+            return N706A_HTTP_ESEND;
+        }
+        PPP_DEBUG_INFO("[HTTP] CA certificate configured: %s", N706A_ROOT_CA_CERT_NAME);
+    }
+    
+    /* 双向认证:配置CA证书、客户端证书和密钥 */
+    if (auth_mode == 2) {
+        if (!ca_cert || !client_cert || !client_key) {
+            PPP_DEBUG_ERROR("[HTTP] CA certificate, client certificate and key required for auth_mode=2");
+            free(response);
+            return N706A_HTTP_EARG;
+        }
+        
+        /* 配置CA证书 */
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSCFG=\"cacert\",%s", ca_cert);
+        ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to set CA certificate: %s", response->response);
+            free(response);
+            return N706A_HTTP_ESEND;
+        }
+        
+        /* 配置客户端证书 */
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSCFG=\"clientcert\",%s", client_cert);
+        ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to set client certificate: %s", response->response);
+            free(response);
+            return N706A_HTTP_ESEND;
+        }
+        
+        /* 配置客户端密钥 */
+        snprintf(at_cmd, sizeof(at_cmd), "AT+HTTPSCFG=\"clientkey\",%s", client_key);
+        ret = n706a_at_send_command(at_cmd, "OK", 5000, response);
+        if (ret != N706A_AT_OK) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to set client key: %s", response->response);
+            free(response);
+            return N706A_HTTP_ESEND;
+        }
+        
+        PPP_DEBUG_INFO("[HTTP] Mutual authentication configured: CA=%s, ClientCert=%s, ClientKey=%s", 
+                      ca_cert, client_cert, client_key);
+    }
+    
+    free(response);
+    return N706A_HTTP_SUCCESS;
 }
 
 /**
@@ -501,8 +1733,20 @@ n706a_httpc_result_t n706a_http_client_close(n706a_http_client_t *client)
         return N706A_HTTP_EARG;
     }
     
+    osal_mutex_lock(&g_http_client_mutex);
+    
     if (client->connected) {
-        n706a_tcp_close(&client->tcp_sock);
+        /* 使用HTTP专用AT指令关闭连接 */
+        n706a_at_response_t *response = (n706a_at_response_t *)malloc(sizeof(n706a_at_response_t));
+        if (response) {
+            memset(response, 0, sizeof(n706a_at_response_t));
+            if (client->is_https) {
+                n706a_at_send_command("AT+HTTPSCLOSE", "OK", 5000, response);
+            } else {
+                n706a_at_send_command("AT+HTTPCLOSE", "OK", 5000, response);
+            }
+            free(response);
+        }
         client->connected = false;
     }
     
@@ -521,6 +1765,47 @@ n706a_httpc_result_t n706a_http_client_close(n706a_http_client_t *client)
         client->auth_password = NULL;
     }
     
+    /* host 和 url_path 是数组,不需要释放,只需清零 */
+    memset(client->host, 0, sizeof(client->host));
+    memset(client->url_path, 0, sizeof(client->url_path));
+    
+    /* 释放响应缓冲区资源 */
+    if (client->response_buffer) {
+        free(client->response_buffer);
+        client->response_buffer = NULL;
+    }
+    
+    if (client->response_mutex) {
+        osal_mutex *mutex = (osal_mutex *)client->response_mutex;
+        osal_mutex_destroy(mutex);
+        free(mutex);
+        client->response_mutex = NULL;
+    }
+    
+    if (client->response_sem) {
+        osal_semaphore *sem = (osal_semaphore *)client->response_sem;
+        osal_sem_destroy(sem);
+        free(sem);
+        client->response_sem = NULL;
+    }
+    
+    /* 清除全局客户端引用(如果当前客户端是全局客户端) */
+    if (g_http_client == client) {
+        g_http_client = NULL;
+        PPP_DEBUG_INFO("[HTTP] Global HTTP client cleared");
+    } else {
+        PPP_DEBUG_WARN("[HTTP] Closing client that is not the global client (g_http_client=%p, client=%p)", 
+                      g_http_client, client);
+    }
+    
+    /* 确保客户端状态被完全清理(防止后续检查时误判为活跃) */
+    client->connected = false;
+    client->response_buffer = NULL;
+    client->response_mutex = NULL;
+    client->response_sem = NULL;
+    
+    osal_mutex_unlock(&g_http_client_mutex);
+    
     return N706A_HTTP_SUCCESS;
 }
 
@@ -533,7 +1818,7 @@ bool n706a_http_client_is_connected(n706a_http_client_t *client)
         return false;
     }
     
-    return client->connected && n706a_tcp_is_connected(&client->tcp_sock);
+    return client->connected;
 }
 
 /**
@@ -548,6 +1833,33 @@ int n706a_http_client_get_response_code(n706a_http_client_t *client)
     return client->response_code;
 }
 
+/**
+ * @brief 设置HTTP流式接收回调
+ */
+n706a_httpc_result_t n706a_http_client_set_stream_callback(n706a_http_client_t *client,
+                                                           n706a_http_stream_callback_t callback,
+                                                           void *user_data)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    client->stream_callback = callback;
+    client->stream_user_data = user_data;
+    client->stream_mode_enabled = (callback != NULL);
+    
+    /* 重置流式接收状态 */
+    client->stream_body_received = 0;
+    client->stream_content_length = 0;
+    client->stream_header_parsed = false;
+    client->stream_is_chunked = false;
+    client->stream_connection_closed = false;
+    
+    PPP_DEBUG_INFO("[HTTP] Stream mode %s", client->stream_mode_enabled ? "enabled" : "disabled");
+    
+    return N706A_HTTP_SUCCESS;
+}
+
 /**
  * @brief 解析完整URL并发起GET请求
  */
@@ -559,23 +1871,19 @@ n706a_httpc_result_t n706a_http_client_get_url(n706a_http_client_t *client,
         return N706A_HTTP_EARG;
     }
     
-    char protocol[8];
-    char host[256];
-    char path[512];
-    uint16_t port;
-    
-    if (http_parse_url(url, protocol, host, &port, path) != 0) {
-        return N706A_HTTP_EPARSE;
+    /* 设置URL并建立连接 */
+    n706a_httpc_result_t ret = n706a_http_client_set_url(client, url, 0);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return ret;
     }
     
-    bool use_ssl = (strcmp(protocol, "https") == 0);
-    
-    n706a_httpc_result_t ret = n706a_http_client_connect(client, host, port, use_ssl);
+    ret = n706a_http_client_setup_connection(client);
     if (ret != N706A_HTTP_SUCCESS) {
         return ret;
     }
     
-    return n706a_http_client_get(client, path, client_data);
+    /* 发送GET请求 */
+    return n706a_http_client_get(client, client->url_path, client_data);
 }
 
 /**
@@ -592,284 +1900,894 @@ n706a_httpc_result_t n706a_http_client_post_url(n706a_http_client_t *client,
         return N706A_HTTP_EARG;
     }
     
-    char protocol[8];
-    char host[256];
-    char path[512];
-    uint16_t port;
-    
-    if (http_parse_url(url, protocol, host, &port, path) != 0) {
-        return N706A_HTTP_EPARSE;
+    /* 设置URL并建立连接 */
+    n706a_httpc_result_t ret = n706a_http_client_set_url(client, url, 0);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return ret;
     }
     
-    bool use_ssl = (strcmp(protocol, "https") == 0);
-    
-    n706a_httpc_result_t ret = n706a_http_client_connect(client, host, port, use_ssl);
+    ret = n706a_http_client_setup_connection(client);
     if (ret != N706A_HTTP_SUCCESS) {
         return ret;
     }
     
-    return n706a_http_client_post(client, path, content_type, body, body_len, client_data);
+    /* 发送POST请求 */
+    return n706a_http_client_post(client, client->url_path, content_type, body, body_len, client_data);
 }
 
 /* ========== 私有函数实现 ========== */
 
 /**
- * @brief 构造HTTP请求
+ * @brief 解析URL
  */
-static int http_build_request(n706a_http_client_t *client,
-                              const char *method,
-                              const char *path,
-                              const char *body,
-                              size_t body_len,
-                              const char *content_type,
-                              char *request_buf,
-                              size_t buf_size)
+static int http_parse_url(const char *url,
+                          char *protocol,
+                          char *host,
+                          uint16_t *port,
+                          char *path)
 {
-    int len = 0;
+    if (!url || !protocol || !host || !port || !path) {
+        return -1;
+    }
     
-    /* 请求行 */
-    len += snprintf(request_buf + len, buf_size - len,
-                   "%s %s HTTP/1.1\r\n", method, path);
+    const char *p = url;
     
-    /* Host头 */
-    len += snprintf(request_buf + len, buf_size - len,
-                   "Host: %s\r\n", client->host);
+    /* 解析协议 */
+    if (strncmp(p, "https://", 8) == 0) {
+        strcpy(protocol, "https");
+        *port = 443;
+        p += 8;
+    } else if (strncmp(p, "http://", 7) == 0) {
+        strcpy(protocol, "http");
+        *port = 80;
+        p += 7;
+    } else {
+        return -1;
+    }
     
-    /* User-Agent */
-    len += snprintf(request_buf + len, buf_size - len,
-                   "User-Agent: N706A-HTTP-Client/1.0\r\n");
+    /* 解析host和port */
+    const char *path_start = strchr(p, '/');
+    const char *port_start = strchr(p, ':');
     
-    /* Accept */
-    len += snprintf(request_buf + len, buf_size - len,
-                   "Accept: */*\r\n");
+    if (port_start && (!path_start || port_start < path_start)) {
+        /* 有端口号 */
+        size_t host_len = port_start - p;
+        strncpy(host, p, host_len);
+        host[host_len] = '\0';
+        
+        *port = atoi(port_start + 1);
+        
+        if (path_start) {
+            strcpy(path, path_start);
+        } else {
+            strcpy(path, "/");
+        }
+    } else {
+        /* 没有端口号 */
+        if (path_start) {
+            size_t host_len = path_start - p;
+            strncpy(host, p, host_len);
+            host[host_len] = '\0';
+            strcpy(path, path_start);
+        } else {
+            strcpy(host, p);
+            strcpy(path, "/");
+        }
+    }
     
-    /* Connection */
-    len += snprintf(request_buf + len, buf_size - len,
-                   "Connection: %s\r\n",
-                   client->keepalive ? "keep-alive" : "close");
+    return 0;
+}
+
+/**
+ * @brief 接收HTTP/HTTPS响应数据(通过URC)
+ * @param client HTTP客户端结构指针
+ * @param client_data 响应数据结构指针
+ * @param timeout_ms 超时时间(毫秒)
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+static n706a_httpc_result_t http_receive_response(n706a_http_client_t *client,
+                                                  n706a_http_client_data_t *client_data,
+                                                  uint32_t timeout_ms)
+{
+    if (!client || !client_data) {
+        return N706A_HTTP_EARG;
+    }
     
-    /* Content-Type和Content-Length(POST/PUT) */
-    if (body && body_len > 0) {
-        if (content_type) {
-            len += snprintf(request_buf + len, buf_size - len,
-                           "Content-Type: %s\r\n", content_type);
-        }
-        
-        len += snprintf(request_buf + len, buf_size - len,
-                       "Content-Length: %zu\r\n", body_len);
+    if (!client->response_mutex || !client->response_sem) {
+        PPP_DEBUG_ERROR("[HTTP] Response buffer not initialized");
+        return N706A_HTTP_EARG;
+    }
+    
+    osal_mutex *mutex = (osal_mutex *)client->response_mutex;
+    osal_semaphore *sem = (osal_semaphore *)client->response_sem;
+    
+    /* 清空响应缓冲区 */
+    osal_mutex_lock(mutex);
+    if (!client->stream_mode_enabled) {
+        /* 传统模式:清空缓冲区 */
+        client->response_received = 0;
+        client->response_complete = false;
+    }
+    /* 流式模式:不清空response_received,因为可能已经有部分响应头数据 */
+    osal_mutex_unlock(mutex);
+    
+    /* 等待HTTP响应URC(+HTTPRECV: 或 +HTTPSRECV:) */
+    /* 注意:URC会在n706a_at_handler中处理并写入response_buffer */
+    int ret = osal_sem_down_timeout(sem, timeout_ms);
+    
+    if (ret != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("[HTTP] Response receive timeout");
+        return N706A_HTTP_ETIMEOUT;
     }
     
-    /* Basic认证 */
-    if (client->auth_user && client->auth_password) {
-        char auth_str[256];
-        char auth_b64[512];
+    /* 流式模式:等待响应头解析完成,然后检查是否还有更多数据 */
+    if (client->stream_mode_enabled) {
+        osal_mutex_lock(mutex);
+        /* 等待响应头解析完成(最多等待1秒) */
+        uint32_t wait_count = 0;
+        while (!client->stream_header_parsed && wait_count < 100) {
+            osal_mutex_unlock(mutex);
+            osal_msleep(10);  /* 等待10ms */
+            wait_count++;
+            osal_mutex_lock(mutex);
+        }
         
-        snprintf(auth_str, sizeof(auth_str), "%s:%s",
-                client->auth_user, client->auth_password);
+        if (!client->stream_header_parsed) {
+            osal_mutex_unlock(mutex);
+            PPP_DEBUG_ERROR("[HTTP] Stream mode: Header parsing timeout");
+            return N706A_HTTP_ETIMEOUT;
+        }
         
-        http_base64_encode(auth_str, strlen(auth_str), auth_b64);
+        /* 设置响应码和Content-Length到client_data */
+        client_data->responseContentLen = client->stream_content_length;
+        client_data->isChunked = client->stream_is_chunked;
         
-        len += snprintf(request_buf + len, buf_size - len,
-                       "Authorization: Basic %s\r\n", auth_b64);
+        /* 检查是否还有更多数据需要接收 */
+        bool has_more = false;
+        if (client->stream_content_length > 0) {
+            /* 有Content-Length,检查是否接收完成 */
+            has_more = (client->stream_body_received < (size_t)client->stream_content_length);
+        } else if (client->stream_is_chunked) {
+            /* Chunked编码,假设还有更多数据(直到收到最后一个chunk) */
+            has_more = true;
+        } else {
+            /* 没有Content-Length且不是chunked,假设没有更多数据 */
+            has_more = false;
+        }
+        client_data->isMore = has_more;
+        
+        osal_mutex_unlock(mutex);
+        
+        PPP_DEBUG_INFO("[HTTP] Stream mode: Header parsed, status=%d, Content-Length=%d, body_received=%zu, isMore=%d", 
+                      client->response_code, client->stream_content_length, 
+                      client->stream_body_received, has_more ? 1 : 0);
+        
+        return N706A_HTTP_SUCCESS;
     }
     
-    /* 自定义请求头 */
-    if (client->header) {
-        len += snprintf(request_buf + len, buf_size - len,
-                       "%s", client->header);
-    }
+    /* 传统模式:读取响应数据 */
+    osal_mutex_lock(mutex);
     
-    /* 空行 */
-    len += snprintf(request_buf + len, buf_size - len, "\r\n");
+    if (client->response_received == 0) {
+        osal_mutex_unlock(mutex);
+        PPP_DEBUG_ERROR("[HTTP] No response data received");
+        return N706A_HTTP_ERECV;
+    }
     
-    /* 请求体 */
-    if (body && body_len > 0) {
-        if (len + body_len < buf_size) {
-            memcpy(request_buf + len, body, body_len);
-            len += body_len;
+    /* 调试:打印完整的response_buffer内容(前512字节) */
+    size_t debug_buf_len = (client->response_received < 512) ? client->response_received : 512;
+    char debug_buf[513] = {0};
+    for (size_t i = 0; i < debug_buf_len; i++) {
+        uint8_t c = client->response_buffer[i];
+        if (c >= 32 && c < 127) {
+            debug_buf[i] = c;
+        } else if (c == '\r') {
+            debug_buf[i] = '\\';
+            if (i + 1 < 512) debug_buf[++i] = 'r';
+        } else if (c == '\n') {
+            debug_buf[i] = '\\';
+            if (i + 1 < 512) debug_buf[++i] = 'n';
         } else {
-            return -1;  // 缓冲区不足
+            debug_buf[i] = '.';
         }
     }
-    
-    return len;
-}
-
-/**
- * @brief 解析HTTP响应
- */
-static n706a_httpc_result_t http_parse_response(const char *response,
-                                                size_t response_len,
-                                                n706a_http_client_t *client,
-                                                n706a_http_client_data_t *data)
-{
-    if (!response || !client || !data) {
-        return N706A_HTTP_EARG;
+    PPP_DEBUG_INFO("[HTTP] Full response_buffer (first %u/%u bytes): %s", 
+                   (unsigned int)debug_buf_len, 
+                   (unsigned int)client->response_received, 
+                   debug_buf);
+    
+    /* 解析HTTP响应 */
+    /* 查找HTTP响应头开始位置 */
+    const char *http_start = (const char *)client->response_buffer;
+    const char *http_header = strstr(http_start, "HTTP/");
+    if (!http_header) {
+        osal_mutex_unlock(mutex);
+        PPP_DEBUG_ERROR("[HTTP] Invalid HTTP response format - 'HTTP/' not found in buffer. "
+                      "Buffer content (first 128 bytes): %.128s", http_start);
+        return N706A_HTTP_EPROTO;
     }
     
-    /* 解析状态行 */
+    size_t http_header_offset = http_header - http_start;
+    PPP_DEBUG_INFO("[HTTP] Found 'HTTP/' at offset %u (skipped %u bytes before HTTP header)", 
+                   (unsigned int)http_header_offset, (unsigned int)http_header_offset);
+    
+    /* 解析状态码 */
     int major, minor, status_code;
-    if (sscanf(response, "HTTP/%d.%d %d", &major, &minor, &status_code) != 3) {
+    if (sscanf(http_header, "HTTP/%d.%d %d", &major, &minor, &status_code) != 3) {
+        osal_mutex_unlock(mutex);
+        PPP_DEBUG_ERROR("[HTTP] Failed to parse HTTP status line");
         return N706A_HTTP_EPROTO;
     }
     
     client->response_code = status_code;
     
-    /* æ\9f¥æ\89¾å¤´å°¾å\88\86ç\95\8c */
-    const char *header_end = strstr(response, "\r\n\r\n");
+    /* æ\9f¥æ\89¾å\93\8dåº\94头ç»\93æ\9d\9fä½\8dç½® */
+    const char *header_end = strstr(http_header, "\r\n\r\n");
     if (!header_end) {
+        osal_mutex_unlock(mutex);
+        PPP_DEBUG_ERROR("[HTTP] HTTP header not complete");
         return N706A_HTTP_EPROTO;
     }
     
     /* 提取响应头 */
-    size_t header_len = header_end - response;
-    if (data->headerBuf && data->headerBufLen > 0) {
-        size_t copy_len = (header_len < (size_t)data->headerBufLen - 1) ?
-                         header_len : (size_t)data->headerBufLen - 1;
-        memcpy(data->headerBuf, response, copy_len);
-        data->headerBuf[copy_len] = '\0';
+    size_t header_len = header_end - http_header;
+    if (client_data->headerBuf && client_data->headerBufLen > 0) {
+        size_t copy_len = (header_len < (size_t)client_data->headerBufLen - 1) ?
+                         header_len : (size_t)client_data->headerBufLen - 1;
+        memcpy(client_data->headerBuf, http_header, copy_len);
+        client_data->headerBuf[copy_len] = '\0';
+        client_data->headerBufLen = copy_len;
     }
     
     /* 提取Content-Length */
-    const char *content_len_str = strcasestr(response, "Content-Length:");
+    const char *content_len_str = strcasestr(http_header, "Content-Length:");
     if (content_len_str && content_len_str < header_end) {
-        data->responseContentLen = atoi(content_len_str + 15);
+        client_data->responseContentLen = atoi(content_len_str + 15);
+        PPP_DEBUG_INFO("[HTTP] Content-Length found: %d", client_data->responseContentLen);
     } else {
-        data->responseContentLen = 0;
+        client_data->responseContentLen = 0;
+        PPP_DEBUG_WARN("[HTTP] Content-Length not found in response header");
     }
     
     /* 检查是否chunked */
-    if (strcasestr(response, "Transfer-Encoding: chunked")) {
-        data->isChunked = true;
+    bool is_chunked = false;
+    if (strcasestr(http_header, "Transfer-Encoding: chunked")) {
+        client_data->isChunked = true;
+        is_chunked = true;
+        PPP_DEBUG_INFO("[HTTP] Response uses Transfer-Encoding: chunked");
     } else {
-        data->isChunked = false;
+        client_data->isChunked = false;
     }
     
     /* 提取响应体 */
     const char *body = header_end + 4;
-    size_t body_len = response_len - (body - response);
+    size_t body_offset = body - http_start;
+    size_t body_len = client->response_received - body_offset;
+    
+    PPP_DEBUG_INFO("[HTTP] Body extraction: body_offset=%u, body_len=%u, response_received=%u", 
+                   (unsigned int)body_offset, 
+                   (unsigned int)body_len, 
+                   (unsigned int)client->response_received);
+    
+    /* 调试:打印响应体原始内容(前256字节) */
+    size_t body_preview_len = (body_len < 256) ? body_len : 256;
+    char body_preview_buf[257] = {0};
+    for (size_t i = 0; i < body_preview_len; i++) {
+        if (body[i] >= 32 && body[i] < 127) {
+            body_preview_buf[i] = body[i];
+        } else if (body[i] == '\r') {
+            body_preview_buf[i] = '\\';
+            if (i + 1 < 256) body_preview_buf[++i] = 'r';
+        } else if (body[i] == '\n') {
+            body_preview_buf[i] = '\\';
+            if (i + 1 < 256) body_preview_buf[++i] = 'n';
+        } else {
+            body_preview_buf[i] = '.';
+        }
+    }
+    PPP_DEBUG_INFO("[HTTP] Body raw content (first %u bytes): %s", (unsigned int)body_preview_len, body_preview_buf);
+    
+    /* 如果使用chunked编码,需要解析chunked格式 */
+    const char *decoded_body = body;
+    size_t decoded_body_len = body_len;
     
-    if (data->responseBuf && data->responseBufLen > 0) {
-        size_t copy_len = (body_len < (size_t)data->responseBufLen - 1) ?
-                         body_len : (size_t)data->responseBufLen - 1;
-        memcpy(data->responseBuf, body, copy_len);
-        data->responseBuf[copy_len] = '\0';
-        data->contentBlockLen = copy_len;
+    if (is_chunked) {
+        /* 解析chunked编码:格式为 <chunk_size_hex>\r\n<chunk_data>\r\n<chunk_size_hex>\r\n<chunk_data>\r\n...0\r\n\r\n */
+        /* 需要跳过chunk size行,只提取chunk data */
+        char *decoded_buf = (char *)malloc(body_len + 1);
+        if (!decoded_buf) {
+            osal_mutex_unlock(mutex);
+            PPP_DEBUG_ERROR("[HTTP] Failed to allocate memory for chunked decoding");
+            return N706A_HTTP_ENOMEM;
+        }
+        
+        size_t decoded_pos = 0;
+        const char *p = body;
+        const char *end = body + body_len;
+        
+        while (p < end) {
+            /* 跳过空白行 */
+            while (p < end && (*p == '\r' || *p == '\n')) {
+                p++;
+            }
+            if (p >= end) break;
+            
+            /* 读取chunk size(十六进制) */
+            const char *chunk_size_start = p;
+            while (p < end && *p != '\r' && *p != '\n' && *p != ';') {
+                p++;
+            }
+            
+            if (p >= end) break;
+            
+            /* 解析chunk size */
+            char chunk_size_str[32] = {0};
+            size_t chunk_size_str_len = (p - chunk_size_start < sizeof(chunk_size_str) - 1) ? 
+                                       (p - chunk_size_start) : (sizeof(chunk_size_str) - 1);
+            memcpy(chunk_size_str, chunk_size_start, chunk_size_str_len);
+            
+            unsigned int chunk_size = 0;
+            if (sscanf(chunk_size_str, "%x", &chunk_size) != 1) {
+                PPP_DEBUG_WARN("[HTTP] Failed to parse chunk size: %s", chunk_size_str);
+                break;
+            }
+            
+            PPP_DEBUG_DEBUG("[HTTP] Parsed chunk size: %u (0x%x)", chunk_size, chunk_size);
+            
+            if (chunk_size == 0) {
+                /* 最后一个chunk,结束 */
+                PPP_DEBUG_INFO("[HTTP] Found final chunk (size=0), chunked decoding complete");
+                break;
+            }
+            
+            /* 跳过chunk size行(包括\r\n) */
+            while (p < end && (*p == '\r' || *p == '\n' || *p == ';')) {
+                p++;
+            }
+            if (p >= end) break;
+            
+            /* 复制chunk data */
+            size_t chunk_data_len = (p + chunk_size <= end) ? chunk_size : (end - p);
+            if (decoded_pos + chunk_data_len <= body_len) {
+                memcpy(decoded_buf + decoded_pos, p, chunk_data_len);
+                decoded_pos += chunk_data_len;
+                p += chunk_size;
+            } else {
+                PPP_DEBUG_WARN("[HTTP] Chunked decode buffer overflow: decoded_pos=%u, chunk_data_len=%u, body_len=%u", 
+                               (unsigned int)decoded_pos, (unsigned int)chunk_data_len, (unsigned int)body_len);
+                break;
+            }
+            
+            /* 跳过chunk data后的\r\n */
+            while (p < end && (*p == '\r' || *p == '\n')) {
+                p++;
+            }
+        }
+        
+        decoded_body = decoded_buf;
+        decoded_body_len = decoded_pos;
+        
+        PPP_DEBUG_INFO("[HTTP] Chunked decoding: original=%u bytes, decoded=%u bytes", 
+                       (unsigned int)body_len, (unsigned int)decoded_body_len);
+        
+        /* 如果Content-Length存在,使用它来限制响应体长度 */
+        if (client_data->responseContentLen > 0 && decoded_body_len > (size_t)client_data->responseContentLen) {
+            decoded_body_len = (size_t)client_data->responseContentLen;
+            PPP_DEBUG_INFO("[HTTP] Truncating decoded body_len to Content-Length=%d", 
+                           client_data->responseContentLen);
+        }
+        
+        /* 将解码后的数据复制到responseBuf */
+        if (client_data->responseBuf && client_data->responseBufLen > 0) {
+            size_t copy_len = (decoded_body_len < (size_t)client_data->responseBufLen - 1) ?
+                             decoded_body_len : (size_t)client_data->responseBufLen - 1;
+            memcpy(client_data->responseBuf, decoded_body, copy_len);
+            client_data->responseBuf[copy_len] = '\0';
+            client_data->responseBufLen = copy_len;
+            client_data->contentBlockLen = copy_len;
+            
+            /* 调试:打印最终提取的响应体内容(前256字节) */
+            size_t final_preview_len = (copy_len < 256) ? copy_len : 256;
+            char final_preview_buf[257] = {0};
+            for (size_t i = 0; i < final_preview_len; i++) {
+                if (client_data->responseBuf[i] >= 32 && client_data->responseBuf[i] < 127) {
+                    final_preview_buf[i] = client_data->responseBuf[i];
+                } else if (client_data->responseBuf[i] == '\r') {
+                    final_preview_buf[i] = '\\';
+                    if (i + 1 < 256) final_preview_buf[++i] = 'r';
+                } else if (client_data->responseBuf[i] == '\n') {
+                    final_preview_buf[i] = '\\';
+                    if (i + 1 < 256) final_preview_buf[++i] = 'n';
+                } else {
+                    final_preview_buf[i] = '.';
+                }
+            }
+            PPP_DEBUG_INFO("[HTTP] Final extracted body (first %u/%u bytes): %s", 
+                           (unsigned int)final_preview_len, 
+                           (unsigned int)copy_len, 
+                           final_preview_buf);
+        }
+        
+        free(decoded_buf);
+        
+        /* 检查是否还有更多数据 */
+        if (client_data->responseContentLen > 0 && decoded_body_len < (size_t)client_data->responseContentLen) {
+            client_data->isMore = true;
+        } else {
+            client_data->isMore = false;
+        }
+        
+        osal_mutex_unlock(mutex);
+        return N706A_HTTP_SUCCESS;
+    }
+    
+    /* 非chunked编码:如果Content-Length存在,使用它来限制响应体长度,避免包含额外的数据(如URC长度字段) */
+    size_t body_len_before_truncate = body_len;
+    if (client_data->responseContentLen > 0 && body_len > (size_t)client_data->responseContentLen) {
+        body_len = (size_t)client_data->responseContentLen;
+        PPP_DEBUG_INFO("[HTTP] Truncating body_len from %u to %u (Content-Length=%d)", 
+                       (unsigned int)body_len_before_truncate, 
+                       (unsigned int)body_len,
+                       client_data->responseContentLen);
+    } else if (client_data->responseContentLen > 0) {
+        PPP_DEBUG_INFO("[HTTP] Body length OK: body_len=%u, Content-Length=%d", 
+                       (unsigned int)body_len, 
+                       client_data->responseContentLen);
+    }
+    
+    if (client_data->responseBuf && client_data->responseBufLen > 0) {
+        size_t copy_len = (body_len < (size_t)client_data->responseBufLen - 1) ?
+                         body_len : (size_t)client_data->responseBufLen - 1;
+        memcpy(client_data->responseBuf, decoded_body, copy_len);
+        client_data->responseBuf[copy_len] = '\0';
+        client_data->responseBufLen = copy_len;
+        client_data->contentBlockLen = copy_len;
+        
+        /* 调试:打印最终提取的响应体内容(前256字节) */
+        size_t final_preview_len = (copy_len < 256) ? copy_len : 256;
+        char final_preview_buf[257] = {0};
+        for (size_t i = 0; i < final_preview_len; i++) {
+            if (client_data->responseBuf[i] >= 32 && client_data->responseBuf[i] < 127) {
+                final_preview_buf[i] = client_data->responseBuf[i];
+            } else if (client_data->responseBuf[i] == '\r') {
+                final_preview_buf[i] = '\\';
+                if (i + 1 < 256) final_preview_buf[++i] = 'r';
+            } else if (client_data->responseBuf[i] == '\n') {
+                final_preview_buf[i] = '\\';
+                if (i + 1 < 256) final_preview_buf[++i] = 'n';
+            } else {
+                final_preview_buf[i] = '.';
+            }
+        }
+        PPP_DEBUG_INFO("[HTTP] Final extracted body (first %u/%u bytes): %s", 
+                       (unsigned int)final_preview_len, 
+                       (unsigned int)copy_len, 
+                       final_preview_buf);
     }
     
     /* 检查是否还有更多数据 */
-    if (data->responseContentLen > 0 && body_len < (size_t)data->responseContentLen) {
-        data->isMore = true;
+    if (client_data->responseContentLen > 0 && body_len < (size_t)client_data->responseContentLen) {
+        client_data->isMore = true;
     } else {
-        data->isMore = false;
+        client_data->isMore = false;
     }
     
+    osal_mutex_unlock(mutex);
+    
+    PPP_DEBUG_INFO("[HTTP] Response received: status=%d, body_len=%u", 
+                   status_code, (unsigned int)body_len);
+    
     return N706A_HTTP_SUCCESS;
 }
 
 /**
- * @brief Base64编码
+ * @brief 流式模式:解析HTTP响应头并提取响应体数据块
+ * @param client HTTP客户端
+ * @param data 接收到的数据
+ * @param length 数据长度
+ * @return 0成功,非0失败
  */
-static void http_base64_encode(const char *input, size_t len, char *output)
+static int http_stream_parse_and_deliver(n706a_http_client_t *client, 
+                                         const uint8_t *data, 
+                                         size_t length)
 {
-    size_t i = 0, j = 0;
-    unsigned char char_array_3[3];
-    unsigned char char_array_4[4];
-    
-    while (len--) {
-        char_array_3[i++] = *(input++);
-        if (i == 3) {
-            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
-            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
-            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
-            char_array_4[3] = char_array_3[2] & 0x3f;
-            
-            for (i = 0; i < 4; i++) {
-                output[j++] = base64_table[char_array_4[i]];
-            }
-            i = 0;
-        }
+    PPP_DEBUG_INFO("[HTTP] http_stream_parse_and_deliver: length=%u, stream_header_parsed=%d", 
+                   (unsigned int)length, client ? client->stream_header_parsed : 0);
+    
+    if (!client || !data || length == 0 || !client->stream_callback) {
+        PPP_DEBUG_ERROR("[HTTP] http_stream_parse_and_deliver: invalid parameters: client=%p, data=%p, length=%u, callback=%p", 
+                       client, data, (unsigned int)length, client ? client->stream_callback : NULL);
+        return -1;
+    }
+    
+    /* 注意:HTTPRECV 不支持分片,所以 length > 2048 是正常的 */
+    /* 这与 TCPRECV 不同,TCPRECV 有明确的 2048 字节限制并会自动分片 */
+    /* HTTPRECV 的 URC 中声明的长度就是当前URC的数据长度(二进制长度) */
+    /* 如果数据很大,模块会发送多个独立的 URC,每个 URC 的 <length> 是该 URC 的数据长度 */
+    if (length > 2048) {
+        PPP_DEBUG_INFO("[HTTP] Stream URC data length=%u exceeds 2048 bytes (normal, HTTPRECV doesn't auto-fragment)", 
+                      (unsigned int)length);
     }
     
-    if (i) {
-        for (size_t k = i; k < 3; k++) {
-            char_array_3[k] = '\0';
+    /* 如果响应头还未解析,先解析响应头 */
+    if (!client->stream_header_parsed) {
+        /* 累积数据到缓冲区(用于解析响应头) */
+        /* 注意:响应头解析阶段不能分片,因为响应头可能跨越多个URC */
+        size_t available_space = client->response_buffer_size - client->response_received;
+        size_t copy_len = (length <= available_space) ? length : available_space;
+        
+        if (copy_len < length) {
+            PPP_DEBUG_WARN("[HTTP] Response buffer overflow while parsing header: need=%u, available=%u", 
+                          (unsigned int)length, (unsigned int)available_space);
+        }
+        
+        if (copy_len > 0) {
+            memcpy(client->response_buffer + client->response_received, data, copy_len);
+            client->response_received += copy_len;
+        }
+        
+        /* 查找HTTP响应头结束位置(使用轻量级手动搜索,避免strstr的栈开销) */
+        const char *http_start = (const char *)client->response_buffer;
+        const char *http_header = NULL;
+        size_t buf_len = client->response_received;
+        
+        /* 手动搜索 "HTTP/" (避免strstr的栈开销) */
+        for (size_t i = 0; i + 5 <= buf_len; i++) {
+            if (http_start[i] == 'H' && http_start[i+1] == 'T' && 
+                http_start[i+2] == 'T' && http_start[i+3] == 'P' && 
+                http_start[i+4] == '/') {
+                http_header = http_start + i;
+                break;
+            }
+        }
+        
+        if (!http_header) {
+            /* 响应头还未完整,继续等待 */
+            return 0;
+        }
+        
+        /* 手动搜索 "\r\n\r\n" (避免strstr的栈开销) */
+        const char *header_end = NULL;
+        size_t header_start_offset = http_header - http_start;
+        for (size_t i = header_start_offset; i + 4 <= buf_len; i++) {
+            if (http_start[i] == '\r' && http_start[i+1] == '\n' &&
+                http_start[i+2] == '\r' && http_start[i+3] == '\n') {
+                header_end = http_start + i;
+                break;
+            }
+        }
+        
+        if (!header_end) {
+            /* 响应头还未完整,继续等待 */
+            return 0;
+        }
+        
+        /* 解析状态码(使用轻量级手动解析,避免sscanf的栈开销) */
+        int status_code = 0;
+        const char *p = http_header + 5;  // 跳过 "HTTP/"
+        /* 跳过版本号 "1.1 " */
+        while (p < header_end && (*p < '0' || *p > '9')) {
+            p++;
+        }
+        /* 解析状态码 */
+        while (p < header_end && *p >= '0' && *p <= '9') {
+            status_code = status_code * 10 + (*p - '0');
+            p++;
+        }
+        
+        if (status_code == 0) {
+            PPP_DEBUG_ERROR("[HTTP] Failed to parse HTTP status code in stream mode");
+            return -1;
+        }
+        
+        client->response_code = status_code;
+        
+        /* 提取Content-Length(使用轻量级手动搜索,避免strcasestr的栈开销) */
+        client->stream_content_length = 0;
+        const char *search_start = http_header;
+        size_t search_len = header_end - search_start;
+        /* 手动搜索 "Content-Length:" (不区分大小写) */
+        for (size_t i = 0; i + 15 <= search_len; i++) {
+            const char *candidate = search_start + i;
+            /* 检查 "Content-Length:" (不区分大小写) */
+            if ((candidate[0] == 'C' || candidate[0] == 'c') &&
+                (candidate[1] == 'O' || candidate[1] == 'o') &&
+                (candidate[2] == 'N' || candidate[2] == 'n') &&
+                (candidate[3] == 'T' || candidate[3] == 't') &&
+                (candidate[4] == 'E' || candidate[4] == 'e') &&
+                (candidate[5] == 'N' || candidate[5] == 'n') &&
+                (candidate[6] == 'T' || candidate[6] == 't') &&
+                candidate[7] == '-' &&
+                (candidate[8] == 'L' || candidate[8] == 'l') &&
+                (candidate[9] == 'E' || candidate[9] == 'e') &&
+                (candidate[10] == 'N' || candidate[10] == 'n') &&
+                (candidate[11] == 'G' || candidate[11] == 'g') &&
+                (candidate[12] == 'T' || candidate[12] == 't') &&
+                (candidate[13] == 'H' || candidate[13] == 'h') &&
+                candidate[14] == ':') {
+                /* 找到Content-Length,解析数值 */
+                p = candidate + 15;
+                while (p < header_end && (*p == ' ' || *p == '\t')) {
+                    p++;  // 跳过空格
+                }
+                while (p < header_end && *p >= '0' && *p <= '9') {
+                    client->stream_content_length = client->stream_content_length * 10 + (*p - '0');
+                    p++;
+                }
+                PPP_DEBUG_INFO("[HTTP] Stream mode: Content-Length=%d", client->stream_content_length);
+                break;
+            }
         }
         
-        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
-        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
-        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+        if (client->stream_content_length == 0) {
+            PPP_DEBUG_WARN("[HTTP] Stream mode: Content-Length not found");
+        }
         
-        for (size_t k = 0; k < i + 1; k++) {
-            output[j++] = base64_table[char_array_4[k]];
+        /* 检查是否chunked(使用轻量级手动搜索,避免strcasestr的栈开销) */
+        client->stream_is_chunked = false;
+        /* 手动搜索 "Transfer-Encoding: chunked" (不区分大小写) */
+        for (size_t i = 0; i + 28 <= search_len; i++) {
+            const char *candidate = search_start + i;
+            /* 检查 "Transfer-Encoding: chunked" (简化检查,只检查关键部分) */
+            if ((candidate[0] == 'T' || candidate[0] == 't') &&
+                (candidate[1] == 'R' || candidate[1] == 'r') &&
+                (candidate[2] == 'A' || candidate[2] == 'a') &&
+                (candidate[3] == 'N' || candidate[3] == 'n') &&
+                (candidate[4] == 'S' || candidate[4] == 's') &&
+                (candidate[5] == 'F' || candidate[5] == 'f') &&
+                (candidate[6] == 'E' || candidate[6] == 'e') &&
+                (candidate[7] == 'R' || candidate[7] == 'r') &&
+                candidate[8] == '-' &&
+                (candidate[9] == 'E' || candidate[9] == 'e') &&
+                (candidate[10] == 'N' || candidate[10] == 'n') &&
+                (candidate[11] == 'C' || candidate[11] == 'c') &&
+                (candidate[12] == 'O' || candidate[12] == 'o') &&
+                (candidate[13] == 'D' || candidate[13] == 'd') &&
+                (candidate[14] == 'I' || candidate[14] == 'i') &&
+                (candidate[15] == 'N' || candidate[15] == 'n') &&
+                (candidate[16] == 'G' || candidate[16] == 'g') &&
+                candidate[17] == ':') {
+                /* 检查后面是否有 "chunked" */
+                p = candidate + 18;
+                while (p < header_end && (*p == ' ' || *p == '\t')) {
+                    p++;  // 跳过空格
+                }
+                if (p + 7 <= header_end &&
+                    (p[0] == 'c' || p[0] == 'C') &&
+                    (p[1] == 'h' || p[1] == 'H') &&
+                    (p[2] == 'u' || p[2] == 'U') &&
+                    (p[3] == 'n' || p[3] == 'N') &&
+                    (p[4] == 'k' || p[4] == 'K') &&
+                    (p[5] == 'e' || p[5] == 'E') &&
+                    (p[6] == 'd' || p[6] == 'D')) {
+                    client->stream_is_chunked = true;
+                    PPP_DEBUG_INFO("[HTTP] Stream mode: Transfer-Encoding: chunked");
+                    break;
+                }
+            }
         }
         
-        while (i++ < 3) {
-            output[j++] = '=';
+        /* 标记响应头已解析 */
+        client->stream_header_parsed = true;
+        
+        /* 计算响应体起始位置 */
+        size_t header_end_offset = (header_end + 4) - http_start;
+        size_t body_start_offset = header_end_offset;
+        
+        /* 提取第一个响应体数据块(如果有) */
+        if (client->response_received > body_start_offset) {
+            size_t body_len = client->response_received - body_start_offset;
+            const uint8_t *body_data = client->response_buffer + body_start_offset;
+            
+            /* ⚠️ 关键:body_len 是从缓冲区中提取的实际数据长度 */
+            /* HTTPRECV 不支持分片,所以 body_len > 2048 是正常的 */
+            /* 这与 TCPRECV 不同,TCPRECV 有明确的 2048 字节限制并会自动分片 */
+            
+            /* 判断是否完成 */
+            bool is_finished = false;
+            if (client->stream_content_length > 0) {
+                /* 有Content-Length,检查是否接收完成 */
+                is_finished = (client->stream_body_received + body_len >= (size_t)client->stream_content_length);
+            } else if (!client->stream_is_chunked) {
+                /* 没有Content-Length且不是chunked,假设这是最后一个数据块 */
+                /* ⚠️ 注意:HTTPRECV 不支持分片,每个 URC 是独立的 */
+                /* 但是,如果没有Content-Length且不是chunked,我们无法准确判断是否还有更多数据,只能假设 */
+                is_finished = true;
+            }
+            
+            /* 调用回调函数 */
+            int ret = client->stream_callback(client->stream_user_data, body_data, body_len, is_finished);
+            if (ret != 0) {
+                PPP_DEBUG_ERROR("[HTTP] Stream callback returned error: %d", ret);
+                return ret;
+            }
+            
+            client->stream_body_received += body_len;
+            
+            /* 清空缓冲区(响应头已解析,不再需要) */
+            client->response_received = 0;
         }
+        
+        return 0;
     }
     
-    output[j] = '\0';
+    /* 响应头已解析,直接传递响应体数据 */
+    /* ⚠️ 关键:length 参数是 n706a_parse_httprecv_urc 返回的 actual_len(实际可用数据长度) */
+    /* HTTPRECV 不支持分片,所以 length > 2048 是正常的 */
+    /* 这与 TCPRECV 不同,TCPRECV 有明确的 2048 字节限制并会自动分片 */
+    /* 因此,这里不需要再次分片,直接调用回调函数即可 */
+    
+    /* 判断是否完成 */
+    bool is_finished = false;
+    
+    /* 优先检查连接是否已关闭(检测到+HTTPSCLOSED/+HTTPCLOSED) */
+    if (client->stream_connection_closed) {
+        is_finished = true;
+        PPP_DEBUG_INFO("[HTTP] Stream connection closed, marking as finished");
+    } else if (client->stream_content_length > 0) {
+        /* 有Content-Length,检查是否接收完成 */
+        is_finished = (client->stream_body_received + length >= (size_t)client->stream_content_length);
+    } else if (!client->stream_is_chunked) {
+        /* 没有Content-Length且不是chunked,假设这是最后一个数据块 */
+        /* ⚠️ 注意:HTTPRECV 不支持分片,每个 URC 是独立的 */
+        /* 但是,如果没有Content-Length且不是chunked,我们无法准确判断是否还有更多数据,只能假设 */
+        is_finished = true;
+    }
+    
+    /* 调用回调函数 */
+    int ret = client->stream_callback(client->stream_user_data, data, length, is_finished);
+    if (ret != 0) {
+        PPP_DEBUG_ERROR("[HTTP] Stream callback returned error: %d", ret);
+        return ret;
+    }
+    
+    client->stream_body_received += length;
+    
+    return 0;
 }
 
 /**
- * @brief 解析URL
+ * @brief HTTP URC数据接收回调(由n706a_at_handler调用)
+ * @param data 接收到的HTTP响应数据
+ * @param length 数据长度
  */
-static int http_parse_url(const char *url,
-                          char *protocol,
-                          char *host,
-                          uint16_t *port,
-                          char *path)
+/**
+ * @brief 标记HTTP连接已关闭(由n706a_at_handler调用,当检测到+HTTPSCLOSED时)
+ */
+void n706a_http_mark_connection_closed(void)
 {
-    if (!url || !protocol || !host || !port || !path) {
-        return -1;
+    osal_mutex_lock(&g_http_client_mutex);
+    if (g_http_client) {
+        g_http_client->stream_connection_closed = true;
+        PPP_DEBUG_INFO("[HTTP] Connection marked as closed (detected +HTTPSCLOSED/+HTTPCLOSED)");
+    }
+    osal_mutex_unlock(&g_http_client_mutex);
+}
+
+void n706a_http_on_urc_received(const uint8_t *data, size_t length, bool is_https)
+{
+    if (!data || length == 0) {
+        PPP_DEBUG_WARN("[HTTP] n706a_http_on_urc_received called with invalid parameters: data=%p, length=%u", 
+                      data, (unsigned int)length);
+        return;
+    }
+    
+    PPP_DEBUG_INFO("[HTTP] n706a_http_on_urc_received: length=%u, is_https=%d", 
+                   (unsigned int)length, is_https);
+    
+    /* 优化:尽快获取client指针并释放全局mutex,减少锁定时间 */
+    n706a_http_client_t *client = NULL;
+    osal_mutex *mutex = NULL;
+    osal_semaphore *sem = NULL;
+    bool stream_mode_enabled = false;
+    void *stream_callback = NULL;
+    void *stream_user_data = NULL;
+    
+    osal_mutex_lock(&g_http_client_mutex);
+    client = g_http_client;
+    if (client) {
+        mutex = (osal_mutex *)client->response_mutex;
+        sem = (osal_semaphore *)client->response_sem;
+        stream_mode_enabled = client->stream_mode_enabled;
+        stream_callback = client->stream_callback;
+        stream_user_data = client->stream_user_data;
+    }
+    osal_mutex_unlock(&g_http_client_mutex);
+    
+    if (!client || !mutex || !client->response_buffer) {
+        PPP_DEBUG_WARN("[HTTP] URC for unregistered client, dropping %u bytes (client=%p, response_mutex=%p, response_buffer=%p)", 
+                      (unsigned int)length, client, 
+                      client ? client->response_mutex : NULL, 
+                      client ? client->response_buffer : NULL);
+        return;
+    }
+    
+    PPP_DEBUG_INFO("[HTTP] Client found: stream_mode_enabled=%d, stream_callback=%p", 
+                   stream_mode_enabled, stream_callback);
+    
+    osal_mutex_lock(mutex);
+    
+    /* 如果启用了流式模式,立即解析并传递数据 */
+    if (stream_mode_enabled && stream_callback) {
+        PPP_DEBUG_INFO("[HTTP] Stream mode enabled, calling http_stream_parse_and_deliver: length=%u", 
+                       (unsigned int)length);
+        int ret = http_stream_parse_and_deliver(client, data, length);
+        if (ret != 0) {
+            PPP_DEBUG_ERROR("[HTTP] Stream parse and deliver failed: %d", ret);
+        } else {
+            PPP_DEBUG_INFO("[HTTP] Stream parse and deliver succeeded");
+        }
+        /* 流式模式下仍然通知信号量(用于http_receive_response的同步) */
+        if (sem) {
+            osal_sem_up(sem);
+        }
+        osal_mutex_unlock(mutex);
+        return;
+    } else {
+        PPP_DEBUG_WARN("[HTTP] Stream mode NOT enabled or callback not set: stream_mode_enabled=%d, stream_callback=%p", 
+                      client->stream_mode_enabled, client->stream_callback);
     }
     
-    const char *p = url;
+    /* 传统模式:累积数据到缓冲区 */
+    /* ⚠️ 关键:length 参数是 n706a_parse_httprecv_urc 返回的 actual_len(实际可用数据长度) */
+    /* HTTPRECV 不支持分片,所以 length > 2048 是正常的 */
+    /* 这与 TCPRECV 不同,TCPRECV 有明确的 2048 字节限制并会自动分片 */
+    /* 因此,我们可以直接使用 length 来复制数据,不需要截断 */
     
-    /* 解析协议 */
-    if (strncmp(p, "https://", 8) == 0) {
-        strcpy(protocol, "https");
-        *port = 443;
-        p += 8;
-    } else if (strncmp(p, "http://", 7) == 0) {
-        strcpy(protocol, "http");
-        *port = 80;
-        p += 7;
-    } else {
-        return -1;
+    /* 检查缓冲区空间 */
+    size_t available_space = client->response_buffer_size - client->response_received;
+    size_t copy_len = (length > available_space) ? available_space : length;
+    
+    if (copy_len == 0) {
+        /* 缓冲区已满,无法继续写入 */
+        PPP_DEBUG_WARN("[HTTP] Response buffer full: received=%u/%u, dropping %u bytes", 
+                      (unsigned int)client->response_received, 
+                      (unsigned int)client->response_buffer_size,
+                      (unsigned int)length);
+        osal_mutex_unlock(mutex);
+        return;
     }
     
-    /* 解析host和port */
-    const char *path_start = strchr(p, '/');
-    const char *port_start = strchr(p, ':');
+    /* 复制数据到缓冲区 */
+    memcpy(client->response_buffer + client->response_received, data, copy_len);
+    client->response_received += copy_len;
+    client->response_complete = true;
     
-    if (port_start && (!path_start || port_start < path_start)) {
-        /* 有端口号 */
-        size_t host_len = port_start - p;
-        strncpy(host, p, host_len);
-        host[host_len] = '\0';
-        
-        *port = atoi(port_start + 1);
+    if (copy_len < length) {
+        PPP_DEBUG_WARN("[HTTP] URC data truncated: actual_len=%u, copied=%u, dropped=%u", 
+                      (unsigned int)length, (unsigned int)copy_len, (unsigned int)(length - copy_len));
+    }
+    
+    if (client->response_received > 0) {
+        PPP_DEBUG_INFO("[HTTP] URC data buffered: received=%u, total=%u/%u", 
+                       (unsigned int)length, 
+                       (unsigned int)client->response_received, 
+                       (unsigned int)client->response_buffer_size);
         
-        if (path_start) {
-            strcpy(path, path_start);
-        } else {
-            strcpy(path, "/");
-        }
-    } else {
-        /* 没有端口号 */
-        if (path_start) {
-            size_t host_len = path_start - p;
-            strncpy(host, p, host_len);
-            host[host_len] = '\0';
-            strcpy(path, path_start);
-        } else {
-            strcpy(host, p);
-            strcpy(path, "/");
+        /* 调试:打印接收到的数据(前256字节) */
+        size_t debug_len = (client->response_received < 256) ? client->response_received : 256;
+        char debug_buf[257] = {0};
+        for (size_t i = 0; i < debug_len; i++) {
+            if (client->response_buffer[i] >= 32 && client->response_buffer[i] < 127) {
+                debug_buf[i] = client->response_buffer[i];
+            } else if (client->response_buffer[i] == '\r') {
+                debug_buf[i] = '\\';
+                if (i + 1 < 256) debug_buf[++i] = 'r';
+            } else if (client->response_buffer[i] == '\n') {
+                debug_buf[i] = '\\';
+                if (i + 1 < 256) debug_buf[++i] = 'n';
+            } else {
+                debug_buf[i] = '.';
+            }
         }
+        PPP_DEBUG_INFO("[HTTP] Received data (first %u bytes): %s", (unsigned int)debug_len, debug_buf);
     }
     
-    return 0;
+    /* 通知等待的接收函数 */
+    if (client->response_received > 0 && sem) {
+        osal_sem_up(sem);
+    }
+    
+    osal_mutex_unlock(mutex);
 }
 
index bdbb00a..64d3cb7 100644 (file)
@@ -6,19 +6,26 @@
 #include "n706a_net_api.h"
 #include "n706a_at_handler.h"
 #include "n706a_tcp_client.h"
+#include "n706a_http_client.h"  /* 用于证书初始化 */
 #include "ppp_config.h"
 #include "ppp_debug.h"
 #include "ppp_power_ctl.h"
 #include "ppp_serial.h"
 #include "ppp_cat1_api.h"
 #include "ppp_interrupt.h"
-#include "ppp_api.h"
 #include "soc_osal.h"
 #include "osal_debug.h"
 #include "schedule/osal_task.h"
 #include "securec.h"
 #include <string.h>
 #include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+
+// 时区相关宏定义(参照 ppp_api.c)
+#define MINUTES_PER_QUARTER     15   /* minutes per quarter */
+#define QUARTERS_PER_HOUR       4    /* quarters per hour */
+#define MAX_TIMEZONE_HOURS      24
 
 // N706A网络状态
 static n706a_net_state_t g_n706a_state = N706A_NET_STATE_IDLE;
@@ -53,9 +60,9 @@ errcode_t n706a_net_power_init(void)
         .reset_pin = S_AGPIO_L24,
         .reset_pin_en_value = 1,
         .ring_pin = S_MGPIO21,  // CTS cat1 wakeup mcu
-        .ring_pin_en_value = 1,
+        .ring_pin_en_value = 2,
         .wakemodem_pin = S_MGPIO22,  // RTS mcu wakeup cat1
-        .wakemodem_pin_en_value = 0
+        .wakemodem_pin_en_value = 1
     };
  
     // 注册4G电源控制引脚配置
@@ -140,7 +147,8 @@ errcode_t n706a_net_init(const n706a_net_config_t *config)
         PPP_DEBUG_ERROR("Failed to power on N706A module\n");
         return ret;
     }
-
+    #define INIT_SERIAL 1 //外接串口调试模组时,串口要设置input防止干扰。
+    #if INIT_SERIAL 
     // 3. 初始化串口
     PPP_DEBUG_PRINT("Step 3: Initializing UART (baudrate: %u)\n", config->baudrate);
     ppp_serial_config_t serial_config = {
@@ -157,7 +165,10 @@ errcode_t n706a_net_init(const n706a_net_config_t *config)
         n706a_net_power_control(false);
         return ERRCODE_FAIL;
     }
-
+    #else
+    ppp_api_leiniao_serial_pin_to_gpio_input();
+    PPP_DEBUG_PRINT("Serial pin to gpio input for pc debug\n");
+    #endif
     // 4. 初始化AT命令处理器
     PPP_DEBUG_PRINT("Step 4: Initializing AT command handler\n");
     ret = ppp_at_handler_init(SIO_UART_BUS, config->baudrate, config->auto_baudrate);
@@ -183,21 +194,46 @@ errcode_t n706a_net_init(const n706a_net_config_t *config)
         return ERRCODE_FAIL;
     }
 
+    // 6.5. 测试AT连接并启用回显(ATE1)
+    // 启用回显可以更好地区分命令响应和异步URC,通过AT+前缀明确识别命令响应
+    PPP_DEBUG_PRINT("Step 6.5: Testing AT communication and enabling echo (ATE1)\n");
+    n706a_at_response_t at_resp;
+    n706a_at_error_t at_ret = n706a_at_send_command("AT", "OK", 3000, &at_resp);
+    if (at_ret != N706A_AT_OK) {
+        PPP_DEBUG_WARN("AT test failed, but continuing...\n");
+        // 不返回错误,继续尝试
+    } else {
+        PPP_DEBUG_INFO("AT communication test passed\n");
+        
+        // 立即执行ATE1启用回显
+        PPP_DEBUG_PRINT("Enabling echo (ATE1) to better distinguish command responses from URCs\n");
+        at_ret = n706a_at_send_command("ATE1", "OK", 3000, &at_resp);
+        if (at_ret != N706A_AT_OK) {
+            PPP_DEBUG_WARN("Failed to enable echo (ATE1), but continuing...\n");
+            // 不返回错误,继续执行(某些模组可能不支持或已启用)
+        } else {
+            PPP_DEBUG_INFO("Echo enabled successfully (ATE1)\n");
+            PPP_DEBUG_INFO("Note: Command echo is now enabled, responses will include AT+ prefix for better identification\n");
+        }
+    }
+
     // 7. 配置TCP接收模式为直接输出(URC模式,HEX上报)
     PPP_DEBUG_PRINT("Step 7: Configuring TCP URC mode (HEX format)\n");
-    n706a_at_response_t at_resp;
-    n706a_at_error_t at_ret = n706a_at_send_command("AT+RECVMODE=1,1", "OK", 3000, &at_resp);
+    at_ret = n706a_at_send_command("AT+RECVMODE=1,1", "OK", 3000, &at_resp);
     if (at_ret != N706A_AT_OK) {
         PPP_DEBUG_WARN("Failed to set RECVMODE=1,1 (HEX mode), trying ASCII mode\n");
         // 如果HEX模式失败,尝试ASCII模式作为备用
         at_ret = n706a_at_send_command("AT+RECVMODE=1,0", "OK", 3000, &at_resp);
         if (at_ret != N706A_AT_OK) {
             PPP_DEBUG_WARN("Failed to set RECVMODE=1,0, using default\n");
+            n706a_at_set_tcp_recv_hex_mode(false);  // 默认使用ASCII模式
         } else {
             PPP_DEBUG_WARN("Using ASCII mode as fallback\n");
+            n706a_at_set_tcp_recv_hex_mode(false);  // ASCII模式
         }
     } else {
         PPP_DEBUG_INFO("TCP receive mode set to HEX format\n");
+        n706a_at_set_tcp_recv_hex_mode(true);  // HEX模式
     }
     PPP_DEBUG_INFO("response: %d , %s\n",at_resp.length,at_resp.response);
     // 8. 注册TCP URC接收回调
@@ -208,6 +244,20 @@ errcode_t n706a_net_init(const n706a_net_config_t *config)
     PPP_DEBUG_PRINT("Step 8.1: Registering +TCPSETUP URC callback\n");
     // n706a_tcp_on_tcpsetup_urc在n706a_tcp_client.c中实现,直接调用
     n706a_at_register_tcpsetup_urc_callback(n706a_tcp_on_tcpsetup_urc);
+    
+    // 8.2. 注册+TCPSEND URC回调(用于TCP数据发送确认)
+    PPP_DEBUG_PRINT("Step 8.2: Registering +TCPSEND URC callback\n");
+    // n706a_tcp_on_tcpsend_urc在n706a_tcp_client.c中实现,通过n706a_tcp_client.h声明
+    n706a_at_register_tcpsend_urc_callback(n706a_tcp_on_tcpsend_urc);
+    
+    // 8.2.1. 注册+TCPCLOSE URC回调(用于TCP连接关闭通知)
+    PPP_DEBUG_PRINT("Step 8.2.1: Registering +TCPCLOSE URC callback\n");
+    // n706a_tcp_on_tcpclose_urc在n706a_tcp_client.c中实现,通过n706a_tcp_client.h声明
+    n706a_at_register_tcpclose_urc_callback(n706a_tcp_on_tcpclose_urc);
+    
+    // 8.3. 提前初始化+TCPSEND URC等待机制(避免在发送时初始化导致信号量创建失败)
+    PPP_DEBUG_PRINT("Step 8.3: Pre-initializing +TCPSEND URC wait mechanism\n");
+    n706a_tcp_preinit_tcpsend_urc_wait();
 
     // 9. 检查GPRS附着状态(AT+CGATT?)
     PPP_DEBUG_PRINT("Step 9: Checking GPRS attachment status (AT+CGATT?)\n");
@@ -257,7 +307,23 @@ errcode_t n706a_net_init(const n706a_net_config_t *config)
     PPP_DEBUG_PRINT("PDP context activated successfully\n");
     PPP_DEBUG_INFO("response: %d , %s\n",at_resp.length,at_resp.response);
 
+    // 12. 初始化HTTPS证书(开机执行一次,检查有证书就跳过,无证书就添加)
+    PPP_DEBUG_PRINT("Step 12: Initializing HTTPS certificate (one-time on boot)\n");
+    n706a_httpc_result_t cert_ret = n706a_http_client_init_certificate();
+    if (cert_ret != N706A_HTTP_SUCCESS) {
+        PPP_DEBUG_WARN("HTTPS certificate initialization failed, but continuing...\n");
+        PPP_DEBUG_WARN("HTTPS connections may fail if certificate is required\n");
+        // 不阻止网络初始化,证书可能已存在或后续会添加
+    } else {
+        PPP_DEBUG_PRINT("HTTPS certificate initialized successfully\n");
+    }
+
     g_n706a_state = N706A_NET_STATE_READY;
+    
+    // 更新网络状态为在线(PDP上下文激活后,网络已可用)
+    cat1_api_runtime_set_cat1_networkstat(1);
+    PPP_DEBUG_PRINT("Network status set to ONLINE (network_on=1)\n");
+    
     PPP_DEBUG_PRINT("=== N706A AT Network Initialized Successfully ===\n");
     PPP_DEBUG_PRINT("Mode: Pure AT commands (no PPP dialing)\n");
     PPP_DEBUG_PRINT("Reception: Asynchronous interrupt + URC push\n");
@@ -385,8 +451,13 @@ errcode_t n706a_get_network_signal_info(void)
  * 
  * 功能说明:
  * - 执行 AT+NWENPWRSAVE=1 命令
- * - 配置为:低电平唤醒,高电平进入休眠
+ * - 配置为:
+ *  0:不允许进入休眠模式(默认)
+ *  1:允许进入休眠模式(DTR 信号低电平进入休眠,高电平退出休眠)
+ *  2:允许进入休眠模式(DTR 信号高电平进入休眠,低电平退出休眠)
  * - N706A_NET始终在AT模式
+ * AT+NWENPWRSAVE=1 时 ppp_register_ctl_pin 设置 ppp_ctl_pin_4g_t wakemodem_pin_en_value = 1 
+ * AT+NWENPWRSAVE=2 时 ppp_register_ctl_pin 设置 ppp_ctl_pin_4g_t wakemodem_pin_en_value = 0
  */
 errcode_t n706a_enable_power_save_mode(void)
 {
@@ -416,6 +487,43 @@ errcode_t n706a_enable_power_save_mode(void)
     return ERRCODE_SUCC;
 }
 
+/**
+ * @brief 启用4G模块数据Ring功能
+ * @return ERRCODE_SUCC-成功,其他-失败
+ * 
+ * 功能说明:
+ * - 执行 AT+NWURCCFG=0,1,200 命令
+ * - 使能 data ring,持续 200ms
+ * - 用于在省电模式下通过 Ring 引脚唤醒 MCU
+ */
+errcode_t n706a_enable_data_ring(void)
+{
+    if (g_n706a_state == N706A_NET_STATE_IDLE) {
+        PPP_DEBUG_ERROR("N706A not initialized");
+        return ERRCODE_FAIL;
+    }
+    
+    PPP_DEBUG_INFO("Enabling data ring (AT+NWURCCFG=0,1,200)...");
+    
+    n706a_at_response_t response;
+    n706a_at_error_t ret = n706a_at_send_command("AT+NWURCCFG=0,1,200", "OK", 5000, &response);
+    
+    if (ret != N706A_AT_OK) {
+        PPP_DEBUG_ERROR("Failed to send AT+NWURCCFG command");
+        return ERRCODE_FAIL;
+    }
+    
+    if (!response.success) {
+        PPP_DEBUG_ERROR("AT+NWURCCFG command failed, response: %s", response.response);
+        return ERRCODE_FAIL;
+    }
+    
+    PPP_DEBUG_CRITICAL("Data ring enabled successfully");
+    PPP_DEBUG_INFO("  Configuration: AT+NWURCCFG=0,1,200 (enabled, 200ms duration)");
+    
+    return ERRCODE_SUCC;
+}
+
 /* ========== Runtime Status Update Helper Functions ========== */
 
 /**
@@ -764,7 +872,10 @@ static errcode_t n706a_api_update_network_status(void)
         if (ret == ERRCODE_SUCC) {
             // 根据网络注册状态设置网络类型
             cat1_api_runtime_set_cat1_networktype(network_status);
-            PPP_DEBUG_CRITICAL("Updated network status (CREG): %d", network_status);
+            // 根据网络注册状态设置网络连接状态(1或5表示已注册)
+            uint8_t network_on = (network_status == 1 || network_status == 5) ? 1 : 0;
+            cat1_api_runtime_set_cat1_networkstat(network_on);
+            PPP_DEBUG_CRITICAL("Updated network status (CREG): %d, network_on: %d", network_status, network_on);
             return ERRCODE_SUCC;
         }
     }
@@ -784,7 +895,10 @@ static errcode_t n706a_api_update_network_status(void)
     
     // 根据网络注册状态设置网络类型
     cat1_api_runtime_set_cat1_networktype(network_status);
-    PPP_DEBUG_CRITICAL("Updated network status (CEREG): %d", network_status);
+    // 根据网络注册状态设置网络连接状态(1或5表示已注册)
+    uint8_t network_on = (network_status == 1 || network_status == 5) ? 1 : 0;
+    cat1_api_runtime_set_cat1_networkstat(network_on);
+    PPP_DEBUG_CRITICAL("Updated network status (CEREG): %d, network_on: %d", network_status, network_on);
     
     return ERRCODE_SUCC;
 }
@@ -884,10 +998,299 @@ errcode_t n706a_api_update_all_runtime_status(void)
         ret = ERRCODE_FAIL;
     }
     
+    // 同步网络时间
+    PPP_DEBUG_CRITICAL("Step 8: Starting network time synchronization...");
+    if (n706a_api_sync_time_from_network() != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to sync time from network");
+        ret = ERRCODE_FAIL;
+    } else {
+        PPP_DEBUG_CRITICAL("Step 8: Network time synchronization completed successfully");
+    }
+    
     PPP_DEBUG_INFO("N706A runtime status update completed");
     return ret;
 }
 
+/**
+ * @brief 解析AT+CCLK响应并转换为时间戳和时区
+ * @param response AT命令响应
+ * @param timestamp 输出UTC时间戳(秒)
+ * @param timezone_quarters 输出时区(quarters,15分钟为单位)
+ * @return ERRCODE_SUCC-成功,其他-失败
+ * 
+ * AT+CCLK响应格式示例:
+ * +CCLK: "25/10/20,02:59:43+32"
+ * 
+ * 重要:CCLK返回的时间字段是UTC时间,不是本地时间!
+ * - 时间字段:02:59:43 是 UTC 时间
+ * - 时区字段:+32 表示本地时区为 UTC+8(32 quarters = 8 hours)
+ * - 本地时间:02:59:43 + 8小时 = 10:59:43
+ */
+static errcode_t n706a_parse_cclk_and_convert_to_timestamp(const char *response, 
+                                                          uint32_t *timestamp, 
+                                                          int *timezone_quarters)
+{
+    if (!response || !timestamp || !timezone_quarters) {
+        return ERRCODE_INVALID_PARAM;
+    }
+    
+    // 查找 "+CCLK: "
+    const char *start = strstr(response, "+CCLK: ");
+    if (start == NULL) {
+        PPP_DEBUG_ERROR("CCLK response format error: no +CCLK: prefix");
+        return ERRCODE_FAIL;
+    }
+    
+    start += 7; // 跳过"+CCLK: "
+    
+    // 查找第一个引号
+    const char *quote_start = strchr(start, '"');
+    if (quote_start == NULL) {
+        PPP_DEBUG_ERROR("CCLK response format error: no opening quote");
+        return ERRCODE_FAIL;
+    }
+    quote_start++; // 跳过引号
+    
+    // 查找第二个引号
+    const char *quote_end = strchr(quote_start, '"');
+    if (quote_end == NULL) {
+        PPP_DEBUG_ERROR("CCLK response format error: no closing quote");
+        return ERRCODE_FAIL;
+    }
+    
+    // 提取并解析时间字符串
+    char time_str[32] = {0};
+    size_t len = quote_end - quote_start;
+    if (len >= sizeof(time_str)) {
+        len = sizeof(time_str) - 1;
+    }
+    strncpy_s(time_str, sizeof(time_str), quote_start, len);
+    time_str[len] = '\0';
+    
+    PPP_DEBUG_INFO("Parsing CCLK time: %s", time_str);
+    
+    // 解析CCLK格式: "YY/MM/DD,HH:MM:SS+TZ"
+    int year, month, day, hour, minute, second, tz_quarters;
+    int parsed = sscanf_s(time_str, "%d/%d/%d,%d:%d:%d%d", 
+                          &year, &month, &day, &hour, &minute, &second, &tz_quarters);
+    
+    if (parsed != 7) {
+        PPP_DEBUG_ERROR("Failed to parse CCLK time format: %s", time_str);
+        return ERRCODE_FAIL;
+    }
+    
+    // 转换2位年份为4位年份(假设20xx年)
+    if (year < 100) {
+        year += 2000;
+    }
+    
+    // 验证年份合法性
+    if (year < 2023) {
+        PPP_DEBUG_ERROR("Invalid year: %d (must be >= 2023)", year);
+        return ERRCODE_FAIL;
+    }
+    
+    // 构造 struct tm 结构(CCLK返回的是UTC时间)
+    struct tm time_info = {0};
+    time_info.tm_year = year - 1900;  // tm_year 是从1900年开始的
+    time_info.tm_mon = month - 1;     // tm_mon 是 0-11
+    time_info.tm_mday = day;
+    time_info.tm_hour = hour;
+    time_info.tm_min = minute;
+    time_info.tm_sec = second;
+    time_info.tm_isdst = -1;          // 让系统自动判断夏令时
+    
+    // 使用 mktime 转换为时间戳
+    // 注意:CCLK返回的时间字段是UTC时间,不是本地时间
+    time_t _timestamp = mktime(&time_info);
+    if (_timestamp == -1) {
+        PPP_DEBUG_ERROR("Failed to convert time to timestamp");
+        return ERRCODE_FAIL;
+    }
+    
+    // CCLK返回的时间本身就是UTC时间,直接使用
+    // 时区字段表示本地时区相对于UTC的偏移
+    uint32_t utc_timestamp = (uint32_t)_timestamp;
+    
+    *timestamp = utc_timestamp;
+    *timezone_quarters = tz_quarters;
+    
+    // 计算本地时间用于显示验证
+    int timezone_hours = tz_quarters / QUARTERS_PER_HOUR;      // 小时部分
+    int timezone_minutes = (tz_quarters % QUARTERS_PER_HOUR) * MINUTES_PER_QUARTER;  // 分钟部分
+    
+    // 计算本地时间:UTC + 时区偏移
+    int local_hour = hour;
+    int local_minute = minute + timezone_minutes;
+    int local_day = day;
+    
+    // 处理分钟进位
+    if (local_minute >= 60) {
+        local_hour += local_minute / 60;
+        local_minute = local_minute % 60;
+    } else if (local_minute < 0) {
+        local_hour += (local_minute / 60) - 1;
+        local_minute = 60 + (local_minute % 60);
+    }
+    
+    // 加上时区的小时部分
+    local_hour += timezone_hours;
+    
+    // 处理小时进位
+    if (local_hour >= 24) {
+        local_day += local_hour / 24;
+        local_hour = local_hour % 24;
+    } else if (local_hour < 0) {
+        local_day += (local_hour / 24) - 1;
+        local_hour = 24 + (local_hour % 24);
+    }
+    
+    PPP_DEBUG_WARN("Parsed UTC time: %04d-%02d-%02d %02d:%02d:%02d", 
+                   year, month, day, hour, minute, second);
+    
+    // 显示时区信息(支持非整数小时)
+    if (timezone_minutes == 0) {
+        PPP_DEBUG_WARN("Timezone: %+d quarters (UTC%+d)", 
+                       tz_quarters, timezone_hours);
+    } else {
+        PPP_DEBUG_WARN("Timezone: %+d quarters (UTC%+d:%02d)", 
+                       tz_quarters, timezone_hours, timezone_minutes);
+    }
+    
+    PPP_DEBUG_WARN("Local time will be: %04d-%02d-%02d %02d:%02d:%02d", 
+                   year, month, local_day, local_hour, local_minute, second);
+    PPP_DEBUG_WARN("UTC timestamp: %u", utc_timestamp);
+    
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 设置系统时间(参照 ppp_api.c 的 ppp_set_system_time 实现)
+ * @param utc_timestamp UTC时间戳(秒)
+ * @param timezone_quarters 时区(quarters,15分钟为单位)
+ * @return ERRCODE_SUCC-成功,其他-失败
+ * 
+ * 时间设置逻辑说明(参照 cat1_porting.c 的 sync_time_utc 实现):
+ * - 输入:UTC时间戳 + 时区偏移
+ * - 设置:tv.tv_sec 设置为本地时间戳(UTC + 时区偏移),tz_minuteswest 设置时区偏移
+ * - 系统时间直接设置为本地时间,tz_minuteswest 用于时区信息记录
+ * 
+ * 示例(UTC+8):
+ * - UTC时间:15:06:28
+ * - 时区:+32 quarters = +8 hours
+ * - tv.tv_sec:本地时间戳(UTC时间戳 + 8小时 = 对应23:06:28)
+ * - tz_minuteswest:+480分钟
+ * - 系统显示:23:06:28
+ */
+static errcode_t n706a_set_system_time(uint32_t utc_timestamp, int timezone_quarters)
+{
+    // 参数验证
+    if (timezone_quarters > (MAX_TIMEZONE_HOURS * QUARTERS_PER_HOUR) ||
+        timezone_quarters < -(MAX_TIMEZONE_HOURS * QUARTERS_PER_HOUR)) {
+        PPP_DEBUG_ERROR("Invalid timezone: %d quarters", timezone_quarters);
+        return ERRCODE_FAIL;
+    }
+    
+    if (utc_timestamp == 0) {
+        PPP_DEBUG_ERROR("Invalid timestamp: 0");
+        return ERRCODE_FAIL;
+    }
+    
+    // 准备时间结构(参照 cat1_porting.c 的 sync_time_utc 实现)
+    struct timeval tv = {0};
+    struct timezone tzone = {0};
+    
+    // settimeofday 的 tv_sec 设置为本地时间戳(UTC + 时区偏移)
+    // 这样系统时间就是本地时间,tz_minuteswest 用于时区信息记录
+    tv.tv_sec = utc_timestamp + timezone_quarters * MINUTES_PER_QUARTER * 60;
+    tv.tv_usec = 0;
+    
+    // 设置时区信息
+    tzone.tz_dsttime = 0;  // 不使用夏令时
+    tzone.tz_minuteswest = timezone_quarters * MINUTES_PER_QUARTER;
+    
+    PPP_DEBUG_INFO("Setting system time:");
+    PPP_DEBUG_INFO("  UTC timestamp: %u", utc_timestamp);
+    PPP_DEBUG_INFO("  Local timestamp: %ld (UTC + %d hours)", tv.tv_sec, timezone_quarters / 4);
+    PPP_DEBUG_INFO("  Timezone: %+d minutes (%+d quarters)", 
+                   tzone.tz_minuteswest, timezone_quarters);
+    
+    // 调用系统API设置时间
+    int ret = settimeofday(&tv, &tzone);
+    if (ret != 0) {
+        PPP_DEBUG_ERROR("settimeofday failed: %d", ret);
+        return ERRCODE_FAIL;
+    }
+    
+    // 验证时间设置成功
+    time_t current_time = time(NULL);
+    struct tm timeinfo;
+    localtime_r(&current_time, &timeinfo);
+    
+    PPP_DEBUG_CRITICAL("System time set successfully: %04d-%02d-%02d %02d:%02d:%02d",
+                      timeinfo.tm_year + 1900,
+                      timeinfo.tm_mon + 1,
+                      timeinfo.tm_mday,
+                      timeinfo.tm_hour,
+                      timeinfo.tm_min,
+                      timeinfo.tm_sec);
+    
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 通过AT+CCLK命令获取网络时间并更新系统时间
+ * @return ERRCODE_SUCC-成功,其他-失败
+ * 
+ * 实现说明(参照 ppp_api_update_time):
+ * 1. 发送 AT+CCLK? 命令获取网络时间
+ * 2. 解析响应,获取UTC时间和时区偏移
+ * 3. 计算本地时间 = UTC时间 + 时区偏移
+ * 4. 使用 settimeofday 设置系统时间为本地时间
+ * 
+ * AT+CCLK 返回格式说明:
+ * - 命令:AT+CCLK?
+ * - 响应:+CCLK: "25/10/20,02:59:43+32"
+ * - 时间字段(02:59:43)是 UTC 时间
+ * - 时区字段(+32)表示本地时区为 UTC+8
+ * - 最终显示时间:02:59:43 + 8小时 = 10:59:43
+ */
+errcode_t n706a_api_sync_time_from_network(void)
+{
+    PPP_DEBUG_INFO("=== Starting network time synchronization ===");
+    PPP_DEBUG_INFO("Sending AT+CCLK? command...");
+    
+    // 发送AT+CCLK?命令
+    n706a_at_response_t response;
+    n706a_at_error_t ret = n706a_at_send_command("AT+CCLK?", "OK", 5000, &response);
+    if (ret != N706A_AT_OK) {
+        PPP_DEBUG_ERROR("Failed to send AT+CCLK command");
+        return ERRCODE_FAIL;
+    }
+    
+    PPP_DEBUG_INFO("AT+CCLK response received");
+    
+    // 解析CCLK响应并转换为时间戳
+    uint32_t utc_timestamp = 0;
+    int timezone_quarters = 0;
+    errcode_t parse_ret = n706a_parse_cclk_and_convert_to_timestamp(response.response, &utc_timestamp, &timezone_quarters);
+    
+    if (parse_ret != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to parse CCLK response");
+        return parse_ret;
+    }
+    
+    // 设置系统时间
+    errcode_t set_ret = n706a_set_system_time(utc_timestamp, timezone_quarters);
+    if (set_ret != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to set system time");
+        return set_ret;
+    }
+    
+    PPP_DEBUG_CRITICAL("=== Network time synchronized successfully ===");
+    return ERRCODE_SUCC;
+}
+
 /**
  * @brief 动态设置N706A模块波特率(在所有模式下)
  * 
index 70e0a5e..a3da82a 100644 (file)
@@ -20,7 +20,7 @@
 #include <stdlib.h>
 
 /* ========== 全局socket管理 ========== */
-#define N706A_MAX_SOCKETS 6  // N706A支持0-5共6个socket
+#define N706A_MAX_SOCKETS 3 // N706A支持0-5共6个socket
 
 static n706a_tcp_socket_t *g_tcp_sockets[N706A_MAX_SOCKETS] = {NULL};
 static osal_mutex g_socket_table_mutex;
@@ -36,8 +36,54 @@ static osal_semaphore g_tcpsetup_urc_sems[N706A_MAX_SOCKETS];
 static bool g_tcpsetup_urc_results[N706A_MAX_SOCKETS];  // true=OK, false=FAIL
 static osal_mutex g_tcpsetup_urc_mutex;
 
+/* +TCPSEND URC等待机制(每个socket一个信号量) */
+static osal_semaphore g_tcpsend_urc_sems[N706A_MAX_SOCKETS];
+static size_t g_tcpsend_urc_sent_bytes[N706A_MAX_SOCKETS];  // 实际发送的字节数
+static bool g_tcpsend_urc_received[N706A_MAX_SOCKETS];  // 是否收到URC
+static osal_mutex g_tcpsend_urc_mutex;
+
 /* ========== 私有函数声明 ========== */
 
+/**
+ * @brief 安全打印AT响应(避免读取超出范围或遇到 \0 字节导致乱码)
+ * @param response AT响应结构
+ * @param max_len 最大打印长度(默认256)
+ */
+static void n706a_tcp_safe_print_response(const n706a_at_response_t *response, size_t max_len)
+{
+    /* 注意:response->response 是一个数组,不是指针,所以不需要检查 !response->response */
+    /* 数组的地址总是非空,检查它会导致编译警告 */
+    if (!response || response->length == 0) {
+        return;
+    }
+    
+    const size_t MAX_PRINT_LEN = (max_len > 0 && max_len < 512) ? max_len : 256;
+    size_t print_len = (response->length < MAX_PRINT_LEN) ? response->length : MAX_PRINT_LEN;
+    
+    /* 检查长度是否异常(可能是计算错误) */
+    if (response->length > 65536) {
+        PPP_DEBUG_ERROR("[N706A_TCP] Response length异常: %u (可能包含二进制数据或计算错误)", 
+                       (unsigned int)response->length);
+        print_len = MAX_PRINT_LEN;  /* 强制限制打印长度 */
+    }
+    
+    char print_buf[MAX_PRINT_LEN + 1];
+    memcpy(print_buf, response->response, print_len);
+    print_buf[print_len] = '\0';
+    
+    /* 替换不可打印字符为 '.'(保留 \r, \n, \t) */
+    for (size_t i = 0; i < print_len; i++) {
+        if (print_buf[i] < 32 || print_buf[i] >= 127) {
+            if (print_buf[i] != '\r' && print_buf[i] != '\n' && print_buf[i] != '\t') {
+                print_buf[i] = '.';
+            }
+        }
+    }
+    
+    PPP_DEBUG_CRITICAL("[N706A_TCP] Response: %s%s", print_buf, 
+                      (response->length > MAX_PRINT_LEN) ? "..." : "");
+}
+
 #ifdef N706A_SSL_USE_MBEDTLS
 static int tcp_mbedtls_send_callback(void *ctx, const unsigned char *buf, size_t len);
 static int tcp_mbedtls_recv_callback(void *ctx, unsigned char *buf, size_t len);
@@ -49,6 +95,8 @@ static void tcp_mbedtls_cleanup_ssl(n706a_tcp_socket_t *sock);
 static n706a_tcp_result_t n706a_tcp_force_close_socket_by_id(int socket_id);
 int n706a_tcp_find_free_socket_id(void);  /* 公共API,供HTTP和WebSocket使用 */
 static bool n706a_tcp_wait_for_tcpsetup_urc(int socket_id, uint32_t timeout_ms, bool *success);
+static void n706a_tcp_init_tcpsend_urc_wait(void);
+static bool n706a_tcp_wait_for_tcpsend_urc(int socket_id, uint32_t timeout_ms, size_t expected_bytes, size_t *sent_bytes);
 
 /* ========== 公共函数实现 ========== */
 
@@ -91,16 +139,29 @@ n706a_tcp_result_t n706a_tcp_socket_create_ex(n706a_tcp_socket_t *sock, int sock
         for (int i = 0; i < N706A_MAX_SOCKETS; i++) {
             if (osal_mutex_init(&g_socket_urc_mutexes[i]) != OSAL_SUCCESS) {
                 PPP_DEBUG_ERROR("Failed to init URC mutex for socket %d", i);
+                // 清理已创建的mutex
+                for (int j = 0; j < i; j++) {
+                    osal_mutex_destroy(&g_socket_urc_mutexes[j]);
+                }
                 return N706A_TCP_ERROR;
             }
-            if (osal_sem_init(&g_socket_urc_sems[i], 0) != OSAL_SUCCESS) {
-                PPP_DEBUG_ERROR("Failed to init URC semaphore for socket %d", i);
+            int sem_ret = osal_sem_init(&g_socket_urc_sems[i], 0);
+            if (sem_ret != OSAL_SUCCESS) {
+                PPP_DEBUG_ERROR("Failed to init URC semaphore for socket %d (ret=0x%x)", i, sem_ret);
+                // 清理已创建的资源
                 osal_mutex_destroy(&g_socket_urc_mutexes[i]);
+                for (int j = 0; j < i; j++) {
+                    osal_mutex_destroy(&g_socket_urc_mutexes[j]);
+                    osal_sem_destroy(&g_socket_urc_sems[j]);
+                }
                 return N706A_TCP_ERROR;
             }
         }
         g_urc_resources_initialized = true;
-        PPP_DEBUG_DEBUG("URC resource pool initialized for %d sockets", N706A_MAX_SOCKETS);
+        PPP_DEBUG_DEBUG("URC resource pool initialized for %d sockets (total %d mutexes + %d semaphores)", 
+                       N706A_MAX_SOCKETS, N706A_MAX_SOCKETS, N706A_MAX_SOCKETS);
+    } else {
+        PPP_DEBUG_DEBUG("URC resource pool already initialized, skipping");
     }
     
     osal_mutex_lock(&g_socket_table_mutex);
@@ -237,7 +298,116 @@ n706a_tcp_result_t n706a_tcp_config_ssl_client_cert(n706a_tcp_socket_t *sock,
 #endif
 
 /**
- * @brief 强制关闭指定socket ID的socket(方案1:连接前强制关闭旧socket)
+ * @brief 查询socket在模块端的状态(使用AT+IPSTATUS)
+ * @param socket_id socket ID (0-5)
+ * @param is_connected 输出:是否已连接(true=CONNECT, false=DISCONNECT)
+ * @param is_ssl 输出:是否是SSL连接(true=SSL, false=TCP,仅在已连接时有效)
+ * @return N706A_TCP_SUCCESS=查询成功,其他值=查询失败
+ */
+n706a_tcp_result_t n706a_tcp_query_socket_status(int socket_id, bool *is_connected, bool *is_ssl)
+{
+    if (socket_id < 0 || socket_id >= N706A_MAX_SOCKETS || !is_connected || !is_ssl) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    *is_connected = false;
+    *is_ssl = false;
+    
+    /* 发送AT+IPSTATUS命令查询socket状态 */
+    /* 注意:AT+IPSTATUS可能不返回OK,只返回+IPSTATUS响应,所以使用NULL作为expected */
+    char at_cmd[32];
+    snprintf(at_cmd, sizeof(at_cmd), "AT+IPSTATUS=%d", socket_id);
+    
+    n706a_at_response_t response;
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 3000, &response);
+    
+    if (ret != N706A_AT_OK || response.length == 0) {
+        PPP_DEBUG_DEBUG("Failed to query socket %d status via AT+IPSTATUS (ret=%d)", socket_id, ret);
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 解析响应:+IPSTATUS: <socket_id>,<status>[,<type>[,<send-buffer-size>]] */
+    /* 示例:+IPSTATUS: 0,CONNECT,TCP,4096 或 +IPSTATUS: 0,DISCONNECT */
+    const char *response_str = response.response;
+    char status_pattern[32];
+    snprintf(status_pattern, sizeof(status_pattern), "+IPSTATUS: %d,", socket_id);
+    
+    const char *status_pos = strstr(response_str, status_pattern);
+    if (!status_pos) {
+        PPP_DEBUG_DEBUG("Socket %d status response format error: %s", socket_id, response_str);
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 跳过 "+IPSTATUS: <socket_id>," */
+    const char *status_start = status_pos + strlen(status_pattern);
+    
+    /* 检查状态:CONNECT 或 DISCONNECT */
+    if (strncmp(status_start, "CONNECT", 7) == 0) {
+        *is_connected = true;
+        
+        /* 检查类型:TCP 或 UDP(SSL连接可能不显示类型,需要根据其他信息判断) */
+        /* 注意:AT+IPSTATUS可能不直接显示SSL,但我们可以通过检查socket的is_ssl字段来判断 */
+        /* 如果本地socket存在且is_ssl=true,认为是SSL连接 */
+        if (g_socket_table_initialized) {
+            osal_mutex_lock(&g_socket_table_mutex);
+            n706a_tcp_socket_t *sock = g_tcp_sockets[socket_id];
+            if (sock != NULL && sock->is_ssl) {
+                *is_ssl = true;
+            }
+            osal_mutex_unlock(&g_socket_table_mutex);
+        }
+        
+        PPP_DEBUG_DEBUG("Socket %d status: CONNECTED (SSL=%d)", socket_id, *is_ssl);
+    } else if (strncmp(status_start, "DISCONNECT", 10) == 0) {
+        *is_connected = false;
+        PPP_DEBUG_DEBUG("Socket %d status: DISCONNECTED", socket_id);
+    } else {
+        PPP_DEBUG_DEBUG("Socket %d status unknown: %s", socket_id, status_start);
+        return N706A_TCP_ERROR;
+    }
+    
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 检查socket在模块端是否已关闭
+ * @param socket_id socket ID (0-5)
+ * @return true=已关闭或不存在,false=可能仍打开
+ */
+static bool n706a_tcp_check_socket_closed(int socket_id)
+{
+    if (socket_id < 0 || socket_id >= N706A_MAX_SOCKETS) {
+        return true;  // 无效socket ID,认为已关闭
+    }
+    
+    /* 检查本地socket状态 */
+    if (g_socket_table_initialized) {
+        osal_mutex_lock(&g_socket_table_mutex);
+        n706a_tcp_socket_t *sock = g_tcp_sockets[socket_id];
+        if (sock != NULL) {
+            /* 如果本地状态是CLOSED,认为socket已关闭 */
+            bool is_closed = (sock->state == N706A_TCP_STATE_CLOSED);
+            osal_mutex_unlock(&g_socket_table_mutex);
+            if (is_closed) {
+                PPP_DEBUG_DEBUG("Socket %d is already CLOSED in local state", socket_id);
+                return true;
+            }
+        } else {
+            /* socket不在表中,可能已经关闭 */
+            osal_mutex_unlock(&g_socket_table_mutex);
+            PPP_DEBUG_DEBUG("Socket %d not in socket table, assuming closed", socket_id);
+            return true;
+        }
+    }
+    
+    /* 本地状态不是CLOSED,需要检查模块端状态 */
+    /* 注意:这里不发送AT命令检查,因为可能会干扰正在进行的操作 */
+    /* 如果本地状态不是CLOSED,保守地认为socket可能仍打开,需要关闭 */
+    return false;
+}
+
+/**
+ * @brief 强制关闭指定socket ID的socket(先检查状态,再决定是否需要关闭)
  * @param socket_id socket ID (0-5)
  * @return N706A_TCP_SUCCESS 成功,其他值失败
  */
@@ -247,28 +417,108 @@ static n706a_tcp_result_t n706a_tcp_force_close_socket_by_id(int socket_id)
         return N706A_TCP_EINVAL;
     }
     
-    /* 尝试通过AT命令强制关闭socket */
+    /* 先检查socket是否已经关闭 */
+    if (n706a_tcp_check_socket_closed(socket_id)) {
+        PPP_DEBUG_DEBUG("Socket %d is already closed, skipping close command", socket_id);
+        return N706A_TCP_SUCCESS;
+    }
+    
+    /* socket可能仍打开,先查询模块端状态,根据状态选择关闭命令 */
+    bool is_connected = false;
+    bool is_ssl = false;
+    n706a_tcp_result_t query_ret = n706a_tcp_query_socket_status(socket_id, &is_connected, &is_ssl);
+    
+    if (query_ret == N706A_TCP_SUCCESS && !is_connected) {
+        /* 模块端socket已断开,不需要关闭 */
+        PPP_DEBUG_DEBUG("Socket %d is already DISCONNECTED at module side, no need to close", socket_id);
+        return N706A_TCP_SUCCESS;
+    }
+    
+    /* socket可能仍打开,需要发送关闭命令 */
+    /* 根据查询到的状态(或本地socket的is_ssl字段)选择关闭命令 */
+    if (query_ret != N706A_TCP_SUCCESS) {
+        /* 查询失败,检查本地socket状态 */
+        if (g_socket_table_initialized) {
+            osal_mutex_lock(&g_socket_table_mutex);
+            n706a_tcp_socket_t *sock = g_tcp_sockets[socket_id];
+            if (sock != NULL) {
+                is_ssl = sock->is_ssl;
+            }
+            osal_mutex_unlock(&g_socket_table_mutex);
+        }
+    }
+    
     char at_cmd[32];
     n706a_at_response_t response;
     
-    /* 先尝试关闭普通TCP */
-    snprintf(at_cmd, sizeof(at_cmd), "AT+TCPCLOSE=%d", socket_id);
-    n706a_at_error_t ret = n706a_at_send_command(at_cmd, "OK", 3000, &response);
+    /* 根据socket类型选择关闭命令 */
+    if (is_ssl) {
+        /* SSL TCP连接,使用AT+SSLTCPCLOSE */
+        PPP_DEBUG_DEBUG("Socket %d is SSL connection, using AT+SSLTCPCLOSE", socket_id);
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCLOSE=%d", socket_id);
+    } else {
+        /* 普通TCP连接,使用AT+TCPCLOSE */
+        PPP_DEBUG_DEBUG("Socket %d is TCP connection, using AT+TCPCLOSE", socket_id);
+        snprintf(at_cmd, sizeof(at_cmd), "AT+TCPCLOSE=%d", socket_id);
+    }
+    
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 3000, &response);
+    
+    /* 检查响应中是否包含关闭URC(无论是 ERROR 还是 OK,都表示命令已确认) */
+    bool close_confirmed = false;
+    if (ret == N706A_AT_OK && response.length > 0) {
+        char close_pattern[32];
+        if (is_ssl) {
+            snprintf(close_pattern, sizeof(close_pattern), "+SSLTCPCLOSE: ");
+        } else {
+            snprintf(close_pattern, sizeof(close_pattern), "+TCPCLOSE: ");
+        }
+        
+        if (strstr(response.response, close_pattern) != NULL) {
+            /* 收到关闭URC,命令已确认 */
+            close_confirmed = true;
+            PPP_DEBUG_DEBUG("Socket %d force close confirmed: received %s URC", socket_id, close_pattern);
+        } else if (strstr(response.response, "OK") != NULL && 
+                   strstr(response.response, "ERROR") == NULL) {
+            /* 没有关闭URC,但有 OK 且没有 ERROR,也认为成功 */
+            PPP_DEBUG_DEBUG("Socket %d force close: received OK (no close URC)", socket_id);
+            close_confirmed = true;
+        }
+    }
     
-    if (ret == N706A_AT_OK && response.length > 0 &&
-        strstr(response.response, "OK") && !strstr(response.response, "ERROR")) {
-        PPP_DEBUG_DEBUG("Socket %d force closed successfully (TCP)", socket_id);
+    if (close_confirmed) {
+        /* 等待一段时间让模块端完全清理socket状态(避免状态混乱) */
+        osal_msleep(100);
         return N706A_TCP_SUCCESS;
     }
     
-    /* 如果普通TCP关闭失败,尝试关闭SSL TCP */
-    snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCLOSE=%d", socket_id);
-    ret = n706a_at_send_command(at_cmd, "OK", 3000, &response);
+    /* 如果第一次关闭失败,尝试另一种关闭方式(可能是状态判断错误) */
+    if (is_ssl) {
+        /* SSL关闭失败,尝试普通TCP关闭 */
+        PPP_DEBUG_WARN("Socket %d SSL close failed, trying TCP close", socket_id);
+        snprintf(at_cmd, sizeof(at_cmd), "AT+TCPCLOSE=%d", socket_id);
+    } else {
+        /* TCP关闭失败,尝试SSL TCP关闭 */
+        PPP_DEBUG_WARN("Socket %d TCP close failed, trying SSL TCP close", socket_id);
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCLOSE=%d", socket_id);
+    }
     
-    if (ret == N706A_AT_OK && response.length > 0 &&
-        strstr(response.response, "OK") && !strstr(response.response, "ERROR")) {
-        PPP_DEBUG_DEBUG("Socket %d force closed successfully (SSL TCP)", socket_id);
-        return N706A_TCP_SUCCESS;
+    ret = n706a_at_send_command(at_cmd, NULL, 3000, &response);
+    
+    if (ret == N706A_AT_OK && response.length > 0) {
+        char close_pattern[32];
+        if (is_ssl) {
+            snprintf(close_pattern, sizeof(close_pattern), "+TCPCLOSE: ");
+        } else {
+            snprintf(close_pattern, sizeof(close_pattern), "+SSLTCPCLOSE: ");
+        }
+        
+        if (strstr(response.response, close_pattern) != NULL || 
+            (strstr(response.response, "OK") != NULL && strstr(response.response, "ERROR") == NULL)) {
+            PPP_DEBUG_DEBUG("Socket %d force close confirmed via alternative method", socket_id);
+            osal_msleep(100);
+            return N706A_TCP_SUCCESS;
+        }
     }
     
     /* 即使关闭命令失败,也返回成功(模块端可能已经关闭) */
@@ -281,42 +531,348 @@ static n706a_tcp_result_t n706a_tcp_force_close_socket_by_id(int socket_id)
 }
 
 /**
- * @brief 查找空闲的socket ID(公共API,供HTTP和WebSocket使用)
+ * @brief 查找空闲的socket ID(通过AT命令查询模块端状态)
  * @return 空闲的socket ID (0-5),如果没有空闲则返回-1
  */
 int n706a_tcp_find_free_socket_id(void)
 {
-    if (!g_socket_table_initialized) {
-        return 0;  // 如果表未初始化,返回0(默认)
+    /* 遍历所有socket ID,通过AT+IPSTATUS查询模块端状态 */
+    for (int i = 0; i < N706A_MAX_SOCKETS; i++) {
+        bool is_connected = false;
+        bool is_ssl = false;
+        n706a_tcp_result_t query_ret = n706a_tcp_query_socket_status(i, &is_connected, &is_ssl);
+        
+        if (query_ret == N706A_TCP_SUCCESS) {
+            /* 查询成功,检查socket是否已断开 */
+            if (!is_connected) {
+                /* 模块端socket已断开,可以使用 */
+                PPP_DEBUG_DEBUG("Found free socket ID: %d (DISCONNECTED at module side)", i);
+                return i;
+            } else {
+                /* socket仍在使用中,跳过 */
+                PPP_DEBUG_DEBUG("Socket %d is in use (CONNECTED at module side)", i);
+                continue;
+            }
+        } else {
+            /* 查询失败(可能是ERROR响应),检查本地状态 */
+            if (g_socket_table_initialized) {
+                osal_mutex_lock(&g_socket_table_mutex);
+                n706a_tcp_socket_t *sock = g_tcp_sockets[i];
+                if (sock == NULL || sock->state == N706A_TCP_STATE_CLOSED) {
+                    /* 本地socket不存在或已关闭,可以使用 */
+                    osal_mutex_unlock(&g_socket_table_mutex);
+                    PPP_DEBUG_DEBUG("Found free socket ID: %d (not in local table or CLOSED)", i);
+                    return i;
+                }
+                osal_mutex_unlock(&g_socket_table_mutex);
+            } else {
+                /* 表未初始化,认为socket可用 */
+                PPP_DEBUG_DEBUG("Found free socket ID: %d (table not initialized)", i);
+                return i;
+            }
+        }
     }
     
-    osal_mutex_lock(&g_socket_table_mutex);
+    /* 没有找到空闲的socket */
+    PPP_DEBUG_WARN("No free socket ID available (all sockets are in use)");
+    return -1;
+}
+
+/**
+ * @brief 初始化+TCPSEND URC等待机制(内部函数)
+ */
+static void n706a_tcp_init_tcpsend_urc_wait(void)
+{
+    static bool initialized = false;
+    if (initialized) {
+        PPP_DEBUG_DEBUG("+TCPSEND URC wait mechanism already initialized, skipping");
+        return;
+    }
     
-    /* 查找空闲的socket ID(表中没有注册的socket) */
+    osal_mutex_init(&g_tcpsend_urc_mutex);
     for (int i = 0; i < N706A_MAX_SOCKETS; i++) {
-        if (g_tcp_sockets[i] == NULL) {
-            osal_mutex_unlock(&g_socket_table_mutex);
-            PPP_DEBUG_DEBUG("Found free socket ID: %d", i);
-            return i;
+        int sem_ret = osal_sem_init(&g_tcpsend_urc_sems[i], 0);
+        if (sem_ret != OSAL_SUCCESS) {
+            PPP_DEBUG_ERROR("Failed to initialize +TCPSEND URC semaphore for socket %d (ret=0x%x)", i, sem_ret);
+            // 继续初始化其他socket,即使某些失败
+            // 注意:如果信号量创建失败,URC回调仍然会被调用,但等待线程无法被唤醒
+            // 这会导致超时,但不会影响URC回调的执行
+        } else {
+            PPP_DEBUG_DEBUG("+TCPSEND URC semaphore initialized for socket %d", i);
         }
+        g_tcpsend_urc_sent_bytes[i] = 0;
+        g_tcpsend_urc_received[i] = false;
+    }
+    initialized = true;
+    PPP_DEBUG_DEBUG("+TCPSEND URC wait mechanism initialized (total %d semaphores)", N706A_MAX_SOCKETS);
+}
+
+/**
+ * @brief 提前初始化+TCPSEND URC等待机制(公共函数,供n706a_net_init调用)
+ */
+void n706a_tcp_preinit_tcpsend_urc_wait(void)
+{
+    n706a_tcp_init_tcpsend_urc_wait();
+}
+
+/**
+ * @brief +TCPSEND URC回调(由n706a_at_handler调用)
+ * @param socket_id Socket ID
+ * @param sent_bytes 实际发送的字节数
+ * 
+ * 注意:此函数由n706a_at_handler.c通过URC回调调用,不是应用层API
+ */
+void n706a_tcp_on_tcpsend_urc(int socket_id, size_t sent_bytes)
+{
+    if (socket_id < 0 || socket_id >= N706A_MAX_SOCKETS) {
+        PPP_DEBUG_ERROR("+TCPSEND URC callback: invalid socket_id=%d", socket_id);
+        return;
     }
     
-    osal_mutex_unlock(&g_socket_table_mutex);
+    PPP_DEBUG_INFO("+TCPSEND URC callback called: socket=%d, sent_bytes=%u", socket_id, (unsigned int)sent_bytes);
     
-    /* 如果所有socket都被占用,尝试查找状态为CLOSED的socket */
-    osal_mutex_lock(&g_socket_table_mutex);
-    for (int i = 0; i < N706A_MAX_SOCKETS; i++) {
-        if (g_tcp_sockets[i] != NULL && 
-            g_tcp_sockets[i]->state == N706A_TCP_STATE_CLOSED) {
+    osal_mutex_lock(&g_tcpsend_urc_mutex);
+    g_tcpsend_urc_sent_bytes[socket_id] = sent_bytes;
+    g_tcpsend_urc_received[socket_id] = true;
+    osal_mutex_unlock(&g_tcpsend_urc_mutex);
+    
+    /* 通知等待的线程 */
+    osal_sem_up(&g_tcpsend_urc_sems[socket_id]);
+    PPP_DEBUG_INFO("+TCPSEND URC: Semaphore signaled for socket %d", socket_id);
+    
+    PPP_DEBUG_INFO("+TCPSEND URC processed: socket=%d, sent_bytes=%u", socket_id, (unsigned int)sent_bytes);
+}
+
+/**
+ * @brief 等待+TCPSEND URC
+ * @param socket_id 等待的socket ID
+ * @param timeout_ms 超时时间(毫秒)
+ * @param expected_bytes 期望发送的字节数
+ * @param sent_bytes 输出:实际发送的字节数
+ * @return true=收到URC,false=超时
+ */
+static bool n706a_tcp_wait_for_tcpsend_urc(int socket_id, uint32_t timeout_ms, size_t expected_bytes, size_t *sent_bytes)
+{
+    if (!sent_bytes || socket_id < 0 || socket_id >= N706A_MAX_SOCKETS) {
+        return false;
+    }
+    
+    /* 初始化等待机制(首次调用时) */
+    n706a_tcp_init_tcpsend_urc_wait();
+    
+    /* 清除之前的结果 */
+    osal_mutex_lock(&g_tcpsend_urc_mutex);
+    g_tcpsend_urc_sent_bytes[socket_id] = 0;
+    g_tcpsend_urc_received[socket_id] = false;
+    osal_mutex_unlock(&g_tcpsend_urc_mutex);
+    
+    /* 等待URC信号量(超时时间) */
+    PPP_DEBUG_INFO("Waiting for +TCPSEND URC: socket=%d, timeout=%u ms, expected_bytes=%u", 
+                  socket_id, timeout_ms, (unsigned int)expected_bytes);
+    
+    /* 检查信号量是否有效(如果创建失败,sem可能无效) */
+    /* 注意:即使信号量创建失败,我们也应该继续等待,因为URC回调可能仍然会被调用 */
+    /* 如果信号量无效,我们需要使用轮询方式检查URC是否已经到达 */
+    
+    /* 在等待之前,先检查URC是否已经到达(可能在发送数据之前就到了) */
+    osal_mutex_lock(&g_tcpsend_urc_mutex);
+    bool already_received = g_tcpsend_urc_received[socket_id];
+    if (already_received) {
+        *sent_bytes = g_tcpsend_urc_sent_bytes[socket_id];
+        osal_mutex_unlock(&g_tcpsend_urc_mutex);
+        PPP_DEBUG_INFO("+TCPSEND URC already received before waiting: socket=%d, sent_bytes=%u (expected=%u)", 
+                      socket_id, (unsigned int)*sent_bytes, (unsigned int)expected_bytes);
+        return true;
+    }
+    osal_mutex_unlock(&g_tcpsend_urc_mutex);
+    
+    /* 使用轮询+信号量的混合方式等待URC(因为信号量可能无效或URC可能延迟) */
+    uint32_t start_time = (uint32_t)osal_get_jiffies();
+    uint32_t elapsed_ms = 0;
+    uint32_t poll_interval_ms = 100;  // 每100ms轮询一次
+    bool urc_received = false;
+    
+    /* 用于检测累积缓冲区变化的变量 */
+    uint32_t last_accumulate_len = 0;
+    uint8_t last_accumulate_preview[64] = {0};
+    uint32_t last_accumulate_preview_len = 0;
+    uint32_t last_check_time_ms = 0;
+    uint32_t check_interval_ms = 1000;  // 每1秒检查一次变化
+    
+    while (elapsed_ms < timeout_ms && !urc_received) {
+        /* 尝试等待信号量(使用较短的超时时间,以便定期轮询) */
+        uint32_t wait_time = (timeout_ms - elapsed_ms < poll_interval_ms) ? 
+                            (timeout_ms - elapsed_ms) : poll_interval_ms;
+        
+        int ret = osal_sem_down_timeout(&g_tcpsend_urc_sems[socket_id], wait_time);
+        
+        /* 检查URC是否已经到达(无论信号量是否成功) */
+        osal_mutex_lock(&g_tcpsend_urc_mutex);
+        if (g_tcpsend_urc_received[socket_id]) {
+            *sent_bytes = g_tcpsend_urc_sent_bytes[socket_id];
+            urc_received = true;
+            osal_mutex_unlock(&g_tcpsend_urc_mutex);
+            
+            if (ret == OSAL_SUCCESS) {
+                PPP_DEBUG_INFO("+TCPSEND URC received via semaphore: socket=%d, sent_bytes=%u (expected=%u, elapsed=%u ms)", 
+                              socket_id, (unsigned int)*sent_bytes, (unsigned int)expected_bytes, elapsed_ms);
+            } else {
+                PPP_DEBUG_INFO("+TCPSEND URC received (polling check): socket=%d, sent_bytes=%u (expected=%u, elapsed=%u ms, sem_ret=%d)", 
+                              socket_id, (unsigned int)*sent_bytes, (unsigned int)expected_bytes, elapsed_ms, ret);
+            }
+            return true;
+        }
+        osal_mutex_unlock(&g_tcpsend_urc_mutex);
+        
+        /* 更新已用时间 */
+        uint32_t current_time = (uint32_t)osal_get_jiffies();
+        elapsed_ms = osal_jiffies_to_msecs(current_time - start_time);
+        
+        /* 检查socket状态,如果已关闭则立即返回 */
+        osal_mutex_lock(&g_socket_table_mutex);
+        n706a_tcp_socket_t *sock = g_tcp_sockets[socket_id];
+        if (sock && sock->state == N706A_TCP_STATE_CLOSED) {
             osal_mutex_unlock(&g_socket_table_mutex);
-            PPP_DEBUG_DEBUG("Found closed socket ID: %d", i);
-            return i;
+            PPP_DEBUG_WARN("+TCPSEND URC wait: Socket %d found CLOSED, stopping wait early (elapsed=%u ms)", 
+                          socket_id, elapsed_ms);
+            return false;  /* 连接已关闭,URC不会到达 */
+        }
+        osal_mutex_unlock(&g_socket_table_mutex);
+        
+        /* 每1秒检查一次累积缓冲区的变化 */
+        if (elapsed_ms - last_check_time_ms >= check_interval_ms) {
+            last_check_time_ms = elapsed_ms;
+            
+            // 检查累积缓冲区是否有数据(可能包含+TCPSEND URC但还未被处理)
+            extern uint32_t n706a_at_get_accumulate_buffer_length(void);
+            extern bool n706a_at_get_accumulate_buffer_preview(uint8_t *buffer, uint32_t max_len, uint32_t *actual_len);
+            extern bool n706a_at_get_accumulate_buffer_all(uint8_t *buffer, uint32_t max_len, uint32_t *actual_len);
+            
+            uint32_t accumulate_len = n706a_at_get_accumulate_buffer_length();
+            
+            // 检查是否有变化(长度变化或内容变化)
+            bool has_changed = false;
+            if (accumulate_len != last_accumulate_len) {
+                has_changed = true;
+            } else if (accumulate_len > 0) {
+                // 长度相同,检查内容是否变化
+                uint8_t current_preview[64] = {0};
+                uint32_t current_preview_len = 0;
+                if (n706a_at_get_accumulate_buffer_preview(current_preview, 64, &current_preview_len)) {
+                    if (current_preview_len != last_accumulate_preview_len ||
+                        memcmp(current_preview, last_accumulate_preview, current_preview_len) != 0) {
+                        has_changed = true;
+                    }
+                }
+            }
+            
+            if (has_changed || accumulate_len > 0) {
+                // 获取完整的累积缓冲区内容(用于分析)
+                // 优化:使用静态缓冲区,减少栈使用(避免在软件定时器任务中栈溢出)
+                static uint8_t full_buffer[256] = {0};  // 静态缓冲区,避免栈分配
+                uint32_t full_len = 0;
+                if (n706a_at_get_accumulate_buffer_all(full_buffer, 256, &full_len)) {
+                    // 打印完整的累积缓冲区内容(十六进制和ASCII)
+                    // 优化:使用静态缓冲区和查表法,减少栈使用
+                    static char hex_str[768] = {0};  // 静态缓冲区,避免栈分配
+                    static char ascii_str[257] = {0};  // 静态缓冲区,避免栈分配
+                    memset(hex_str, 0, sizeof(hex_str));
+                    memset(ascii_str, 0, sizeof(ascii_str));
+                    
+                    /* 使用查表法替代snprintf,减少栈使用 */
+                    static const char hex_chars[] = "0123456789ABCDEF";
+                    uint32_t ascii_idx = 0;
+                    for (uint32_t i = 0; i < full_len && i < 256; i++) {
+                        /* HEX转换:使用查表法 */
+                        if (i * 3 + 3 < sizeof(hex_str)) {
+                            hex_str[i * 3] = hex_chars[(full_buffer[i] >> 4) & 0x0F];
+                            hex_str[i * 3 + 1] = hex_chars[full_buffer[i] & 0x0F];
+                            hex_str[i * 3 + 2] = ' ';
+                        }
+                        /* ASCII转换 */
+                        if (full_buffer[i] >= 32 && full_buffer[i] < 127) {
+                            if (ascii_idx < 256) ascii_str[ascii_idx++] = full_buffer[i];
+                        } else if (full_buffer[i] == '\r') {
+                            if (ascii_idx < 255) {
+                                ascii_str[ascii_idx++] = '\\';
+                                ascii_str[ascii_idx++] = 'r';
+                            }
+                        } else if (full_buffer[i] == '\n') {
+                            if (ascii_idx < 255) {
+                                ascii_str[ascii_idx++] = '\\';
+                                ascii_str[ascii_idx++] = 'n';
+                            }
+                        } else {
+                            if (ascii_idx < 256) ascii_str[ascii_idx++] = '.';
+                        }
+                    }
+                    ascii_str[ascii_idx < 256 ? ascii_idx : 256] = '\0';
+                    
+                    PPP_DEBUG_INFO("Still waiting for +TCPSEND URC: socket=%d, elapsed=%u ms, timeout=%u ms", 
+                                  socket_id, elapsed_ms, timeout_ms);
+                    PPP_DEBUG_INFO("  [累积缓冲区变化检测] 长度=%u 字节 (上次=%u), 完整内容:", 
+                                  accumulate_len, last_accumulate_len);
+                    PPP_DEBUG_INFO("  HEX: %s", hex_str);
+                    PPP_DEBUG_INFO("  ASCII: %s", ascii_str);
+                    
+                    // 检查是否包含+TCPSEND相关字符串
+                    const char *buffer_str = (const char *)full_buffer;
+                    if (strstr(buffer_str, "TCPSEND") != NULL) {
+                        PPP_DEBUG_WARN("  ⚠️ 累积缓冲区包含 'TCPSEND' 字符串!可能包含+TCPSEND URC但未被识别");
+                    }
+                    if (strstr(buffer_str, "OPERATION") != NULL || strstr(buffer_str, "EXPIRED") != NULL) {
+                        PPP_DEBUG_WARN("  ⚠️ 累积缓冲区包含 'OPERATION' 或 'EXPIRED' 字符串!可能是错误响应");
+                    }
+                } else {
+                    // 如果无法获取完整内容,至少打印预览
+                    static uint8_t preview_bytes[32] = {0};  // 静态缓冲区,避免栈分配
+                    static char hex_str[96] = {0};  // 静态缓冲区,避免栈分配
+                    memset(preview_bytes, 0, sizeof(preview_bytes));
+                    memset(hex_str, 0, sizeof(hex_str));
+                    uint32_t preview_len = 0;
+                    if (n706a_at_get_accumulate_buffer_preview(preview_bytes, 32, &preview_len)) {
+                        /* 使用查表法替代snprintf,减少栈使用 */
+                        static const char hex_chars[] = "0123456789ABCDEF";
+                        for (uint32_t i = 0; i < preview_len && i * 3 + 3 < sizeof(hex_str); i++) {
+                            hex_str[i * 3] = hex_chars[(preview_bytes[i] >> 4) & 0x0F];
+                            hex_str[i * 3 + 1] = hex_chars[preview_bytes[i] & 0x0F];
+                            hex_str[i * 3 + 2] = ' ';
+                        }
+                        PPP_DEBUG_INFO("Still waiting for +TCPSEND URC: socket=%d, elapsed=%u ms, timeout=%u ms, accumulate_buffer=%u bytes, last %u bytes: %s", 
+                                      socket_id, elapsed_ms, timeout_ms, accumulate_len, preview_len, hex_str);
+                    }
+                }
+                
+                // 更新上次的值
+                last_accumulate_len = accumulate_len;
+                if (accumulate_len > 0) {
+                    uint8_t current_preview[64] = {0};
+                    uint32_t current_preview_len = 0;
+                    if (n706a_at_get_accumulate_buffer_preview(current_preview, 64, &current_preview_len)) {
+                        memcpy(last_accumulate_preview, current_preview, current_preview_len);
+                        last_accumulate_preview_len = current_preview_len;
+                    }
+                } else {
+                    memset(last_accumulate_preview, 0, sizeof(last_accumulate_preview));
+                    last_accumulate_preview_len = 0;
+                }
+            } else {
+                // 没有变化,只打印简单的等待信息
+                PPP_DEBUG_INFO("Still waiting for +TCPSEND URC: socket=%d, elapsed=%u ms, timeout=%u ms (累积缓冲区无变化,长度=%u)", 
+                              socket_id, elapsed_ms, timeout_ms, accumulate_len);
+            }
+        }
+        
+        /* 如果信号量等待失败且不是超时,记录警告 */
+        if (ret != OSAL_SUCCESS && ret != OSAL_ETIME) {
+            PPP_DEBUG_WARN("+TCPSEND URC semaphore wait failed: ret=%d (0x%x), elapsed=%u ms", ret, ret, elapsed_ms);
         }
     }
-    osal_mutex_unlock(&g_socket_table_mutex);
     
-    PPP_DEBUG_WARN("No free socket ID found, will use socket 0");
-    return -1;  // 没有空闲socket,返回-1表示失败
+    /* 超时或失败 */
+    PPP_DEBUG_WARN("+TCPSEND URC wait timeout for socket %d (timeout=%u ms, elapsed=%u ms, expected_bytes=%u)", 
+                  socket_id, timeout_ms, elapsed_ms, (unsigned int)expected_bytes);
+    return false;
 }
 
 /**
@@ -326,16 +882,23 @@ static void n706a_tcp_init_tcpsetup_urc_wait(void)
 {
     static bool initialized = false;
     if (initialized) {
+        PPP_DEBUG_DEBUG("+TCPSETUP URC wait mechanism already initialized, skipping");
         return;
     }
     
     osal_mutex_init(&g_tcpsetup_urc_mutex);
     for (int i = 0; i < N706A_MAX_SOCKETS; i++) {
-        osal_sem_init(&g_tcpsetup_urc_sems[i], 0);
+        int sem_ret = osal_sem_init(&g_tcpsetup_urc_sems[i], 0);
+        if (sem_ret != OSAL_SUCCESS) {
+            PPP_DEBUG_ERROR("Failed to initialize +TCPSETUP URC semaphore for socket %d (ret=0x%x)", i, sem_ret);
+            // 继续初始化其他socket,即使某些失败
+        } else {
+            PPP_DEBUG_DEBUG("+TCPSETUP URC semaphore initialized for socket %d", i);
+        }
         g_tcpsetup_urc_results[i] = false;
     }
     initialized = true;
-    PPP_DEBUG_DEBUG("+TCPSETUP URC wait mechanism initialized");
+    PPP_DEBUG_DEBUG("+TCPSETUP URC wait mechanism initialized (total %d semaphores)", N706A_MAX_SOCKETS);
 }
 
 /**
@@ -449,8 +1012,8 @@ n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock,
         }
     }
     
-    /* 额外检查:即使状态是CLOSED,也强制关闭模块端的socket(防止模块端状态不一致) */
-    PPP_DEBUG_DEBUG("Force closing socket %d at module side before new connection", sock->socket_id);
+    /* 额外检查:检查模块端socket状态,如果需要则关闭(防止模块端状态不一致) */
+    PPP_DEBUG_DEBUG("Checking socket %d status at module side before new connection", sock->socket_id);
     n706a_tcp_force_close_socket_by_id(sock->socket_id);
     
     /* 等待模块端完全清理socket状态(避免状态混乱导致连接失败) */
@@ -482,7 +1045,7 @@ n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock,
         
         PPP_DEBUG_CRITICAL("[N706A_TCP] AT+SSLTCPSETUP response: ret=%d, len=%u", ret, response.length);
         if (response.length > 0) {
-            PPP_DEBUG_CRITICAL("[N706A_TCP] Response: %s", response.response);
+            n706a_tcp_safe_print_response(&response, 256);
         }
         
         /* ========== 方案3:验证+TCPSETUP URC响应 ========== */
@@ -533,8 +1096,12 @@ n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock,
                 return N706A_TCP_ECONNREF;
             }
         } else {
-            PPP_DEBUG_ERROR("[N706A_TCP] SSL TCP connection failed! Response: %s", 
-                           response.length > 0 ? response.response : "(empty)");
+            PPP_DEBUG_ERROR("[N706A_TCP] SSL TCP connection failed! Response: ");
+            if (response.length > 0) {
+                n706a_tcp_safe_print_response(&response, 256);
+            } else {
+                PPP_DEBUG_ERROR("(empty)");
+            }
             
             /* ========== 方案2:如果当前socket连接失败,尝试使用其他socket ID ========== */
             int free_socket_id = n706a_tcp_find_free_socket_id();
@@ -587,56 +1154,106 @@ n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock,
         
         PPP_DEBUG_CRITICAL("[N706A_TCP] AT+TCPSETUP response: ret=%d, len=%u", ret, response.length);
         if (response.length > 0) {
-            PPP_DEBUG_CRITICAL("[N706A_TCP] Response: %s", response.response);
+            n706a_tcp_safe_print_response(&response, 256);
         }
         
         /* ========== 方案3:验证+TCPSETUP URC响应 ========== */
-        /* 检查响应是否包含OK且不包含ERROR */
-        if (ret == N706A_AT_OK && response.length > 0 &&
-            strstr(response.response, "OK") &&
-            !strstr(response.response, "ERROR")) {
+        /* 检查响应是否包含OK */
+        /* 注意:响应中可能包含其他命令的响应(如+TCPCLOSE: ERROR),
+         * 需要检查AT+TCPSETUP命令本身的响应,而不是整个响应块 */
+        if (ret == N706A_AT_OK && response.length > 0) {
+            /* 查找AT+TCPSETUP命令在响应中的位置 */
+            char cmd_pattern[64];
+            snprintf(cmd_pattern, sizeof(cmd_pattern), "AT+TCPSETUP=%d,", sock->socket_id);
+            const char *cmd_pos = strstr(response.response, cmd_pattern);
             
-            /* 进一步验证:检查是否有+TCPSETUP URC(可能在响应中,也可能是异步URC) */
-            bool urc_ok = false;
+            /* 检查AT+TCPSETUP命令后是否有OK响应 */
+            bool has_ok_after_cmd = false;
+            bool has_error_after_cmd = false;
             
-            /* 检查响应中是否包含+TCPSETUP: n,OK */
-            char urc_pattern[32];
-            snprintf(urc_pattern, sizeof(urc_pattern), "+TCPSETUP: %d,OK", sock->socket_id);
-            if (strstr(response.response, urc_pattern)) {
-                urc_ok = true;
-                PPP_DEBUG_CRITICAL("[N706A_TCP] Found +TCPSETUP: %d,OK in response", sock->socket_id);
+            if (cmd_pos) {
+                /* 在AT+TCPSETUP命令后查找OK或ERROR */
+                const char *search_start = cmd_pos;
+                const char *ok_pos = strstr(search_start, "OK");
+                const char *error_pos = strstr(search_start, "ERROR");
+                
+                /* 检查OK是否在AT+TCPSETUP命令之后 */
+                if (ok_pos && ok_pos > cmd_pos) {
+                    /* 检查OK前面是否有+TCPSETUP: n,FAIL(表示连接失败) */
+                    char fail_pattern[32];
+                    snprintf(fail_pattern, sizeof(fail_pattern), "+TCPSETUP: %d,FAIL", sock->socket_id);
+                    if (strstr(search_start, fail_pattern)) {
+                        has_error_after_cmd = true;
+                    } else {
+                        has_ok_after_cmd = true;
+                    }
+                }
+                
+                /* 检查ERROR是否在AT+TCPSETUP命令之后,且是+TCPSETUP相关的ERROR */
+                if (error_pos && error_pos > cmd_pos) {
+                    char error_pattern[32];
+                    snprintf(error_pattern, sizeof(error_pattern), "+TCPSETUP: %d,FAIL", sock->socket_id);
+                    if (strstr(search_start, error_pattern)) {
+                        has_error_after_cmd = true;
+                    }
+                }
             } else {
-                /* 检查是否有+TCPSETUP: n,FAIL */
-                snprintf(urc_pattern, sizeof(urc_pattern), "+TCPSETUP: %d,FAIL", sock->socket_id);
+                /* 如果找不到AT+TCPSETUP命令,检查整个响应是否包含OK */
+                if (strstr(response.response, "OK")) {
+                    has_ok_after_cmd = true;
+                }
+            }
+            
+            if (has_error_after_cmd) {
+                PPP_DEBUG_ERROR("[N706A_TCP] Found +TCPSETUP: %d,FAIL in response", sock->socket_id);
+                sock->state = N706A_TCP_STATE_ERROR;
+                return N706A_TCP_ECONNREF;
+            }
+            
+            if (has_ok_after_cmd) {
+                /* 进一步验证:检查是否有+TCPSETUP URC(可能在响应中,也可能是异步URC) */
+                bool urc_ok = false;
+                
+                /* 检查响应中是否包含+TCPSETUP: n,OK */
+                char urc_pattern[32];
+                snprintf(urc_pattern, sizeof(urc_pattern), "+TCPSETUP: %d,OK", sock->socket_id);
                 if (strstr(response.response, urc_pattern)) {
-                    PPP_DEBUG_ERROR("[N706A_TCP] Found +TCPSETUP: %d,FAIL in response", sock->socket_id);
-                    sock->state = N706A_TCP_STATE_ERROR;
-                    return N706A_TCP_ECONNREF;
+                    urc_ok = true;
+                    PPP_DEBUG_CRITICAL("[N706A_TCP] Found +TCPSETUP: %d,OK in response", sock->socket_id);
+                } else {
+                    /* 如果没有找到明确的URC,等待一下看是否有异步URC到达 */
+                    bool urc_success = false;
+                    if (n706a_tcp_wait_for_tcpsetup_urc(sock->socket_id, 500, &urc_success)) {
+                        urc_ok = urc_success;
+                    } else {
+                        /* 超时,但命令返回OK,认为连接成功(可能是URC还没到达) */
+                        PPP_DEBUG_WARN("[N706A_TCP] +TCPSETUP URC wait timeout, but command returned OK, assuming success");
+                        urc_ok = true;
+                    }
                 }
                 
-                /* 如果没有找到明确的URC,等待一下看是否有异步URC到达 */
-                bool urc_success = false;
-                if (n706a_tcp_wait_for_tcpsetup_urc(sock->socket_id, 500, &urc_success)) {
-                    urc_ok = urc_success;
+                if (urc_ok) {
+                    PPP_DEBUG_CRITICAL("[N706A_TCP] TCP connection established successfully!");
+                    sock->state = N706A_TCP_STATE_CONNECTED;
+                    return N706A_TCP_SUCCESS;
                 } else {
-                    /* 超时,但命令返回OK,认为连接成功(可能是URC还没到达) */
-                    PPP_DEBUG_WARN("[N706A_TCP] +TCPSETUP URC wait timeout, but command returned OK, assuming success");
-                    urc_ok = true;
+                    PPP_DEBUG_ERROR("[N706A_TCP] TCP connection failed: URC indicates FAIL");
+                    sock->state = N706A_TCP_STATE_ERROR;
+                    return N706A_TCP_ECONNREF;
                 }
-            }
-            
-            if (urc_ok) {
-                PPP_DEBUG_CRITICAL("[N706A_TCP] TCP connection established successfully!");
-                sock->state = N706A_TCP_STATE_CONNECTED;
-                return N706A_TCP_SUCCESS;
             } else {
-                PPP_DEBUG_ERROR("[N706A_TCP] TCP connection failed: URC indicates FAIL");
+                /* AT+TCPSETUP命令后没有OK响应 */
+                PPP_DEBUG_ERROR("[N706A_TCP] AT+TCPSETUP command did not return OK");
                 sock->state = N706A_TCP_STATE_ERROR;
                 return N706A_TCP_ECONNREF;
             }
         } else {
-            PPP_DEBUG_ERROR("[N706A_TCP] TCP connection failed! Response: %s", 
-                           response.length > 0 ? response.response : "(empty)");
+            PPP_DEBUG_ERROR("[N706A_TCP] TCP connection failed! Response: ");
+            if (response.length > 0) {
+                n706a_tcp_safe_print_response(&response, 256);
+            } else {
+                PPP_DEBUG_ERROR("(empty)");
+            }
             
             /* ========== 方案2:如果当前socket连接失败,尝试使用其他socket ID ========== */
             /* 检查是否有空闲的socket ID可以使用 */
@@ -695,7 +1312,7 @@ n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock,
     
     PPP_DEBUG_CRITICAL("[N706A_TCP] AT+TCPSETUP response: ret=%d, len=%u", ret, response.length);
     if (response.length > 0) {
-        PPP_DEBUG_CRITICAL("[N706A_TCP] Response: %s", response.response);
+        n706a_tcp_safe_print_response(&response, 256);
     }
     
     /* ========== 方案3:验证+TCPSETUP URC响应 ========== */
@@ -915,9 +1532,237 @@ n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock,
 }
 
 /**
- * @brief 发送TCP数据
+ * @brief 单次TCP数据发送(内部辅助函数)
+ * @param sock TCP socket指针
+ * @param data 要发送的数据
+ * @param len 要发送的数据长度
+ *   - HEX模式:必须 <= 2048字节(二进制数据长度)
+ *   - ASCII模式:必须 <= 4096字节
+ * @param sent 实际发送的字节数(输出,二进制数据长度)
+ * @return 操作结果
  */
-n706a_tcp_result_t n706a_tcp_send(n706a_tcp_socket_t *sock,
+static n706a_tcp_result_t n706a_tcp_send_single(n706a_tcp_socket_t *sock,
+                                                 const uint8_t *data,
+                                                 size_t len,
+                                                 size_t *sent)
+{
+    if (!sock || !data || len == 0 || !sent) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    /* 检查是否使用HEX模式(根据AT+RECVMODE设置) */
+    bool hex_mode = n706a_at_get_tcp_recv_hex_mode();
+    
+    /* 检查长度限制 */
+    size_t max_len = hex_mode ? 2048 : 4096;  /* HEX模式:2048字节,ASCII模式:4096字节 */
+    if (len > max_len) {
+        PPP_DEBUG_ERROR("[TCP_SEND] Requested length %u exceeds max (%u for %s mode)", 
+                       (unsigned int)len, (unsigned int)max_len, hex_mode ? "HEX" : "ASCII");
+        return N706A_TCP_EINVAL;
+    }
+    
+    char at_cmd[64];
+    n706a_at_response_t response;
+    
+    /* 如果使用HEX模式,需要将二进制数据转换为HEX字符串 */
+    uint8_t *send_data = (uint8_t *)data;
+    size_t send_len = len;
+    char *hex_buffer = NULL;
+    
+    if (hex_mode) {
+        /* 分配HEX字符串缓冲区(二进制长度 * 2) */
+        size_t hex_str_len = len * 2;
+        hex_buffer = (char *)malloc(hex_str_len);
+        if (!hex_buffer) {
+            PPP_DEBUG_ERROR("Failed to allocate HEX buffer (%u bytes)", (unsigned int)hex_str_len);
+            return N706A_TCP_ENOBUFS;
+        }
+        
+        /* 将二进制数据转换为HEX字符串 */
+        static const char hex_chars[] = "0123456789ABCDEF";
+        size_t i;
+        for (i = 0; i < len; i++) {
+            hex_buffer[i * 2] = hex_chars[(data[i] >> 4) & 0x0F];
+            hex_buffer[i * 2 + 1] = hex_chars[data[i] & 0x0F];
+        }
+        
+        send_data = (uint8_t *)hex_buffer;
+        send_len = hex_str_len;
+        
+        PPP_DEBUG_VERBOSE("[TCP_SEND] TCP send: binary_len=%u -> hex_len=%u (HEX mode)", 
+                       (unsigned int)len, (unsigned int)send_len);
+    }
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    if (sock->is_ssl) {
+        /* SSL TCP发送命令格式: AT+SSLTCPSEND=<n>,<data_length>
+         * 注意:根据AT命令手册和Python验证代码:
+         * 1. SSL TCP没有HEX模式参数(,,1),HEX模式通过AT+RECVMODE设置
+         * 2. length参数是指二进制数据的字节数,而不是HEX字符串长度
+         * 示例:AT+SSLTCPSEND=0,1024 表示发送1024字节的二进制数据
+         */
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPSEND=%d,%zu",
+                sock->socket_id, len);
+    } else
+#endif
+    {
+        if (hex_mode) {
+            /* Buffer模式,HEX模式:AT+TCPSEND=<n>,<binary_len>,,1
+             * 注意:根据AT命令手册和Python验证代码,length参数是指二进制数据的字节数,而不是HEX字符串长度
+             * 示例:AT+TCPSEND=0,1024,,1 表示发送1024字节的二进制数据(对应2048个HEX字符)
+             */
+            snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu,,1",
+                    sock->socket_id, len);
+        } else {
+            /* Buffer模式,ASCII模式(默认,与Python代码一致):AT+TCPSEND=<n>,<binary_len> */
+            snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu",
+                    sock->socket_id, len);
+        }
+    }
+    
+    /* 发送 AT+TCPSEND 命令,期望收到 '>' 提示符 */
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, ">", 5000, &response);
+    if (ret != N706A_AT_OK) {
+        if (hex_buffer) {
+            free(hex_buffer);
+        }
+        /* 检查响应中是否包含 "SOCKET ID OPEN FAILED" 错误 */
+        if (response.length > 0 && strstr(response.response, "SOCKET ID OPEN FAILED") != NULL) {
+            PPP_DEBUG_ERROR("Socket %d is closed (SOCKET ID OPEN FAILED), updating state to CLOSED", sock->socket_id);
+            sock->state = N706A_TCP_STATE_CLOSED;
+            return N706A_TCP_ENOTCONN;
+        }
+        PPP_DEBUG_ERROR("Failed to send %s command (expected '>' prompt not received)", at_cmd);
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 检查响应中是否包含 "SOCKET ID OPEN FAILED" 错误(即使命令返回 OK) */
+    if (response.length > 0 && strstr(response.response, "SOCKET ID OPEN FAILED") != NULL) {
+        if (hex_buffer) {
+            free(hex_buffer);
+        }
+        PPP_DEBUG_ERROR("Socket %d is closed (SOCKET ID OPEN FAILED in response), updating state to CLOSED", sock->socket_id);
+        sock->state = N706A_TCP_STATE_CLOSED;
+        return N706A_TCP_ENOTCONN;
+    }
+    
+    /* 验证成功,说明响应中已包含 '>' 提示符 */
+    PPP_DEBUG_VERBOSE("[TCP_SEND] AT+TCPSEND command sent successfully, received '>' prompt");
+    
+    /* 延迟50-100ms(根据AT命令手册要求,Python代码使用100ms) */
+    PPP_DEBUG_VERBOSE("[TCP_SEND] Received '>' prompt, delaying 100ms before sending data...");
+    osal_msleep(100);
+    
+    /* 发送实际数据(HEX模式下发送HEX字符串,ASCII模式下发送原始数据) */
+    PPP_DEBUG_VERBOSE("[TCP_SEND] Sending TCP data (%u bytes as %s, original binary=%u bytes)...", 
+                   (unsigned int)send_len, hex_mode ? "HEX string" : "raw data", (unsigned int)len);
+    
+    /* 打印实际发送的数据内容(用于调试,详细模式) */
+#if PPP_DEBUG_VERBOSE_ENABLE
+    if (hex_mode) {
+        /* HEX模式:打印HEX字符串的前128个字符(64字节) */
+        size_t print_len = (send_len > 128) ? 128 : send_len;
+        static char hex_preview[256] = {0};  /* 使用静态缓冲区 */
+        memset(hex_preview, 0, sizeof(hex_preview));
+        for (size_t i = 0; i < print_len && i < sizeof(hex_preview) - 1; i++) {
+            hex_preview[i] = send_data[i];  // HEX模式下,send_data是ASCII字符
+        }
+        if (send_len > 128) {
+            PPP_DEBUG_VERBOSE("[TCP_SEND] HEX data (first 128 chars): %s... (total %u chars)", 
+                          hex_preview, (unsigned int)send_len);
+        } else {
+            PPP_DEBUG_VERBOSE("[TCP_SEND] HEX data: %s", hex_preview);
+        }
+    } else {
+        /* ASCII模式:打印原始二进制数据的HEX表示(前64字节) */
+        size_t print_len = (send_len > 64) ? 64 : send_len;
+        static char hex_preview[256] = {0};  /* 使用静态缓冲区 */
+        memset(hex_preview, 0, sizeof(hex_preview));
+        /* 使用查表法替代snprintf,减少栈使用 */
+        static const char hex_chars[] = "0123456789ABCDEF";
+        for (size_t i = 0; i < print_len && i * 3 + 3 < sizeof(hex_preview); i++) {
+            hex_preview[i * 3] = hex_chars[(send_data[i] >> 4) & 0x0F];
+            hex_preview[i * 3 + 1] = hex_chars[send_data[i] & 0x0F];
+            hex_preview[i * 3 + 2] = ' ';
+        }
+        if (send_len > 64) {
+            PPP_DEBUG_VERBOSE("[TCP_SEND] Binary data (first 64 bytes, HEX): %s... (total %u bytes)", 
+                          hex_preview, (unsigned int)send_len);
+        } else {
+            PPP_DEBUG_VERBOSE("[TCP_SEND] Binary data (HEX): %s", hex_preview);
+        }
+    }
+#endif
+    
+    ppp_error_t write_result = ppp_serial_write(send_data, send_len);
+    
+    /* 释放HEX缓冲区 */
+    if (hex_buffer) {
+        free(hex_buffer);
+    }
+    
+    if (write_result != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to send TCP data via ppp_serial_write: error=%d", write_result);
+        return N706A_TCP_ERROR;
+    }
+    PPP_DEBUG_VERBOSE("[TCP_SEND] TCP data sent successfully (%u bytes as %s, original binary=%u bytes)", 
+                   (unsigned int)send_len, hex_mode ? "HEX string" : "raw data", (unsigned int)len);
+    
+    /* 等待发送完成响应(+TCPSEND: <n>,<length> 或 +TCPSEND: ERROR) */
+    /* 按照Python代码逻辑:等待30秒,如果没收到URC就返回失败 */
+    /* 
+     * 注意:根据AT命令手册,+TCPSEND URC返回的length参数:
+     * - HEX模式:返回的是二进制数据长度(不是HEX字符串长度)
+     * - ASCII模式:返回的是二进制数据长度
+     * 因此,无论哪种模式,期望值都应该是原始二进制数据长度(len)
+     */
+    size_t actual_sent = 0;
+    size_t expected_urc_bytes = len;  // 无论HEX还是ASCII模式,URC都返回二进制数据长度
+    bool urc_received = n706a_tcp_wait_for_tcpsend_urc(sock->socket_id, 30000, expected_urc_bytes, &actual_sent);
+    
+    if (!urc_received) {
+        PPP_DEBUG_WARN("+TCPSEND URC wait timeout (30s), checking TCP connection state...");
+        /* 超时后检查TCP连接状态,而不是立即判定为失败 */
+        bool is_connected = false;
+        bool is_ssl = false;
+        n706a_tcp_result_t query_ret = n706a_tcp_query_socket_status(sock->socket_id, &is_connected, &is_ssl);
+        if (query_ret == N706A_TCP_SUCCESS) {
+            if (is_connected) {
+                /* 连接仍然有效,可能是URC丢失,但数据已发送成功 */
+                PPP_DEBUG_WARN("+TCPSEND URC timeout but socket is still connected, assuming send succeeded");
+                *sent = len;  // 假设发送成功
+                return N706A_TCP_SUCCESS;
+            } else {
+                /* 连接已断开 */
+                PPP_DEBUG_ERROR("+TCPSEND URC timeout and socket is disconnected, send failed");
+                sock->state = N706A_TCP_STATE_CLOSED;
+                return N706A_TCP_ENOTCONN;
+            }
+        } else {
+            /* 查询失败,保守处理:判定为发送失败 */
+            PPP_DEBUG_ERROR("+TCPSEND URC timeout and failed to query socket status, send failed");
+            return N706A_TCP_ERROR;
+        }
+    }
+    
+    /* 检查实际发送的字节数是否与期望一致 */
+    /* 注意:根据AT命令手册,+TCPSEND URC返回的length是二进制数据长度,不是HEX字符串长度 */
+    if (actual_sent != len) {
+        PPP_DEBUG_WARN("+TCPSEND URC: sent_bytes=%u (binary), expected=%u (binary), hex_str_len=%u", 
+                      (unsigned int)actual_sent, (unsigned int)len, (unsigned int)send_len);
+        /* 即使字节数不一致,也认为发送成功(可能是模块端的问题) */
+    }
+    *sent = actual_sent;  // 返回实际发送的二进制数据长度
+    
+    PPP_DEBUG_VERBOSE("[TCP_SEND] TCP data send confirmed: %u bytes sent successfully (%s mode, URC returned binary length=%u)", 
+                   (unsigned int)*sent, hex_mode ? "HEX" : "ASCII", (unsigned int)actual_sent);
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 发送TCP数据(支持多次发送,确保发送完所有数据)
+ */
+n706a_tcp_result_t n706a_tcp_send(n706a_tcp_socket_t *sock,
                                   const uint8_t *data,
                                   size_t len,
                                   size_t *sent)
@@ -933,6 +1778,7 @@ n706a_tcp_result_t n706a_tcp_send(n706a_tcp_socket_t *sock,
 #ifdef N706A_SSL_USE_MBEDTLS
     /* mbedtls模式:通过SSL发送 */
     if (sock->is_ssl && sock->ssl_initialized) {
+        /* mbedtls会自动处理分片,不需要手动分片 */
         int ret = mbedtls_ssl_write(&sock->ssl, data, len);
         if (ret > 0) {
             *sent = ret;
@@ -944,52 +1790,519 @@ n706a_tcp_result_t n706a_tcp_send(n706a_tcp_socket_t *sock,
 #endif
     
     /* AT指令模式或普通TCP */
-    /* 注意:使用HEX模式发送(mode=1),数据需要编码为HEX字符串 */
+    /* 注意:根据AT命令手册,AT+TCPSEND的最大长度限制:
+     * - HEX模式:2048字节(二进制数据长度)
+     * - ASCII模式:4096字节
+     * 如果数据超过限制,需要多次发送,直到发送完所有数据
+     */
+    
+    /* 检查是否使用HEX模式(根据AT+RECVMODE设置) */
+    bool hex_mode = n706a_at_get_tcp_recv_hex_mode();
+    size_t max_len = hex_mode ? 2048 : 4096;  /* HEX模式:2048字节,ASCII模式:4096字节 */
+    
+    /* 如果请求长度 <= 最大限制,直接调用单次发送 */
+    if (len <= max_len) {
+        return n706a_tcp_send_single(sock, data, len, sent);
+    }
+    
+    /* 请求长度 > 最大限制,需要多次发送 */
+    PPP_DEBUG_INFO("[TCP_SEND] Requested length %u exceeds AT+TCPSEND max (%u for %s mode), will perform multiple sends", 
+                  (unsigned int)len, (unsigned int)max_len, hex_mode ? "HEX" : "ASCII");
+    
+    size_t total_sent = 0;
+    const uint8_t *current_data = data;
+    size_t remaining = len;
+    
+    /* 循环发送,每次最多发送max_len字节 */
+    while (remaining > 0 && total_sent < len) {
+        size_t single_send_len = (remaining > max_len) ? max_len : remaining;
+        size_t single_sent = 0;
+        
+        n706a_tcp_result_t ret = n706a_tcp_send_single(sock, current_data, single_send_len, &single_sent);
+        
+        if (ret != N706A_TCP_SUCCESS) {
+            /* 发送失败,返回已发送的数据 */
+            *sent = total_sent;
+            if (ret == N706A_TCP_ENOTCONN) {
+                /* 连接已关闭 */
+                return N706A_TCP_ENOTCONN;
+            }
+            /* 其他错误,返回已发送的数据 */
+            if (total_sent > 0) {
+                PPP_DEBUG_WARN("[TCP_SEND] Partial send completed: %u/%u bytes (error during send)", 
+                              (unsigned int)total_sent, (unsigned int)len);
+                return N706A_TCP_SUCCESS;
+            }
+            return ret;
+        }
+        
+        if (single_sent == 0) {
+            /* 没有数据被发送,可能连接已关闭 */
+            *sent = total_sent;
+            if (total_sent > 0) {
+                PPP_DEBUG_WARN("[TCP_SEND] Partial send completed: %u/%u bytes (no data sent)", 
+                              (unsigned int)total_sent, (unsigned int)len);
+                return N706A_TCP_SUCCESS;
+            }
+            return N706A_TCP_ERROR;
+        }
+        
+        /* 累积已发送的数据 */
+        total_sent += single_sent;
+        current_data += single_sent;
+        remaining -= single_sent;
+        
+        PPP_DEBUG_VERBOSE("[TCP_SEND] Multi-send progress: %u/%u bytes (remaining=%u)", 
+                         (unsigned int)total_sent, (unsigned int)len, (unsigned int)remaining);
+        
+        /* 如果本次发送的数据少于请求的长度,说明发送失败 */
+        if (single_sent < single_send_len) {
+            PPP_DEBUG_WARN("[TCP_SEND] Multi-send partial: %u/%u bytes (expected %u, got %u)", 
+                          (unsigned int)total_sent, (unsigned int)len, 
+                          (unsigned int)single_send_len, (unsigned int)single_sent);
+            *sent = total_sent;
+            return N706A_TCP_SUCCESS;
+        }
+    }
+    
+    /* 所有数据发送完成 */
+    *sent = total_sent;
+    PPP_DEBUG_INFO("[TCP_SEND] Multi-send completed successfully: %u/%u bytes", 
+                  (unsigned int)total_sent, (unsigned int)len);
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 单次TCP数据读取(内部辅助函数,最多读取2048字节)
+ * @param sock TCP socket指针
+ * @param buffer 接收缓冲区
+ * @param len 请求读取的长度(必须 <= 2048)
+ * @param received 实际读取的字节数(输出)
+ * @param timeout_ms 超时时间(毫秒)
+ * @return 操作结果
+ */
+static n706a_tcp_result_t n706a_tcp_recv_single(n706a_tcp_socket_t *sock,
+                                                 uint8_t *buffer,
+                                                 size_t len,
+                                                 size_t *received,
+                                                 uint32_t timeout_ms)
+{
+    if (!sock || !buffer || len == 0 || !received || len > 2048) {
+        return N706A_TCP_EINVAL;
+    }
+    
     char at_cmd[64];
     n706a_at_response_t response;
     
+    PPP_DEBUG_VERBOSE("[TCP_RECV] Calling AT+TCPREAD: sock->socket_id=%d, len=%u, timeout=%u ms", 
+                  sock->socket_id, (unsigned int)len, timeout_ms);
+    
 #ifdef N706A_SSL_USE_AT_COMMAND
     if (sock->is_ssl) {
-        /* HEX模式:AT+SSLTCPSEND=<n>,<binary_len>,,1 */
-        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPSEND=%d,%zu,,1",
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPREAD=%d,%zu",
                 sock->socket_id, len);
     } else
 #endif
     {
-        /* HEX模式:AT+TCPSEND=<n>,<binary_len>,,1 */
-        snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu,,1",
+        snprintf(at_cmd, sizeof(at_cmd), "AT+TCPREAD=%d,%zu",
                 sock->socket_id, len);
     }
     
-    /* 发送 AT+TCPSEND 命令 */
-    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 5000, &response);
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, timeout_ms, &response);
     if (ret != N706A_AT_OK) {
-        PPP_DEBUG_ERROR("Failed to send %s command", at_cmd);
-        return N706A_TCP_ERROR;
-    }
-    
-    /* 检查响应中是否包含 '>' 提示符(根据AT命令手册要求) */
-    if (strstr((const char *)response.response, ">") == NULL) {
-        PPP_DEBUG_ERROR("Response does not contain '>' prompt: %s", response.response);
-        return N706A_TCP_ERROR;
+        return N706A_TCP_ETIMEDOUT;
     }
     
-    /* 延迟50-100ms(根据AT命令手册要求,这里取75ms) */
-    PPP_DEBUG_DEBUG("Received '>' prompt, delaying 75ms before sending data...");
-    osal_msleep(75);
-    
-    /* 发送实际数据 */
-    ret = ppp_at_send_raw_data(data, len, sock->timeout_ms);
-    if (ret != N706A_AT_OK) {
-        return N706A_TCP_ERROR;
+    /* 解析响应,提取数据(HEX模式) */
+    if (response.length > 0) {
+        const char *response_str = response.response;
+        
+        /* 优先检查响应中是否包含 +TCPRECV 或 +SSLTCPRECV URC(异步接收的数据) */
+        /* 这些URC包含实际的数据,应该优先处理 */
+        /* 
+         * 注意:根据AT指令手册,AT+TCPREAD的响应格式应该是:
+         *   +TCPREAD: <n>,<len>,<data>
+         * 但实际模块可能在响应中包含 +TCPRECV URC,而不是 +TCPREAD: 格式。
+         * 这种情况下,+TCPRECV URC 中的 socket_id 可能与 AT+TCPREAD 命令中的 socket_id 不一致,
+         * 需要验证 socket_id 匹配,避免数据错误。
+         */
+        /* 注意:响应开头可能有 \x00 字节,需要手动跳过才能使用 strstr */
+        /* 虽然 n706a_extract_complete_response 会跳过开头的 \x00,但写入环形缓冲区的数据可能仍包含 \x00 */
+        const char *tcprecv_start = NULL;
+        const char *ssltcprecv_start = NULL;
+        
+        /* 手动查找 +TCPRECV 或 +SSLTCPRECV,跳过 \x00 字节 */
+        for (size_t i = 0; i < response.length; i++) {
+            if (response_str[i] == '\0') {
+                continue;  // 跳过 \x00 字节
+            }
+            /* 检查是否匹配 "+TCPRECV: " */
+            if (i + 10 <= response.length && strncmp(response_str + i, "+TCPRECV: ", 10) == 0) {
+                tcprecv_start = response_str + i;
+                break;
+            }
+            /* 检查是否匹配 "+SSLTCPRECV: " */
+            if (i + 13 <= response.length && strncmp(response_str + i, "+SSLTCPRECV: ", 13) == 0) {
+                ssltcprecv_start = response_str + i;
+                break;
+            }
+        }
+        
+        /* 如果手动查找没找到,使用 strstr 作为后备(处理正常情况,没有 \x00 字节) */
+        if (!tcprecv_start && !ssltcprecv_start) {
+            tcprecv_start = strstr(response_str, "+TCPRECV: ");
+            ssltcprecv_start = strstr(response_str, "+SSLTCPRECV: ");
+        }
+        
+        if (!tcprecv_start && !ssltcprecv_start) {
+            /* 没有找到 +TCPRECV URC,记录调试信息 */
+            PPP_DEBUG_WARN("[TCP_RECV] No +TCPRECV or +SSLTCPRECV URC found in response (len=%u)", 
+                          (unsigned int)response.length);
+            /* 详细调试:打印响应的前32字节(默认关闭,编译期移除) */
+#if PPP_DEBUG_VERBOSE_ENABLE
+            static char debug_buf[128] = {0};  /* 使用静态缓冲区:32字节 * 4字符/字节(最坏情况)= 128字节 */
+            memset(debug_buf, 0, sizeof(debug_buf));
+            size_t debug_len = (response.length < 32) ? response.length : 32;  /* 只打印前32字节 */
+            for (size_t i = 0; i < debug_len && strlen(debug_buf) + 5 < sizeof(debug_buf); i++) {
+                if (response_str[i] == '\0') {
+                    strncat(debug_buf, "\\x00", sizeof(debug_buf) - strlen(debug_buf) - 1);
+                } else if (response_str[i] >= 32 && response_str[i] < 127) {
+                    char c = response_str[i];
+                    size_t pos = strlen(debug_buf);
+                    if (pos + 1 < sizeof(debug_buf)) {
+                        debug_buf[pos] = c;
+                        debug_buf[pos + 1] = '\0';
+                    }
+                } else {
+                    char hex_str[5];
+                    snprintf(hex_str, sizeof(hex_str), "\\x%02X", (uint8_t)response_str[i]);
+                    strncat(debug_buf, hex_str, sizeof(debug_buf) - strlen(debug_buf) - 1);
+                }
+            }
+            PPP_DEBUG_VERBOSE("[TCP_RECV] Response preview (first %u bytes): %s", (unsigned int)debug_len, debug_buf);
+#endif
+        }
+        
+        if (tcprecv_start || ssltcprecv_start) {
+            /* 找到 +TCPRECV 或 +SSLTCPRECV URC,提取其中的数据 */
+            const char *urc_start = tcprecv_start ? tcprecv_start : ssltcprecv_start;
+            size_t parse_len = response.length - (urc_start - response_str);
+            
+            PPP_DEBUG_VERBOSE("[TCP_RECV] Found +TCPRECV URC in response: offset=%u, parse_len=%u, response_len=%u", 
+                          (unsigned int)(urc_start - response_str), (unsigned int)parse_len, (unsigned int)response.length);
+            
+            int socket_id;
+            size_t data_len;
+            const uint8_t *tcp_data;
+            
+            /* 使用外部函数解析 +TCPRECV URC */
+            extern bool n706a_parse_tcprecv_urc_public(const char *urc_data, size_t urc_len,
+                                                       int *socket_id, size_t *data_len, const uint8_t **data);
+            
+            if (n706a_parse_tcprecv_urc_public(urc_start, parse_len, &socket_id, &data_len, &tcp_data)) {
+                PPP_DEBUG_VERBOSE("[TCP_RECV] Successfully parsed +TCPRECV URC: socket_id=%d, data_len=%u", 
+                              socket_id, (unsigned int)data_len);
+                /* 验证socket_id是否匹配 */
+                PPP_DEBUG_VERBOSE("[TCP_RECV] Extracted +TCPRECV URC from AT+TCPREAD response: URC_socket_id=%d, sock->socket_id=%d, data_len=%u", 
+                              socket_id, sock->socket_id, (unsigned int)data_len);
+                if (socket_id != sock->socket_id) {
+                    PPP_DEBUG_WARN("[TCP_RECV] +TCPRECV URC socket_id MISMATCH: expected=%d (sock->socket_id), got=%d (URC_socket_id), dropping data", 
+                                  sock->socket_id, socket_id);
+                    PPP_DEBUG_WARN("[TCP_RECV] This indicates a potential data corruption risk - URC data belongs to different socket!");
+                    *received = 0;
+                    return N706A_TCP_SUCCESS;
+                }
+                
+                /* 检查是否使用HEX模式 */
+                bool hex_mode = n706a_at_get_tcp_recv_hex_mode();
+                size_t binary_data_len = data_len;
+                const uint8_t *binary_data = tcp_data;
+                
+                if (hex_mode) {
+                    /* HEX模式:需要解码HEX字符串 */
+                    /* 注意:不能使用 strlen,因为 HEX 字符串后面可能跟着 \r\n 和其他数据 */
+                    /* 应该使用 data_len * 2 作为 HEX 字符串长度,或者手动计算连续 HEX 字符的长度 */
+                    size_t expected_hex_str_len = data_len * 2;
+                    
+                    /* 手动计算实际 HEX 字符串长度(只计算连续的 HEX 字符) */
+                    size_t actual_hex_str_len = 0;
+                    const char *hex_str = (const char *)tcp_data;
+                    size_t remaining_len = parse_len - (hex_str - (const char *)urc_start);
+                    
+                    /* 计算连续 HEX 字符的长度 */
+                    for (size_t i = 0; i < remaining_len && actual_hex_str_len < expected_hex_str_len; i++) {
+                        char c = hex_str[i];
+                        if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
+                            actual_hex_str_len++;
+                        } else {
+                            /* 遇到非 HEX 字符,停止计算 */
+                            break;
+                        }
+                    }
+                    
+                    /* 使用实际 HEX 字符串长度(不能超过期望长度) */
+                    size_t hex_str_len = (actual_hex_str_len < expected_hex_str_len) ? actual_hex_str_len : expected_hex_str_len;
+                    
+                    if (hex_str_len != expected_hex_str_len) {
+                        PPP_DEBUG_WARN("+TCPRECV HEX length mismatch: expected=%u, got=%u (actual_hex_str_len=%u)", 
+                                      (unsigned int)expected_hex_str_len, (unsigned int)hex_str_len, (unsigned int)actual_hex_str_len);
+                        /* 如果实际长度小于期望长度,说明数据不完整,返回错误 */
+                        if (hex_str_len < expected_hex_str_len) {
+                            *received = 0;
+                            return N706A_TCP_SUCCESS;
+                        }
+                        /* 如果实际长度大于期望长度,截断到期望长度(不应该发生,但为了安全) */
+                        hex_str_len = expected_hex_str_len;
+                    }
+                    
+                    /* 解码HEX字符串 */
+                    size_t decode_len = (data_len < len) ? data_len : len;
+                    uint8_t *decode_buffer = (uint8_t *)malloc(decode_len);
+                    if (!decode_buffer) {
+                        *received = 0;
+                        return N706A_TCP_ERROR;
+                    }
+                    
+                    size_t decoded = 0;
+                    for (size_t i = 0; i < hex_str_len && decoded < decode_len; i += 2) {
+                        uint8_t high = (tcp_data[i] >= '0' && tcp_data[i] <= '9') ? 
+                                       (tcp_data[i] - '0') :
+                                       ((tcp_data[i] >= 'A' && tcp_data[i] <= 'F') ? 
+                                        (tcp_data[i] - 'A' + 10) :
+                                        (tcp_data[i] - 'a' + 10));
+                        uint8_t low = (tcp_data[i+1] >= '0' && tcp_data[i+1] <= '9') ? 
+                                      (tcp_data[i+1] - '0') :
+                                      ((tcp_data[i+1] >= 'A' && tcp_data[i+1] <= 'F') ? 
+                                       (tcp_data[i+1] - 'A' + 10) :
+                                       (tcp_data[i+1] - 'a' + 10));
+                        decode_buffer[decoded++] = (high << 4) | low;
+                    }
+                    
+                    memcpy(buffer, decode_buffer, decoded);
+                    free(decode_buffer);
+                    *received = decoded;
+                    PPP_DEBUG_VERBOSE("[TCP_RECV] Successfully extracted data from +TCPRECV URC: socket_id=%d (matched), hex_len=%u, binary_len=%u", 
+                                  socket_id, (unsigned int)hex_str_len, (unsigned int)decoded);
+                    return N706A_TCP_SUCCESS;
+                } else {
+                    /* ASCII模式:直接复制 */
+                    size_t copy_len = (data_len < len) ? data_len : len;
+                    memcpy(buffer, tcp_data, copy_len);
+                    *received = copy_len;
+                    PPP_DEBUG_VERBOSE("[TCP_RECV] Successfully extracted data from +TCPRECV URC (ASCII): socket_id=%d (matched), len=%u", 
+                                  socket_id, (unsigned int)copy_len);
+                    return N706A_TCP_SUCCESS;
+                }
+            }
+        }
+        
+        /* 如果没有找到 +TCPRECV URC,继续处理 +TCPREAD: 响应 */
+        /* 
+         * 根据AT指令手册,AT+TCPREAD的标准响应格式是:
+         *   +TCPREAD: <n>,<len>,<data>
+         * 这是命令响应格式,与 +TCPRECV URC 格式不同。
+         * 如果响应中包含 +TCPRECV URC,应该优先处理(上面的代码已处理)。
+         * 如果响应中只包含 +TCPREAD: 格式,则在这里处理。
+         */
+        /* 查找 +TCPREAD: 或 +SSLTCPREAD: */
+        const char *tcpread_start = strstr(response_str, "+TCPREAD:");
+        if (!tcpread_start) {
+#ifdef N706A_SSL_USE_AT_COMMAND
+            tcpread_start = strstr(response_str, "+SSLTCPREAD:");
+            if (!tcpread_start) {
+#endif
+                /* 没有找到 +TCPREAD: 响应,返回超时状态,减少轮询频率 */
+                *received = 0;
+                return N706A_TCP_ETIMEDOUT;  /* 返回超时,让上层知道需要等待 */
+#ifdef N706A_SSL_USE_AT_COMMAND
+            }
+#endif
+        }
+        
+        /* 解析格式:+TCPREAD: <n>,<hex_len>,<hex_data> 或 +SSLTCPREAD: <n>,<hex_len>,<hex_data> */
+        int socket_id;
+        size_t hex_length;
+        const char *hex_data_start;
+        
+        /* 跳过 "+TCPREAD: " 或 "+SSLTCPREAD: " */
+        const char *p = tcpread_start;
+        if (strncmp(p, "+TCPREAD:", 9) == 0) {
+            p += 9;
+        } else if (strncmp(p, "+SSLTCPREAD:", 12) == 0) {
+            p += 12;
+        } else {
+            *received = 0;
+            return N706A_TCP_SUCCESS;
+        }
+        
+        /* 跳过空格 */
+        while (*p == ' ' || *p == '\t') {
+            p++;
+        }
+        
+        /* 检查是否是 ERROR 响应(连接已关闭) */
+        if (strncmp(p, "ERROR", 5) == 0) {
+            /* +TCPREAD: ERROR 表示连接已关闭 */
+            PPP_DEBUG_ERROR("[TCP_RECV] AT+TCPREAD returned ERROR for socket %d, connection closed", sock->socket_id);
+            sock->state = N706A_TCP_STATE_CLOSED;
+            *received = 0;
+            return N706A_TCP_ENOTCONN;
+        }
+        
+        /* 解析socket_id */
+        socket_id = 0;
+        while (*p >= '0' && *p <= '9') {
+            socket_id = socket_id * 10 + (*p - '0');
+            p++;
+        }
+        
+        if (*p != ',') {
+            *received = 0;
+            return N706A_TCP_SUCCESS;
+        }
+        p++;  // 跳过 ','
+        
+        /* 解析hex_length */
+        hex_length = 0;
+        while (*p >= '0' && *p <= '9') {
+            hex_length = hex_length * 10 + (*p - '0');
+            p++;
+        }
+        
+        if (*p != ',') {
+            *received = 0;
+            return N706A_TCP_SUCCESS;
+        }
+        p++;  // 跳过 ','
+        
+        /* 检查是否是 +TCPREAD: 0,0(没有数据)的情况 */
+        /* 这种情况下,应该返回ETIMEDOUT,让上层知道需要等待,而不是立即重试 */
+        if (hex_length == 0) {
+            /* 没有数据,返回超时状态,让上层增加延迟再重试 */
+            PPP_DEBUG_DEBUG("[TCP_RECV] AT+TCPREAD returned 0 bytes (no data), returning ETIMEDOUT to reduce polling");
+            *received = 0;
+            return N706A_TCP_ETIMEDOUT;  /* 返回超时,让上层知道需要等待 */
+        }
+        
+        /* hex_data_start指向HEX数据开始位置 */
+        hex_data_start = p;
+        
+        /* 查找数据结束位置(\r\n或字符串结束) */
+        const char *hex_data_end = strstr(hex_data_start, "\r\n");
+        if (!hex_data_end) {
+            hex_data_end = response_str + response.length;
+        }
+        
+        size_t hex_data_str_len = hex_data_end - hex_data_start;
+        
+        /* 检查是否使用HEX模式(根据AT+RECVMODE设置) */
+        bool hex_mode = n706a_at_get_tcp_recv_hex_mode();
+        bool is_hex_format = false;
+        
+        if (hex_mode) {
+            /* HEX模式:强制尝试HEX解码 */
+            if (hex_data_str_len > 0 && hex_data_str_len % 2 == 0 && hex_length == hex_data_str_len) {
+                /* 检查是否所有字符都是十六进制字符 */
+                const char *check_p = hex_data_start;
+                size_t check_count = (hex_data_str_len < 100) ? hex_data_str_len : 100;  // 检查前100个字符
+                bool all_hex = true;
+                size_t i;
+                for (i = 0; i < check_count; i++) {
+                    if (!((*check_p >= '0' && *check_p <= '9') || 
+                          (*check_p >= 'A' && *check_p <= 'F') || 
+                          (*check_p >= 'a' && *check_p <= 'f'))) {
+                        all_hex = false;
+                        break;
+                    }
+                    check_p++;
+                }
+                if (all_hex) {
+                    is_hex_format = true;
+                } else {
+                    PPP_DEBUG_WARN("HEX mode enabled but data contains non-HEX characters");
+                }
+            } else {
+                PPP_DEBUG_WARN("HEX mode enabled but data length mismatch (hex_data_str_len=%u, hex_length=%u, hex_data_str_len%%2=%u)", 
+                              (unsigned int)hex_data_str_len, (unsigned int)hex_length, (unsigned int)(hex_data_str_len % 2));
+            }
+        } else {
+            /* ASCII模式:检查是否是HEX格式(自动检测) */
+            if (hex_data_str_len > 0 && hex_data_str_len % 2 == 0) {
+                /* 简单检查:前几个字符是否都是十六进制字符 */
+                const char *check_p = hex_data_start;
+                size_t check_count = (hex_data_str_len < 20) ? hex_data_str_len : 20;  // 检查前20个字符
+                bool all_hex = true;
+                size_t i;
+                for (i = 0; i < check_count; i++) {
+                    if (!((*check_p >= '0' && *check_p <= '9') || 
+                          (*check_p >= 'A' && *check_p <= 'F') || 
+                          (*check_p >= 'a' && *check_p <= 'f'))) {
+                        all_hex = false;
+                        break;
+                    }
+                    check_p++;
+                }
+                if (all_hex && hex_length == hex_data_str_len) {
+                    /* 如果AT+TCPREAD返回的len等于HEX字符串长度,说明是HEX格式 */
+                    is_hex_format = true;
+                }
+            }
+        }
+        
+        if (is_hex_format) {
+            /* HEX格式:需要解码 */
+            size_t binary_len = hex_data_str_len / 2;
+            if (binary_len > len) {
+                binary_len = len;
+            }
+            
+            /* 解码HEX字符串 */
+            uint8_t *decode_buffer = (uint8_t *)malloc(binary_len);
+            if (!decode_buffer) {
+                *received = 0;
+                return N706A_TCP_ERROR;
+            }
+            
+            size_t decoded_len = 0;
+            size_t i;
+            for (i = 0; i < hex_data_str_len && decoded_len < binary_len; i += 2) {
+                uint8_t high = (hex_data_start[i] >= '0' && hex_data_start[i] <= '9') ? 
+                               (hex_data_start[i] - '0') :
+                               ((hex_data_start[i] >= 'A' && hex_data_start[i] <= 'F') ? 
+                                (hex_data_start[i] - 'A' + 10) :
+                                (hex_data_start[i] - 'a' + 10));
+                uint8_t low = (hex_data_start[i+1] >= '0' && hex_data_start[i+1] <= '9') ? 
+                              (hex_data_start[i+1] - '0') :
+                              ((hex_data_start[i+1] >= 'A' && hex_data_start[i+1] <= 'F') ? 
+                               (hex_data_start[i+1] - 'A' + 10) :
+                               (hex_data_start[i+1] - 'a' + 10));
+                decode_buffer[decoded_len++] = (high << 4) | low;
+            }
+            
+            memcpy(buffer, decode_buffer, decoded_len);
+            free(decode_buffer);
+            *received = decoded_len;
+            PPP_DEBUG_DEBUG("TCPREAD HEX decoded: hex_len=%u -> binary_len=%u", 
+                          (unsigned int)hex_data_str_len, (unsigned int)decoded_len);
+            return N706A_TCP_SUCCESS;
+        } else {
+            /* ASCII格式:直接复制 */
+            size_t copy_len = (hex_data_str_len < len) ? hex_data_str_len : len;
+            memcpy(buffer, hex_data_start, copy_len);
+            *received = copy_len;
+            PPP_DEBUG_DEBUG("TCPREAD ASCII: len=%u", (unsigned int)copy_len);
+            return N706A_TCP_SUCCESS;
+        }
     }
     
-    *sent = len;
-    return N706A_TCP_SUCCESS;
+    /* 如果没有找到 +TCPREAD: 响应,或者解析失败,返回超时状态 */
+    /* 这样可以减少轮询频率,降低功耗 */
+    *received = 0;
+    return N706A_TCP_ETIMEDOUT;  /* 返回超时,让上层知道需要等待,而不是立即重试 */
 }
 
 /**
- * @brief 接收TCP数据
+ * @brief 接收TCP数据(支持多次读取,确保读取完所有请求的数据)
  */
 n706a_tcp_result_t n706a_tcp_recv(n706a_tcp_socket_t *sock,
                                   uint8_t *buffer,
@@ -1006,26 +2319,96 @@ n706a_tcp_result_t n706a_tcp_recv(n706a_tcp_socket_t *sock,
     }
     
     /* 优先从URC缓存读取数据(如果有) */
-    if (sock->urc_recv_buffer && sock->urc_recv_mutex && sock->urc_recv_length > 0) {
+    if (sock->urc_recv_buffer && sock->urc_recv_mutex) {
         osal_mutex *mutex = (osal_mutex *)sock->urc_recv_mutex;
         osal_mutex_lock(mutex);
         
-        size_t copy_len = (sock->urc_recv_length < len) ? sock->urc_recv_length : len;
-        memcpy(buffer, sock->urc_recv_buffer, copy_len);
-        
-        // 移动剩余数据到缓冲区开头
-        if (copy_len < sock->urc_recv_length) {
-            memmove(sock->urc_recv_buffer, sock->urc_recv_buffer + copy_len, 
-                   sock->urc_recv_length - copy_len);
+        // 如果缓存有数据,直接读取
+        if (sock->urc_recv_length > 0) {
+            size_t copy_len = (sock->urc_recv_length < len) ? sock->urc_recv_length : len;
+            memcpy(buffer, sock->urc_recv_buffer, copy_len);
+            
+            // 移动剩余数据到缓冲区开头
+            if (copy_len < sock->urc_recv_length) {
+                memmove(sock->urc_recv_buffer, sock->urc_recv_buffer + copy_len, 
+                       sock->urc_recv_length - copy_len);
+            }
+            sock->urc_recv_length -= copy_len;
+            
+            osal_mutex_unlock(mutex);
+            
+            *received = copy_len;
+            
+            /* 详细调试:打印读取的数据内容(默认关闭,编译期移除) */
+#if PPP_DEBUG_VERBOSE_ENABLE
+            PPP_DEBUG_VERBOSE("[TCP_RECV] Read from URC buffer: socket=%d, len=%u, remaining=%u", 
+                           sock->socket_id, (unsigned int)copy_len, (unsigned int)sock->urc_recv_length);
+            if (copy_len > 0) {
+                size_t print_len = (copy_len > 32) ? 32 : copy_len;  /* 只打印前32字节 */
+                static char data_hex[128] = {0};  /* 使用静态缓冲区 */
+                memset(data_hex, 0, sizeof(data_hex));
+                for (size_t i = 0; i < print_len && i * 3 + 3 < sizeof(data_hex); i++) {
+                    snprintf(data_hex + i * 3, 4, "%02X ", buffer[i]);
+                }
+                if (copy_len > 32) {
+                    PPP_DEBUG_VERBOSE("[TCP_RECV] URC buffer data (first 32 bytes, HEX): %s... (total %u bytes)", 
+                              data_hex, (unsigned int)copy_len);
+                } else {
+                    PPP_DEBUG_VERBOSE("[TCP_RECV] URC buffer data (HEX): %s", data_hex);
+                }
+            }
+#endif
+            
+            return N706A_TCP_SUCCESS;
         }
-        sock->urc_recv_length -= copy_len;
         
-        osal_mutex_unlock(mutex);
-        
-        *received = copy_len;
-        PPP_DEBUG_DEBUG("Read from URC buffer: socket=%d, len=%u, remaining=%u", 
-                       sock->socket_id, (unsigned int)copy_len, (unsigned int)sock->urc_recv_length);
-        return N706A_TCP_SUCCESS;
+        // 缓存为空,如果有信号量,先等待URC数据
+        // 优化:使用较长的超时时间(60秒),主要依赖URC机制,减少轮询
+        // 如果timeout_ms小于60秒,使用timeout_ms;否则使用60秒(避免无限等待)
+        if (sock->urc_data_available_sem) {
+            osal_semaphore *sem = (osal_semaphore *)sock->urc_data_available_sem;
+            osal_mutex_unlock(mutex);  // 释放mutex,避免死锁
+            
+            // 等待信号量:使用较长的超时时间(60秒),完全依赖URC机制
+            // 这样可以降低功耗和AT命令开销,适合小系统
+            uint32_t urc_wait_timeout = (timeout_ms > 60000) ? 60000 : timeout_ms;
+            if (urc_wait_timeout < timeout_ms) {
+                PPP_DEBUG_DEBUG("[TCP_RECV] Using URC wait timeout %u ms (capped at 60s) instead of %u ms for socket %d", 
+                              urc_wait_timeout, timeout_ms, sock->socket_id);
+            }
+            int sem_ret = osal_sem_down_timeout(sem, urc_wait_timeout);
+            if (sem_ret == 0) {
+                // 信号量被触发,重新检查缓存
+                osal_mutex_lock(mutex);
+                if (sock->urc_recv_length > 0) {
+                    size_t copy_len = (sock->urc_recv_length < len) ? sock->urc_recv_length : len;
+                    memcpy(buffer, sock->urc_recv_buffer, copy_len);
+                    
+                    // 移动剩余数据到缓冲区开头
+                    if (copy_len < sock->urc_recv_length) {
+                        memmove(sock->urc_recv_buffer, sock->urc_recv_buffer + copy_len, 
+                               sock->urc_recv_length - copy_len);
+                    }
+                    sock->urc_recv_length -= copy_len;
+                    
+                    osal_mutex_unlock(mutex);
+                    
+                    *received = copy_len;
+                    PPP_DEBUG_DEBUG("Read from URC buffer after wait: socket=%d, len=%u, remaining=%u", 
+                                   sock->socket_id, (unsigned int)copy_len, (unsigned int)sock->urc_recv_length);
+                    return N706A_TCP_SUCCESS;
+                }
+                osal_mutex_unlock(mutex);
+            } else {
+                // 等待超时,完全依赖URC机制,不再调用AT+TCPREAD
+                // 这样可以降低功耗和AT命令开销,适合小系统
+                PPP_DEBUG_DEBUG("URC semaphore wait timeout, returning ETIMEDOUT (fully relying on URC mechanism): socket=%d", sock->socket_id);
+                *received = 0;
+                return N706A_TCP_ETIMEDOUT;  /* 返回超时,让上层知道需要等待或重连 */
+            }
+        } else {
+            osal_mutex_unlock(mutex);
+        }
     }
     
 #ifdef N706A_SSL_USE_MBEDTLS
@@ -1046,46 +2429,75 @@ n706a_tcp_result_t n706a_tcp_recv(n706a_tcp_socket_t *sock,
     }
 #endif
     
-    /* AT指令模式或普通TCP */
-    char at_cmd[64];
-    n706a_at_response_t response;
-    
-#ifdef N706A_SSL_USE_AT_COMMAND
-    if (sock->is_ssl) {
-        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPREAD=%d,%zu",
-                sock->socket_id, len);
-    } else
-#endif
-    {
-        snprintf(at_cmd, sizeof(at_cmd), "AT+TCPREAD=%d,%zu",
-                sock->socket_id, len);
-    }
-    
-    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, timeout_ms, &response);
-    if (ret != N706A_AT_OK) {
-        return N706A_TCP_ETIMEDOUT;
-    }
-    
-    /* 解析响应,提取数据 */
-    if (response.length > 0) {
-        /* 查找数据部分 */
-        const char *data_start = strstr(response.response, "\r\n");
-        if (data_start) {
-            data_start += 2;  // 跳过\r\n
-            size_t data_len = response.length - (data_start - response.response);
+    /* AT指令模式或普通TCP:完全依赖URC机制,不再主动调用AT+TCPREAD */
+    /* 优化:移除AT+TCPREAD轮询机制,完全依赖URC机制,降低功耗和AT命令开销 */
+    /* 如果URC buffer为空且没有信号量,说明URC机制未初始化 */
+    /* 这种情况可能发生在握手阶段,此时需要初始化URC buffer */
+    if (!sock->urc_recv_buffer || !sock->urc_data_available_sem) {
+        PPP_DEBUG_WARN("[TCP_RECV] URC mechanism not initialized for socket %d, initializing now...", sock->socket_id);
+        
+        /* 尝试初始化URC buffer(使用默认大小) */
+        n706a_tcp_result_t init_ret = n706a_tcp_init_urc_buffer(sock, 0);  // 0表示使用默认大小
+        if (init_ret != N706A_TCP_SUCCESS) {
+            PPP_DEBUG_ERROR("[TCP_RECV] Failed to initialize URC buffer for socket %d (ret=%d), cannot receive data", 
+                          sock->socket_id, init_ret);
+            *received = 0;
+            return N706A_TCP_ERROR;
+        }
+        
+        PPP_DEBUG_INFO("[TCP_RECV] URC buffer initialized successfully for socket %d, now waiting for URC data...", sock->socket_id);
+        
+        /* 初始化成功后,继续等待URC数据(重新检查buffer,然后等待信号量) */
+        /* 注意:初始化后,sock->urc_data_available_sem应该已经设置,可以继续等待 */
+        if (sock->urc_data_available_sem) {
+            osal_semaphore *sem = (osal_semaphore *)sock->urc_data_available_sem;
             
-            if (data_len > len) {
-                data_len = len;
+            /* 等待信号量:使用较长的超时时间(60秒),完全依赖URC机制 */
+            uint32_t urc_wait_timeout = (timeout_ms > 60000) ? 60000 : timeout_ms;
+            if (urc_wait_timeout < timeout_ms) {
+                PPP_DEBUG_DEBUG("[TCP_RECV] Using URC wait timeout %u ms (capped at 60s) instead of %u ms for socket %d", 
+                              urc_wait_timeout, timeout_ms, sock->socket_id);
+            }
+            int sem_ret = osal_sem_down_timeout(sem, urc_wait_timeout);
+            if (sem_ret == 0) {
+                /* 信号量被触发,重新检查缓存 */
+                osal_mutex *mutex = (osal_mutex *)sock->urc_recv_mutex;
+                if (mutex) {
+                    osal_mutex_lock(mutex);
+                    if (sock->urc_recv_length > 0) {
+                        size_t copy_len = (sock->urc_recv_length < len) ? sock->urc_recv_length : len;
+                        memcpy(buffer, sock->urc_recv_buffer, copy_len);
+                        
+                        /* 移动剩余数据到缓冲区开头 */
+                        if (copy_len < sock->urc_recv_length) {
+                            memmove(sock->urc_recv_buffer, sock->urc_recv_buffer + copy_len, 
+                                   sock->urc_recv_length - copy_len);
+                        }
+                        sock->urc_recv_length -= copy_len;
+                        
+                        osal_mutex_unlock(mutex);
+                        
+                        *received = copy_len;
+                        PPP_DEBUG_DEBUG("[TCP_RECV] Read from URC buffer after initialization and wait: socket=%d, len=%u, remaining=%u", 
+                                       sock->socket_id, (unsigned int)copy_len, (unsigned int)sock->urc_recv_length);
+                        return N706A_TCP_SUCCESS;
+                    }
+                    osal_mutex_unlock(mutex);
+                }
+            } else {
+                /* 等待超时 */
+                PPP_DEBUG_DEBUG("[TCP_RECV] URC semaphore wait timeout after initialization, returning ETIMEDOUT: socket=%d", sock->socket_id);
+                *received = 0;
+                return N706A_TCP_ETIMEDOUT;
             }
-            
-            memcpy(buffer, data_start, data_len);
-            *received = data_len;
-            return N706A_TCP_SUCCESS;
         }
     }
     
+    /* 如果到达这里,说明URC buffer为空且信号量等待超时 */
+    /* 完全依赖URC机制,不再调用AT+TCPREAD */
+    PPP_DEBUG_DEBUG("[TCP_RECV] URC buffer empty and semaphore timeout, returning ETIMEDOUT (fully relying on URC): socket=%d", sock->socket_id);
     *received = 0;
-    return N706A_TCP_SUCCESS;
+    return N706A_TCP_ETIMEDOUT;
 }
 
 /**
@@ -1152,9 +2564,13 @@ n706a_tcp_result_t n706a_tcp_close(n706a_tcp_socket_t *sock)
                 delay_count++;
             }
         } else {
-            PPP_DEBUG_ERROR("Socket %d close failed after %d attempts! Response: %s", 
-                          sock->socket_id, max_retries,
-                          response.length > 0 ? response.response : "(empty)");
+            PPP_DEBUG_ERROR("Socket %d close failed after %d attempts! Response: ", 
+                          sock->socket_id, max_retries);
+            if (response.length > 0) {
+                n706a_tcp_safe_print_response(&response, 256);
+            } else {
+                PPP_DEBUG_ERROR("(empty)");
+            }
             /* 即使失败也标记为关闭,避免资源泄露,但记录错误 */
         }
     }
@@ -1164,7 +2580,26 @@ n706a_tcp_result_t n706a_tcp_close(n706a_tcp_socket_t *sock)
     sock->state = N706A_TCP_STATE_CLOSED;
     sock->is_ssl = false;
     
-    // 从全局socket表中移除
+    // 清空累积缓冲区中的残留数据(避免遗留字符影响后续操作)
+    extern void n706a_at_clear_accumulate_buffer(void);
+    n706a_at_clear_accumulate_buffer();
+    
+    // 从全局socket表中移除(延迟移除,避免异步URC丢失)
+    // 注意:先标记socket为关闭状态,但暂时不从表中移除,给异步URC一些时间到达
+    // 延迟100ms后再从表中移除并释放URC buffer,避免异步URC(如+TCPRECV)丢失
+    if (g_socket_table_initialized) {
+        osal_mutex_lock(&g_socket_table_mutex);
+        // 暂时不从表中移除,延迟释放
+        // g_tcp_sockets[sock->socket_id] = NULL;
+        osal_mutex_unlock(&g_socket_table_mutex);
+    }
+    
+    // 延迟释放URC接收缓存,给异步URC一些时间到达(避免+TCPRECV URC丢失)
+    // 注意:这里使用简单的延迟,实际应用中可以考虑使用定时器或后台任务
+    // 延迟500ms后释放URC buffer,给异步URC足够的时间到达
+    osal_msleep(500);  // 延迟500ms
+    
+    // 现在从全局socket表中移除
     if (g_socket_table_initialized) {
         osal_mutex_lock(&g_socket_table_mutex);
         g_tcp_sockets[sock->socket_id] = NULL;
@@ -1174,7 +2609,7 @@ n706a_tcp_result_t n706a_tcp_close(n706a_tcp_socket_t *sock)
     // 释放URC接收缓存
     n706a_tcp_free_urc_buffer(sock);
     
-    PPP_DEBUG_INFO("Socket %d closed and URC buffer freed", sock->socket_id);
+    PPP_DEBUG_INFO("Socket %d closed and URC buffer freed (after 100ms delay)", sock->socket_id);
     
     return N706A_TCP_SUCCESS;
 }
@@ -1336,17 +2771,15 @@ static int tcp_mbedtls_send_callback(void *ctx, const unsigned char *buf, size_t
             sock->socket_id, len);
     
     n706a_at_response_t response;
-    n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 5000, &response);
+    /* 发送 AT+TCPSEND 命令,期望收到 '>' 提示符 */
+    n706a_at_error_t ret = n706a_at_send_command(at_cmd, ">", 5000, &response);
     if (ret != N706A_AT_OK) {
-        PPP_DEBUG_ERROR("Failed to send %s command (mbedtls)", at_cmd);
+        PPP_DEBUG_ERROR("Failed to send %s command (mbedtls, expected '>' prompt not received)", at_cmd);
         return MBEDTLS_ERR_NET_SEND_FAILED;
     }
     
-    /* 检查响应中是否包含 '>' 提示符(根据AT命令手册要求) */
-    if (strstr((const char *)response.response, ">") == NULL) {
-        PPP_DEBUG_ERROR("Response does not contain '>' prompt (mbedtls): %s", response.response);
-        return MBEDTLS_ERR_NET_SEND_FAILED;
-    }
+    /* 验证成功,说明响应中已包含 '>' 提示符 */
+    PPP_DEBUG_DEBUG("AT+TCPSEND command sent successfully (mbedtls), received '>' prompt");
     
     /* 延迟50-100ms(根据AT命令手册要求,这里取75ms) */
     PPP_DEBUG_DEBUG("Received '>' prompt (mbedtls), delaying 75ms before sending data...");
@@ -1592,3 +3025,31 @@ void n706a_tcp_on_urc_received(int socket_id, const uint8_t *data, size_t length
     }
 }
 
+/**
+ * @brief +TCPCLOSE URC回调(由n706a_at_handler调用)
+ * @param socket_id Socket ID
+ * 
+ * 注意:此函数由n706a_at_handler.c通过URC回调调用,不是应用层API
+ * 当服务器主动关闭连接时,会调用此函数更新socket状态
+ */
+void n706a_tcp_on_tcpclose_urc(int socket_id)
+{
+    if (socket_id < 0 || socket_id >= N706A_MAX_SOCKETS) {
+        PPP_DEBUG_WARN("+TCPCLOSE URC: Invalid socket_id=%d", socket_id);
+        return;
+    }
+    
+    PPP_DEBUG_INFO("+TCPCLOSE URC received: socket=%d, updating socket state to CLOSED", socket_id);
+    
+    // 更新socket状态
+    osal_mutex_lock(&g_socket_table_mutex);
+    n706a_tcp_socket_t *sock = g_tcp_sockets[socket_id];
+    if (sock) {
+        sock->state = N706A_TCP_STATE_CLOSED;
+        PPP_DEBUG_INFO("Socket %d state updated to CLOSED (server closed connection)", socket_id);
+    } else {
+        PPP_DEBUG_WARN("+TCPCLOSE URC: Socket %d not found in socket table", socket_id);
+    }
+    osal_mutex_unlock(&g_socket_table_mutex);
+}
+
index 1720bea..24a9c34 100644 (file)
  * - 分片传输
  */
 
+/* 定义 _GNU_SOURCE 以使用 strcasestr(不区分大小写的字符串查找) */
+/* 注意:必须在包含 <string.h> 之前定义 */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
 #include "n706a_websocket.h"
 #include "n706a_tcp_client.h"
 #include "ppp_debug.h"
 #include <time.h>
 #include <stdint.h>
 
+/* 包含 WebSocket 解析器(使用 wrapper 头文件,实际实现需要链接 websocket_parser.c) */
+/* 注意:如果 websocket_parser.c 不在同一个编译单元,需要确保它被编译并链接 */
+/* 这里我们直接包含源文件以简化集成(如果项目允许) */
+/* 或者使用 wrapper 头文件,并在构建系统中确保 websocket_parser.c 被编译 */
+#ifdef N706A_WS_USE_PARSER_SOURCE
+    /* 直接包含源文件(如果项目允许) */
+    #include "../../websocket/src/websocket_parser.c"
+#else
+    /* 使用 wrapper 头文件(需要确保 websocket_parser.c 被编译并链接) */
+    #include "../../websocket/include/websocket_parser_wrapper.h"
+#endif
+
 #ifdef __unix__
 #include <pthread.h>
 #include <unistd.h>
 #elif defined(__rtthread__)
 #include <rtthread.h>
+#else
+/* 嵌入式平台(CMSIS OS) */
+#include "custom_utils.h"  /* 用于 uapi_create_osThread_custom */
+#include "errcode.h"       /* 用于 ERRCODE_SUCC */
+#include "cmsis_os2.h"     /* 用于 osThreadFunc_t */
 #endif
 
 /* WebSocket GUID(RFC 6455) */
@@ -41,16 +64,51 @@ static n706a_websocket_client_t *g_ws_clients[N706A_WS_MAX_CLIENTS] = {NULL};
 
 /* ========== 私有函数声明 ========== */
 
-static int ws_perform_handshake(n706a_websocket_client_t *client);
+static int ws_perform_handshake(n706a_websocket_client_t *client,const char* header_option);
 static int ws_send_frame(n706a_websocket_client_t *client,
                         n706a_ws_opcode_t opcode,
                         const uint8_t *data,
                         size_t length,
                         bool fin);
+static void ws_send_close_if_needed(n706a_websocket_client_t *client,
+                                    n706a_ws_close_code_t code,
+                                    const char *reason);
+
+static void ws_send_close_if_needed(n706a_websocket_client_t *client,
+                                    n706a_ws_close_code_t code,
+                                    const char *reason)
+{
+    if (!client || client->state != N706A_WS_STATE_OPEN) {
+        return;
+    }
+    
+    uint8_t close_data[128];
+    size_t close_len = 2;
+    close_data[0] = (code >> 8) & 0xFF;
+    close_data[1] = code & 0xFF;
+    
+    if (reason && reason[0] != '\0') {
+        size_t reason_len = strlen(reason);
+        if (reason_len > sizeof(close_data) - 2) {
+            reason_len = sizeof(close_data) - 2;
+        }
+        memcpy(close_data + 2, reason, reason_len);
+        close_len += reason_len;
+    }
+    
+    ws_send_frame(client, N706A_WS_OPCODE_CLOSE, close_data, close_len, true);
+}
 static int ws_recv_frame(n706a_websocket_client_t *client);
 static void ws_generate_mask_key(uint8_t *mask_key);
 static void ws_mask_data(uint8_t *data, size_t len, const uint8_t *mask_key);
+static void ws_recv_thread_func_impl(n706a_websocket_client_t *client);
+#ifdef __unix__
 static void *ws_recv_thread_func(void *arg);
+#elif defined(__rtthread__)
+static void ws_recv_thread_func(void *arg);
+#else
+static void ws_recv_thread_func(void *arg);
+#endif
 static int ws_parse_url(const char *url, char *protocol, char *host,
                        uint16_t *port, char *path);
 
@@ -126,6 +184,18 @@ int n706a_websocket_constructor(n706a_ws_on_open_callback_t on_open,
     client->recv_buffer_len = 0;
     client->running = false;
     
+    /* 初始化 WebSocket 帧解析器 */
+    client->parser = ws_parser_init();
+    if (!client->parser) {
+        PPP_DEBUG_ERROR("[WebSocket] Failed to initialize parser for client %d", client_slot);
+        if (client->recv_buffer) {
+            free(client->recv_buffer);
+        }
+        free(client);
+        return -1;
+    }
+    client->fragment_opcode = N706A_WS_OPCODE_TEXT;  /* 默认初始 opcode */
+    
     g_ws_clients[client_slot] = client;
     
     return client_slot;
@@ -174,6 +244,18 @@ int n706a_websocket_constructor_with_socket(int socket_id,
         return -1;
     }
     
+    /* 初始化 WebSocket 帧解析器 */
+    client->parser = ws_parser_init();
+    if (!client->parser) {
+        PPP_DEBUG_ERROR("[WebSocket] Failed to initialize parser for client %d", socket_id);
+        if (client->recv_buffer) {
+            free(client->recv_buffer);
+        }
+        free(client);
+        return -1;
+    }
+    client->fragment_opcode = N706A_WS_OPCODE_TEXT;  /* 默认初始 opcode */
+    
     g_ws_clients[socket_id] = client;
     
     return socket_id;
@@ -182,7 +264,7 @@ int n706a_websocket_constructor_with_socket(int socket_id,
 /**
  * @brief 连接到WebSocket服务器
  */
-int n706a_websocket_connect(int client_id, const char *url, const char *option)
+int n706a_websocket_connect(int client_id, const char *url, const char *header_option)
 {
     if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
         return N706A_WS_ERROR_INVALID_PARAM;
@@ -193,8 +275,51 @@ int n706a_websocket_connect(int client_id, const char *url, const char *option)
         return N706A_WS_ERROR_INVALID_PARAM;
     }
     
+    /* 如果状态不是 CLOSED,根据状态处理 */
     if (client->state != N706A_WS_STATE_CLOSED) {
-        return N706A_WS_ERROR_ALREADY_OPEN;
+        if (client->state == N706A_WS_STATE_OPEN) {
+            /* 连接已经打开,直接返回成功 */
+            PPP_DEBUG_INFO("[WebSocket] Client %d is already OPEN, reusing existing connection", client_id);
+            return N706A_WS_SUCCESS;
+        } else if (client->state == N706A_WS_STATE_CONNECTING) {
+            /* 正在连接中,等待连接完成(最多等待5秒) */
+            PPP_DEBUG_INFO("[WebSocket] Client %d is CONNECTING, waiting for connection to complete...", client_id);
+            uint32_t wait_count = 0;
+            const uint32_t max_wait_count = 50;  /* 5秒 = 50 * 100ms */
+            while (client->state == N706A_WS_STATE_CONNECTING && wait_count < max_wait_count) {
+                osal_msleep(100);
+                wait_count++;
+            }
+            
+            if (client->state == N706A_WS_STATE_OPEN) {
+                PPP_DEBUG_INFO("[WebSocket] Client %d connection completed, state is now OPEN", client_id);
+                return N706A_WS_SUCCESS;
+            } else if (client->state == N706A_WS_STATE_CLOSED) {
+                /* 连接失败,状态变为 CLOSED,可以继续尝试连接 */
+                PPP_DEBUG_WARN("[WebSocket] Client %d connection failed during wait, state is now CLOSED", client_id);
+                /* 继续执行连接逻辑 */
+            } else {
+                /* 其他状态(CLOSING),需要关闭后重试 */
+                PPP_DEBUG_WARN("[WebSocket] Client %d is in state %d after wait, closing first...", 
+                              client_id, client->state);
+                n706a_websocket_close(client_id);
+                osal_msleep(100);
+            }
+        } else {
+            /* CLOSING 或其他状态,先关闭连接 */
+            PPP_DEBUG_WARN("[WebSocket] Client %d is not in CLOSED state (current state=%d), closing first...", 
+                          client_id, client->state);
+            n706a_websocket_close(client_id);
+            /* 等待一小段时间,确保关闭操作完成 */
+            osal_msleep(100);
+            
+            /* 再次检查状态 */
+            if (client->state != N706A_WS_STATE_CLOSED) {
+                PPP_DEBUG_ERROR("[WebSocket] Client %d still not in CLOSED state after close (state=%d)", 
+                              client_id, client->state);
+                return N706A_WS_ERROR_ALREADY_OPEN;
+            }
+        }
     }
     
     /* 解析URL */
@@ -230,7 +355,7 @@ int n706a_websocket_connect(int client_id, const char *url, const char *option)
     }
     
     /* WebSocket握手 */
-    if (ws_perform_handshake(client) != 0) {
+    if (ws_perform_handshake(client,header_option) != 0) {
         n706a_tcp_close(&client->tcp_sock);
         client->state = N706A_WS_STATE_CLOSED;
         if (client->on_error) {
@@ -243,12 +368,15 @@ int n706a_websocket_connect(int client_id, const char *url, const char *option)
     
     /* 启动接收线程 */
     client->running = true;
+    PPP_DEBUG_INFO("[WebSocket] Starting receive thread for client %d", client_id);
 #ifdef __unix__
-    if (pthread_create((pthread_t *)&client->recv_thread, NULL, ws_recv_thread_func, client) != 0) {
-        PPP_DEBUG_ERROR("Failed to create WebSocket receive thread for client %d", client_id);
+    int thread_ret = pthread_create((pthread_t *)&client->recv_thread, NULL, ws_recv_thread_func, client);
+    if (thread_ret != 0) {
+        PPP_DEBUG_ERROR("[WebSocket] Failed to create receive thread for client %d: pthread_create returned %d", client_id, thread_ret);
         client->running = false;
         return N706A_WS_ERROR_SEND_FAIL;
     }
+    PPP_DEBUG_INFO("[WebSocket] Receive thread created successfully for client %d (thread_id=%p)", client_id, (void *)client->recv_thread);
 #elif defined(__rtthread__)
     /* RT-Thread使用rt_thread_create */
     rt_thread_t thread = rt_thread_create("ws_recv",
@@ -266,8 +394,25 @@ int n706a_websocket_connect(int client_id, const char *url, const char *option)
         return N706A_WS_ERROR_SEND_FAIL;
     }
 #else
-    /* 其他平台暂不支持线程 */
-    PPP_DEBUG_WARN("WebSocket receive thread not supported on this platform");
+    /* 嵌入式平台(CMSIS OS)使用 uapi_create_osThread_custom_with_args 传递 client 参数 */
+    app_task_definition_ext_t ws_recv_task_cfg = {0};
+    ws_recv_task_cfg.attr.name = "ws_recv";
+    ws_recv_task_cfg.attr.priority = 24;  /* 优先级24(中等优先级) */
+    ws_recv_task_cfg.attr.stack_size = 8192;  /* 8KB栈(增加栈大小以避免栈溢出,因为调用链较深且可能有较大的栈缓冲区) */
+    ws_recv_task_cfg.func = (osThreadFunc_t)ws_recv_thread_func;
+    ws_recv_task_cfg.use_mem = USE_EXTEND_MEM;  /* 使用扩展内存(PSRAM)分配栈 */
+    ws_recv_task_cfg.create_queue_handle = NULL;
+    ws_recv_task_cfg.inst_args = client;  /* 传递 client 参数给线程函数 */
+    
+    errcode_t thread_ret = uapi_create_osThread_custom_with_args(&ws_recv_task_cfg);
+    if (thread_ret != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("[WebSocket] Failed to create receive thread for client %d: uapi_create_osThread_custom_with_args returned 0x%x", client_id, thread_ret);
+        client->running = false;
+        return N706A_WS_ERROR_SEND_FAIL;
+    }
+    
+    client->recv_thread = (void *)(uintptr_t)ws_recv_task_cfg.task_handle;
+    PPP_DEBUG_INFO("[WebSocket] Receive thread created successfully for client %d (task_handle=%p)", client_id, (void *)client->recv_thread);
 #endif
     
     /* 调用on_open回调 */
@@ -371,24 +516,7 @@ int n706a_websocket_close_with_reason(int client_id,
     }
     
     if (client->state == N706A_WS_STATE_OPEN) {
-        /* 发送关闭帧 */
-        uint8_t close_data[128];
-        size_t close_len = 2;
-        
-        close_data[0] = (close_code >> 8) & 0xFF;
-        close_data[1] = close_code & 0xFF;
-        
-        if (reason) {
-            size_t reason_len = strlen(reason);
-            if (reason_len > sizeof(close_data) - 2) {
-                reason_len = sizeof(close_data) - 2;
-            }
-            memcpy(close_data + 2, reason, reason_len);
-            close_len += reason_len;
-        }
-        
-        ws_send_frame(client, N706A_WS_OPCODE_CLOSE, close_data, close_len, true);
-        
+        ws_send_close_if_needed(client, close_code, reason);
         client->state = N706A_WS_STATE_CLOSING;
     }
     
@@ -404,11 +532,23 @@ int n706a_websocket_close_with_reason(int client_id,
         rt_thread_delete((rt_thread_t)client->recv_thread);
         client->recv_thread = NULL;
     }
+#else
+    /* 嵌入式平台(CMSIS OS)使用 osThreadTerminate */
+    if (client->recv_thread) {
+        osThreadId_t thread_id = (osThreadId_t)(uintptr_t)client->recv_thread;
+        osThreadTerminate(thread_id);
+        client->recv_thread = NULL;
+    }
 #endif
     
     /* 关闭TCP连接 */
     n706a_tcp_close(&client->tcp_sock);
     
+    /* 重置解析器 */
+    if (client->parser) {
+        ws_parser_reset(client->parser);
+    }
+    
     client->state = N706A_WS_STATE_CLOSED;
     
     /* 调用on_close回调 */
@@ -436,6 +576,23 @@ n706a_ws_state_t n706a_websocket_get_state(int client_id)
     return client->state;
 }
 
+/**
+ * @brief 获取WebSocket的底层TCP socket ID
+ */
+int n706a_websocket_get_socket_id(int client_id)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return -1;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client) {
+        return -1;
+    }
+    
+    return client->tcp_sock.socket_id;
+}
+
 /**
  * @brief 检查WebSocket是否已连接
  */
@@ -464,6 +621,11 @@ int n706a_websocket_destructor(int client_id)
     }
     
     /* 释放资源 */
+    if (client->parser) {
+        ws_parser_destroy(client->parser);
+        client->parser = NULL;
+    }
+    
     if (client->recv_buffer) {
         free(client->recv_buffer);
     }
@@ -479,7 +641,7 @@ int n706a_websocket_destructor(int client_id)
 /**
  * @brief 执行WebSocket握手
  */
-static int ws_perform_handshake(n706a_websocket_client_t *client)
+static int ws_perform_handshake(n706a_websocket_client_t *client,const char* header_option)
 {
     /* 动态分配握手请求缓冲区 */
     char *handshake_req = (char *)malloc(1024);
@@ -494,18 +656,114 @@ static int ws_perform_handshake(n706a_websocket_client_t *client)
     strncpy(client->sec_key, sec_key, sizeof(client->sec_key) - 1);
     
     /* 构造握手请求 */
-    int len = snprintf(handshake_req, 1024,
-        "GET %s HTTP/1.1\r\n"
-        "Host: %s\r\n"
-        "Upgrade: websocket\r\n"
-        "Connection: Upgrade\r\n"
-        "Sec-WebSocket-Key: %s\r\n"
-        "Sec-WebSocket-Version: 13\r\n"
-        "\r\n",
-        client->path,
-        client->host,
-        sec_key
-    );
+    /* 注意:path应该包含查询参数(如果有),例如:/path?key=value */
+    /* Host头需要包含端口号(与Python代码保持一致) */
+    /*
+            "GET /%s?%s HTTP/1.1\r\n"
+            "Host: %s:%d\r\n"
+            "Upgrade: websocket\r\n"
+            "Connection: Upgrade\r\n"
+            "Sec-WebSocket-Key: %s\r\n"
+            "Sec-WebSocket-Version: 13\r\n"
+            "Origin: http://%s:%d\r\n"
+            "%s\r\n"
+            "\r\n",
+    */
+    /* HTTP请求必须以 \r\n\r\n 结尾(两个 \r\n),第一个结束最后一行,第二个表示请求头结束 */
+    int len = 0;
+    if (header_option != NULL) { //header_option 自带\r\n 注意不能重复添加\r\n,结尾只能又两个\r\n
+        len = snprintf(handshake_req, 1024,
+            "GET %s HTTP/1.1\r\n"
+            "Host: %s:%d\r\n"
+            "Upgrade: websocket\r\n"
+            "Connection: Upgrade\r\n"
+            "Sec-WebSocket-Key: %s\r\n"
+            "Sec-WebSocket-Version: 13\r\n"
+            "Origin: http://%s:%d\r\n"
+            "%s"
+            "\r\n",
+            client->path, client->host, client->port, sec_key, client->host, client->port, header_option);
+    } else {
+        len = snprintf(handshake_req, 1024,
+            "GET %s HTTP/1.1\r\n"
+            "Host: %s:%d\r\n"
+            "Upgrade: websocket\r\n"
+            "Connection: Upgrade\r\n"
+            "Sec-WebSocket-Key: %s\r\n"
+            "Sec-WebSocket-Version: 13\r\n"
+            "Origin: http://%s:%d\r\n"
+            "\r\n",
+            client->path,
+            client->host,
+            client->port,
+            sec_key,
+            client->host,
+            client->port
+        );
+    }
+    /* 调试:打印握手请求(完整ASCII码,\r\n可见) */
+    PPP_DEBUG_INFO("[WebSocket] Sending handshake request: %d bytes", len);
+    
+    /* 详细调试:打印握手请求的详细内容(默认关闭,编译期移除) */
+#if PPP_DEBUG_VERBOSE_ENABLE
+    PPP_DEBUG_VERBOSE("[WebSocket] ========== Handshake Request (ASCII) ==========");
+    /* 打印ASCII码,\r\n显示为可见字符 */
+    char *ascii_display = (char *)malloc(len * 4 + 1);  // 每个字节最多4个字符(\r\n等)
+    if (ascii_display) {
+        int ascii_idx = 0;
+        for (int i = 0; i < len; i++) {
+            uint8_t c = (uint8_t)handshake_req[i];
+            if (c == '\r') {
+                ascii_display[ascii_idx++] = '\\';
+                ascii_display[ascii_idx++] = 'r';
+            } else if (c == '\n') {
+                ascii_display[ascii_idx++] = '\\';
+                ascii_display[ascii_idx++] = 'n';
+            } else if (c >= 32 && c < 127) {
+                ascii_display[ascii_idx++] = c;
+            } else {
+                // 其他不可打印字符,显示为\xXX格式
+                ascii_display[ascii_idx++] = '\\';
+                ascii_display[ascii_idx++] = 'x';
+                char hex[3];
+                snprintf(hex, sizeof(hex), "%02X", c);
+                ascii_display[ascii_idx++] = hex[0];
+                ascii_display[ascii_idx++] = hex[1];
+            }
+        }
+        ascii_display[ascii_idx] = '\0';
+        PPP_DEBUG_VERBOSE("[WebSocket] %s", ascii_display);
+        free(ascii_display);
+    }
+    
+    /* 打印HEX格式(每16字节一行) */
+    PPP_DEBUG_VERBOSE("[WebSocket] ========== Handshake Request (HEX) ==========");
+    for (int i = 0; i < len; i += 16) {
+        char hex_line[128] = {0};
+        char ascii_line[32] = {0};
+        int hex_idx = 0;
+        int ascii_idx = 0;
+        
+        for (int j = 0; j < 16 && (i + j) < len; j++) {
+            uint8_t c = (uint8_t)handshake_req[i + j];
+            snprintf(hex_line + hex_idx, 4, "%02X ", c);
+            hex_idx += 3;
+            
+            if (c >= 32 && c < 127) {
+                ascii_line[ascii_idx++] = c;
+            } else {
+                ascii_line[ascii_idx++] = '.';
+            }
+        }
+        ascii_line[ascii_idx] = '\0';
+        PPP_DEBUG_VERBOSE("[WebSocket] [%04d] %-48s | %s", i, hex_line, ascii_line);
+    }
+    
+    /* 打印原始字符串(用于对比) */
+    PPP_DEBUG_VERBOSE("[WebSocket] ========== Handshake Request (Raw String) ==========");
+    PPP_DEBUG_VERBOSE("[WebSocket] %s", handshake_req);
+    PPP_DEBUG_VERBOSE("[WebSocket] ====================================================");
+#endif
     
     /* 发送握手请求 */
     size_t sent;
@@ -517,6 +775,10 @@ static int ws_perform_handshake(n706a_websocket_client_t *client)
     free(handshake_req);
     
     if (ret != N706A_TCP_SUCCESS) {
+        if (ret == N706A_TCP_ENOTCONN) {
+            PPP_DEBUG_ERROR("[WebSocket] Socket closed during handshake send");
+            client->state = N706A_WS_STATE_CLOSED;
+        }
         return -1;
     }
     
@@ -534,22 +796,110 @@ static int ws_perform_handshake(n706a_websocket_client_t *client)
                         10000);
     
     if (ret != N706A_TCP_SUCCESS || received == 0) {
+        PPP_DEBUG_ERROR("[WebSocket] Failed to receive handshake response: ret=%d, received=%u", 
+                       ret, (unsigned int)received);
         free(response);
         return -1;
     }
     
     response[received] = '\0';
     
-    /* 验证响应 */
+    /* 关键信息:握手响应接收成功 */
+    PPP_DEBUG_INFO("[WebSocket] Handshake response received: %u bytes", (unsigned int)received);
+    
+    /* 详细调试:打印响应的详细内容(默认关闭,编译期移除) */
+#if PPP_DEBUG_VERBOSE_ENABLE
+    PPP_DEBUG_VERBOSE("[WebSocket] ========== Handshake Response (ASCII) ==========");
+    /* 打印ASCII码,\r\n显示为可见字符 */
+    char *ascii_display_resp = (char *)malloc(received * 4 + 1);  // 每个字节最多4个字符(\r\n等)
+    if (ascii_display_resp) {
+        int ascii_idx = 0;
+        for (size_t i = 0; i < received; i++) {
+            uint8_t c = response[i];
+            if (c == '\r') {
+                ascii_display_resp[ascii_idx++] = '\\';
+                ascii_display_resp[ascii_idx++] = 'r';
+            } else if (c == '\n') {
+                ascii_display_resp[ascii_idx++] = '\\';
+                ascii_display_resp[ascii_idx++] = 'n';
+            } else if (c >= 32 && c < 127) {
+                ascii_display_resp[ascii_idx++] = c;
+            } else {
+                // 其他不可打印字符,显示为\xXX格式
+                ascii_display_resp[ascii_idx++] = '\\';
+                ascii_display_resp[ascii_idx++] = 'x';
+                char hex[3];
+                snprintf(hex, sizeof(hex), "%02X", c);
+                ascii_display_resp[ascii_idx++] = hex[0];
+                ascii_display_resp[ascii_idx++] = hex[1];
+            }
+        }
+        ascii_display_resp[ascii_idx] = '\0';
+        PPP_DEBUG_VERBOSE("[WebSocket] %s", ascii_display_resp);
+        free(ascii_display_resp);
+    }
+    
+    /* 打印HEX格式(每16字节一行,使用静态缓冲区避免栈溢出) */
+    PPP_DEBUG_VERBOSE("[WebSocket] ========== Handshake Response (HEX) ==========");
+    static char hex_line[128] = {0};  /* 使用静态缓冲区:16字节 * 3字符/字节 = 48字节,留有余量 */
+    static char ascii_line[32] = {0};  /* 使用静态缓冲区:16字节 + 余量 */
+    for (size_t i = 0; i < received; i += 16) {
+        memset(hex_line, 0, sizeof(hex_line));
+        memset(ascii_line, 0, sizeof(ascii_line));
+        int hex_idx = 0;
+        int ascii_idx = 0;
+        
+        for (int j = 0; j < 16 && (i + j) < received && hex_idx + 3 < sizeof(hex_line) && ascii_idx < sizeof(ascii_line) - 1; j++) {
+            uint8_t c = response[i + j];
+            snprintf(hex_line + hex_idx, 4, "%02X ", c);
+            hex_idx += 3;
+            
+            if (c >= 32 && c < 127) {
+                ascii_line[ascii_idx++] = c;
+            } else {
+                ascii_line[ascii_idx++] = '.';
+            }
+        }
+        ascii_line[ascii_idx] = '\0';
+        PPP_DEBUG_VERBOSE("[WebSocket] [%04zu] %-48s | %s", i, hex_line, ascii_line);
+    }
+    
+    /* 打印原始字符串(用于对比) */
+    PPP_DEBUG_VERBOSE("[WebSocket] ========== Handshake Response (Raw String) ==========");
+    PPP_DEBUG_VERBOSE("[WebSocket] %s", (char *)response);
+    PPP_DEBUG_VERBOSE("[WebSocket] ====================================================");
+#endif
+    
+    /* 检查是否是HTTP错误响应 */
+    if (strstr((char *)response, "HTTP/1.1 400") || 
+        strstr((char *)response, "HTTP/1.1 404") ||
+        strstr((char *)response, "HTTP/1.1 500")) {
+        PPP_DEBUG_ERROR("[WebSocket] Server returned error response");
+        free(response);
+        return -1;
+    }
+/*
+HTTP/1.1 101 
+Connection: upgrade
+Upgrade: websocket
+Sec-WebSocket-Accept: i1xQGZhF6iELYH+tPNPGRUz0bYU=
+Date: Wed, 19 Nov 2025 07:10:54 GMT
+X-Kong-Upstream-Latency: 2
+X-Kong-Proxy-Latency: 0
+Via: kong/2.5.0
+*/
+    /* 验证响应(使用不区分大小写的查找,因为 HTTP 头部字段名不区分大小写) */
     if (!strstr((char *)response, "HTTP/1.1 101") ||
-        !strstr((char *)response, "Upgrade: websocket") ||
-        !strstr((char *)response, "Connection: Upgrade")) {
+        !strcasestr((char *)response, "Upgrade: websocket") ||
+        !strcasestr((char *)response, "Connection: Upgrade")
+    ) {
+        PPP_DEBUG_ERROR("[WebSocket] Invalid handshake response format");
         free(response);
         return -1;
     }
     
-    /* 验证Sec-WebSocket-Accept */
-    const char *accept_start = strstr((char *)response, "Sec-WebSocket-Accept:");
+    /* 验证Sec-WebSocket-Accept(使用不区分大小写的查找) */
+    const char *accept_start = strcasestr((char *)response, "Sec-WebSocket-Accept:");
     if (accept_start) {
         accept_start += 21;  // 跳过"Sec-WebSocket-Accept:"
         while (*accept_start == ' ') accept_start++;
@@ -602,137 +952,444 @@ static int ws_send_frame(n706a_websocket_client_t *client,
     memcpy(frame + frame_len, mask_key, 4);
     frame_len += 4;
     
-    /* 发送帧头 */
-    size_t sent;
-    n706a_tcp_send(&client->tcp_sock, frame, frame_len, &sent);
-    
-    /* 发送payload(掩码后) */
+    /* 关键信息:发送帧的基本信息 */
+    PPP_DEBUG_INFO("[WebSocket] Sending frame: opcode=0x%02X (%s), payload_len=%u bytes",
+                   opcode, 
+                   (opcode == N706A_WS_OPCODE_TEXT) ? "TEXT" :
+                   (opcode == N706A_WS_OPCODE_BINARY) ? "BINARY" :
+                   (opcode == N706A_WS_OPCODE_CLOSE) ? "CLOSE" :
+                   (opcode == N706A_WS_OPCODE_PING) ? "PING" :
+                   (opcode == N706A_WS_OPCODE_PONG) ? "PONG" : "UNKNOWN",
+                   (unsigned int)length);
+    
+    /* 详细调试:打印帧头的详细内容(默认关闭,编译期移除) */
+#if PPP_DEBUG_VERBOSE_ENABLE
+    PPP_DEBUG_VERBOSE("[WebSocket] Frame details: fin=%d, frame_header_len=%u",
+                      fin ? 1 : 0, (unsigned int)frame_len);
+    
+    /* 打印帧头HEX内容(使用静态缓冲区避免栈溢出) */
+    static char frame_header_hex[64] = {0};  /* 使用静态缓冲区:14字节 * 3字符/字节 = 42字节 */
+    memset(frame_header_hex, 0, sizeof(frame_header_hex));
+    for (size_t i = 0; i < frame_len && i * 3 + 3 < sizeof(frame_header_hex); i++) {
+        snprintf(frame_header_hex + i * 3, 4, "%02X ", frame[i]);
+    }
+    PPP_DEBUG_VERBOSE("[WebSocket] Frame header (HEX): %s", frame_header_hex);
+    PPP_DEBUG_VERBOSE("[WebSocket] Mask key: %02X %02X %02X %02X", 
+                   mask_key[0], mask_key[1], mask_key[2], mask_key[3]);
+    
+    /* 打印payload内容(如果有,使用静态缓冲区避免栈溢出) */
     if (data && length > 0) {
-        uint8_t *masked_data = (uint8_t *)malloc(length);
-        if (masked_data) {
-            memcpy(masked_data, data, length);
-            ws_mask_data(masked_data, length, mask_key);
-            
-            n706a_tcp_send(&client->tcp_sock, masked_data, length, &sent);
-            
-            free(masked_data);
+        size_t print_len = (length > 32) ? 32 : length;  // 只打印前32字节以节省栈空间
+        static char payload_hex[128] = {0};  /* 使用静态缓冲区:32字节 * 3字符/字节 = 96字节 */
+        memset(payload_hex, 0, sizeof(payload_hex));
+        for (size_t i = 0; i < print_len && i * 3 + 3 < sizeof(payload_hex); i++) {
+            snprintf(payload_hex + i * 3, 4, "%02X ", data[i]);
+        }
+        if (length > 32) {
+            PPP_DEBUG_VERBOSE("[WebSocket] Payload (first 32 bytes, HEX): %s... (total %u bytes)", 
+                          payload_hex, (unsigned int)length);
+        } else {
+            PPP_DEBUG_VERBOSE("[WebSocket] Payload (HEX): %s", payload_hex);
         }
+        
+        /* 如果是TEXT帧,也打印ASCII内容(可打印字符,使用静态缓冲区避免栈溢出) */
+        if (opcode == N706A_WS_OPCODE_TEXT) {
+            static char payload_ascii[64] = {0};  /* 使用静态缓冲区:32字节 + 余量 */
+            memset(payload_ascii, 0, sizeof(payload_ascii));
+            size_t ascii_len = (length < sizeof(payload_ascii) - 1) ? length : sizeof(payload_ascii) - 1;
+            for (size_t i = 0; i < ascii_len && i < print_len; i++) {
+                payload_ascii[i] = (data[i] >= 32 && data[i] < 127) ? data[i] : '.';
+            }
+            payload_ascii[ascii_len] = '\0';
+            PPP_DEBUG_VERBOSE("[WebSocket] Payload (ASCII): %s", payload_ascii);
+        }
+    }
+#endif
+    
+    size_t total_len = frame_len + length;
+    uint8_t *send_buffer = (uint8_t *)malloc(total_len);
+    if (!send_buffer) {
+        PPP_DEBUG_ERROR("[WebSocket] Failed to allocate frame buffer (%u bytes)", (unsigned int)total_len);
+        return N706A_WS_ERROR_SEND_FAIL;
+    }
+    
+    memcpy(send_buffer, frame, frame_len);
+    if (length > 0 && data) {
+        memcpy(send_buffer + frame_len, data, length);
+        ws_mask_data(send_buffer + frame_len, length, mask_key);
+    }
+    
+    size_t sent = 0;
+    n706a_tcp_result_t ret = n706a_tcp_send(&client->tcp_sock, send_buffer, total_len, &sent);
+    free(send_buffer);
+    
+    if (ret != N706A_TCP_SUCCESS || sent != total_len) {
+        if (ret == N706A_TCP_ENOTCONN) {
+            PPP_DEBUG_ERROR("[WebSocket] Socket closed during frame send, closing WebSocket connection");
+            client->state = N706A_WS_STATE_CLOSING;
+            if (client->on_error) {
+                client->on_error(client->client_id,
+                                N706A_WS_ERROR_SEND_FAIL,
+                                "Socket closed during send");
+            }
+        } else {
+            PPP_DEBUG_ERROR("[WebSocket] Failed to send frame: ret=%d, sent=%u/%u",
+                           ret, (unsigned int)sent, (unsigned int)total_len);
+        }
+        return N706A_WS_ERROR_SEND_FAIL;
     }
     
     return N706A_WS_SUCCESS;
 }
 
 /**
- * @brief 接收WebSocket帧
+ * @brief 接收WebSocket帧(使用 websocket_parser 进行流式解析)
  */
 static int ws_recv_frame(n706a_websocket_client_t *client)
 {
-    uint8_t header[2];
-    size_t received;
+    if (!client || !client->parser) {
+        return -1;
+    }
+    
+    /* 在接收前检查socket状态,如果已关闭则立即返回 */
+    if (client->tcp_sock.state == N706A_TCP_STATE_CLOSED) {
+        PPP_DEBUG_WARN("[WebSocket] Socket already CLOSED before recv, updating WebSocket state to CLOSING");
+        client->state = N706A_WS_STATE_CLOSING;
+        return -1;
+    }
     
-    /* 接收帧头前2字节 */
+    /* 从 TCP 接收数据到缓冲区 */
+    /* 优化:使用较长的超时时间(60秒),完全依赖URC机制等待数据,不再主动轮询 */
+    /* 这样可以降低功耗和AT命令开销,适合小系统 */
+    /* TCP接收函数会阻塞在URC信号量上,直到数据到达或超时 */
+    size_t received = 0;
+    PPP_DEBUG_VERBOSE("[WebSocket] Calling n706a_tcp_recv: buffer_size=%u, timeout=60000ms (fully relying on URC)", 
+                   (unsigned int)client->recv_buffer_size);
     n706a_tcp_result_t ret = n706a_tcp_recv(&client->tcp_sock,
-                                            header,
-                                            2,
+                                            client->recv_buffer,
+                                            client->recv_buffer_size,
                                             &received,
-                                            5000);
+                                            60000);  /* 60秒超时,完全依赖URC机制 */
     
-    if (ret != N706A_TCP_SUCCESS || received < 2) {
+    PPP_DEBUG_VERBOSE("[WebSocket] n706a_tcp_recv returned: ret=%d, received=%u bytes", ret, (unsigned int)received);
+    
+    /* 接收后再次检查socket状态(可能在接收过程中被关闭) */
+    if (client->tcp_sock.state == N706A_TCP_STATE_CLOSED) {
+        PPP_DEBUG_WARN("[WebSocket] Socket CLOSED during recv, updating WebSocket state to CLOSING");
+        client->state = N706A_WS_STATE_CLOSING;
         return -1;
     }
     
-    bool fin = (header[0] & WS_FIN_BIT) != 0;
-    n706a_ws_opcode_t opcode = (n706a_ws_opcode_t)(header[0] & WS_OPCODE_MASK);
-    bool masked = (header[1] & WS_MASK_BIT) != 0;
-    uint64_t payload_len = header[1] & WS_PAYLOAD_MASK;
-    
-    /* 扩展payload长度 */
-    if (payload_len == 126) {
-        uint8_t len_buf[2];
-        n706a_tcp_recv(&client->tcp_sock, len_buf, 2, &received, 5000);
-        payload_len = (len_buf[0] << 8) | len_buf[1];
-    } else if (payload_len == 127) {
-        uint8_t len_buf[8];
-        n706a_tcp_recv(&client->tcp_sock, len_buf, 8, &received, 5000);
-        payload_len = 0;
-        for (int i = 0; i < 8; i++) {
-            payload_len = (payload_len << 8) | len_buf[i];
-        }
+    if (ret == N706A_TCP_ETIMEDOUT) {
+        /* 超时:没有数据到达,这是正常的,继续等待 */
+        /* 注意:超时不应该断开连接,应该继续循环等待 */
+        PPP_DEBUG_DEBUG("[WebSocket] TCP recv timeout (no data), continuing to wait");
+        return 0;  /* 返回 0 表示没有错误,只是没有数据 */
     }
     
-    /* 接收masking key(如果有) */
-    uint8_t mask_key[4] = {0};
-    if (masked) {
-        n706a_tcp_recv(&client->tcp_sock, mask_key, 4, &received, 5000);
-        if (received < 4) {
-            return -1;
-        }
+    if (ret == N706A_TCP_ENOTCONN) {
+        /* Socket已关闭,这是真正的错误 */
+        PPP_DEBUG_ERROR("[WebSocket] Socket closed during recv (ENOTCONN), updating WebSocket state to CLOSING");
+        client->state = N706A_WS_STATE_CLOSING;
+        return -1;
     }
     
-    /* 接收payload */
-    if (payload_len > 0) {
-        uint8_t *payload = (uint8_t *)malloc(payload_len + 1);
-        if (!payload) {
-            return -1;
-        }
+    if (ret != N706A_TCP_SUCCESS) {
+        /* 其他错误 */
+        PPP_DEBUG_ERROR("[WebSocket] TCP recv error: ret=%d", ret);
+        return -1;
+    }
+    
+    if (received == 0) {
+        /* 接收到0字节,可能是连接关闭或暂时没有数据 */
+        /* 注意:如果socket已关闭,n706a_tcp_recv应该返回ENOTCONN,而不是SUCCESS with received=0 */
+        /* 这里收到0字节可能是正常的(暂时没有数据),继续等待 */
+        PPP_DEBUG_DEBUG("[WebSocket] TCP recv returned 0 bytes, continuing to wait");
+        return 0;  /* 返回 0 表示没有错误,只是没有数据 */
+    }
+    
+    /* 详细调试:打印接收到的原始数据(默认关闭,编译期移除) */
+#if PPP_DEBUG_VERBOSE_ENABLE
+    PPP_DEBUG_VERBOSE("[WebSocket] Received TCP data: %u bytes", (unsigned int)received);
+    /* 注意:使用静态缓冲区避免栈溢出,只打印前32字节 */
+    size_t print_len = (received > 32) ? 32 : received;  /* 只打印前32字节以节省栈空间 */
+    static char recv_hex[128] = {0};  /* 使用静态缓冲区:32字节 * 3字符/字节 = 96字节,留有余量 */
+    memset(recv_hex, 0, sizeof(recv_hex));
+    for (size_t i = 0; i < print_len && i * 3 + 3 < sizeof(recv_hex); i++) {
+        snprintf(recv_hex + i * 3, 4, "%02X ", client->recv_buffer[i]);
+    }
+    if (received > 32) {
+        PPP_DEBUG_VERBOSE("[WebSocket] Received TCP data (first 32 bytes, HEX): %s... (total %u bytes)", 
+                      recv_hex, (unsigned int)received);
+    } else {
+        PPP_DEBUG_VERBOSE("[WebSocket] Received TCP data (HEX): %s", recv_hex);
+    }
+#endif
+    
+    /* 使用 websocket_parser 进行流式解析 */
+    /* 注意:ws_parse_stream 返回处理的字节数(>=0),参数无效时返回 -1 */
+    PPP_DEBUG_VERBOSE("[WebSocket] Calling ws_parse_stream: received=%u bytes", (unsigned int)received);
+    int processed = ws_parse_stream(client->parser, client->recv_buffer, received);
+    PPP_DEBUG_VERBOSE("[WebSocket] ws_parse_stream returned: processed=%d bytes", processed);
+    
+    if (processed < 0) {
+        /* 参数无效 */
+        PPP_DEBUG_ERROR("[WebSocket] Invalid parameters for ws_parse_stream");
+        return -1;
+    }
+    
+    /* 检查解析器状态 */
+    int parser_state = ws_parser_get_state(client->parser);
+    bool has_error = ws_parser_has_error(client->parser);
+    PPP_DEBUG_VERBOSE("[WebSocket] Parser state: %d, has_error=%d", parser_state, has_error ? 1 : 0);
+    
+    /* 检查解析器是否处于错误状态 */
+    if (has_error) {
+        PPP_DEBUG_ERROR("[WebSocket] Frame parsing error for client %d", client->client_id);
+        ws_parser_reset(client->parser);
+        return -1;
+    }
+    
+    /* 检查是否解析完成一个完整的帧 */
+    if (parser_state == WS_STATE_COMPLETE) {
+        PPP_DEBUG_VERBOSE("[WebSocket] ✅ Frame parsing complete! Extracting frame data...");
+        const ws_header_t *header = ws_get_header(client->parser);
+        const uint8_t *payload = ws_get_payload(client->parser);
+        size_t payload_len = ws_get_payload_length(client->parser);
         
-        n706a_tcp_recv(&client->tcp_sock, payload, payload_len, &received, 10000);
-        if (received < payload_len) {
-            free(payload);
+        PPP_DEBUG_VERBOSE("[WebSocket] Extracted frame: header=%p, payload=%p, payload_len=%u", 
+                      (void *)header, (void *)payload, (unsigned int)payload_len);
+        
+        if (!header) {
+            PPP_DEBUG_ERROR("[WebSocket] Failed to get frame header");
+            ws_parser_reset(client->parser);
             return -1;
         }
-        payload[payload_len] = '\0';
         
-        /* 如果数据被掩码,需要解掩码 */
-        if (masked) {
-            ws_mask_data(payload, payload_len, mask_key);
+        PPP_DEBUG_VERBOSE("[WebSocket] Frame header: FIN=%d, RSV1=%d, RSV2=%d, RSV3=%d, opcode=0x%02X, mask=%d, payload_len=%u",
+                       header->fin ? 1 : 0,
+                       header->rsv1 ? 1 : 0,
+                       header->rsv2 ? 1 : 0,
+                       header->rsv3 ? 1 : 0,
+                       header->opcode,
+                       header->mask ? 1 : 0,
+                       (unsigned int)payload_len);
+        
+        /* 映射 websocket_parser 的 opcode 到 n706a_ws_opcode_t */
+        n706a_ws_opcode_t opcode;
+        switch (header->opcode) {
+            case WS_CONTINUATION:
+                opcode = N706A_WS_OPCODE_CONTINUATION;
+                break;
+            case WS_TEXT:
+                opcode = N706A_WS_OPCODE_TEXT;
+                break;
+            case WS_BINARY:
+                opcode = N706A_WS_OPCODE_BINARY;
+                break;
+            case WS_CLOSE:
+                opcode = N706A_WS_OPCODE_CLOSE;
+                break;
+            case WS_PING:
+                opcode = N706A_WS_OPCODE_PING;
+                break;
+            case WS_PONG:
+                opcode = N706A_WS_OPCODE_PONG;
+                break;
+            default:
+                PPP_DEBUG_ERROR("[WebSocket] Unknown opcode: 0x%02X", header->opcode);
+                ws_parser_reset(client->parser);
+                return -1;
         }
         
         /* 处理不同类型的帧 */
         switch (opcode) {
+            case N706A_WS_OPCODE_CONTINUATION:
+                /* CONTINUATION 帧:继续之前的分片消息 */
+                PPP_DEBUG_VERBOSE("[WebSocket] Received CONTINUATION frame: payload_len=%u, fin=%d", 
+                              (unsigned int)payload_len, header->fin ? 1 : 0);
+                if (client->on_message && payload) {
+                    PPP_DEBUG_VERBOSE("[WebSocket] Calling on_message callback for CONTINUATION: client_id=%d, payload_len=%u", 
+                                  client->client_id, (unsigned int)payload_len);
+                    /* 对于 TEXT 消息,确保数据以 \0 结尾(使用 recv_buffer 作为临时缓冲区) */
+                    const char *message_data = (const char *)payload;
+                    if (client->fragment_opcode == N706A_WS_OPCODE_TEXT && payload_len > 0) {
+                        /* 检查 recv_buffer 是否有足够空间(至少需要 payload_len + 1) */
+                        if (payload_len + 1 <= client->recv_buffer_size) {
+                            memcpy(client->recv_buffer, payload, payload_len);
+                            client->recv_buffer[payload_len] = '\0';
+                            message_data = (const char *)client->recv_buffer;
+                        } else {
+                            PPP_DEBUG_WARN("[WebSocket] Payload too large for null-termination buffer (%u > %u), passing as-is", 
+                                         (unsigned int)payload_len, (unsigned int)client->recv_buffer_size);
+                        }
+                    }
+                    /* 使用之前保存的 fragment_opcode */
+                    client->on_message(client->client_id,
+                                     message_data,
+                                     payload_len,
+                                     header->fin ? true : false,
+                                     client->fragment_opcode == N706A_WS_OPCODE_BINARY);
+                    PPP_DEBUG_VERBOSE("[WebSocket] on_message callback for CONTINUATION returned");
+                }
+                /* 如果这是最后一帧(FIN=1),重置 fragment_opcode */
+                if (header->fin) {
+                    client->fragment_opcode = N706A_WS_OPCODE_TEXT;  /* 重置为默认值 */
+                }
+                break;
+                
             case N706A_WS_OPCODE_TEXT:
             case N706A_WS_OPCODE_BINARY:
-                if (client->on_message) {
+                /* 保存初始 opcode,用于后续的 CONTINUATION 帧 */
+                client->fragment_opcode = opcode;
+                
+                /* 关键信息:接收到的消息 */
+                PPP_DEBUG_INFO("[WebSocket] Received %s message: payload_len=%u bytes", 
+                              opcode == N706A_WS_OPCODE_TEXT ? "TEXT" : "BINARY",
+                              (unsigned int)payload_len);
+                /* 详细调试:打印消息内容(默认关闭,编译期移除) */
+#if PPP_DEBUG_VERBOSE_ENABLE
+                if (payload && payload_len > 0) {
+                    /* 注意:使用静态缓冲区避免栈溢出,只打印前32字节 */
+                    size_t print_len = (payload_len > 32) ? 32 : payload_len;  /* 只打印前32字节 */
+                    static char msg_hex[128] = {0};  /* 使用静态缓冲区:32字节 * 3字符/字节 = 96字节 */
+                    static char msg_ascii[64] = {0};  /* 使用静态缓冲区:32字节 + 余量 */
+                    memset(msg_hex, 0, sizeof(msg_hex));
+                    memset(msg_ascii, 0, sizeof(msg_ascii));
+                    
+                    for (size_t i = 0; i < print_len && i * 3 + 3 < sizeof(msg_hex) && i < sizeof(msg_ascii) - 1; i++) {
+                        snprintf(msg_hex + i * 3, 4, "%02X ", payload[i]);
+                        msg_ascii[i] = (payload[i] >= 32 && payload[i] < 127) ? payload[i] : '.';
+                    }
+                    msg_ascii[print_len < sizeof(msg_ascii) - 1 ? print_len : sizeof(msg_ascii) - 1] = '\0';
+                    
+                    if (payload_len > 32) {
+                        PPP_DEBUG_VERBOSE("[WebSocket] Message content (first 32 bytes): HEX=%s... ASCII=%s...", 
+                                          msg_hex, msg_ascii);
+                    } else {
+                        PPP_DEBUG_VERBOSE("[WebSocket] Message content: HEX=%s ASCII=%s", 
+                                          msg_hex, msg_ascii);
+                    }
+                }
+#endif
+                
+                if (client->on_message && payload) {
+                    PPP_DEBUG_VERBOSE("[WebSocket] Calling on_message callback: client_id=%d, payload_len=%u, fin=%d, is_binary=%d", 
+                                  client->client_id, (unsigned int)payload_len, header->fin ? 1 : 0, 
+                                  opcode == N706A_WS_OPCODE_BINARY ? 1 : 0);
+                    /* 对于 TEXT 消息,确保数据以 \0 结尾(使用 recv_buffer 作为临时缓冲区) */
+                    const char *message_data = (const char *)payload;
+                    if (opcode == N706A_WS_OPCODE_TEXT && payload_len > 0) {
+                        /* 检查 recv_buffer 是否有足够空间(至少需要 payload_len + 1) */
+                        if (payload_len + 1 <= client->recv_buffer_size) {
+                            memcpy(client->recv_buffer, payload, payload_len);
+                            client->recv_buffer[payload_len] = '\0';
+                            message_data = (const char *)client->recv_buffer;
+                            PPP_DEBUG_VERBOSE("[WebSocket] TEXT message null-terminated in recv_buffer (%u bytes)", 
+                                            (unsigned int)payload_len);
+                        } else {
+                            PPP_DEBUG_WARN("[WebSocket] Payload too large for null-termination buffer (%u > %u), passing as-is", 
+                                         (unsigned int)payload_len, (unsigned int)client->recv_buffer_size);
+                        }
+                    }
                     client->on_message(client->client_id,
-                                     (char *)payload,
+                                     message_data,
                                      payload_len,
-                                     fin,
+                                     header->fin ? true : false,
                                      opcode == N706A_WS_OPCODE_BINARY);
+                    PPP_DEBUG_VERBOSE("[WebSocket] on_message callback returned");
+                } else {
+                    if (!client->on_message) {
+                        PPP_DEBUG_WARN("[WebSocket] on_message callback is NULL, message not delivered!");
+                    }
+                    if (!payload) {
+                        PPP_DEBUG_WARN("[WebSocket] payload is NULL, message not delivered!");
+                    }
+                }
+                /* 如果这是最后一帧(FIN=1),重置 fragment_opcode */
+                if (header->fin) {
+                    client->fragment_opcode = N706A_WS_OPCODE_TEXT;  /* 重置为默认值 */
                 }
                 break;
                 
             case N706A_WS_OPCODE_PING:
-                /* 自动回复Pong */
-                ws_send_frame(client, N706A_WS_OPCODE_PONG, payload, payload_len, true);
+                /* 自动回复 Pong */
+                if (payload) {
+                    ws_send_frame(client, N706A_WS_OPCODE_PONG, payload, payload_len, true);
+                } else {
+                    ws_send_frame(client, N706A_WS_OPCODE_PONG, NULL, 0, true);
+                }
                 break;
                 
             case N706A_WS_OPCODE_PONG:
-                /* 忽略Pong */
+                /* 忽略 Pong(心跳响应) */
+                PPP_DEBUG_VERBOSE("[WebSocket] Received Pong frame");
                 break;
                 
             case N706A_WS_OPCODE_CLOSE:
                 /* 服务器关闭连接 */
+                PPP_DEBUG_INFO("[WebSocket] Received CLOSE frame from server, closing connection");
                 client->state = N706A_WS_STATE_CLOSING;
                 
                 uint16_t close_code = N706A_WS_CLOSE_NO_STATUS;
-                if (payload_len >= 2) {
+                if (payload && payload_len >= 2) {
                     close_code = (payload[0] << 8) | payload[1];
+                    PPP_DEBUG_INFO("[WebSocket] Close code: %u", close_code);
+                }
+                
+                /* 根据 RFC 6455,收到 CLOSE 帧后应该发送 CLOSE 帧响应 */
+                /* 注意:这里不直接调用 n706a_websocket_close,因为会关闭 TCP 连接 */
+                /* 而是发送 CLOSE 帧响应,然后让接收线程退出时关闭 TCP 连接 */
+                if (payload && payload_len >= 2) {
+                    /* 回显相同的关闭码 */
+                    uint8_t close_response[128];
+                    close_response[0] = (close_code >> 8) & 0xFF;
+                    close_response[1] = close_code & 0xFF;
+                    size_t close_response_len = 2;
+                    
+                    /* 如果有原因,也回显 */
+                    if (payload_len > 2) {
+                        size_t reason_len = payload_len - 2;
+                        if (reason_len < sizeof(close_response) - 2) {
+                            memcpy(close_response + 2, payload + 2, reason_len);
+                            close_response_len += reason_len;
+                        }
+                    }
+                    
+                    ws_send_frame(client, N706A_WS_OPCODE_CLOSE, close_response, close_response_len, true);
+                } else {
+                    /* 没有关闭码,发送空的 CLOSE 帧 */
+                    ws_send_frame(client, N706A_WS_OPCODE_CLOSE, NULL, 0, true);
                 }
                 
                 if (client->on_close) {
                     client->on_close(client->client_id, close_code,
-                                   payload_len > 2 ? (char *)(payload + 2) : NULL);
+                                   (payload && payload_len > 2) ? (char *)(payload + 2) : NULL);
                 }
                 break;
                 
             default:
+                PPP_DEBUG_WARN("[WebSocket] Unhandled opcode: 0x%02X", opcode);
                 break;
         }
         
-        free(payload);
+        /* 重置解析器,准备解析下一帧 */
+        ws_parser_reset(client->parser);
+        
+        PPP_DEBUG_VERBOSE("[WebSocket] ✅ Frame processing completed successfully, ready for next frame");
+        return 0;  /* 成功解析一帧 */
+    } else if (parser_state == WS_STATE_ERROR) {
+        /* 解析错误 */
+        PPP_DEBUG_ERROR("[WebSocket] ❌ Parser in error state, resetting parser");
+        ws_parser_reset(client->parser);
+        return -1;
+    } else {
+        /* 数据不完整,需要继续接收 */
+        /* 注意:这里返回 0 表示没有错误,但帧还未完成 */
+        /* 下次调用时会继续解析 */
+        PPP_DEBUG_VERBOSE("[WebSocket] Frame incomplete (state=%d), need more data, continuing...", parser_state);
+        return 0;
     }
-    
-    return 0;
 }
 
 /**
@@ -740,7 +1397,8 @@ static int ws_recv_frame(n706a_websocket_client_t *client)
  */
 static void ws_generate_mask_key(uint8_t *mask_key)
 {
-    for (int i = 0; i < 4; i++) {
+    int i;
+    for (i = 0; i < 4; i++) {
         mask_key[i] = rand() & 0xFF;
     }
 }
@@ -757,6 +1415,9 @@ static void ws_mask_data(uint8_t *data, size_t len, const uint8_t *mask_key)
 
 /**
  * @brief 解析WebSocket URL
+ * 
+ * 解析格式:ws://host:port/path?query#fragment 或 wss://host:port/path?query#fragment
+ * path 会包含完整的路径、query参数和fragment(如果存在)
  */
 static int ws_parse_url(const char *url, char *protocol, char *host,
                        uint16_t *port, char *path)
@@ -780,34 +1441,79 @@ static int ws_parse_url(const char *url, char *protocol, char *host,
         return -1;
     }
     
-    /* 解析host */
+    /* 解析host、port和path */
+    /* 查找路径开始位置(第一个 '/') */
     const char *path_start = strchr(p, '/');
+    /* 查找端口号开始位置(第一个 ':',但必须在路径之前) */
     const char *port_start = strchr(p, ':');
     
+    /* 如果找到端口号,且端口号在路径之前(或没有路径) */
     if (port_start && (!path_start || port_start < path_start)) {
+        /* 提取host(从当前位置到端口号) */
         size_t host_len = port_start - p;
+        if (host_len >= 128) {
+            host_len = 127;  // host缓冲区大小限制
+        }
         strncpy(host, p, host_len);
         host[host_len] = '\0';
         
-        *port = atoi(port_start + 1);
+        /* 解析端口号(从 ':' 后开始,到路径或字符串结束) */
+        const char *port_end = path_start ? path_start : (p + strlen(p));
+        size_t port_str_len = port_end - (port_start + 1);
+        if (port_str_len > 0 && port_str_len < 10) {
+            char port_str[16] = {0};
+            strncpy(port_str, port_start + 1, port_str_len);
+            port_str[port_str_len] = '\0';
+            *port = (uint16_t)atoi(port_str);
+        }
         
+        /* 提取path(包含query参数和fragment,如果有) */
         if (path_start) {
-            strcpy(path, path_start);
+            /* 计算path长度(包括query和fragment) */
+            size_t path_len = strlen(path_start);
+            if (path_len >= N706A_WS_MAX_URL_LEN) {
+                path_len = N706A_WS_MAX_URL_LEN - 1;  // path缓冲区大小限制(N706A_WS_MAX_URL_LEN字节,包括'\0')
+                PPP_DEBUG_WARN("[WebSocket] Path too long, truncated to %u bytes", (unsigned int)path_len);
+            }
+            strncpy(path, path_start, path_len);
+            path[path_len] = '\0';
         } else {
             strcpy(path, "/");
         }
     } else {
+        /* 没有端口号,或路径在端口号之前(异常情况) */
         if (path_start) {
+            /* 提取host(从当前位置到路径) */
             size_t host_len = path_start - p;
+            if (host_len >= 128) {
+                host_len = 127;  // host缓冲区大小限制
+            }
             strncpy(host, p, host_len);
             host[host_len] = '\0';
-            strcpy(path, path_start);
+            
+            /* 提取path(包含query参数和fragment,如果有) */
+            size_t path_len = strlen(path_start);
+            if (path_len >= N706A_WS_MAX_URL_LEN) {
+                path_len = N706A_WS_MAX_URL_LEN - 1;  // path缓冲区大小限制(N706A_WS_MAX_URL_LEN字节,包括'\0')
+                PPP_DEBUG_WARN("[WebSocket] Path too long, truncated to %u bytes", (unsigned int)path_len);
+            }
+            strncpy(path, path_start, path_len);
+            path[path_len] = '\0';
         } else {
-            strcpy(host, p);
+            /* 没有路径,只有host */
+            size_t host_len = strlen(p);
+            if (host_len >= 128) {
+                host_len = 127;
+            }
+            strncpy(host, p, host_len);
+            host[host_len] = '\0';
             strcpy(path, "/");
         }
     }
     
+    PPP_DEBUG_VERBOSE("[WebSocket] Parsed URL: protocol=%s, host=%s, port=%u, path=%s", 
+                   protocol, host, *port, path);
+    
     return 0;
 }
 
@@ -985,24 +1691,38 @@ static void sha1_simple(const unsigned char *input, size_t len, unsigned char ou
 }
 
 /**
- * @brief WebSocket接收线程函数
+ * @brief WebSocket接收线程函数(内部实现)
  * 
  * 持续接收WebSocket帧并调用回调函数通知应用层
  */
-static void *ws_recv_thread_func(void *arg)
+static void ws_recv_thread_func_impl(n706a_websocket_client_t *client)
 {
-    n706a_websocket_client_t *client = (n706a_websocket_client_t *)arg;
     if (!client) {
-        return NULL;
+        return;
     }
     
-    PPP_DEBUG_INFO("WebSocket receive thread started for client %d", client->client_id);
+    PPP_DEBUG_INFO("[WebSocket] ✅ Receive thread started for client %d", client->client_id);
     
+    uint32_t loop_count = 0;
     while (client->running && client->state == N706A_WS_STATE_OPEN) {
+        loop_count++;
+#if PPP_DEBUG_VERBOSE_ENABLE
+        if (loop_count % 100 == 0) {
+            /* 每100次循环打印一次,确认线程在运行(详细调试,编译期移除) */
+            PPP_DEBUG_VERBOSE("[WebSocket] Receive thread loop #%u: client_id=%d, state=%d, running=%d", 
+                          loop_count, client->client_id, client->state, client->running ? 1 : 0);
+        }
+#endif
+        
         /* 尝试接收一帧数据 */
+        PPP_DEBUG_VERBOSE("[WebSocket] Receive thread loop #%u: calling ws_recv_frame...", loop_count);
         int ret = ws_recv_frame(client);
+        PPP_DEBUG_VERBOSE("[WebSocket] Receive thread loop #%u: ws_recv_frame returned %d", loop_count, ret);
         
         if (ret != 0) {
+            if (client->state == N706A_WS_STATE_OPEN) {
+                ws_send_close_if_needed(client, N706A_WS_CLOSE_ABNORMAL, "Receive frame failed");
+            }
             /* 接收失败,可能是连接断开或超时 */
             if (client->running) {
                 PPP_DEBUG_WARN("WebSocket receive frame failed for client %d (ret=%d)", 
@@ -1021,28 +1741,42 @@ static void *ws_recv_thread_func(void *arg)
             }
         }
         
-        /* 如果状态变为CLOSING,退出循环 */
+        /* 如果状态变为CLOSING,退出循环并关闭连接 */
         if (client->state == N706A_WS_STATE_CLOSING || 
             client->state == N706A_WS_STATE_CLOSED) {
             break;
         }
         
-        /* 短暂延迟,避免CPU占用过高 */
+        /* 优化:移除延迟,让线程在TCP接收函数中完全阻塞
+         * TCP接收函数会阻塞在URC信号量上,直到数据到达或超时
+         * 这样可以达到最低功耗和CPU占用,适合小系统
+         * 
+         * 如果ret==0(成功但没有数据),说明TCP接收超时返回
+         * 此时不需要延迟,直接再次调用TCP接收,让它继续阻塞等待
+         * 
+         * 如果ret!=0(错误),说明连接可能断开,短暂延迟后重试
+         */
+        if (ret != 0) {
+            /* 有错误,短暂延迟后重试(避免快速重试导致CPU占用过高) */
 #ifdef __unix__
-        usleep(1000);  // 1ms
+            usleep(1000);  // 1ms
 #elif defined(__rtthread__)
-        rt_thread_mdelay(1);
+            rt_thread_mdelay(1);
 #else
-        /* 其他平台:简单延迟循环 */
-        volatile int i;
-        for (i = 0; i < 1000; i++);
+            osDelay(1);  /* 1ms延迟 */
 #endif
+        }
+        /* ret==0时不需要延迟,直接继续循环,让TCP接收函数阻塞等待URC */
     }
     
     PPP_DEBUG_INFO("WebSocket receive thread stopped for client %d", client->client_id);
     
-    /* 如果是因为接收失败而退出,关闭连接 */
-    if (client->state == N706A_WS_STATE_OPEN || client->state == N706A_WS_STATE_CLOSING) {
+    /* 如果是因为接收失败或发送失败而退出,关闭连接 */
+    if (client->state == N706A_WS_STATE_OPEN ||
+        client->state == N706A_WS_STATE_CLOSING) {
+        /* 关闭 TCP 连接 */
+        n706a_tcp_close(&client->tcp_sock);
+        
         client->state = N706A_WS_STATE_CLOSED;
         
         /* 调用关闭回调 */
@@ -1052,9 +1786,34 @@ static void *ws_recv_thread_func(void *arg)
                            "Receive thread exited");
         }
     }
-    
+}
+
+#ifdef __unix__
+/**
+ * @brief WebSocket接收线程函数(POSIX线程包装)
+ */
+static void *ws_recv_thread_func(void *arg)
+{
+    ws_recv_thread_func_impl((n706a_websocket_client_t *)arg);
     return NULL;
 }
+#elif defined(__rtthread__)
+/**
+ * @brief WebSocket接收线程函数(RT-Thread包装)
+ */
+static void ws_recv_thread_func(void *arg)
+{
+    ws_recv_thread_func_impl((n706a_websocket_client_t *)arg);
+}
+#else
+/**
+ * @brief WebSocket接收线程函数(CMSIS OS包装)
+ */
+static void ws_recv_thread_func(void *arg)
+{
+    ws_recv_thread_func_impl((n706a_websocket_client_t *)arg);
+}
+#endif
 
 /**
  * @brief Base64编码
@@ -1084,7 +1843,8 @@ static void base64_encode(const unsigned char *input, size_t len, char *output)
     }
     
     if (i) {
-        for (size_t k = i; k < 3; k++) {
+        size_t k;
+        for (k = i; k < 3; k++) {
             char_array_3[k] = '\0';
         }
         
@@ -1092,7 +1852,7 @@ static void base64_encode(const unsigned char *input, size_t len, char *output)
         char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
         char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
         
-        for (size_t k = 0; k < i + 1; k++) {
+        for (k = 0; k < i + 1; k++) {
             output[j++] = base64_table[char_array_4[k]];
         }
         
index b7150ef..3a78bce 100644 (file)
@@ -235,11 +235,11 @@ static void ppp_irq_process_thread(void *arg)
         
         // 只打印前16字节
         PPP_DEBUG_PRINT("  Data: ");
-        for (uint32_t i = 0; i < msg.length && i < 16; i++) {
+        for (uint32_t i = 0; i < msg.length && i < 32; i++) {
             PPP_DEBUG_PRINT("%02X ", buffer->data[i]);
         }
-        if (msg.length > 16) {
-            PPP_DEBUG_PRINT("... (%u more bytes)", msg.length - 16);
+        if (msg.length > 32) {
+            PPP_DEBUG_PRINT("... (%u more bytes)", msg.length - 32);
         }
         PPP_DEBUG_PRINT("\n");
         
index 67169b6..d144d79 100644 (file)
@@ -346,7 +346,7 @@ void ppp_api_wakeup_cat1(uint8_t wakeup)
     uint8_t wake_value = g_ppp_cat1_ctl_pins.wakemodem_pin_en_value;
     
     if (wake_pin == PIN_NONE) {
-        PPP_DEBUG_PRINT("PPP: wakeup_cat1 wake_pin is none\n");
+        PPP_DEBUG_CRITICAL("PPP: ⚠️ wakeup_cat1 wake_pin is none, wakeup=%d\n", wakeup);
         return;
     }
     
@@ -354,17 +354,19 @@ void ppp_api_wakeup_cat1(uint8_t wakeup)
     if (wakeup) {
         if (wake_value) {
             uapi_gpio_set_val(wake_pin, GPIO_LEVEL_HIGH);
+            PPP_DEBUG_CRITICAL("PPP: 🔔 Wakeup Cat1: pin=%d, level=HIGH (wake_value=1)\n", wake_pin);
         } else {
             uapi_gpio_set_val(wake_pin, GPIO_LEVEL_LOW);
+            PPP_DEBUG_CRITICAL("PPP: 🔔 Wakeup Cat1: pin=%d, level=LOW (wake_value=0)\n", wake_pin);
         }
-        PPP_DEBUG_PRINT("PPP: Wakeup Cat1 pin %d\n", wake_pin);
     } else {
         if (wake_value) {
             uapi_gpio_set_val(wake_pin, GPIO_LEVEL_LOW);
+            PPP_DEBUG_CRITICAL("PPP: 🔕 Release Wakeup Cat1: pin=%d, level=LOW (wake_value=1)\n", wake_pin);
         } else {
             uapi_gpio_set_val(wake_pin, GPIO_LEVEL_HIGH);
+            PPP_DEBUG_CRITICAL("PPP: 🔕 Release Wakeup Cat1: pin=%d, level=HIGH (wake_value=0)\n", wake_pin);
         }
-        PPP_DEBUG_PRINT("PPP: Release Wakeup Cat1 pin %d\n", wake_pin);
     }
 }
 
@@ -395,57 +397,24 @@ errcode_t ppp_register_ctl_pin(ppp_ctl_pin_4g_t *ctl_pin)
 }
 
 /**
- * @brief PPP模块Cat1唤醒中断回调函数(带去抖处理)
+ * @brief PPP模块Cat1唤醒中断回调函数(无防抖,收到就打印)
  */
 void ppp_ring_isr_callback(pin_t pin, uintptr_t param)
 {
     (void)param;
     
-    // ⚠️ 去抖处理:防止信号抖动导致频繁中断
-    static uint64_t last_interrupt_time = 0;
-    static gpio_level_t last_level = GPIO_LEVEL_LOW;
-    static uint32_t same_level_count = 0;
-    
-    uint64_t current_time = uapi_tcxo_get_ms();  // 获取当前时间(毫秒)
+    // 直接读取电平并打印,不做防抖处理
     gpio_level_t val = uapi_gpio_get_val(pin);
     
-    // 去抖时间窗口:50ms(可根据实际情况调整)
-    #define RING_DEBOUNCE_TIME_MS 50
-    
-    // 检查是否在去抖时间内
-    if ((current_time - last_interrupt_time) < RING_DEBOUNCE_TIME_MS) {
-        // 在去抖时间内,检查电平是否一致
-        if (val == last_level) {
-            same_level_count++;
-            // 如果同一电平连续触发超过5次,打印警告
-            if (same_level_count == 5) {
-                PPP_DEBUG_WARN("PPP: ⚠️ Ring pin %d signal bouncing (level=%d, count=%u)\n", 
-                              pin, val, same_level_count);
-            }
-        } else {
-            same_level_count = 1;
-            last_level = val;
-        }
-        
-        // 清除中断标志,但不处理
-        // uapi_gpio_clear_interrupt(pin);
-        // return;
-    }
-    
-    // 去抖时间已过,处理中断
-    last_interrupt_time = current_time;
-    last_level = val;
-    same_level_count = 1;
-    
     if (val == GPIO_LEVEL_HIGH) {
-        PPP_DEBUG_WARN("PPP: Cat1 ring interrupt on pin %d → HIGH \n", pin);
+        PPP_DEBUG_WARN("PPP: Cat1 ring interrupt on pin %d → HIGH\n", pin);
         cat1_api_add_cat1_vote();
-} else {
+        cat1_api_wakeup_cat1(true);
+    } else {
         PPP_DEBUG_WARN("PPP: Cat1 ring interrupt on pin %d → LOW\n", pin);
         //idle后 remove
     }
     uapi_gpio_clear_interrupt(pin);
-    
 }
 
 /**
index 4d30498..142d384 100644 (file)
@@ -164,6 +164,7 @@ ppp_error_t ppp_serial_init(const ppp_serial_config_t *config)
     // - 中断模式(SUPPORT_PPP_SERVICE_INTERRUPT):uart_porting.c中已完整初始化UART,
     //   这里只需要设置波特率
     
+    errcode_t ret;
     #if defined(SUPPORT_PPP_SERVICE_POLLING)
         // 轮询模式:完整初始化UART(注意:DMA已在chip_init.c:152-153初始化,不要重复调用)
         PPP_DEBUG_WARN("Polling mode: Initializing UART for the first time");
@@ -173,13 +174,13 @@ ppp_error_t ppp_serial_init(const ppp_serial_config_t *config)
         g_serial_ctx.extra_attr.rx_dma_enable = UART_DMA_RX_ENABLE;
         g_serial_ctx.extra_attr.rx_int_threshold = 0;
         PPP_DEBUG_WARN("Polling mode with DMA: TX DMA and RX DMA both enabled");
-        errcode_t ret = uapi_uart_init(g_serial_ctx.uart_bus, 
+        ret = uapi_uart_init(g_serial_ctx.uart_bus, 
             &g_serial_ctx.pin_config,
             &g_serial_ctx.uart_attr, 
             &g_serial_ctx.extra_attr, 
             &g_serial_ctx.buffer_config);
         #else
-        errcode_t ret = uapi_uart_init(g_serial_ctx.uart_bus, 
+        ret = uapi_uart_init(g_serial_ctx.uart_bus, 
             &g_serial_ctx.pin_config,
             &g_serial_ctx.uart_attr, 
             NULL, 
@@ -188,7 +189,7 @@ ppp_error_t ppp_serial_init(const ppp_serial_config_t *config)
     #elif defined(SUPPORT_PPP_SERVICE_INTERRUPT) || defined(SUPPORT_N706A_NET)
         // 中断模式/N706A_NET模式:UART已在uart_porting.c:uart_config_init()中初始化,只设置波特率
         PPP_DEBUG_WARN("Interrupt/N706A_NET mode: UART already initialized, updating baudrate");
-        errcode_t ret = uapi_uart_set_attr(g_serial_ctx.uart_bus, &g_serial_ctx.uart_attr);
+        ret = uapi_uart_set_attr(g_serial_ctx.uart_bus, &g_serial_ctx.uart_attr);
     #else
         #error "SUPPORT_PPP_SERVICE_POLLING, SUPPORT_PPP_SERVICE_INTERRUPT, or SUPPORT_N706A_NET must be defined"
     #endif
@@ -281,7 +282,7 @@ void check_cat1_uart_wakeup(void)
 /* 如需清空缓冲区,请使用 ppp_serial_flush_rx() */
 
 /**
- * @brief 发送数据 - 两种模式都直接通过UART发送
+ * @brief 发送数据 - n706a_net 只使用 AT 模式,直接通过 UART 发送
  */
 ppp_error_t ppp_serial_write(const uint8_t *data, uint32_t length)
 {
@@ -293,81 +294,35 @@ ppp_error_t ppp_serial_write(const uint8_t *data, uint32_t length)
 
     PPP_DEBUG_DEBUG("=== PPP Serial Write Start ===");
     #if defined(PPP_LOW_POWER_MODE) && (PPP_LOW_POWER_MODE == 1)
+    PPP_DEBUG_CRITICAL("PPP Serial Write: Waking up CAT1 module before sending data...");
     ppp_api_wakeup_cat1(true);
     osal_msleep(1);
+    PPP_DEBUG_CRITICAL("PPP Serial Write: CAT1 wakeup completed, proceeding to send data");
+    #else
+    PPP_DEBUG_CRITICAL("PPP Serial Write: PPP_LOW_POWER_MODE is disabled, skipping wakeup");
     #endif
-    PPP_DEBUG_DEBUG("Write request: %u bytes in mode %d", length, g_serial_ctx.current_mode);
+    PPP_DEBUG_DEBUG("Write request: %u bytes (AT mode)", length);
     PPP_DEBUG_DEBUG("UART Bus: %u", g_serial_ctx.uart_bus);
 
+    /* n706a_net 只使用 AT 模式,直接使用 UART 发送 */
+    PPP_DEBUG_DEBUG("Sending %u bytes via UART (AT mode)", length);
+    
+    PPP_DEBUG_DEBUG("Calling uapi_uart_write(bus=%u, data=%p, len=%u, timeout=1000)...", 
+                  g_serial_ctx.uart_bus, data, length, 1000);
+    
+    int32_t write_result = uapi_uart_write(g_serial_ctx.uart_bus, data, length, 1000);
+    PPP_DEBUG_DEBUG("uapi_uart_write returned: %d (expected: %u)", write_result, length);
+    
     ppp_error_t result = PPP_ERR_OK;
-    int32_t write_result = 0;
-    switch (g_serial_ctx.current_mode) {
-        case PPP_SERIAL_MODE_AT:
-            /* AT命令模式:直接使用UART发送 */
-            PPP_DEBUG_DEBUG("Sending %u bytes via UART (AT mode)", length);
-            // PPP_DEBUG_HEXDUMP(data, length);
-            
-            PPP_DEBUG_DEBUG("Calling uapi_uart_write(bus=%u, data=%p, len=%u, timeout=1000)...", 
-                          g_serial_ctx.uart_bus, data, length, 1000);
-            
-            write_result = uapi_uart_write(g_serial_ctx.uart_bus, data, length, 1000);
-            PPP_DEBUG_DEBUG("uapi_uart_write returned: %d (expected: %u)", write_result, length);
-            
-            if (write_result != (int32_t)length) {
-                PPP_DEBUG_ERROR("UART write failed in AT mode: returned %d, expected %u", write_result, length);
-                PPP_DEBUG_ERROR("Possible causes: UART not initialized, hardware error, or timeout");
-                g_serial_ctx.tx_errors++;
-                result = PPP_ERR_UART;
-            } else {
-                g_serial_ctx.tx_bytes += length;
-                PPP_DEBUG_INFO("Sent %u bytes successfully via UART (total tx: %u bytes)", 
-                              length, g_serial_ctx.tx_bytes);
-            }
-            break;
-
-        case PPP_SERIAL_MODE_PPP_DATA:
-            /* PPP数据模式:直接使用UART发送(与AT模式一致) */
-            PPP_DEBUG_INFO("Sending %u bytes via UART (PPP data mode)", length);
-            
-            /* 检查UART状态 */
-            PPP_DEBUG_DEBUG("UART Bus: %u, Mode: PPP_DATA, TX Bytes: %u, TX Errors: %u", 
-                          g_serial_ctx.uart_bus, g_serial_ctx.tx_bytes, g_serial_ctx.tx_errors);
-            
-            /* 打印要发送的数据 */
-            PPP_DEBUG_PRINT("PPP TX Data: ");
-            for (uint32_t i = 0; i < length && i < 16; i++) {
-                PPP_DEBUG_PRINT("%02X ", data[i]);
-            }
-            if (length > 16) {
-                PPP_DEBUG_PRINT("... (total %u bytes)", length);
-            }
-            PPP_DEBUG_PRINT("\n");
-            
-            PPP_DEBUG_DEBUG("Calling uapi_uart_write(bus=%u, data=%p, len=%u, timeout=%d)...", 
-                          g_serial_ctx.uart_bus, data, length, PPP_OPTIMIZED_UART_WRITE_TIMEOUT);
-            
-            /* 优化UART写超时:使用配置的优化值以提高PPP协商速度 */
-            write_result = uapi_uart_write(g_serial_ctx.uart_bus, data, length, PPP_OPTIMIZED_UART_WRITE_TIMEOUT);
-            PPP_DEBUG_INFO("uapi_uart_write returned: %d (expected: %u)", write_result, length);
-            
-            if (write_result != (int32_t)length) {
-                PPP_DEBUG_ERROR("UART write failed in PPP data mode: returned %d, expected %u", write_result, length);
-                PPP_DEBUG_ERROR("UART状态: bus=%u, 总发送字节=%u, 总错误=%u", 
-                              g_serial_ctx.uart_bus, g_serial_ctx.tx_bytes, g_serial_ctx.tx_errors);
-                PPP_DEBUG_ERROR("可能原因: UART未初始化、硬件错误、超时或缓冲区满");
-                g_serial_ctx.tx_errors++;
-                result = PPP_ERR_UART;
-            } else {
-                g_serial_ctx.tx_bytes += length;
-                PPP_DEBUG_INFO("Sent %u bytes successfully via UART (PPP mode, total tx: %u bytes)", 
-                              length, g_serial_ctx.tx_bytes);
-            }
-            break;
-
-        default:
-            PPP_DEBUG_ERROR("Invalid serial mode: %d", g_serial_ctx.current_mode);
-            result = PPP_ERR_INVALID_PARAM;
-            break;
+    if (write_result != (int32_t)length) {
+        PPP_DEBUG_ERROR("UART write failed: returned %d, expected %u", write_result, length);
+        PPP_DEBUG_ERROR("Possible causes: UART not initialized, hardware error, or timeout");
+        g_serial_ctx.tx_errors++;
+        result = PPP_ERR_UART;
+    } else {
+        g_serial_ctx.tx_bytes += length;
+        PPP_DEBUG_INFO("Sent %u bytes successfully via UART (total tx: %u bytes)", 
+                      length, g_serial_ctx.tx_bytes);
     }
 
     PPP_DEBUG_INFO("Write result: %s (error code: %d)", 
@@ -382,7 +337,7 @@ ppp_error_t ppp_serial_write(const uint8_t *data, uint32_t length)
 }
 
 /**
- * @brief 读取数据 - AT模式直接从UART读取,PPP模式从软件缓冲区读取
+ * @brief 读取数据 - n706a_net 只使用 AT 模式,直接从 UART 读取
  */
 ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *bytes_read)
 {
@@ -397,135 +352,27 @@ ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *byt
     *bytes_read = 0;
     ppp_error_t result = PPP_ERR_OK;
 
-    switch (g_serial_ctx.current_mode) {
-        case PPP_SERIAL_MODE_AT:
-        /* AT命令模式:直接从UART读取 */
-            {
-
-                #if defined(CONFIG_UART_SUPPORT_DMA)
-                uart_write_dma_config_t dma_cfg = {
-                    .src_width = UART_DMA_WIDTH,
-                    .dest_width = UART_DMA_WIDTH,
-                    .burst_length = UART_DMA_BURST_LENGTH,
-                    .priority = UART_DMA_PRIORITY
-                };
-                int32_t read_result = uapi_uart_read_by_dma(g_serial_ctx.uart_bus, buffer, buffer_size,&dma_cfg);
-                    
-                #else
-                int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, buffer, buffer_size, 100);
-                #endif
-                if (read_result > 0) {
-                    *bytes_read = (uint32_t)read_result;
-                    g_serial_ctx.rx_bytes += *bytes_read;
-                    PPP_DEBUG_DEBUG("Read %u bytes via UART (AT mode)", *bytes_read);
-                } else if (read_result < 0) {
-                    PPP_DEBUG_ERROR("UART read error: %d", read_result);
-                    g_serial_ctx.rx_errors++;
-                    result = PPP_ERR_UART;
-                }
-            }
-            break;
-
-        case PPP_SERIAL_MODE_PPP_DATA:
-        /* PPP数据模式:使用优化的直接UART读取,平衡响应性和完整性 */
-            {
-                /* 使用类似SLIP的高效读取策略:
-                 * 1. 首次尝试非阻塞读取(1ms超时)
-                 * 2. 如果有数据,继续快速读取直到无数据
-                 * 3. 这样可以批量读取连续到达的数据,提高效率
-                 */
-                uint32_t total_read = 0;
-                const uint32_t MAX_BATCH_READ = 8; // 最多批量读取8次
-                uint32_t batch_count = 0;
-                
-                while (batch_count < MAX_BATCH_READ && total_read < buffer_size) {
-                    uint32_t remaining = buffer_size - total_read;
-                    uint8_t *current_buffer = buffer + total_read;
-                    
-                    // 使用短超时进行读取,类似sio_tryread的1ms超时
-                    
-                    #if defined(CONFIG_UART_SUPPORT_DMA)
-                    uart_write_dma_config_t dma_cfg = {
-                        .src_width = UART_DMA_WIDTH,
-                        .dest_width = UART_DMA_WIDTH,
-                        .burst_length = UART_DMA_BURST_LENGTH,
-                        .priority = UART_DMA_PRIORITY
-                    };
-                    int32_t read_result = uapi_uart_read_by_dma(g_serial_ctx.uart_bus, current_buffer, 
-                        remaining,&dma_cfg);
-                        
-                    #else
-                    int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, current_buffer, 
-                        remaining, batch_count == 0 ? 10 : 1);
-                    #endif
-                    if (read_result > 0) {
-                        total_read += (uint32_t)read_result;
-                        g_serial_ctx.rx_bytes += (uint32_t)read_result;
-                        batch_count++;
-                        
-                        PPP_DEBUG_DEBUG("PPP batch read %u: got %d bytes (total: %u)", 
-                                       batch_count, read_result, total_read);
-                        
-                        // 如果读取的数据少于请求的数据,说明没有更多数据了
-                        if ((uint32_t)read_result < remaining) {
-                            break;
-                        }
-                    } else if (read_result == 0) {
-                        // 超时,无更多数据
-                        break;
-                    } else {
-                        // 读取错误
-                        if (total_read == 0) {
-                            PPP_DEBUG_ERROR("PPP UART Read: Error %d", read_result);
-                            result = PPP_ERR_UART;
-                            break;
-                        }
-                        break;
-                    }
-                }
-                
-                *bytes_read = total_read;
-                
-                if (total_read > 0) {
-                    PPP_DEBUG_DEBUG("PPP optimized read: %u bytes in %u batches", total_read, batch_count);
-                    result = PPP_ERR_OK;
-                    break;
-                } else {
-                    result = PPP_ERR_OK;
-                    break; // 无数据但无错误
-                }
-            }
-            break;
-#if 0
-        case PPP_SERIAL_MODE_PPP_DATA:
-            /* PPP数据模式:从软件环形缓冲区读取 */
-            osal_mutex_lock(&g_serial_ctx.rx_mutex);
-
-            uint32_t available = ppp_serial_buffer_data_count();
-            if (available == 0) {
-                osal_mutex_unlock(&g_serial_ctx.rx_mutex);
-                return PPP_ERR_OK; // 没有数据可读
-            }
-
-            uint32_t to_read = (available < buffer_size) ? available : buffer_size;
-            
-            for (uint32_t i = 0; i < to_read; i++) {
-                buffer[i] = g_serial_ctx.rx_buffer[g_serial_ctx.rx_tail];
-                g_serial_ctx.rx_tail = (g_serial_ctx.rx_tail + 1) % g_serial_ctx.rx_buffer_size;
-                g_serial_ctx.rx_count--;
-            }
-
-            *bytes_read = to_read;
-            osal_mutex_unlock(&g_serial_ctx.rx_mutex);
-
-            PPP_DEBUG_DEBUG("Read %u bytes from software buffer (PPP data mode)", to_read);
-            // PPP_DEBUG_HEXDUMP(buffer, to_read);
-            break;
-#endif
-        default:
-            PPP_DEBUG_ERROR("Invalid serial mode: %d", g_serial_ctx.current_mode);
-            result = PPP_ERR_INVALID_PARAM;
-            break;
+    /* n706a_net 只使用 AT 模式,直接从 UART 读取 */
+    #if defined(CONFIG_UART_SUPPORT_DMA)
+    uart_write_dma_config_t dma_cfg = {
+        .src_width = UART_DMA_WIDTH,
+        .dest_width = UART_DMA_WIDTH,
+        .burst_length = UART_DMA_BURST_LENGTH,
+        .priority = UART_DMA_PRIORITY
+    };
+    int32_t read_result = uapi_uart_read_by_dma(g_serial_ctx.uart_bus, buffer, buffer_size, &dma_cfg);
+    #else
+    int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, buffer, buffer_size, 100);
+    #endif
+    
+    if (read_result > 0) {
+        *bytes_read = (uint32_t)read_result;
+        g_serial_ctx.rx_bytes += *bytes_read;
+        PPP_DEBUG_DEBUG("Read %u bytes via UART (AT mode)", *bytes_read);
+    } else if (read_result < 0) {
+        PPP_DEBUG_ERROR("UART read error: %d", read_result);
+        g_serial_ctx.rx_errors++;
+        result = PPP_ERR_UART;
     }
 
     #if defined(PPP_LOW_POWER_MODE) && (PPP_LOW_POWER_MODE == 1)
@@ -536,7 +383,7 @@ ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *byt
 }
 
 /**
- * @brief PPP快速尝试读取 - 类似SLIP的sio_tryread,使用1ms超时
+ * @brief 快速尝试读取 - n706a_net 只使用 AT 模式,使用短超时读取
  * @param buffer 接收缓冲区
  * @param buffer_size 缓冲区大小
  * @param bytes_read 实际读取字节数
@@ -550,90 +397,20 @@ ppp_error_t ppp_serial_tryread(uint8_t *buffer, uint32_t buffer_size, uint32_t *
 
     *bytes_read = 0;
 
-    if (g_serial_ctx.current_mode == PPP_SERIAL_MODE_PPP_DATA) {
-        /* PPP数据模式:混合策略(先轮询检测,有数据时用 DMA) */
-        #if defined(CONFIG_UART_SUPPORT_DMA)
-        // ⚠️ 核心问题:uapi_uart_read_by_dma 会阻塞 1 秒等待数据,不适合无数据时的轮询
-        // 解决方案:先用普通读取(timeout=0)快速检测,有数据时再用 DMA 批量读取
-        
-        // 第 1 步:非阻塞检测是否有数据(立即返回)
-        uint8_t probe_buf[8];
-        int32_t probe_result = uapi_uart_read(g_serial_ctx.uart_bus, probe_buf, sizeof(probe_buf), 0);
-        
-        int32_t read_result = 0;
-        
-        if (probe_result > 0) {
-            // 有数据!先复制探测数据,再用 DMA 读取剩余数据
-            static uint32_t dma_call_count = 0;
-            static uint32_t dma_success_count = 0;
-            
-            // 复制探测数据
-            if ((uint32_t)probe_result <= buffer_size) {
-                memcpy(buffer, probe_buf, probe_result);
-                read_result = probe_result;
-            }
-            
-            // 如果 buffer 还有空间,尝试用 DMA 批量读取剩余数据
-            if ((uint32_t)probe_result < buffer_size) {
-                uint32_t remaining = buffer_size - probe_result;
-                // DMA 读取策略:既然已确认有数据,就充分利用 DMA 批量传输能力
-                // 只对足够大的块使用 DMA(>=64字节),小块数据用 DMA 开销反而更大
-                uint32_t dma_size = (remaining >= 64) ? remaining : 0;
-                
-                if (dma_size > 0) {
-                    uart_write_dma_config_t dma_cfg = {
-                        .src_width = UART_DMA_WIDTH,
-                        .dest_width = UART_DMA_WIDTH,
-                        .burst_length = UART_DMA_BURST_LENGTH,
-                        .priority = UART_DMA_PRIORITY
-                    };
-                    
-                    dma_call_count++;
-                    if (dma_call_count == 1) {
-                        PPP_DEBUG_WARN("=== Hybrid Mode: Poll + DMA ===");
-                        PPP_DEBUG_WARN("Poll detects data (instant), DMA reads bulk (8-64 bytes)");
-                    }
-                    
-                    int32_t dma_result = uapi_uart_read_by_dma(g_serial_ctx.uart_bus, 
-                                                               buffer + probe_result, 
-                                                               dma_size, &dma_cfg);
-                    
-                    if (dma_result > 0) {
-                        read_result += dma_result;
-                        dma_success_count++;
-                        if (dma_success_count == 1 || dma_success_count % 50 == 0) {
-                            PPP_DEBUG_WARN("DMA Success #%u: poll %d + DMA %d = %d bytes", 
-                                          dma_success_count, probe_result, dma_result, read_result);
-                        }
-                    } else if (dma_result < 0 && dma_call_count <= 5) {
-                        PPP_DEBUG_ERROR("DMA failed (%d), but poll got %d bytes", 
-                                       dma_result, probe_result);
-                    }
-                }
-            }
-        } else {
-            // 没有数据,立即返回(无阻塞)
-            read_result = 0;
-        }
-        
-        #else
-        int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, buffer, buffer_size, 0);
-        #endif
-        if (read_result > 0) {
-            *bytes_read = (uint32_t)read_result;
-            g_serial_ctx.rx_bytes += *bytes_read;
-            return PPP_ERR_OK;
-        } else if (read_result == 0) {
-            /* 超时,无数据可读 */
-            return PPP_ERR_OK;
-        } else {
-            /* UART读取错误 */
-            PPP_DEBUG_ERROR("PPP UART tryread error: %d", read_result);
-            return PPP_ERR_UART;
-        }
+    /* n706a_net 只使用 AT 模式,使用短超时(0ms)快速读取 */
+    int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, buffer, buffer_size, 0);
+    
+    if (read_result > 0) {
+        *bytes_read = (uint32_t)read_result;
+        g_serial_ctx.rx_bytes += *bytes_read;
+        return PPP_ERR_OK;
+    } else if (read_result == 0) {
+        /* 超时,无数据可读 */
+        return PPP_ERR_OK;
     } else {
-        /* AT模式:使用标准读取 */
-        return ppp_serial_read(buffer, buffer_size, bytes_read);
+        /* UART读取错误 */
+        PPP_DEBUG_ERROR("UART tryread error: %d", read_result);
+        return PPP_ERR_UART;
     }
 }
 
diff --git a/code/sdk/zhihui/ppp/doc/Neoway_功能模块TCP应用指南_V1.5.md b/code/sdk/zhihui/ppp/doc/Neoway_功能模块TCP应用指南_V1.5.md
new file mode 100644 (file)
index 0000000..91041c3
--- /dev/null
@@ -0,0 +1,6892 @@
+# TCP应用指南\r
+\r
+## 功能\r
+\r
+## 模组\r
+\r
+### TCP 应用指南\r
+\r
+###### 版本 1.5 日期 2025-04-24\r
+\r
+功能模组TCP 应用指南\r
+\r
+版权声明\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+ii\r
+\r
+版权声明\r
+\r
+版权所有 © 深圳市有方科技股份有限公司 2025。深圳市有方科技股份有限公司保留所有权利。\r
+\r
+未经深圳市有方科技股份有限公司书面同意,任何单位和个人不得擅自摘抄、复制本文档内容的部分或全部,\r
+并不得以任何形式传播。\r
+\r
+是深圳市有方科技股份有限公司所有商标。\r
+\r
+本文档中出现的其他商标,由商标所有者所有。\r
+\r
+说明\r
+\r
+本文档对应产品为功能模组。\r
+\r
+本文档的使用对象为系统工程师,开发工程师及测试工程师。\r
+\r
+本设计指南为客户产品设计提供支持,客户须按照本文中的规范和参数进行产品设计和调试。如因客户操作\r
+不当造成的人身伤害和财产损失,有方概不承担责任。\r
+\r
+由于产品版本升级或其它原因,本文档内容会在不预先通知的情况下进行必要的更新。\r
+\r
+除非另有约定,本文档中的所有陈述、信息和建议不构成任何明示或暗示的担保。\r
+\r
+深圳市有方科技股份有限公司为客户提供全方位的技术支持,任何垂询请直接联系您的客户经理或发送邮件\r
+至以下邮箱:\r
+\r
+Sales@neoway.com\r
+\r
+Support@neoway.com\r
+\r
+公司网址:http://www.neoway.com\r
+\r
+功能模组TCP 应用指南\r
+\r
+目录\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+iii\r
+\r
+#### 目 录\r
+\r
+###### 关于本文档 ....................................................................................................... v\r
+\r
+范围 ................................................................................................................................................... v\r
+\r
+读者对象 ............................................................................................................................................ v\r
+\r
+修订记录 ............................................................................................................................................ v\r
+\r
+符号约定 ............................................................................................................................................ v \r
+1 概述 .............................................................................................................. 6\r
+\r
+1.1 TCP 协议 ..................................................................................................................................... 6\r
+\r
+1.2 TCP 传输模式 .............................................................................................................................. 6 \r
+2 模块初始化流程 ........................................................................................... 8\r
+\r
+2.1 标准指令初始化流程 ................................................................................................................... 9\r
+\r
+2.2 国网指令初始化流程 ................................................................................................................. 10 \r
+3 标准指令TCP 应用 .................................................................................... 11\r
+\r
+3.1 TCP 非透传客户端 ..................................................................................................................... 12\r
+\r
+3.1.1 异常处理 .......................................................................................................................... 13\r
+\r
+3.1.2 示例.................................................................................................................................. 16\r
+\r
+3.2 TCP 非透传服务器端 ................................................................................................................. 18\r
+\r
+3.2.1 异常处理 .......................................................................................................................... 19\r
+\r
+3.2.2 示例.................................................................................................................................. 20\r
+\r
+3.3 TCP 透传客户端 ........................................................................................................................ 22\r
+\r
+3.3.1 异常处理 .......................................................................................................................... 23\r
+\r
+3.3.2 示例.................................................................................................................................. 25 \r
+4 国网指令TCP 应用 .................................................................................... 27\r
+\r
+4.1 TCP 非透传客户端 ..................................................................................................................... 27\r
+\r
+4.1.1 异常处理 .......................................................................................................................... 29\r
+\r
+4.1.2 示例.................................................................................................................................. 32\r
+\r
+4.2 TCP 非透传服务器端 ................................................................................................................. 34\r
+\r
+4.2.1 异常处理 .......................................................................................................................... 35\r
+\r
+4.2.2 示例.................................................................................................................................. 37\r
+\r
+4.3 TCP 透传客户端 ........................................................................................................................ 38\r
+\r
+4.3.1 异常处理 .......................................................................................................................... 40\r
+\r
+4.3.2 示例.................................................................................................................................. 42 \r
+5 外部协议栈TCP 应用 ................................................................................ 45\r
+\r
+5.1 异常处理 ................................................................................................................................... 45\r
+\r
+5.2 示例 .......................................................................................................................................... 46\r
+\r
+功能模组TCP 应用指南\r
+\r
+目录\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+iv\r
+\r
+###### A 关键AT 命令 .............................................................................................. 47\r
+\r
+A.1 标准AT 指令 ............................................................................................................................ 47\r
+\r
+A.1.1 AT+CGDCONT–设置PDP 格式 ...................................................................................... 47\r
+\r
+A.1.2 AT+XGAUTH–用户认证 .................................................................................................. 48\r
+\r
+A.1.3 AT+XIIC–建立PPP 链接 ................................................................................................. 49\r
+\r
+A.1.4 AT+TCPSETUP–建立TCP 连接 ..................................................................................... 50\r
+\r
+A.1.5 AT+TCPSEND–发送TCP 数据 ....................................................................................... 51\r
+\r
+A.1.6 AT+IPSTATUS–查询TCP/UDP 链路状态 ....................................................................... 53\r
+\r
+A.1.7 AT+TCPACK–查询TCP 链路发送数据状态 .................................................................... 54\r
+\r
+A.1.8 AT+TCPLISTEN–设置服务器侦听功能............................................................................ 55\r
+\r
+A.1.9 AT+CLOSELISTEN–关闭侦听链接 ................................................................................. 56\r
+\r
+A.1.10 AT+CLOSECLIENT–关闭主站链接 ............................................................................... 57\r
+\r
+A.1.11 +TCPRECV(S)–接收到主站的数据 ............................................................................... 58\r
+\r
+A.1.12 AT+TCPREADS–读取到主站的数据 ............................................................................. 58\r
+\r
+A.1.13 AT+TCPSENDS–发送给主站的数据 ............................................................................. 59\r
+\r
+A.1.14 AT+CLIENTSTATUS–查询主站链路的状态 ................................................................... 60\r
+\r
+A.1.15 AT+TCPACKS–查询TCP 服务器发送数据状态 ............................................................ 61\r
+\r
+A.1.16 AT+TCPTRANS–建立TCP 透传链接 ............................................................................ 62\r
+\r
+A.1.17 AT+TRANSCLOSE–关闭透传方式链接......................................................................... 63\r
+\r
+A.1.18 ATD*99#–GPRS ........................................................................................................... 64\r
+\r
+A.2 国网AT 指令 ............................................................................................................................ 65\r
+\r
+A.2.1 AT$MYNETCON–设置网络连接初始化参数 .................................................................... 65\r
+\r
+A.2.2 AT$MYNETACT–激活/去激活网络连接 ........................................................................... 66\r
+\r
+A.2.3 AT$MYNETSRV–设置非透明传输服务参数 .................................................................... 67\r
+\r
+A.2.4 AT$MYNETOPEN–开启服务........................................................................................... 68\r
+\r
+A.2.5 AT$MYNETREAD–读取数据 ........................................................................................... 69\r
+\r
+A.2.6 AT$MYNETWRITE–发送数据 ......................................................................................... 69\r
+\r
+A.2.7 AT$MYNETCLOSE–关闭连接......................................................................................... 70\r
+\r
+A.2.8 AT$MYNETACK–查询TCPACK 信息.............................................................................. 71\r
+\r
+A.2.9 AT$MYNETACCEPT–接受侦听请求 ............................................................................... 71\r
+\r
+A.2.10 AT$MYNETCREATE–开启透明传输服务 ...................................................................... 72\r
+\r
+A.2.11 $MYURCREAD–数据到来主动上报 .............................................................................. 74\r
+\r
+A.2.12 $MYURCCLOSE–链路断开主动上报 ............................................................................ 74\r
+\r
+A.2.13 $MYURCACT–网络连接状态主动上报.......................................................................... 75\r
+\r
+A.2.14 $MYURCCLIENT–客户端连接主动上报 ........................................................................ 76\r
+\r
+功能模组TCP 应用指南\r
+\r
+关于本文档\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+v\r
+\r
+#### 关于本文档\r
+\r
+##### 范围\r
+\r
+本文档对应产品为功能模组。\r
+\r
+##### 读者对象\r
+\r
+本文档的使用对象为系统工程师,开发工程师及测试工程师。\r
+\r
+##### 修订记录\r
+\r
+版本 \r
+日期 \r
+变更 \r
+作者\r
+\r
+1.0 \r
+2018-11 \r
+初始版本 \r
+Zhang Song\r
+\r
+1.1 \r
+2020-01\r
+\r
+⚫ \r
+删除各TCP 连接中的模块初始化部分。\r
+\r
+⚫ \r
+新增第2 章 模块初始化流程。 \r
+Zhang Song\r
+\r
+1.2 \r
+2020-09 \r
+优化初始化流程图 \r
+Zhang Song\r
+\r
+1.3 \r
+2022-02 \r
+优化各流程图样式 \r
+Zhang Song\r
+\r
+1.4 \r
+2022-06 \r
+修改初始化流程图 \r
+Zhang Song\r
+\r
+1.5 \r
+2025-04 \r
+优化第2 章模块初始化流程图 \r
+Zhang Song\r
+\r
+##### 符号约定\r
+\r
+符号 \r
+含义\r
+\r
+危险或警告,用户必须遵从的规则,否则会造成模块或客户设备不可逆的故障损坏,\r
+\r
+甚至可能造成人员身体伤害。\r
+\r
+注意,警示用户使用模块时应该特别注意的地方,如不遵从,模块或客户设备可能\r
+\r
+出现故障。\r
+\r
+说明或提示,提供模块使用的意见或建议。\r
+\r
+功能模组TCP 应用指南\r
+\r
+第1 章概述\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+6\r
+\r
+#### 1 概述\r
+\r
+本文主要介绍如何使用有方科技股份有限公司功能模块的TCP 功能。\r
+\r
+##### 1.1 TCP 协议\r
+\r
+TCP(Transmission Control Protocol 传输控制协议)协议是一种面向连接的、可靠地、基于字节\r
+\r
+流的运输层协议。相比UDP 协议,TCP 具有更高的可靠性。对于数据传输有高可靠性要求的应用场景,\r
+\r
+建议选用此协议。\r
+\r
+用户使用功能模块TCP 功能时,可以用模块内置的TCP 协议栈(内部协议栈)也可以使用用户主\r
+\r
+控自带的协议栈(外部协议栈)。二者的区别在于:模块内部协议栈已经将PPP 协议、TCP 协议封装成\r
+\r
+了对应的AT 指令,主控 只需要操作对应的AT 指令就可以完成拨号、建立和关闭TCP 连接、收发数\r
+\r
+据等操作。外部协议栈主要依赖于主控自己的协议栈,需要主控处理PPP 协议、TCP 协议具体的数据\r
+\r
+帧和数据内容。\r
+\r
+为适应不同客户使用场景和行业要求,有方功能模块内置的TCP 协议栈具有丰富的AT 指令,主要\r
+\r
+分为两类:国网指令和标准指令。国网指令是国家电网在376.3 规约中定义的AT 指令,主要用于电力\r
+\r
+行业;标准指令是我司自定义格式的AT 指令。详见各模块对应的AT 指令集。\r
+\r
+##### 1.2 TCP 传输模式\r
+\r
+功能模块支持TCP 客户端和TCP 服务端功能。其中,TCP 客户端功能具有两种模式:\r
+\r
+⚫ \r
+透明传输模式\r
+\r
+建立TCP 连接后,主控从端口(USB、UART)发送给模块的字符(包括AT 命令)都将被当作\r
+\r
+TCP 数据发送到网络,模块从网络侧接收到的TCP 数据将会直接从串口上报给主控,不带任何提\r
+\r
+示回码。\r
+\r
+主控可向模块发送“+++”退出透明传输模式,查询信号强度,网络状态等;在TCP 连接未断开的\r
+\r
+情况下,可以向模块发送“ATO”返回透明传输模式。\r
+\r
+⚫ \r
+非透明传输模式\r
+\r
+建立TCP 连接后,主控每发送一次数据都需要调用发送数据的AT 命令,模块从网络侧接收到的\r
+\r
+TCP 数据将会直接从串口上报给主控,但在TCP 数据内容前会带有对应的提示数据头\r
+\r
++TCPRECV:<SocketID>,<data_length>,<data>。\r
+\r
+功能模组TCP 应用指南\r
+\r
+第1 章概述\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+7\r
+\r
+本文按照标准指令集、国网指令集的顺序介绍模块内置协议栈TCP 功能的使用方式,主要包括相\r
+\r
+关AT 命令的介绍、TCP 客户端和服务器端的使用流程、异常的排查与处理等内容。最后,我们会简单\r
+\r
+介绍外部协议栈的使用方式。\r
+\r
+功能模组TCP 应用指南\r
+\r
+第2 章模块初始化流程\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+8\r
+\r
+#### 2 模块初始化流程\r
+\r
+请确保模块在建立TCP 连接前已执行过该初始化步骤。\r
+\r
+⚫ \r
+发送AT 命令后,必须等待收到回码才能发送下一条AT 命令。\r
+\r
+⚫ \r
+发送AT 命令后,若未返回任何回码,则继续发送,1s/次,最多3 次,若发送3 次后,仍未返\r
+\r
+回任何回码,则重启模块。\r
+\r
+⚫ \r
+上述流程中设置的APN、用户名、密码、鉴权方式等参数,仅用于举例说明。实际应用过程\r
+\r
+中,需结合所使用的SIM 卡的签约信息,设置正确的APN、用户名、密码、鉴权方式等参\r
+\r
+数。\r
+\r
+⚫ \r
+针对单模块使用双卡或者三卡的情况,在设置注册网络参数指令后(APN、用户名密码、鉴权\r
+\r
+方式),需要增加指令(例如AT+COPS=2;AT+COPS=0,具体指令在设计流程时可与有方\r
+\r
+技术支持沟通),让模块注销网络并重新注册网络。\r
+\r
+⚫ \r
+重启模块请参照《Neoway_标准模块_开关机流程操作指南》进行。24 小时内过多的重启可能\r
+\r
+会导致FLASH 及SIM 卡不可逆的损坏。\r
+\r
+⚫ \r
+若客户产品需要使用模组2G SMS、CSD 功能,需要确保模组注册上CS 域。所以初始化流\r
+\r
+程中,主控必须执行AT+CREG?查询模组CS 域的注册情况。若客户产品未使用模组2G\r
+\r
+SMS、CSD 功能,则主控可以不执行AT+CREG?查询模组CS 域的注册情况。\r
+\r
+⚫ \r
+若客户产品需要使用模组分组数据业务功能,需要确保模组附着上PS 域。所以初始化流程\r
+\r
+中,主控必须执行AT+CGATT?查询模组PS 域的附着情况。若客户产品未使用模组分组数据\r
+\r
+业务功能,则主控可以不执行AT+CGATT?查询模组PS 域的附着情况。在查询模组PS 域附\r
+\r
+着情况前,可以按需使用AT+CGREG?、AT+CEREG?查询PS 域的注册情况:\r
+\r
+- \r
+2G/3G 模组或者4G 模组2G/3G 模式下,使用AT+CGREG?查询PS 域(GPRS 网络)\r
+\r
+的注册情况。\r
+\r
+- \r
+4G 模组4G 模式下,使用AT+CGREG?查询PS 域(EPS 网络)的注册情况。\r
+\r
+功能模组TCP 应用指南\r
+\r
+第2 章模块初始化流程\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+9\r
+\r
+##### 2.1 标准指令初始化流程\r
+\r
+延时\r
+\r
+"OK"?\r
+\r
+1. AT\r
+\r
+2. AT+CGSN\r
+\r
+3. AT+GMR\r
+\r
+Yes\r
+\r
+4. AT+CPIN?\r
+\r
++CPIN: READY ?\r
+\r
+5. AT+CIMI\r
+\r
+8. AT+CSQ\r
+\r
++CSQ: <signal>,<ber>\r
+ and 0 signal 31 ?\r
+\r
+12. AT+CGATT?\r
+\r
+"+CGATT: 1"?\r
+\r
+No\r
+\r
+No\r
+\r
+13. AT$MYSYSINFO\r
+\r
+查询<40次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+查询<15次?\r
+\r
+查询<30次?\r
+手动附着\r
+\r
+查询<10次?\r
+\r
+模块初始化完成\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+2G模块延时3s,4G模块延时10s。该过程中模块处于\r
+初始化阶段,可能出现未响应或响应出错的情况。\r
+\r
+- 4.检查SIM卡状态\r
+- 若返回"+CPIN: READY",则可进入下一步。\r
+- 若未返回任何回码或者返回其他错误回码,则继续\r
+\r
+查询,1s/次,最多15次。若查询15次后仍未返回\r
+"+CPIN: READY",则重启模块。\r
+\r
+模块重启\r
+模块开机\r
+\r
+6. AT+CGDCONT=1,"IP","CMNET"\r
+\r
+"OK"?\r
+\r
+7. AT+XGAUTH=1,1,"gsm","1234"\r
+\r
+"OK"?\r
+\r
+No\r
+\r
+查询<2次?\r
+\r
+拨号<3次?\r
+\r
+重新设置<3次?\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+No\r
+Yes\r
+\r
+No\r
+Yes\r
+No\r
+\r
+Yes\r
+\r
+No\r
+Yes\r
+No\r
+\r
+1. 检查串口是否已通\r
+- 若返回"OK",则可进入下一步。\r
+- 若未返回任何回码或者返回其他错误回码,则继续尝\r
+\r
+试,1s/次,最少10次。若尝试10次后,仍未返回\r
+"OK ",则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+- 5.获取SIM卡IMSI\r
+- 若返回"OK",则可进入下一步。\r
+- 若未返回任何回码或者返回其他错误回码,则继续查\r
+\r
+询,最多2次。若查询2次后仍未返回"OK",则进入下\r
+一步。\r
+\r
+6. 设置APN\r
+- 若返回"OK",则可进入下一步。\r
+- 若未收到任何回码或收到其他错误回码,则继续设\r
+\r
+置,1s/次,最多3次。若重新设置3次后,仍未若返\r
+回"OK ",则重启模块。\r
+\r
+7. 设置身份认证参数\r
+- 若返回"OK",则可进入下一步。\r
+- 若未返回任何回码或者返回其他错误回码,则继续\r
+\r
+若收到其他错误回码设置,1s/次,最多3次。若重\r
+新设置3次后,仍未若返回"OK ",则重启模块。\r
+\r
+8.查询信号强度\r
+• 若返回"+CSQ: <signal>,<ber>"且0 signal 31则有信\r
+\r
+号。建议signal 12时,再进入下一步。极端恶劣网\r
+络环境下,只要有信号强度即可进入下一步。\r
+• 若未返回任何回码、返回的回码包含"+CSQ: 99,"、\r
+\r
+或者返回其他错误回码,则继续查询,1s/次,最多40\r
+次。若查询40次后仍无信号,则重启模块。\r
+\r
+12. 查询网络附着情况\r
+- 若返回"+CGATT: 1",则进入下一步\r
+- 若返回"+CGATT: 0",或其他错误回码,则继续查\r
+\r
+询,1s/次,最多30次。若查询30次后,仍未返回\r
+"+CGATT: 1",则手动进行网络附着。最长等待2分\r
+钟返回回码。\r
+• 手动进行网络附着后,仍未返回"+CGATT: 1",则\r
+\r
+重启模块。\r
+\r
+13.查询当前网络制式\r
+\r
+2. 获取模块IMEI\r
+\r
+3. 获取模块固件版本\r
+\r
+"OK"?\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+10. AT+CGREG?\r
+\r
++CGREG: 0,1 or\r
+\r
++CGREG: 0,5?\r
+查询<40次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+9. AT+CREG?\r
+\r
++CREG: 0,1 or\r
+\r
++CREG: 0,5?\r
+查询<40次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+\r
+11. AT+CEREG?\r
+\r
++CEREG: 0,1 or\r
+\r
++CEREG: 0,5?\r
+查询<40次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+\r
+9. 查询GSM网络CS域注册情况\r
+- 若回码包含"+CREG: 0,1"或者"+CREG: 0,5"字段,则进\r
+\r
+入下一步。\r
+• 若未返回任何回码或返回其他错误回码,则继续查\r
+\r
+询,1s/次,最多40次。若查询40次后,回码仍不包\r
+含"+CREG: 0,1"或者"+CREG: 0,5"字段:针对需要使用\r
+2G短信/CSD功能的场景,需要确保模组注册上CS\r
+域,可重启模块。针对不需要使用2G短信/CSD功能\r
+的场景,可进入下一步。\r
+\r
+10. 查询GPRS网络注册情况\r
+- 若回码包含"+CGREG: 0,1"或者"+CGREG: 0,5"字段,\r
+\r
+则进入下一步。\r
+• 若未返回任何回码或返回其他错误回码,则继续查\r
+\r
+询,1s/次,最多40次。若查询40次后,回码仍不包\r
+含"+CGREG: 0,1"或者"+CGREG: 0,5"字段,可进入下\r
+一步。\r
+\r
+11. 查询EPS网络注册情况\r
+- 若回码包含"+CEREG: 0,1"或者"+CEREG: 0,5"字段,则\r
+\r
+进入下一步。\r
+• 若未返回任何回码或返回其他错误回码,则继续查\r
+\r
+询,1s/次,最多40次。若查询40次后,回码仍不包\r
+含"+CEREG: 0,1"或者"+CEREG: 0,5"字段,可进入下一\r
+步\r
+\r
+功能模组TCP 应用指南\r
+\r
+第2 章模块初始化流程\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+10\r
+\r
+##### 2.2 国网指令初始化流程\r
+\r
+Yes\r
+\r
+延时\r
+\r
+"OK"?\r
+\r
+1. AT\r
+\r
+2. AT+CGSN\r
+\r
+3. AT+GMR\r
+\r
+Yes\r
+\r
+4. AT+CPIN?\r
+\r
++CPIN: READY ?\r
+\r
+5. AT+CIMI\r
+\r
+"OK"?\r
+\r
+9. AT+CSQ\r
+\r
++CSQ: <signal>,<ber>\r
+ and 0 signal 31 ?\r
+\r
+11. AT+CGREG?\r
+\r
++CGREG: 0,1 or\r
+\r
++CGREG: 0,5?\r
+\r
++CGATT: 1?\r
+\r
+13. AT+CGATT?\r
+\r
+Yes\r
+\r
+No\r
+\r
+14. AT$MYSYSINFO\r
+\r
+查询<40次?\r
+\r
+查询<40次?\r
+\r
+Yes\r
+\r
+查询<15次?\r
+\r
+查询<30次?\r
+\r
+```\r
+AT+CGATT=1\r
+```\r
+\r
+手动附着\r
+\r
+查询<10次?\r
+\r
+模块初始化完成\r
+\r
+No\r
+Yes\r
+\r
+No\r
+No\r
+\r
+2G模块延时3s,4G模块延时10s。该过程中模块处于\r
+初始化阶段,可能出现未响应或响应出错的情况。\r
+\r
+4. 检查SIM卡状态\r
+- 若返回"+CPIN: READY",则可进入下一步\r
+- 若未返回任何回码或者返回其他错误回码,则继续查\r
+\r
+询,1s/次,最多15次。若查询15次后仍未返回\r
+"+CPIN: READY",则重启模块。\r
+\r
+模块重启\r
+模块开机\r
+\r
+6. AT$MYNETCON=0,"APN","CMNET"\r
+\r
+"OK"?\r
+\r
+7. AT$MYNETCON=0,"USERPWD","CARD,CARD"\r
+\r
+"OK"?\r
+\r
+No\r
+\r
+查询<2次?\r
+\r
+拨号<3次?\r
+\r
+重新设置<3次?\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+No\r
+Yes\r
+\r
+No\r
+\r
+1. 检查串口是否已通\r
+- 若返回"OK",则可进入下一步。\r
+- 若未返回任何回码或者返回其他错误回码,则继续尝\r
+\r
+试,1s/次,最少10次。若尝试10次后,仍未返回\r
+"OK ",则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+- 5.获取SIM卡IMSI\r
+- 若返回"OK",则可进入下一步。\r
+- 若未返回任何回码或返回其他错误回码,则继续查\r
+\r
+询,最多2次。若查询2次后仍未返回"OK",则进入下\r
+一步。\r
+\r
+6. 设置APN\r
+- 若返回"OK",则可进入下一步。\r
+- 若未返回任何回码或返回其他错误回码,则继续设\r
+\r
+置,1s/次,最多3次。若重新设置3次后,仍未若\r
+返回"OK ",则重启模块。\r
+\r
+7. 设置身份认证参数\r
+- 若返回"OK",则可进入下一步。\r
+- 若未返回任何回码或返回其他错误回码,则继续设\r
+\r
+置,1s/次,最多3次。若重新设置3次后,仍未若返\r
+回"OK ",则重启模块。\r
+\r
+8. AT$MYNETCON=0,"AUTH",1\r
+\r
+OK?\r
+\r
+9. 查询信号强度\r
+- 若返回"+CSQ: <signal>,<ber>"且0 signal 31则有信\r
+\r
+号。建议signal 12时,再进入下一步。极端恶劣网\r
+络环境下,只要有信号强度即可进入下一步。\r
+• 若未返回任何回码、回码包含"+CSQ: 99,"、返回其\r
+\r
+他错误回码,则继续查询,1s/次,最多40次。若查\r
+询40次后仍无信号,则重启模块。\r
+\r
+Yes\r
+\r
+2. 获取模块IMEI\r
+\r
+3. 获取模块固件版本\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+No\r
+Yes\r
+No\r
+\r
+查询<40次?\r
+No\r
+No\r
+Yes\r
+\r
+No\r
+No\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+8. 设置身份认证参数\r
+- 若返回"OK",则可进入下一步。\r
+- 若未返回任何回码或返回其他错误回码,则继续设\r
+\r
+置,1s/次,最多3次。若重新设置3次后,仍未若返\r
+回"OK ",则重启模块。\r
+\r
+13. 查询PS域附着情况\r
+- 若返回"+CGATT: 1",则进入下一步\r
+- 若未返回任何回码、返回"+CGATT: 0"、返回其他\r
+\r
+错误回码,则继续查询,1s/次,最多30次。若查\r
+询30次后,仍未返回"+CGATT: 1",则手动进行网\r
+络附着。最长等待2分钟返回回码。\r
+• 手动进行网络附着后,仍未返回"+CGATT: 1",则\r
+\r
+重启模块。\r
+\r
+14.查询当前网络制式\r
+\r
+10. 查询GSM网络CS域注册情况\r
+- 若回码包含"+CREG: 0,1"或者"+CREG: 0,5"字段,则\r
+\r
+进入下一步。\r
+• 若未返回任何回码或返回其他错误回码,则继续查\r
+\r
+询,1s/次,最多40次。若查询40次后,回码仍不\r
+包含"+CREG: 0,1"或者"+CREG: 0,5"字段:针对需要\r
+使用2G短信/CSD功能的场景,需要确保模组注册上\r
+CS域,可重启模块。针对不需要使用2G短信/CSD功\r
+能的场景,可进入下一步。\r
+\r
+11. 查询GPRS网络注册情况\r
+- 若回码包含"+CGREG: 0,1"或者"+CGREG: 0,5"字段,\r
+\r
+则进入下一步。\r
+• 若未返回任何回码或返回其他错误回码,则继续查\r
+\r
+询,1s/次,最多40次。若查询40次后,回码仍不\r
+包含"+CGREG: 0,1"或者"+CGREG: 0,5"字段,可进入\r
+下一步。\r
+\r
+10. AT+CREG?\r
+\r
++CREG: 0,1 or\r
+\r
++CREG: 0,5?\r
+查询<40次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+\r
+12. AT+CEREG?\r
+\r
++CEREG: 0,1 or\r
+\r
++CEREG: 0,5?\r
+查询<40次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+12. 查询EPS网络注册情况\r
+- 若回码包含"+CEREG: 0,1"或者"+CEREG: 0,5"字段,\r
+\r
+则进入下一步。\r
+• 若未返回任何回码或返回其他错误回码,则继续查\r
+\r
+询,1s/次,最多40次。若查询40次后,回码仍不\r
+包含"+CEREG: 0,1"或者"+CEREG: 0,5"字段,可进入\r
+下一步\r
+No\r
+\r
+No\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+11\r
+\r
+#### 3 标准指令TCP 应用\r
+\r
+本章主要介绍如何使用有方标准指令实现TCP 功能(包括TCP 非透传客户端、TCP 非透传服务器\r
+\r
+端、TCP 透传客户端功能)。内容包括对应功能相关的AT 指令、AT 流程、异常处理以及简单示例。\r
+\r
+连接注意事项:\r
+\r
+⚫ \r
+发送AT 命令后必须等待收到回码后,才能发送下一条AT 命令。\r
+\r
+⚫ \r
+在建立PPP 连接后,主动去附着前,只要收到"GPRS DISCONNECTION",则返回重新激活PPP 连\r
+\r
+接。\r
+\r
+⚫ \r
+在建立TCP 连接后,主动断开TCP 连接前,只要收到"+TCPCLOSE: X,Link Closed",则返回重新\r
+\r
+建立TCP 连接。\r
+\r
+⚫ \r
+重启模块请参照《Neoway_标准模块_开关机流程操作指南》进行。24 小时内过多的重启可能会导致\r
+\r
+FLASH 及SIM 卡不可逆的损坏。\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+12\r
+\r
+##### 3.1 TCP 非透传客户端\r
+\r
+本节主要实现TCP 非透传客户端功能相关的AT 指令、AT 流程、异常处理以及简单示例。在进行\r
+\r
+PPP 拨号前,请确保模块已经过初始化(具体初始化流程参考第2 章)。\r
+\r
+图 3-1 连接流程\r
+\r
+设置PPP拨号参数\r
+\r
+"OK"?\r
+\r
+1. AT+XIIC=1\r
+\r
+"+XIIC: 1"?\r
+\r
+2. AT+XIIC?\r
+\r
+"+TCPCLOSE: X,OK" or "+TCPCLOSE:\r
+\r
+ERROR"?\r
+\r
+3. AT+TCPCLOSE=X\r
+\r
+Yes\r
+\r
+4. AT+TCPSETUP=X,<IP/域名>,<Port>\r
+\r
+"+TCPSETUP: X,OK"?\r
+\r
+5. AT+IPSTATUS=X\r
+\r
+"+IPSTATUS: \r
+X,CONNECT,TCP,4096"?\r
+\r
+9. AT+CGATT=0\r
+\r
+"OK" or "ERROR"?\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+重连<3次?\r
+\r
+查询<3次?\r
+\r
+关机\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+模块重启\r
+模块初始化流程\r
+\r
+6. AT+TCPSEND=X,<Length>\r
+\r
+7. AT+TCPACK=X\r
+\r
+"+TCPACK: \r
+X,<Data_send>,<Acked_data>"?\r
+\r
+No\r
+\r
+查询<5次?\r
+\r
+重发<3次?\r
+\r
+查询<5次?\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+No\r
+Yes\r
+\r
+No\r
+Yes\r
+No\r
+\r
+1. 进行PPP拨号\r
+- 若返回"OK",则可进入下一步。\r
+- 若未收到任何回码或收到其他错误回码,则继续拨号,1s/次,最\r
+\r
+多3次。若重新拨号3次后,仍未若返回"OK ",则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+4. 建立TCP连接\r
+- 该指令等待返回"+TCPSETUP: X,OK"的超时时间为30s\r
+- 若返回"+TCPSETUP: X,OK",则可进入下一步。\r
+- 若出现以下情况,则继续重连,最多3次,若3次仍无法建立TCP连\r
+\r
+接,则重启模块。\r
+ -- 收到回码:"+TCPSETUP: X,FAIL"/"+TCPSETUP: ERROR"\r
+ -- 收到其他错误回码或者未收到任何回码\r
+\r
+5. 查询链路状态\r
+- 若返回"+IPSTATUS: X,CONNECT,TCP,4096",则进行下一步。\r
+- 若回码包含"+IPSTATUS: X,DISCONNECT",则返回重新建立TCP连接。\r
+- 若回码包含"+IPSTATUS: X,CONNECT",Buffer大小不为4096,则循环\r
+\r
+查询链路状态,1s/次,最多5次,若查询5次后,Buffer大小不为\r
+4096,则返回关闭当前连接,重新建立TCP连接。\r
+\r
+6. 发送数据\r
+无论是否返回 > ,都延时50-100ms,再将数据发送到模块串口, \r
+等待数据发送成功的回码 +TCPSEND:X,<Length> 。\r
+• 若返回 +TCPSEND:X,<Length> ,则进入下一步。\r
+• 若返回 +TCPSEND: SOCKET ID OPEN FAILED ,则返回重新建立TCP\r
+\r
+连接。\r
+• 若返回其他错误回码,则继续执行该命令重发数据,最多3次。若\r
+\r
+重发3次仍然失败,则返回重新建立TCP连接。\r
+\r
+8. 关闭Socket\r
+- 若返回"+TCPCLOSE: X,OK""+TCPCLOSE: ERROR"则进入下一步,等待\r
+\r
+返回的超时时间为30s。\r
+• 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次,\r
+\r
+若重复关闭3次后,仍未收到"+TCPCLOSE: X,OK"或者"+TCPCLOSE: \r
+ERROR"则重启模块。\r
+\r
+9. 去附着网络\r
+- 若返回"OK"或者" ERROR",则进入下一步,等返回的超时时间为\r
+\r
+15s。\r
+• 若超时未收到回码或者收到其他错误回码,则继续执行,最多3\r
+\r
+次,若重复关闭3次后,仍未收到"OK"或者" ERROR"则重启模块。\r
+\r
+"+TCPCLOSE: X,OK" or "+TCPCLOSE:\r
+\r
+ERROR"?\r
+\r
+7. 查询数据发送情况\r
+- 若返回"+TCPACK: X,<Data_send>,<Acked_data>"且<Data_send>与\r
+\r
+<Acked_data>数值相等,则可进入下一步:继续发送数据或者接收\r
+数据。\r
+• 若返回"+TCPACK: 0,DISCONNECT",则返回重新建立TCP连接。\r
+• 若返回"+TCPACK: X,<Data_send>,<Acked_data>"且若<Data_send>大\r
+\r
+于<Acked_data>数值,则循环查询循环查询1s/次,最多5次。若查\r
+询5次,回码中<Data_send>与<Acked_data>数值仍不相等,则返回\r
+关闭当前连接,重新建立连接,以释放缓冲区。\r
+• 若收到其他错误回码、超时时间到未收到任何回码,则重启模块。\r
+\r
+"+TCPSEND: X,<Length>"?\r
+\r
+8. AT+TCPCLOSE=X\r
+\r
+查询<30次?\r
+\r
+关闭<3次?\r
+\r
+关闭<3次?\r
+\r
+去附着<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+Yes\r
+\r
+Yes\r
+\r
+No\r
+No\r
+\r
+2. 查询PPP连接建立情况\r
+- 若回码中包含"+XIIC: 1 ",则可进入下一步。\r
+- 若回码未包含"+XIIC: 1",则循环查询,1s/次,最多30次。若30次\r
+\r
+后仍未返回"+XIIC: 1",则重新拨号,最多重拨3次,若重新拨号3次\r
+后,仍未获取到IP,则重启模块。\r
+\r
+3. 关闭待用Socket\r
+- 该指令等待返回"+TCPCLOSE: X,OK""+TCPCLOSE: ERROR"的超时时间\r
+\r
+为30s。\r
+• 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次,\r
+\r
+若重复关闭3次后,仍未收到"+TCPCLOSE: X,OK"或者"+TCPCLOSE: \r
+ERROR"则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+13\r
+\r
+###### 3.1.1 异常处理\r
+\r
+###### 模块附着PS 域失败\r
+\r
+1. \r
+检查模块串口通信是否正常。若串口不通,可参考以下方式排查解决:\r
+\r
+− \r
+确保模块已经开机。若模块不开机需要检查电源设计和开机时序\r
+\r
+− \r
+确保串口电平变换正常。若电平变换不正常需要检查串口电路设计和器件选型\r
+\r
+− \r
+确保软件上串口配置正常,软件处理是否有误。比如自适应逻辑、是否带回车换行发送AT\r
+\r
+2. \r
+检查模块是否已经识别到SIM 卡。若模块无法识别SIM 卡,可参考以下方式排查解决(涉及到的\r
+\r
+AT 命令,请参考功能 AT 指令集):\r
+\r
+− \r
+确保SIM 卡是否插好、SIM 卡本身完好无损、与卡座接触是否良好\r
+\r
+− \r
+确保SIM 卡座相关电路无设计问题,是否有虚假焊等\r
+\r
+− \r
+确保SIM 卡没有被锁卡\r
+\r
+若通过以上排查模块仍无法识别到SIM 卡,可以联系运营商处理或者更换SIM 卡解决。\r
+\r
+3. \r
+查询当前模块的接收信号是否满足通信条件。我们建议信号强度值为12(-83dbm)再进行通信。\r
+\r
+若现场信号弱,可通过以下方式排查:\r
+\r
+− \r
+确保天线接好、天线本身完好无损(断路、短路)。\r
+\r
+− \r
+确认模块注册的网络制式(AT$MYSYSINFO)等信息,若为现场网络环境问题,则联系运营\r
+\r
+商增强网络覆盖,或者更换其他运营商SIM 卡解决。\r
+\r
+4. \r
+若现场信号良好,模块能识别到SIM 卡且SIM 卡未欠费,但模块不能附着上PS 域,则需要联系\r
+\r
+运营商确定SIM 卡配置:\r
+\r
+− \r
+SIM 卡是否开通数据业务\r
+\r
+− \r
+SIM 卡配置鉴权参数是否正常\r
+\r
+− \r
+SIM 卡是否有绑定设备\r
+\r
+− \r
+SIM 卡数据业务相关的APN、用户名、密码、鉴权方式是否正常\r
+\r
+###### 模块激活PDP 失败\r
+\r
+若模块正常注册上PS 域,但是无法激活PPP 连接。可以参考以下方式排查解决(涉及到的AT 命\r
+\r
+令,请参考功能 AT 指令集):\r
+\r
+⚫ \r
+确保SIM 未欠费。\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+14\r
+\r
+⚫ \r
+使用AT+CGDCONT?/AT+XGAUTH?查询APN、用户名、密码,确保这些参数与运营商提供的\r
+\r
+一致。\r
+\r
+若以上参数都没有问题,模块仍无法激活PPP 连接,请联系当地运营商协助检查SIM 卡和后台服\r
+\r
+务器参数配置。\r
+\r
+###### 模块建立TCP 非透传客户端连接失败\r
+\r
+若模块正常获取到IP,但是建立TCP 非透传客户端连接失败。可以参考以下方式排查解决(涉及\r
+\r
+到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认服务器IP 和端口都已配置好,端口已经建立服务器端侦听,IP 能被客户端PING 通。\r
+\r
+可以通过用其他客户端设备来连接该服务器IP 和端口确认。\r
+\r
+2. \r
+确认模块获取到的IP 是否能访问服务器。\r
+\r
+可通过发送AT+PING=<IP/域名>给模块,操作模块PING 服务器来确认。\r
+\r
+− \r
+若模块能PING 通服务器,则需要检查服务器软件和端口配置;\r
+\r
+− \r
+若模块不能PING 通服务器,则需要检查APN、用户名、密码设置是否正确,SIM 卡配置是\r
+\r
+否正确。\r
+\r
+3. \r
+确认SIM 卡配置是否有问题。\r
+\r
+可以直接联系运营商确认SIM 卡是否可以访问目的IP 或者让运营商直接跟踪后台消息排查原因。\r
+\r
+###### 模块发送TCP 数据失败\r
+\r
+若模块正常建立TCP 非透传客户端连接,但是发送TCP 数据失败。可以参考以下方式排查解决\r
+\r
+(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认是否是服务器问题。\r
+\r
+可通过更换服务器对比确认是否是服务器配置问题。\r
+\r
+2. \r
+确认SIM 卡是否也有欠费。\r
+\r
+可联系运营商确认。\r
+\r
+3. \r
+确认是否是长时间没有发送数据导致的假连接。\r
+\r
+可通过缩短心跳周期或者是提高数据发送频率来确认。\r
+\r
+若缩短心跳周期或者是提高数据发送频率后,数据发送正常则可能是假连接引起的问题。\r
+\r
+4. \r
+确认TCP 连接是否还存在。\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+15\r
+\r
+可通过”AT+IPSTATUS=<SocketID>”命令的返回值来确认。\r
+\r
+− \r
+若返回值中含有“CONNECT”字符串,则表示TCP 连接存在;\r
+\r
+− \r
+若返回“DISCONNECT” 字符串,则表示TCP 连接不存在。\r
+\r
+5. \r
+确认终端(或主控)是否已经将数据发送到模块。\r
+\r
+可以通过模块串口与终端(或主控)串口之间交互的AT 命令和数据内容来确认。\r
+\r
+为此,我司建议主控具备保存或打印模块与终端串口交互log 功能。只要AT+TCPSEND 命令返\r
+\r
+回“+TCPSEND: <SocketID>,<DataLength>”就表示主控数据已经发送到模块。\r
+\r
+6. \r
+确认模块底层协议栈是否收到服务器端TCP 协议层的ACK。\r
+\r
+可通过”AT+TCPACK=<SocketID>”命令的返回值来确认。\r
+\r
+− \r
+若返回值中< data_sent >字段与<acked_recv>字段数值相同,则表示数据已经发送到服务器\r
+\r
+且服务器已经确认。\r
+\r
+− \r
+若返回值中< data_sent >字段数值大于<acked_recv>字段数值,则有可能是数据没有发送到\r
+\r
+服务器,或者是服务器接收有延时还未收到数据,需要在服务器和模块同时抓取TCP 层数据\r
+\r
+来排查,可以联系我司FAE 跟进排查。\r
+\r
+###### 模块接收TCP 数据失败\r
+\r
+若模块正常建立TCP 非透传客户端连接,但是接收TCP 数据失败。可以参考以下方式排查解决\r
+\r
+(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认是否是服务器问题。\r
+\r
+可通过更换服务器对比确认是否是服务器配置问题。\r
+\r
+2. \r
+确认SIM 卡是否也有欠费。\r
+\r
+可联系运营商确认。\r
+\r
+3. \r
+确认TCP 连接是否还存在。\r
+\r
+可通过”AT+IPSTATUS=<SocketID>”命令的返回值来确认。\r
+\r
+− \r
+若返回值中含有“CONNECT”字符串,则表示TCP 连接存在。\r
+\r
+− \r
+若返回“DISCONNECT” 字符串,则表示TCP 连接不存在。\r
+\r
+4. \r
+确认是否是长时间没有发送数据导致的假连接。\r
+\r
+可通过缩短心跳周期或者是提高数据发送频率来确认。\r
+\r
+− \r
+若缩短心跳周期或者是提高数据发送频率后,数据接收正常则可能是假连接引起的问题。\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+16\r
+\r
+− \r
+若缩短心跳或提高数据发送频率仍无法确认接收不到数据的原因,需要在服务器和模块同时\r
+\r
+抓取TCP 层数据来排查,可以联系我司FAE 跟进排查。\r
+\r
+###### 3.1.2 示例\r
+\r
+1. \r
+设置APN、用户名、密码,检查并确保模块已成功注册上PS 域\r
+\r
+AT \r
+//确认模块能正常响应AT 命令 \r
+OK \r
+AT+CGSN \r
++CGSN: "865324037375316" \r
+OK \r
+AT+CGMR \r
++CGMR: N720_EAB0CM_BZ_V008 \r
+OK \r
+AT+CPIN?\r
+\r
++CPIN: READY \r
+//确认模块能识别到SIM 卡 \r
+OK\r
+\r
+AT+CIMI \r
+460016657520885 \r
+OK\r
+\r
+AT+CGDCONT=1,"IP","UNINET" \r
+//设置APN \r
+OK \r
+AT+XGAUTH=1,1, "CARD","CARD" \r
+//设置用户名、密码、鉴权方式\r
+\r
+OK\r
+\r
+AT+CSQ \r
++CSQ: 31, 99 \r
+//确认信号良好 \r
+OK \r
+AT+CREG=2 \r
+OK \r
+AT+CREG? \r
++CREG: 2,1,"FFFE","61C7E47",7 //确认模块注册上CS 域 \r
+OK \r
+AT+CREG=0 \r
+OK\r
+\r
+AT+CGATT? \r
++CGATT: 1 \r
+//确认模块附着上PS 域 \r
+OK \r
+AT$MYSYSINFO \r
+$MYSYSINFO: 4,02 \r
+//确认模块注册的网络制式 \r
+OK\r
+\r
+2. \r
+建立PPP 连接\r
+\r
+At+XIIC=1 \r
+//激活PPP 连接 \r
+OK \r
+AT+XIIC? \r
+//查询模块本地IP \r
++XIIC: 1,10.211.164.210 \r
+OK\r
+\r
+3. \r
+建立TCP 非透传客户端连接并收发数据\r
+\r
+AT+TCPCLOSE=0 \r
+ \r
+ \r
+//主动关闭待用SocketID \r
++TCPCLOSE: ERROR\r
+\r
+AT+TCPSETUP=0,183.239.240.40,5002 \r
+OK\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+17\r
+\r
++TCPSETUP: 0,OK \r
+AT+IPSTATUS=0 \r
+ \r
+ \r
+ \r
+ //确认TCP 连接是否正常 \r
++IPSTATUS: 0,CONNECT,TCP,4096 \r
+AT+TCPACK=0 \r
+ \r
+ \r
+ \r
+ //确认数据发送情况 \r
++TCPACK: 0,0,0 \r
+AT+TCPSEND=0,10 \r
+> \r
+OK\r
+\r
++TCPSEND: 0,10 \r
++TCPRECV: 0,6,Neoway \r
+AT+IPSTATUS=0 \r
++IPSTATUS: 0,CONNECT,TCP,4096 \r
+AT+TCPACK=0 \r
+ \r
+ \r
+ \r
+ \r
+//确认数据发送情况 \r
++TCPACK: 0,10,10 \r
+AT+TCPSEND=0,10 \r
+ \r
+ \r
+ \r
+ \r
+//发送数据 \r
+>\r
+\r
+OK\r
+\r
++TCPSEND: 0,10 \r
++TCPRECV: 0,6,Neoway \r
+AT+TCPCLOSE=0\r
+\r
++TCPCLOSE: 0,OK \r
+AT+CGATT=0 \r
+GPRS DISCONNECTION\r
+\r
+OK\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+18\r
+\r
+##### 3.2 TCP 非透传服务器端\r
+\r
+本节主要实现TCP 非透传服务端功能相关的AT 指令、AT 流程、异常处理以及简单示例。在进行\r
+\r
+PPP 拨号前,请确保模块已经过初始化(具体初始化流程参考第2 章)。\r
+\r
+图 3-2 连接流程\r
+\r
+Yes\r
+\r
+设置PPP拨号参数\r
+\r
+"OK"?\r
+\r
+1. AT+XIIC=1\r
+\r
+"+XIIC: 1"?\r
+\r
+2. AT+XIIC?\r
+\r
+"ERROR" or "+CLOSELISTEN: 0,local\r
+\r
+link closed" ?\r
+\r
+3. AT+CLOSELISTEN\r
+\r
+Yes\r
+\r
+4. AT+TCPLISTEN=<Port>\r
+\r
+" +TCPLISTEN: 0,OK" ?\r
+\r
+5.AT+CLIENTSTATUS=Y\r
+\r
+"+CLIENTSTATUS: \r
+Y,CONNECT,TCP,61440" ?\r
+\r
+9. AT+CGATT=0\r
+\r
+"OK" or "ERROR"?\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+重连<3次?\r
+\r
+查询<3次?\r
+\r
+关机\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+模块重启\r
+模块初始化流程\r
+\r
+6. AT+TCPSENDS=Y,<Length>\r
+\r
+7. AT+TCPACKS=Y\r
+\r
+"+TCPACKS: \r
+Y,<Data_send>,<Acked_data>"\r
+\r
+No\r
+\r
+查询<5次?\r
+\r
+重发<3次?\r
+\r
+查询<5次?\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+No\r
+Yes\r
+\r
+No\r
+Yes\r
+No\r
+\r
+1. 进行PPP拨号\r
+- 若返回"OK",则可进入下一步。\r
+- 若未收到任何回码或收到其他错误回码,则继续拨号,1s/次,最多\r
+\r
+3次。若重新拨号3次后,仍未若返回"OK ",则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+4. 建立服务器端侦听\r
+- 若返回"+TCPLISTEN: 0,OK"或者"Listening...",则可进入下一步。\r
+- 若出现以下情况,则继续建立侦听,最多3次。\r
+- 收到回码:"ERROR"/"+TCPLISTEN:bind error"。\r
+- 收到其他错误回码或者未收到任何回码。\r
+- 若重新建立3次侦听,仍未返回"+TCPLISTEN: 0,OK"或者"Listening..."\r
+\r
+则重启模块。\r
+\r
+等待接收客户端连接请求\r
+需要设置等待客户端连接的超时时间,若超时时间到,没有收到任\r
+何客户端连接请求,则主动重新拨号建立侦听。\r
+\r
+5. 查询链路状态\r
+- 若返回"+CLIENTSTATUS: Y,CONNECT,TCP,61440",则进行下一步。\r
+- 若回码包含"+CLIENTSTATUS: Y,DISCONNECT",则客户端连接未建立或者已经断\r
+\r
+开,等待客户端重新建立TCP连接。\r
+• 若回码包含"+CLIENTSTATUS: Y,CONNECT",Buffer大小不为61440,则循环查询链\r
+\r
+路状态,最多5次。若查询5次后,Buffer大小不为61440,则关闭当前连接,\r
+等待客户端重新建立TCP连接。\r
+• 若未返回任何回码或者返回其他错误回码,则继续查询1s/次,最多3次。若查\r
+\r
+询3次后,仍无回码或者返回其他错误回码,则重启模块。\r
+\r
+7. 查询数据发送情况\r
+- 若返回"+TCPACK: Y,<Data_send>,<Acked_data>"且<Data_send>与\r
+\r
+<Acked_data>数值相等,则可进入下一步:继续发送数据或者接收\r
+数据。\r
+• 若返回"+TCPACK: Y,DISCONNECT",则返回重新建立TCP连接。\r
+• 若返回"+TCPACK: Y,<Data_send>,<Acked_data>"且若<Data_send>大\r
+\r
+于<Acked_data>数值,则继续查询,1s/次,最多5次。若查询5次,\r
+回码中<Data_send>与<Acked_data>数值仍不相等,则返回关闭当\r
+前连接,重新建立连接,以释放缓冲区。\r
+• 若收到其他错误回码、超时时间到未收到任何回码,则重启模块。\r
+\r
+9.去附着网络\r
+若返回"OK"或者" ERROR",则进入下一步,等返回的超时时间为15s。\r
+若超时未收到回码或者收到其他错误回码,则继续执行,最多3次,\r
+若重复关闭3次后,仍未收到"OK"或者" ERROR"则重启模块。\r
+\r
++TCPRECV(S): Y,<length>,<data>\r
+\r
+"ERROR" or "+CLOSELISTEN: 0,local\r
+\r
+link closed" ?\r
+\r
+6. 发送数据\r
+- 若返回">",则延时50-100ms,将数据发送到模块串口,等待数据发\r
+\r
+- 送成功的回码"+TCPSENDS: Y,<Length>",超时时间为30s。\r
+- 若返回"+TCPSENDS: Y,<Length>",则进入下一步。\r
+- 若返回"+TCPSENDS:SOCKET ID NOT ACTIVE",则等待客户端连接请\r
+\r
+求。\r
+• 若收到其他错误回码、超时时间到未收到任何回码,则继续执行该\r
+\r
+命令重发数据,最多3次。若重发3次仍然失败,则重启模块。\r
+\r
+"+TCPSENDS: Y,<Length>" ?\r
+\r
+8. AT+CLOSELISTEN\r
+\r
+查询<30次?\r
+\r
+重新建立侦听<3次?\r
+\r
+关闭<3次?\r
+\r
+去附着<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+Yes\r
+\r
+Yes\r
+No\r
+No\r
+\r
+2. PPP连接建立情况\r
+- 若回码中包含"+XIIC:    1 ",则可进入下一步。\r
+- 若回码未包含"+XIIC:    1",则循环查询,1s/次,最多30次。若30次\r
+\r
+后仍未返回"+XIIC: 1",则重新拨号,最多重拨3次,若重新拨号3次\r
+后,仍未获取到IP,则重启模块。\r
+\r
+3. 关闭服务器侦听\r
+- 该指令等待返回回码的超时时间为20s。\r
+- 若返回"+CLOSELISTEN: 0,local link closed"或者"ERROR",则可进入下\r
+\r
+- 一步。\r
+- 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次。\r
+- 若重复关闭3次后,仍未收到"+CLOSELISTEN: 0,local link closed"或者\r
+\r
+"ERROR"则重启模块。\r
+\r
+Connect AcceptSocket=Y,\r
+ClientAddr=119.123.77.133,ClientPort=8000\r
+\r
+8. 关闭服务器侦听\r
+- 该指令等待返回回码的超时时间为20s。若返回"+CLOSELISTEN:\r
+\r
+- 0,local link closed"或者"ERROR",则可进入下一步。\r
+- 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次。\r
+- 若重复关闭3次后,仍未收到"+CLOSELISTEN: 0,local link closed"或者\r
+\r
+"ERROR"则重启模块。\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+19\r
+\r
+###### 3.2.1 异常处理\r
+\r
+###### 模块附着PS 域失败\r
+\r
+1. \r
+检查模块串口通信是否正常。若串口不通,可参考以下方式排查解决:\r
+\r
+− \r
+确保模块已经开机:若模块不开机需要检查电源设计和开机时序\r
+\r
+− \r
+确保串口电平变换正常:若电平变换不正常需要检查串口电路设计和器件选型\r
+\r
+− \r
+确保软件上串口配置正常,软件处理是否有误。比如自适应逻辑、是否带回车换行发送AT\r
+\r
+2. \r
+检查模块是否已经识别到SIM 卡。若模块无法识别SIM 卡,可参考以下方式排查解决(涉及到的\r
+\r
+AT 命令,请参考功能 AT 指令集):\r
+\r
+− \r
+确保SIM 卡是否插好、SIM 卡本身完好无损、与卡座接触是否良好\r
+\r
+− \r
+确保SIM 卡座相关电路无设计问题,是否有虚假焊等\r
+\r
+− \r
+确保SIM 卡没有被锁卡\r
+\r
+若通过以上排查模块仍无法识别到SIM 卡,可以联系运营商处理或者更换SIM 卡解决。\r
+\r
+3. \r
+查询当前模块的接收信号是否满足通信条件。\r
+\r
+我们建议信号强度值为12(-83dbm)再进行通信。若现场信号弱,可通过以下方式排查:\r
+\r
+− \r
+确保天线接好、天线本身完好无损(断路、短路)\r
+\r
+− \r
+确认模块注册的网络制式(AT$MYSYSINFO)等信息,若为现场网络环境问题,则联系运营\r
+\r
+商增强网络覆盖,或者更换其他运营商SIM 卡解决。\r
+\r
+4. \r
+若现场信号良好,模块能识别到SIM 卡且SIM 卡未欠费,但模块不能附着上PS 域,则需要联系\r
+\r
+运营商确定SIM 卡配置:\r
+\r
+− \r
+SIM 卡是否开通数据业务\r
+\r
+− \r
+SIM 卡配置鉴权参数是否正常\r
+\r
+− \r
+SIM 卡是否有绑定设备\r
+\r
+− \r
+SIM 卡数据业务相关的APN、用户名、密码、鉴权方式是否正常\r
+\r
+###### 模块激活PDP 失败\r
+\r
+若模块正常注册上PS 域,但是无法激活PPP 连接。可以参考以下方式排查解决(涉及到的AT 命\r
+\r
+令,请参考功能 AT 指令集):\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+20\r
+\r
+⚫ \r
+确保SIM 未欠费。\r
+\r
+⚫ \r
+使用AT+CGDCONT?/AT+XGAUTH?查询APN、用户名、密码,确保这些参数与运营商提供的\r
+\r
+一致。\r
+\r
+若以上参数都没有问题,模块仍无法激活PPP 连接,请联系当地运营商协助检查SIM 卡和后台服\r
+\r
+务器参数配置。\r
+\r
+###### 客户端无法连接服务器\r
+\r
+若模块正常建立服务器端侦听后,客户端无法连接上服务器。可以参考以下方式排查解决(涉及到\r
+\r
+的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认服务器IP 和端口都已配置好,端口已经建立服务器端侦听,IP 能被客户端PING 通。\r
+\r
+可以通过用其他客户端设备来连接该服务器IP 和端口确认。\r
+\r
+2. \r
+确认模块获取到的IP 与客户端的IP 是否在同一个网段内。\r
+\r
+可通过在PC 上PING 模块来确认。\r
+\r
+− \r
+若PC 客户端能PING 通服务器,则需要检查服务器软件和端口配置。\r
+\r
+− \r
+若PC 客户端不能PING 通服务器,则需要检查APN、用户名、密码设置是否正确,SIM 卡\r
+\r
+配置是否正确。\r
+\r
+3. \r
+确认SIM 卡配置是否有问题。\r
+\r
+可以直接联系运营商客户端是否可以访问SIM 卡的IP 或者让运营商直接跟踪后台消息排查原因。\r
+\r
+###### 3.2.2 示例\r
+\r
+1. \r
+设置APN、用户名、密码,检查并确保模块已成功注册上PS 域\r
+\r
+AT \r
+//确认模块能正常响应AT 命令 \r
+OK\r
+\r
+AT+CGSN \r
++CGSN: "865324037375316" \r
+OK\r
+\r
+AT+CGMR \r
++CGMR: N720_EAB0CM_BZ_V008 \r
+OK \r
+AT+CPIN? \r
++CPIN: READY \r
+//确认模块能识别到SIM 卡 \r
+OK \r
+AT+CIMI \r
+460016657520885 \r
+OK\r
+\r
+AT+CGDCONT=1,"IP","UNINET" \r
+//设置APN \r
+OK \r
+AT+XGAUTH=1,1, "CARD","CARD" \r
+//设置用户名、密码、鉴权方式\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+21\r
+\r
+OK \r
+AT+CSQ \r
++CSQ: 31, 99 \r
+//确认信号良好 \r
+OK \r
+AT+CREG=2 \r
+OK \r
+AT+CREG? \r
++CREG: 2,1,"FFFE","61C7E47",7 //确认模块注册上CS 域\r
+\r
+OK \r
+AT+CREG=0 \r
+OK \r
+AT+CGATT? \r
++CGATT: 1 \r
+//确认模块附着上PS 域 \r
+OK \r
+AT$MYSYSINFO \r
+$MYSYSINFO: 4,02 \r
+//确认模块注册的网络制式 \r
+OK\r
+\r
+2. \r
+建立PPP 连接\r
+\r
+AT+XIIC=1 \r
+//激活PPP 连接 \r
+OK\r
+\r
+AT+XIIC? \r
+//查询模块本地IP \r
++XIIC: 1,10.211.164.210 \r
+OK\r
+\r
+3. \r
+建立TCP 非透传服务器端连接并收发数据\r
+\r
+AT+CLOSELISTEN \r
+ \r
+ \r
+ \r
+ //主动关闭待用SocketID \r
+ERROR \r
+AT+TCPLISTEN=6800 \r
+ \r
+ \r
+//建立侦听 \r
+ \r
++TCPLISTEN:0,OK \r
+Connect AcceptSocket=1,ClientAddr=119.123.77.133 //客户端连接建立 \r
+AT+CLIENTSTATUS=1 \r
+ \r
+ \r
+ \r
+ //确认TCP 连接是否正常 \r
++IPSTATUS: 1,CONNECT,TCP,1024\r
+\r
+```\r
+AT+TCPSENDS=1,10\r
+```\r
+\r
+> \r
+OK\r
+\r
++TCPSENDS: 1,10 \r
+AT+TCPACKS=1 \r
+ \r
+ \r
+ \r
+ //确认数据发送情况\r
+\r
++TCPACKS: 1,10,10 \r
++TCPRECV(S): 1,6,Neoway \r
+AT+ CLOSELISTEN \r
+ \r
+ \r
+ \r
+ \r
+ \r
+//关闭侦听\r
+\r
++CLOSELISTEN: 0,local link closed\r
+\r
+AT+CGATT=0 \r
+GPRS DISCONNECTION \r
+OK\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+22\r
+\r
+##### 3.3 TCP 透传客户端\r
+\r
+本节主要实现TCP 透传客户端功能相关的AT 指令、AT 流程、异常处理以及简单示例。在进行PPP\r
+\r
+拨号前,请确保模块已经过初始化(具体初始化流程参考第2 章)。\r
+\r
+图 3-3 连接流程\r
+\r
+设置PPP拨号参数\r
+\r
+"OK"?\r
+\r
+1. AT+XIIC=1\r
+\r
+"+XIIC: 1"?\r
+\r
+2. AT+XIIC?\r
+\r
+"+TRANSCLOSE: 0,OK" or " ERROR"?\r
+\r
+3. AT+TRANSCLOSE\r
+\r
+Yes\r
+\r
+4. AT+TCPTRANS=<IP/域名>,<Port>\r
+\r
+"+TCPTRANS: OK"?\r
+\r
+收发数据\r
+\r
+9. AT+CGATT=0\r
+\r
+"OK" or "ERROR"?\r
+\r
+No\r
+\r
+Yes\r
+重连<3次?\r
+\r
+查询<3次?\r
+\r
+关机\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+模块重启\r
+模块初始化流程\r
+\r
+6. AT+TCPACK\r
+\r
+"+TCPACK: \r
+X,<Data_send>,<Acked_data>"?\r
+\r
+No\r
+\r
+退出透传<3次?\r
+\r
+查询<5次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+1. 进行PPP拨号\r
+- 若返回"OK",则可进入下一步。\r
+- 若未收到任何回码或收到其他错误回码,则继续拨号,1s/次,最多\r
+\r
+3次。若重新拨号3次后,仍未若返回"OK ",则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+4. 建立TCP连接\r
+- 该指令等待返回"+TCPTRANS: OK"的超时时间为30s\r
+- 若返回"+TCPSETUP: X,OK",则可进入下一步。\r
+- 若出现以下情况,则继续重连,最多3次,若3次仍无法建立TCP连\r
+\r
+接,则重启模块。\r
+ -- 收到回码:"+TCPSETUP: X,FAIL"/"+TCPSETUP: ERROR"\r
+ -- 收到其他错误回码或者未收到任何回码\r
+\r
+5. 退出透传\r
+- 若返回"OK",则进入下一步。\r
+- 若返回其他错误回码、或者未返回任何回码,则继续重发,最多3\r
+\r
+次。若重发3次仍未收到"OK",则重启模块。\r
+• "+++"必须以字符串的形式发送到串口,且"+++"不必带回车发送\r
+\r
+8. 关闭Socket\r
+- 若返回"+TCPCLOSE: X,OK""+TCPCLOSE: ERROR"则进入下一步,等待\r
+\r
+返回的超时时间为30s。\r
+• 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次,\r
+\r
+若重复关闭3次后,仍未收到"+TCPCLOSE: X,OK"或者"+TCPCLOSE: \r
+ERROR"则重启模块。\r
+\r
+9. 去附着网络\r
+- 若返回"OK"或者" ERROR",则进入下一步,等返回的超时时间为\r
+\r
+15s。\r
+• 若超时未收到回码或者收到其他错误回码,则继续执行,最多3\r
+\r
+次,若重复关闭3次后,仍未收到"OK"或者" ERROR"则重启模块。\r
+\r
+7. ATO\r
+\r
+"+TRANSCLOSE: 0,OK"\r
+\r
+or "ERROR"?\r
+\r
+6. 查询数据发送情况\r
+- 若返回"+TCPACK: X,<Data_send>,<Acked_data>"且<Data_send>与\r
+\r
+<Acked_data>数值相等,则可进入下一步:继续发送数据或者接收\r
+数据。\r
+• 若返回"+TCPACK: 0,DISCONNECT",则返回重新建立TCP连接。\r
+• 若返回"+TCPACK: X,<Data_send>,<Acked_data>"且若<Data_send>大\r
+\r
+于<Acked_data>数值,则循环查询循环查询1s/次,最多5次。若查\r
+询5次,回码中<Data_send>与<Acked_data>数值仍不相等,则返回\r
+关闭当前连接,重新建立连接,以释放缓冲区。\r
+• 若收到其他错误回码、超时时间到未收到任何回码,则重启模\r
+\r
+块。\r
+\r
+"OK"?\r
+\r
+8. AT+TRANSCLOSE\r
+\r
+查询<30次?\r
+\r
+关闭<3次?\r
+\r
+关闭<3次?\r
+\r
+去附着<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+2. 查询PPP连接建立情况\r
+- 若回码中包含"+XIIC: 1 ",则可进入下一步。\r
+- 若回码未包含"+XIIC: 1",则循环查询,1s/次,最多30次。若30次\r
+\r
+后仍未返回"+XIIC: 1",则重新拨号,最多重拨3次,若重新拨号3次\r
+后,仍未获取到IP,则重启模块。\r
+\r
+3. 关闭连接\r
+- 该指令等待返回"+TRANSCLOSE: 0,OK""ERROR"的超时时间为30s。\r
+- 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次。\r
+- 若重复关闭3次后,仍未收到"+TRANSCLOSE: 0,OK"或"ERROR"则重\r
+\r
+启模块。\r
+\r
++++\r
+\r
+"CONNECT" ?\r
+返回透传<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+7. 返回透传模式\r
+- 若返回"CONNECT",则进入下一步:继续发送数据。\r
+- 若返回其他错误回码、或者未返回任何回码,则继续重发,最多3\r
+\r
+次。若重发3次仍未收到"OK",则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+23\r
+\r
+###### 3.3.1 异常处理\r
+\r
+###### 模块附着PS 域失败\r
+\r
+1. \r
+使用AT+CPIN?检查模块是否已经识别到SIM 卡。若模块无法识别SIM 卡,可参考以下方式排查\r
+\r
+解决(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+− \r
+确保SIM 卡是否插好;\r
+\r
+− \r
+确保SIM 卡本身完好无损;\r
+\r
+− \r
+确保SIM 卡没有被锁卡;\r
+\r
+− \r
+确保SIM 卡卡座完好,不存在假焊;\r
+\r
+− \r
+擦拭SIM 卡及卡座的触点(该触点发生氧化、脏污也会产生读不到SIM 卡的现象),确保\r
+\r
+SIM 卡与模块卡座接触良好。\r
+\r
+若通过以上排查模块仍无法识别到SIM 卡,可以更换SIM 卡解决。\r
+\r
+2. \r
+检查天线是否完好,使用AT+CSQ 命令查询当前模块的接收信号是否满足通信条件。\r
+\r
+我们建议信号强度值为15(-83dbm)再进行通信。\r
+\r
+3. \r
+若现场信号良好,模块能识别到SIM 卡且SIM 卡未欠费,但模块不能附着上PS 域,可通过\r
+\r
+```\r
+AT+CGATT=1 命令,主动附着PS 域。\r
+```\r
+\r
+###### 模块激活PDP 失败\r
+\r
+若模块正常注册上PS 域,但是无法激活PPP 连接。可以参考以下方式排查解决(涉及到的AT 命\r
+\r
+令,请参考功能 AT 指令集):\r
+\r
+⚫ \r
+确保SIM 卡未欠费。\r
+\r
+⚫ \r
+使用AT+CGDCONT?/AT+NETAPN?查询APN、用户名、密码,确保这些参数与运营商提供的\r
+\r
+一致。\r
+\r
+若以上参数都没有问题,模块仍无法激活PPP 连接,请联系当地运营商协助检查SIM 卡和后台服\r
+\r
+务器参数配置。\r
+\r
+###### 模块建立TCP 透传客户端连接失败\r
+\r
+若模块正常获取到IP,但是建立TCP 非透传客户端连接。可以参考以下方式排查解决(涉及到的\r
+\r
+AT 命令,请参考功能 AT 指令集):\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+24\r
+\r
+1. \r
+确认服务器IP 和端口都已配置好,端口已经建立服务器端侦听,IP 能被客户端PING 通。\r
+\r
+可以通过用其他客户端设备来连接该服务器IP 和端口确认。\r
+\r
+2. \r
+确认模块获取到的IP 是否能访问服务器。\r
+\r
+可通过发送AT+PING=<IP/域名>给模块,操作模块PINGf 服务器来确认。\r
+\r
+− \r
+若模块能PING 通服务器,则需要检查服务器软件和端口配置。\r
+\r
+− \r
+若模块不能PING 通服务器,则需要检查APN、用户名、密码设置是否正确,SIM 卡配置是\r
+\r
+否正确。\r
+\r
+3. \r
+确认SIM 卡配置是否有问题。\r
+\r
+可以直接联系运营商确认SIM 卡是否可以访问目的IP 或者让运营商直接跟踪后台消息排查原因。\r
+\r
+###### 模块发送TCP 数据失败\r
+\r
+若模块正常建立TCP 非透传客户端连接,但是发送TCP 数据失败。可以参考以下方式排查解决\r
+\r
+(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认是否是服务器问题。\r
+\r
+可通过更换服务器对比确认是否是服务器配置问题。\r
+\r
+2. \r
+确认SIM 卡是否也有欠费。\r
+\r
+可联系运营商确认。\r
+\r
+3. \r
+确认是否是长时间没有发送数据导致的假连接。\r
+\r
+可通过缩短心跳周期或者是提高数据发送频率来确认。\r
+\r
+若缩短心跳周期或者是提高数据发送频率后,数据发送正常则可能是假连接引起的问题。\r
+\r
+4. \r
+确认TCP 连接是否还存在。\r
+\r
+可通过”AT+IPSTATUS=<SocketID>”命令的返回值来确认。\r
+\r
+− \r
+若返回值中含有“CONNECT”字符串,则表示TCP 连接存在。\r
+\r
+− \r
+若返回“DISCONNECT” 字符串,则表示TCP 连接不存在。\r
+\r
+5. \r
+确认终端(或主控)是否已经将数据发送到模块。\r
+\r
+可以通过模块串口与终端(或主控)串口之间交互的AT 命令和数据内容来确认。\r
+\r
+为此,我司建议主控具备保存或打印模块与终端串口交互log 功能。只要AT+TCPSEND 命令返\r
+\r
+回“+TCPSEND: <SocketID>,<DataLength>”就表示主控数据已经发送到模块。\r
+\r
+6. \r
+确认模块底层协议栈是否收到服务器端TCP 协议层的ACK。\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+25\r
+\r
+可通过”AT+TCPACK=<SocketID>”命令的返回值来确认。\r
+\r
+− \r
+若返回值中< data_sent >字段与<acked_recv>字段数值相同,则表示数据已经发送到服务器\r
+\r
+且服务器已经确认。\r
+\r
+− \r
+若返回值中< data_sent >字段数值大于<acked_recv>字段数值,则有可能是数据没有发送到\r
+\r
+服务器,或者是服务器接收有延时还未收到数据,需要在服务器和模块同时抓取TCP 层数据\r
+\r
+来排查,可以联系我司FAE 跟进排查。\r
+\r
+###### 模块接收TCP 数据失败\r
+\r
+若模块正常建立TCP 非透传客户端连接,但是接收TCP 数据失败。可以参考以下方式排查解决\r
+\r
+(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认是否是服务器问题。\r
+\r
+可通过更换服务器对比确认是否是服务器配置问题。\r
+\r
+2. \r
+确认SIM 卡是否也有欠费。\r
+\r
+可联系运营商确认。\r
+\r
+3. \r
+确认TCP 连接是否还存在。\r
+\r
+可通过”AT+IPSTATUS=<SocketID>”命令的返回值来确认。\r
+\r
+− \r
+若返回值中含有“CONNECT”字符串,则表示TCP 连接存在。\r
+\r
+− \r
+若返回“DISCONNECT” 字符串,则表示TCP 连接不存在。\r
+\r
+4. \r
+确认是否是长时间没有发送数据导致的假连接。\r
+\r
+可通过缩短心跳周期或者是提高数据发送频率来确认。\r
+\r
+− \r
+若缩短心跳周期或者是提高数据发送频率后,数据接收正常则可能是假连接引起的问题。\r
+\r
+− \r
+若缩短心跳或提高数据发送频率仍无法确认接收不到数据的原因,需要在服务器和模块同时\r
+\r
+抓取TCP 层数据来排查,可以联系我司FAE 跟进排查。\r
+\r
+###### 3.3.2 示例\r
+\r
+1. \r
+设置APN、用户名、密码,检查并确保模块已成功注册上PS 域\r
+\r
+AT \r
+//确认模块能正常响应AT 命令 \r
+OK \r
+AT+CGSN \r
++CGSN: "865324037375316"\r
+\r
+OK\r
+\r
+```\r
+AT+CGMR\r
+```\r
+\r
++CGMR: N720_EAB0CM_BZ_V008 \r
+OK\r
+\r
+功能模组TCP 应用指南 \r
+第3 章标准指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+26\r
+\r
+AT+CPIN? \r
++CPIN: READY \r
+//确认模块能识别到SIM 卡 \r
+OK \r
+AT+CIMI \r
+460016657520885 \r
+OK \r
+AT+CGDCONT=1,"IP","UNINET" \r
+//设置APN \r
+OK\r
+\r
+AT+XGAUTH=1,1, "CARD","CARD" \r
+//设置用户名、密码、鉴权方式 \r
+OK \r
+AT+CSQ \r
++CSQ: 31, 99 \r
+//确认信号良好 \r
+OK \r
+AT+CREG=2 \r
+OK \r
+AT+CREG? \r
++CREG: 2,1,"FFFE","61C7E47",7 //确认模块注册上CS 域\r
+\r
+OK \r
+AT+CREG=0\r
+\r
+OK \r
+AT+CGATT? \r
++CGATT: 1 \r
+//确认模块附着上PS 域\r
+\r
+OK \r
+AT$MYSYSINFO \r
+$MYSYSINFO: 4,02 \r
+//确认模块注册的网络制式\r
+\r
+OK\r
+\r
+2. \r
+建立PPP 连接\r
+\r
+At+XIIC=1 \r
+//激活PPP 连接 \r
+OK \r
+AT+XIIC? \r
+//查询模块本地IP \r
++XIIC: 1,10.211.164.210 \r
+OK\r
+\r
+3. \r
+建立TCP 非透传服务器端连接并收发数据\r
+\r
+```\r
+AT+TRANSCLOSE \r
+ERROR \r
+AT+TCPTRANS=183.239.240.40,5009              //建立TCP 透传连接\r
+```\r
+\r
+OK \r
++TCPTRANS: OK\r
+\r
+//收发数据,发送+++(不带回车换行)退出透传 \r
+OK \r
+AT+IPSTATUS \r
+//查询链路状态\r
+\r
++IPSTATUS: CONNECT,TCP,4096\r
+\r
+ATO \r
+//切换回透传模式 \r
+CONNECT \r
+ \r
+ \r
+ \r
+ \r
+//收发数据,发送+++(不带回车换行)退出透传 \r
+OK \r
+AT+TRANSCLOSE \r
+//关闭透传连接 \r
++TRANSCLOSE: 0,OK \r
+AT+CGATT=0 \r
+GPRS DISCONNECTION \r
+OK\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+27\r
+\r
+#### 4 国网指令TCP 应用\r
+\r
+本章主要介绍如何使用有方标准指令实现TCP 功能(包括TCP 非透传客户端、TCP 非透传服务器\r
+\r
+端、TCP 透传客户端功能) 。内容包括对应功能相关的AT 指令、AT 流程、异常处理以及简单示例。\r
+\r
+连接注意事项:\r
+\r
+⚫ \r
+发送AT 命令后必须等待收到回码后,才能发送下一条AT 命令。\r
+\r
+⚫ \r
+在建立PPP 连接后,主动去附着前,只要收到"$MYURCACT: 0,0",则返回重新激活PPP 连接。\r
+\r
+⚫ \r
+在建立TCP 连接后,主动断开TCP 连接前,只要收到"$MYURCCLOSE: X",则返回重新建立TCP\r
+\r
+连接。\r
+\r
+##### 4.1 TCP 非透传客户端\r
+\r
+本节主要实现TCP 非透传客户端功能相关的AT 指令、AT 流程、异常处理以及简单示例。在进行\r
+\r
+PPP 拨号前,请确保模块已经过初始化(具体初始化流程参考第2 章)。\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+28\r
+\r
+图 4-1 连接流程\r
+\r
+设置PPP拨号参数\r
+\r
+"$MYURCACT: 0,1"?\r
+\r
+1. AT$MYNETACT=0,1\r
+\r
+"$MYNETACT: 0,1"?\r
+\r
+2. AT$MYNETACT?\r
+\r
+"$MYNETCLOSE: X"\r
+\r
+or "ERROR: 913"?\r
+\r
+3. AT$MYNETCLOSE=X\r
+\r
+Yes\r
+\r
+4. AT$MYNETSRV=0,X,0,0,"IP:Port"\r
+\r
+"OK"?\r
+\r
+5. AT$MYNETOPEN=X\r
+\r
+" $MYNETOPEN: X,2000" or"\r
+\r
+ERROR: 912" ?\r
+\r
+9. AT+CGATT=0\r
+\r
+"OK" or "ERROR"?\r
+\r
+No\r
+\r
+Yes\r
+重设<3次?\r
+\r
+拨号<3次?\r
+\r
+关机\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+模块重启\r
+模块初始化流程\r
+\r
+6. AT$MYNETWRITE=X,<Length>\r
+\r
+"$MYNETWRITE: X,<Length>" ?\r
+\r
+输入要发送的数据\r
+\r
+"OK"?\r
+\r
+No\r
+\r
+重新开启服务<3次?\r
+\r
+重发<3次?\r
+\r
+查询<5次?\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+No\r
+Yes\r
+\r
+No\r
+Yes\r
+No\r
+\r
+1. PPP拨号\r
+\r
+• 若返回"$MYURCACT: 0,1"或"ERROR: 902",则进入下一步。该指令等待返\r
+\r
+回"$MYURCACT: 0,1"或"ERROR: 902"的超时时间为30s。\r
+• 若未收到任何回码或收到其他错误回码,则继续拨号,最多3次。若重\r
+\r
+新拨号3次后,仍未若返回"OK ",则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+4. 设置非透明传输服务参数\r
+- 若返回"OK",则进入下一步。\r
+- 若未收到回码或收到其他错误回码,则继续设置,最多3次。若重复设\r
+\r
+置3次后,仍未收到"OK",则重启模块。\r
+注:信道号必须与已激活的信道号一致,第三个参数必须设置为0。\r
+\r
+5. 开启服务(建立TCP连接)\r
+- 若返回"$MYNETOPEN: 0,2000"或" ERROR: 912" ,则进入下一步。该指令等\r
+\r
+- 待返回"$MYNETOPEN: 0,2000"或" ERROR: 912"的超时时间为30s。\r
+- 若返回"ERROR: 901"则重新设置参数进行PPP拨号。\r
+- 若超时未收到回码或收到其他错误回码,则继续开启,最多3次。若重\r
+\r
+复开启3次后,仍未收到"$MYNETOPEN: 0,2000"或" ERROR: 912"则重启模\r
+块。\r
+\r
+6. 发送数据\r
+- 若返回"$MYNETWRITE: X,<Length>",则延时50-100ms,将数据发送到模块串\r
+\r
+- 口,等待数据发送成功的回码"OK"。若返回"OK",则进入下一步。\r
+- 若返回"ERROR: 901",则返回重新进行PPP拨号。\r
+- 若返回"ERROR: 913",则重新建立TCP连接。\r
+- 若返回其他错误回码、或者未返回任何回码,则继续重发,最多3次。\r
+\r
+若重发3次仍未收到"OK",则重启模块。\r
+\r
+8. 读取数据\r
+- 若返回"$MYNETREAD: X,0",则可进入下一步:继续发送数据或者接收\r
+\r
+- 数据。若回码中"<data_Length>"字段不为0,则继续读取。\r
+- 若返回"ERROR: 901",则重新PPP拨号。\r
+- 若返回"ERROR: 913",则重建TCP连接。\r
+- 若返回其他错误回码、或者未返回任何回码,则继续读取,最多3次。\r
+\r
+若重读3次仍返回其他错误回码、或者未返回任何回码,则重启模块。\r
+\r
+9. 去附着网络\r
+- 若返回"OK"或者" ERROR",则进入下一步,等返回的超时时间为15s。\r
+- 若超时未收到回码或者收到其他错误回码,则继续执行,最多3次,若\r
+\r
+重复关闭3次后,仍未收到"OK"或者" ERROR"则重启模块。\r
+\r
+7.AT$MYNETACK=X\r
+\r
+"$MYNETREAD: X,0" ?\r
+\r
+7. 查询数据发送情况\r
+- 若返回"$MYNETACK: X,<unAcked_dataLen>,<rest_bufferLen>"且\r
+\r
+<unAcked_dataLen>值为0,则进入下一步:继续发送数据或者接收数\r
+据。\r
+• 若返回"ERROR: 901",则重新PPP拨号。\r
+• 若返回"ERROR: 913",则重新建立TCP连接。\r
+• 若回码中"<unAcked_dataLen>"字段不为0,则继续查询,1s/次,最多5\r
+\r
+次。若查询5次,"<unAcked_dataLen>"字段不为0,则重新建立TCP连\r
+接。\r
+• 若返回其他错误回码、或者未返回任何回码,则继续重发,最多3次。\r
+\r
+若重发3次仍返回其他错误回码、或者未返回任何回码,则重启模块。\r
+\r
+8. AT$MYNETREAD=X,<data_len>\r
+\r
+关闭<3次?\r
+\r
+读取<3次?\r
+\r
+去附着<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+Yes\r
+\r
+Yes\r
+\r
+No\r
+\r
+2. 查询PPP连接建立情况\r
+\r
+- 若回码中包含"$MYNETACT: 0,1"字段,则进入下一步。\r
+- 若回码中包含"$MYNETACT: 0,0"则重新设置参数进行PPP拨号。\r
+- 若返回其他错误回码,则循环查询,1s/次,最多3次。若查询3次后仍\r
+\r
+未返回"$MYNETACT: 0,1",则重新设置参数进行PPP拨号。\r
+• 若未返回任何回码,则继续查询,1s/次,最多3次。若查询继续3次\r
+\r
+后,仍未返回任何回码,则重启模块。\r
+\r
+3. 关闭待用Socket\r
+- 若返回"$MYNETCLOSE: X"或"ERROR: 913",则进入下一步。该指令等待返\r
+\r
+- 回"$MYNETCLOSE: X"或"ERROR: 913"的超时时间为30s。\r
+- 若返回"ERROR: 901"则重新设置参数进行PPP拨号。\r
+- 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次。若重\r
+\r
+复关闭3次后,仍未收到"$MYNETCLOSE: X"或"ERROR: 913"则重启模块。\r
+\r
+"$MYNETACK: X,0" ?\r
+\r
+$MYURCREAD: X\r
+\r
+No\r
+查询<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+查询<5次?\r
+\r
+Yes\r
+No\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+29\r
+\r
+###### 4.1.1 异常处理\r
+\r
+###### 模块附着PS 域失败\r
+\r
+1. \r
+检查模块串口通信是否正常。\r
+\r
+若串口不通,可参考以下方式排查解决:\r
+\r
+− \r
+确保模块已经开机:若模块不开机需要检查电源设计和开机时序\r
+\r
+− \r
+确保串口电平变换正常:若电平变换不正常需要检查串口电路设计和器件选型\r
+\r
+− \r
+确保软件上串口配置正常,软件处理是否有误。比如自适应逻辑、是否带回车换行发送AT\r
+\r
+2. \r
+检查模块是否已经识别到SIM 卡。\r
+\r
+若模块无法识别SIM 卡,可参考以下方式排查解决(涉及到的AT 命令,请参考功能 AT 指令\r
+\r
+集):\r
+\r
+− \r
+确保SIM 卡是否插好、SIM 卡本身完好无损、与卡座接触是否良好\r
+\r
+− \r
+确保SIM 卡座相关电路无设计问题,是否有虚假焊等\r
+\r
+− \r
+确保SIM 卡没有被锁卡\r
+\r
+若通过以上排查模块仍无法识别到SIM 卡,可以联系运营商处理或者更换SIM 卡解决。\r
+\r
+3. \r
+查询当前模块的接收信号是否满足通信条件。\r
+\r
+我们建议信号强度值为12(-83dbm)再进行通信。若现场信号弱,可通过以下方式排查:\r
+\r
+− \r
+确保天线接好、天线本身完好无损(断路、短路)\r
+\r
+− \r
+确认模块注册的网络制式(AT$MYSYSINFO)等信息,若为现场网络环境问题,则联系运营\r
+\r
+商增强网络覆盖,或者更换其他运营商SIM 卡解决。\r
+\r
+4. \r
+若现场信号良好,模块能识别到SIM 卡且SIM 卡未欠费,但模块不能附着上PS 域,则需要联系\r
+\r
+运营商确定SIM 卡配置:\r
+\r
+− \r
+SIM 卡是否开通数据业务\r
+\r
+− \r
+SIM 卡配置鉴权参数是否正常\r
+\r
+− \r
+SIM 卡是否有绑定设备\r
+\r
+− \r
+SIM 卡数据业务相关的APN、用户名、密码、鉴权方式是否正常\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+30\r
+\r
+###### 模块激活PDP 失败\r
+\r
+若模块正常注册上PS 域,但是无法激活PPP 连接。可以参考以下方式排查解决(涉及到的AT 命\r
+\r
+令,请参考功能 AT 指令集):\r
+\r
+⚫ \r
+确保SIM 未欠费。\r
+\r
+⚫ \r
+使用AT$MYNETCON?查询APN、用户名、密码,确保这些参数与运营商提供的一致。\r
+\r
+若以上参数都没有问题,模块仍无法激活PPP 连接,请联系当地运营商协助检查SIM 卡和后台服\r
+\r
+务器参数配置。\r
+\r
+###### 模块建立TCP 非透传客户端连接失败\r
+\r
+若模块正常获取到IP,但是建立TCP 非透传客户端连接。可以参考以下方式排查解决(涉及到的\r
+\r
+AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认服务器IP 和端口都已配置好,端口已经建立服务器端侦听,IP 能被客户端PING 通。\r
+\r
+可以通过用其他客户端设备来连接该服务器IP 和端口确认。\r
+\r
+2. \r
+确认模块获取到的IP 是否能访问服务器。\r
+\r
+可通过发送AT+PING=<IP/域名>给模块,操作模块PING 服务器来确认。\r
+\r
+若模块能PING 通服务器,则需要检查服务器软件和端口配置。\r
+\r
+若模块不能PING 通服务器,则需要检查APN、用户名、密码设置是否正确,SIM 卡配置是否正\r
+\r
+确。\r
+\r
+3. \r
+确认SIM 卡配置是否有问题。\r
+\r
+可以直接联系运营商确认SIM 卡是否可以访问目的IP 或者让运营商直接跟踪后台消息排查原因。\r
+\r
+###### 模块发送TCP 数据失败\r
+\r
+若模块正常建立TCP 非透传客户端连接,但是发送TCP 数据失败。可以参考以下方式排查解决\r
+\r
+(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认是否是服务器问题。\r
+\r
+可通过更换服务器对比确认是否是服务器配置问题。\r
+\r
+2. \r
+确认SIM 卡是否也有欠费。\r
+\r
+可联系运营商确认。\r
+\r
+3. \r
+确认是否是长时间没有发送数据导致的假连接。\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+31\r
+\r
+可通过缩短心跳周期或者是提高数据发送频率来确认。\r
+\r
+若缩短心跳周期或者是提高数据发送频率后,数据发送正常则可能是假连接引起的问题。\r
+\r
+4. \r
+确认TCP 连接是否还存在。\r
+\r
+可通过”AT$MYNETOPEN?”命令的返回值来确认。\r
+\r
+若返回值中含有“$MYNETOPEN:”字符串,则表示TCP 连接存在;若返回字段只有OK,则表示\r
+\r
+TCP 连接不存在。\r
+\r
+5. \r
+确认终端(或主控)是否已经将数据发送到模块。\r
+\r
+可以通过模块串口与终端(或主控)串口之间交互的AT 命令和数据内容来确认。\r
+\r
+为此,我司建议主控具备保存或打印模块与终端串口交互log 功能。只要AT$MYNETWRITE 命\r
+\r
+令返回“OK”就表示主控数据已经发送到模块。\r
+\r
+6. \r
+确认模块底层协议栈是否收到服务器端TCP 协议层的ACK。\r
+\r
+可通过”AT$MYNETACK =<SocketID>”命令的返回值来确认。\r
+\r
+若返回值中<unAcked_dataLen>字段数值为0,则表示数据已经发送到服务器且服务器已经确\r
+\r
+认。\r
+\r
+若返回值中<unAcked_dataLen>字段数值不为0,则有可能是数据没有发送到服务器,或者是服\r
+\r
+务器接收有延时还未收到数据,需要在服务器和模块同时抓取TCP 层数据来排查,可以联系我司\r
+\r
+FAE 跟进排查。\r
+\r
+###### 模块接收TCP 数据失败\r
+\r
+若模块正常建立TCP 非透传客户端连接,但是接收TCP 数据失败。可以参考以下方式排查解决\r
+\r
+(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认是否是服务器问题。\r
+\r
+可通过更换服务器对比确认是否是服务器配置问题。\r
+\r
+2. \r
+确认SIM 卡是否也有欠费。\r
+\r
+可联系运营商确认。\r
+\r
+3. \r
+确认TCP 连接是否还存在。\r
+\r
+可通过”AT$MYNETOPEN?”命令的返回值来确认。\r
+\r
+若返回值中含有“$MYNETOPEN:”字符串,则表示TCP 连接存在。\r
+\r
+若返回字段只有OK,则表示TCP 连接不存在。\r
+\r
+4. \r
+确认是否是长时间没有发送数据导致的假连接。\r
+\r
+可通过缩短心跳周期或者是提高数据发送频率来确认。\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+32\r
+\r
+若缩短心跳周期或者是提高数据发送频率后,数据接收正常则可能是假连接引起的问题。\r
+\r
+若缩短心跳或提高数据发送频率仍无法确认接收不到数据的原因,需要在服务器和模块同时抓取\r
+\r
+TCP 层数据来排查,可以联系我司FAE 跟进排查。\r
+\r
+###### 4.1.2 示例\r
+\r
+1. \r
+设置APN、用户名、密码,检查并确保模块已成功注册上PS 域\r
+\r
+AT \r
+//确认模块能正常响应AT 命令 \r
+OK \r
+AT+CGSN \r
++CGSN: "865324037375316" \r
+OK \r
+AT+CGMR \r
++CGMR: N720_DAC0CM_DL_V004\r
+\r
+OK \r
+AT+CPIN? \r
++CPIN: READY \r
+//确认模块能识别到SIM 卡\r
+\r
+OK \r
+AT+CIMI \r
+460016657520885\r
+\r
+OK \r
+AT$MYNETCON=0,"APN","CMNET" \r
+ \r
+ \r
+//设置APN \r
+OK\r
+\r
+AT$MYNETCON=0,"USERPWD","CARD,CARD" \r
+ \r
+//设置用户名和密码 \r
+OK \r
+AT$MYNETCON=0,"AUTH",1 \r
+ \r
+ \r
+ \r
+//设置鉴权方式 \r
+OK \r
+AT+CSQ \r
++CSQ: 31, 99 \r
+//确认信号良好 \r
+OK \r
+AT+CREG=2 \r
+OK \r
+AT+CREG?\r
+\r
++CREG: 2,1,"FFFE","61C7E47",7 //确认模块注册上CS 域\r
+\r
+OK \r
+AT+CREG=0 \r
+OK \r
+AT+CGATT? \r
++CGATT: 1 \r
+//确认模块附着上PS 域 \r
+OK \r
+AT$MYSYSINFO\r
+\r
+$MYSYSINFO: 4,02 \r
+//确认模块注册的网络制式\r
+\r
+OK\r
+\r
+2. \r
+激活网络连接\r
+\r
+AT$MYNETURC=1 \r
+OK \r
+AT$MYNETACT=0,1 \r
+ \r
+ \r
+ \r
+ \r
+//激活PPP 连接 \r
+OK \r
+$MYURCACT: 0,1,"10.51.228.13" \r
+AT$MYNETACT? \r
+ \r
+ \r
+ \r
+ \r
+//查询PPP 连接是否正常\r
+\r
+$MYNETACT: 0,1,"10.51.228.13" \r
+OK\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+33\r
+\r
+3. \r
+建立TCP 非透传客户端连接并收发数据\r
+\r
+AT$MYNETCLOSE=0 \r
+ERROR: 913 \r
+AT$MYNETSRV=0,0,0,0,"183.239.24.40:5002" //使用域名或者IP 均可以建立连接 \r
+OK \r
+AT$MYNETOPEN=0 \r
+ \r
+ \r
+ \r
+//建立TCP 连接\r
+\r
+$MYNETOPEN: 0,2000 \r
+OK \r
+AT$MYNETWRITE=0,6 \r
+ \r
+ \r
+//发送数据 \r
+ \r
+$MYNETWRITE: 0,6 \r
+OK \r
+AT$MYNETREAD=0,1 \r
+$MYNETREAD: 0,0 \r
+OK \r
+$MYURCREAD: 0 \r
+ \r
+ \r
+ \r
+//接收到服务器发送的数据\r
+\r
+AT$MYNETREAD=0,2048 \r
+ \r
+ \r
+//读取数据 \r
+$MYNETREAD: 0,10 \r
+1234567890 \r
+OK\r
+\r
+AT$MYNETCLOSE=0 \r
+$MYNETCLOSE: 0 \r
+OK\r
+\r
+AT+CGATT=0 \r
+OK\r
+\r
+$MYURCACT: 0,0\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+34\r
+\r
+##### 4.2 TCP 非透传服务器端\r
+\r
+本节主要实现TCP 非透传服务端功能相关的AT 指令、AT 流程、异常处理以及简单示例。在进行\r
+\r
+PPP 拨号前,请确保模块已经过初始化(具体初始化流程参考第2 章)。\r
+\r
+图 4-2 连接流程\r
+\r
+设置PPP拨号参数\r
+\r
+"$MYURCACT: 0,1"?\r
+\r
+1. AT$MYNETACT=0,1\r
+\r
+"$MYNETCLOSE: X"\r
+\r
+or "ERROR: 913"?\r
+\r
+3. AT$MYNETCLOSE=X\r
+\r
+"OK" ?\r
+\r
+4. AT$MYNETSRV=0,X,1,0,"127.0.0.1:Port"\r
+\r
+Yes\r
+\r
+5. AT$MYNETOPEN=X\r
+\r
+"$MYNETOPEN: X" or" ERROR: 912" ?\r
+\r
+6.AT$MYNETACCEPT=Y,0,0\r
+\r
+"OK" ?\r
+\r
+$MYURCREAD: Y\r
+\r
+"OK" or "ERROR"?\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+重新开启<3次?\r
+\r
+查询<3次?\r
+\r
+关机\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+模块重启\r
+模块初始化流程\r
+\r
+7. AT$MYNETWRITE=Y,<Length>\r
+\r
+"$MYNETWRITE: Y,<Length>" ?\r
+\r
+输入要发送的数据\r
+\r
+"OK"?\r
+\r
+No\r
+\r
+去附着<3次?\r
+\r
+重发<3次?\r
+\r
+重发<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+No\r
+Yes\r
+\r
+No\r
+Yes\r
+No\r
+\r
+1. 进行PPP拨号\r
+- 若返回"OK",则可进入下一步。\r
+- 若未收到任何回码或收到其他错误回码,则继续拨号,1s/次,最多\r
+\r
+3次。若重新拨号3次后,仍未若返回"OK ",则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+4. 设置非透明传输服务参数\r
+- 若返回"OK",则进入下一步;\r
+- 若未收到回码或收到其他错误回码,则继续设置,最多3次。若重\r
+\r
+复设置3次后,仍未收到"OK",则重启模块。\r
+注:信道号必须与已激活的信道号一致,第三个参数必须设置为1;\r
+\r
+5. 开启服务器服务\r
+\r
+• 若返回"$MYNETOPEN: X"或" ERROR: 912" ,则进入下一步。该指令等\r
+\r
+- 待返回"$MYNETOPEN: X"或" ERROR: 912"的超时时间为30s。\r
+- 若返回"ERROR: 901"则重新设置参数进行PPP拨号。\r
+- 若超时未收到回码或收到其他错误回码,则继续开启,最多3次。\r
+\r
+若重复开启3次后,仍未收到"$MYNETOPEN: X"或" ERROR: 912"则\r
+重启模块。\r
+\r
+等待接收客户端连接请求\r
+需要设置等待客户端连接的超时时间,若超时时间到,没有收到任何\r
+\r
+客户端连接请求,则主动重新拨号建立侦听。\r
+\r
+7. 发送数据\r
+- 若返回"$MYNETWRITE: Y,<Length>",则延时50-100ms,将数据发送到\r
+\r
+模块串口,等待数据发送成功的回码"OK"。若返回"OK",则进入下\r
+一步。\r
+• 若返回"ERROR: 901",则返回重新进行PPP拨号。\r
+• 若返回"ERROR: 913",则等待客户端连接。\r
+• 若返回其他错误回码、或者未返回任何回码,则继续重发,最多3\r
+\r
+次。若重发3次仍未收到"OK",则重启模块。\r
+\r
+11. 去附着网络\r
+- 若返回"OK"或者" ERROR",则进入下一步,等返回的超时时间为\r
+\r
+15s。\r
+• 若超时未收到回码或者收到其他错误回码,则继续执行,最多3\r
+\r
+次,若重复关闭3次后,仍未收到"OK"或者" ERROR"则重启模块。\r
+\r
+8. AT$MYNETACK=Y\r
+\r
+"$MYNETACK: Y,0" ?\r
+\r
+- 6.接受客户端的连接请求\r
+- 若返回"OK"则进入下一步;\r
+- 若超时未收到回码或者收到其他错误回码,则继续去附着,最多3\r
+\r
+次。若重复关闭3次后,仍未收到"OK"则重启模块。\r
+\r
+关闭<3次?\r
+\r
+设置<3次?\r
+\r
+查询<3次?\r
+\r
+去附着<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+Yes\r
+\r
+Yes\r
+\r
+No\r
+No\r
+\r
+2. 查询PPP连接建立情况\r
+- 若回码中包含"+XIIC:    1 ",则可进入下一步。\r
+- 若回码未包含"+XIIC:    1",则循环查询,1s/次,最多30次。若30次\r
+\r
+后仍未返回"+XIIC: 1",则重新拨号,最多重拨3次,若重新拨号3次\r
+后,仍未获取到IP,则重启模块。\r
+\r
+3. 关闭侦听\r
+- 若返回"$MYNETCLOSE: X"或"ERROR: 913",则进入下一步。该指令等\r
+\r
+- 待返回"$MYNETCLOSE: X"或"ERROR: 913"的超时时间为30s。\r
+- 若返回"ERROR: 901"则重新设置参数进行PPP拨号。\r
+- 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次。\r
+\r
+若重复关闭3次后,仍未收到"$MYNETCLOSE: X"或"ERROR: 913"则重\r
+启模块。\r
+\r
+$MYURCCLIENT: Y,"10.91.215.190",63342\r
+\r
+8. 查询数据发送情况\r
+- 若回码包含"$MYNETACK: Y,0,",则进入下一步:继续发送或者接收\r
+\r
+- 数据。\r
+- 若返回"ERROR: 901",则返回重新PPP拨号。\r
+- 若返回"ERROR: 913",则等待客户端连接。\r
+- 若回码中"<unAcked_dataLen>"字段不为0,则继续查询,1s/次,最\r
+\r
+多5次;若查询5次,"<unAcked_dataLen>"字段不为0,则返回关闭\r
+当前连接,重新建立连接。\r
+• 若返回其他错误回码、或者未返回任何回码,则继续重发,最多3\r
+\r
+次。若重发3次仍返回其他错误回码、或者未返回任何回码,则重\r
+启模块。\r
+\r
+9. AT$MYNETREAD=Y,<data_len>\r
+\r
+"$MYNETREAD: Y,0" ?\r
+\r
+10. AT$MYNETCLOSE=X\r
+\r
+"$MYNETCLOSE: X"\r
+\r
+or "ERROR: 913"?\r
+\r
+11. AT+CGATT=0\r
+\r
+读取<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+\r
+关闭侦听<3次?\r
+\r
+"$MYNETCLOSE: X"\r
+\r
+or "ERROR: 913"?\r
+\r
+2. AT$MYNETACT?\r
+\r
+查询<3次?\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+9. 读取数据\r
+- 若返回"$MYNETREAD: Y,0",则可进入下一步:继续发送或者接收数\r
+\r
+据。若回码中"<unAcked_dataLen>"字段不为0,则继续读取。\r
+• 若返回"ERROR: 901",则重新PPP拨号;返回"ERROR: 913",则等待客\r
+\r
+户端连接。\r
+• 若返回其他错误回码、或者未返回任何回码,则继续读取,最多3\r
+\r
+次。若重读3次仍返回其他错误回码、或者未返回任何回码,则重\r
+启模块。\r
+\r
+10. 关闭侦听\r
+- 若返回"$MYNETCLOSE: X"或"ERROR: 913",则进入下一步。该指令等\r
+\r
+待返回"$MYNETCLOSE: X"或"ERROR: 913"的超时时间为30s。\r
+• 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次。\r
+\r
+若重复关闭3次后,仍未收到"$MYNETCLOSE: X"或"ERROR: 913"则重\r
+启模块。\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+No\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+No\r
+\r
+Yes\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+35\r
+\r
+###### 4.2.1 异常处理\r
+\r
+###### 模块附着PS 域失败\r
+\r
+1. \r
+检查模块串口通信是否正常。若串口不通,可参考以下方式排查解决:\r
+\r
+− \r
+确保模块已经开机:若模块不开机需要检查电源设计和开机时序\r
+\r
+− \r
+确保串口电平变换正常:若电平变换不正常需要检查串口电路设计和器件选型\r
+\r
+− \r
+确保软件上串口配置正常,软件处理是否有误。比如自适应逻辑、是否带回车换行发送AT\r
+\r
+2. \r
+检查模块是否已经识别到SIM 卡。若模块无法识别SIM 卡,可参考以下方式排查解决(涉及到的\r
+\r
+AT 命令,请参考功能 AT 指令集):\r
+\r
+− \r
+确保SIM 卡是否插好、SIM 卡本身完好无损、与卡座接触是否良好\r
+\r
+− \r
+确保SIM 卡座相关电路无设计问题,是否有虚假焊等\r
+\r
+− \r
+确保SIM 卡没有被锁卡\r
+\r
+若通过以上排查模块仍无法识别到SIM 卡,可以联系运营商处理或者更换SIM 卡解决。\r
+\r
+3. \r
+查询当前模块的接收信号是否满足通信条件。我们建议信号强度值为12(-83dbm)再进行通信。\r
+\r
+若现场信号弱,可通过以下方式排查:\r
+\r
+− \r
+确保天线接好、天线本身完好无损(断路、短路)\r
+\r
+− \r
+确认模块注册的网络制式(AT$MYSYSINFO)等信息。\r
+\r
+若为现场网络环境问题,则联系运营商增强网络覆盖,或者更换其他运营商SIM 卡解决。\r
+\r
+4. \r
+若现场信号良好,模块能识别到SIM 卡且SIM 卡未欠费,但模块不能附着上PS 域,则需要联系\r
+\r
+运营商确定SIM 卡配置:\r
+\r
+− \r
+SIM 卡是否开通数据业务\r
+\r
+− \r
+SIM 卡配置鉴权参数是否正常\r
+\r
+− \r
+SIM 卡是否有绑定设备\r
+\r
+− \r
+SIM 卡数据业务相关的APN、用户名、密码、鉴权方式是否正常\r
+\r
+###### 模块激活PDP 失败\r
+\r
+若模块正常注册上PS 域,但是无法激活PPP 连接。可以参考以下方式排查解决(涉及到的AT 命\r
+\r
+令,请参考功能 AT 指令集):\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+36\r
+\r
+⚫ \r
+确保SIM 未欠费.\r
+\r
+⚫ \r
+使用AT+$MYNETCON?查询APN、用户名、密码,确保这些参数与运营商提供的一致。\r
+\r
+若以上参数都没有问题,模块仍无法激活PPP 连接,请联系当地运营商协助检查SIM 卡和后台服\r
+\r
+务器参数配置。\r
+\r
+###### 客户端无法连接服务器\r
+\r
+若模块正常建立服务器端侦听后,客户端无法连接上服务器。可以参考以下方式排查解决(涉及到\r
+\r
+的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认服务器IP 和端口都已配置好,端口已经建立服务器端侦听,IP 能被客户端PING 通。\r
+\r
+可以通过用其他客户端设备来连接该服务器IP 和端口确认。\r
+\r
+2. \r
+确认模块获取到的IP 与客户端的IP 是否在同一个网段内。\r
+\r
+可通过在PC 上PING 模块来确认。\r
+\r
+− \r
+若PC 客户端能PING 通服务器,则需要检查服务器软件和端口配置。\r
+\r
+− \r
+若PC 客户端不能PING 通服务器,则需要检查APN、用户名、密码设置是否正确,SIM 卡\r
+\r
+配置是否正确。\r
+\r
+3. \r
+确认SIM 卡配置是否有问题。\r
+\r
+可以直接联系运营商客户端是否可以访问SIM 卡的IP 或者让运营商直接跟踪后台消息排查原因。\r
+\r
+###### 模块发送TCP 数据失败\r
+\r
+若模块正常建立TCP 非透传客户端连接,但是发送TCP 数据失败。可以参考以下方式排查解决\r
+\r
+(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认是否是服务器问题。\r
+\r
+可通过更换服务器对比确认是否是服务器配置问题。\r
+\r
+2. \r
+确认SIM 卡是否也有欠费。\r
+\r
+可联系运营商确认。\r
+\r
+3. \r
+确认是否是长时间没有发送数据导致的假连接。\r
+\r
+可通过缩短心跳周期或者是提高数据发送频率来确认。\r
+\r
+若缩短心跳周期或者是提高数据发送频率后,数据发送正常则可能是假连接引起的问题。\r
+\r
+4. \r
+确认TCP 连接是否还存在。\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+37\r
+\r
+可通过”AT$MYNETOPEN?”命令的返回值来确认.\r
+\r
+− \r
+若返回值中含有“$MYNETOPEN:”字符串,则表示TCP 连接存在。\r
+\r
+− \r
+若返回字段只有OK,则表示TCP 连接不存在。\r
+\r
+5. \r
+确认终端(或主控)是否已经将数据发送到模块。\r
+\r
+可以通过模块串口与终端(或主控)串口之间交互的AT 命令和数据内容来确认。\r
+\r
+为此,我司建议主控具备保存或打印模块与终端串口交互log 功能。只要AT$MYNETWRITE 命\r
+\r
+令返回“OK”就表示主控数据已经发送到模块。\r
+\r
+6. \r
+确认模块底层协议栈是否收到服务器端TCP 协议层的ACK。\r
+\r
+可通过”AT$MYNETACK =<SocketID>”命令的返回值来确认。\r
+\r
+− \r
+若返回值中<unAcked_dataLen>字段数值为0,则表示数据已经发送到服务器且服务器已经\r
+\r
+确认。\r
+\r
+− \r
+若返回值中<unAcked_dataLen>字段数值不为0,则有可能是数据没有发送到服务器,或者\r
+\r
+是服务器接收有延时还未收到数据,需要在服务器和模块同时抓取TCP 层数据来排查,可以\r
+\r
+联系我司FAE 跟进排查。\r
+\r
+###### 4.2.2 示例\r
+\r
+1. \r
+设置APN、用户名、密码,检查并确保模块已成功注册上PS 域\r
+\r
+AT \r
+//确认模块能正常响应AT 命令 \r
+OK \r
+AT+CGSN \r
++CGSN: "865324037375316" \r
+OK\r
+\r
+AT+CGMR \r
++CGMR: N720_DAC0CM_DL_V004 \r
+OK \r
+AT+CPIN? \r
++CPIN: READY \r
+//确认模块能识别到SIM 卡 \r
+OK \r
+AT+CIMI\r
+\r
+460016657520885 \r
+OK\r
+\r
+AT$MYNETCON=0,"APN","CMNET" \r
+ \r
+ \r
+//设置APN \r
+OK \r
+AT$MYNETCON=0,"USERPWD","CARD,CARD" \r
+ \r
+//设置用户名和密码 \r
+OK \r
+AT$MYNETCON=0,"AUTH",1 \r
+ \r
+ \r
+ \r
+//设置鉴权方式 \r
+OK \r
+AT+CSQ \r
++CSQ: 31, 99 \r
+//确认信号良好 \r
+OK \r
+AT+CREG=2\r
+\r
+OK \r
+AT+CREG? \r
++CREG: 2,1,"FFFE","61C7E47",7 //确认模块注册上CS 域\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+38\r
+\r
+OK \r
+AT+CREG=0 \r
+OK \r
+AT+CGATT? \r
++CGATT: 1 \r
+//确认模块附着上PS 域 \r
+OK \r
+AT$MYSYSINFO \r
+$MYSYSINFO: 4,02 \r
+//确认模块注册的网络制式\r
+\r
+OK\r
+\r
+2. \r
+激活网络连接\r
+\r
+AT$MYNETURC=1 \r
+OK \r
+AT$MYNETACT=0,1 \r
+ \r
+ \r
+ \r
+ \r
+//激活PPP 连接 \r
+OK \r
+$MYURCACT: 0,1,"10.51.228.13"\r
+\r
+AT$MYNETACT? \r
+ \r
+ \r
+ \r
+ \r
+//查询PPP 连接是否正常 \r
+$MYNETACT: 0,1,"10.51.228.13" \r
+OK\r
+\r
+3. \r
+建立TCP 非透传服务器端侦听并与客户端建立连接、收发数据\r
+\r
+AT$MYNETCLOSE=0 \r
+ERROR: 913 \r
+AT$MYNETSRV=0,0,1,0,"127.0.0.1:6800"\r
+\r
+OK\r
+\r
+AT$MYNETOPEN=0 \r
+$MYNETOPEN: 0 \r
+OK \r
+$MYURCCLIENT: 1,"112.97.54.136",14759 \r
+AT$MYNETACCEPT=1,0,0 \r
+OK \r
+AT$MYNETWRITE=1,10 \r
+$MYNETWRITE: 0,10 \r
+ 输入要发送的数据\r
+\r
+OK\r
+\r
+$MYURCREAD: 1 \r
+AT$MYNETREAD=1,2048 \r
+$MYNETREAD: 1,6\r
+\r
+Neoway \r
+OK\r
+\r
+AT$MYNETACK=1 \r
+$MYNETACK: 1,0,10240 \r
+OK\r
+\r
+AT$MYNETCLOSE=X\r
+\r
+OK \r
+$MYURCCLOSE: 1 \r
+$MYURCCLOSE: 0 \r
+AT+CGATT=0 \r
+OK \r
+$MYURCACT: 0,0\r
+\r
+##### 4.3 TCP 透传客户端\r
+\r
+本节主要实现TCP 透传客户端功能相关的AT 指令、AT 流程、异常处理以及简单示例。在进行PPP\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+39\r
+\r
+拨号前,请确保模块已经过初始化(具体初始化流程参考第2 章)。\r
+\r
+图 4-3 连接流程\r
+\r
+设置PPP拨号参数\r
+\r
+"$MYURCACT: 0,1"?\r
+\r
+1. AT$MYNETACT=0,1\r
+\r
+"$MYNETACT: 0,1"?\r
+\r
+2. AT$MYNETACT?\r
+\r
+"$MYNETCLOSE: X"\r
+\r
+or "ERROR: 913"?\r
+\r
+3. AT$MYNETCLOSE=X\r
+\r
+Yes\r
+\r
+4. AT$MYNETCREATE=0,0,0,"IP",Port\r
+\r
+"CONNECT"?\r
+\r
+收发数据\r
+\r
+9. AT+CGATT=0\r
+\r
+"OK" or "ERROR"?\r
+\r
+No\r
+\r
+Yes\r
+重设<3次?\r
+\r
+拨号<3次?\r
+\r
+关机\r
+\r
+No\r
+\r
+No\r
+\r
+Yes\r
+\r
+模块重启\r
+模块初始化流程\r
+\r
+5. +++\r
+\r
+"OK" ?\r
+\r
+6. AT$MYNETACK=X\r
+\r
+"$MYNETACK: X,0"?\r
+\r
+No\r
+\r
+重发<3次?\r
+\r
+查询<3次?\r
+\r
+返回透传<3次?\r
+\r
+Yes\r
+No\r
+\r
+No\r
+Yes\r
+\r
+No\r
+Yes\r
+\r
+1. PPP拨号\r
+\r
+• 若返回"$MYURCACT: 0,1"或"ERROR: 902",则进入下一步。该指令等待返\r
+\r
+回"$MYURCACT: 0,1"或"ERROR: 902"的超时时间为30s。\r
+• 若未收到任何回码或收到其他错误回码,则继续拨号,最多3次。若重\r
+\r
+新拨号3次后,仍未若返回"OK ",则重启模块。\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+4. 建立透明TCP客户端连接\r
+- 若返回"CONNECT"或" ERROR: 912" ,则进入下一步。该指令等待返回\r
+\r
+- "CONNECT"或" ERROR: 912"的超时时间为30s。\r
+- 若返回"ERROR: 901"则重新设置参数进行PPP拨号。\r
+- 若超时未收到回码或收到其他错误回码,则继续开启,最多3次。若重\r
+\r
+复开启3次后,仍未收到"CONNECT"或" ERROR: 912"则重启模块。\r
+\r
+5. 退出透传\r
+- 若返回"OK",则进入下一步;\r
+- 若返回其他错误回码、或者未返回任何回码,则继续重发,最多3次。若\r
+\r
+重发3次仍未收到"OK",则重启模块。\r
+• 注:"+++"必须以字符串的形式发送到串口,且"+++"不必带回车发送\r
+\r
+8. 关闭待用Socket\r
+- 若返回"$MYNETCLOSE: X"或"ERROR: 913",则进入下一步。该指令等待返\r
+\r
+- 回"$MYNETCLOSE: X"或"ERROR: 913"的超时时间为30s。\r
+- 若返回"ERROR: 901"则重新设置参数进行PPP拨号。\r
+- 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次。若重\r
+\r
+复关闭3次后,仍未收到"$MYNETCLOSE: X"或"ERROR: 913"则重启模块。\r
+\r
+9. 去附着网络\r
+- 若返回"OK"或者" ERROR",则进入下一步,等返回的超时时间为15s。\r
+- 若超时未收到回码或者收到其他错误回码,则继续执行,最多3次,若\r
+\r
+重复关闭3次后,仍未收到"OK"或者" ERROR"则重启模块。\r
+\r
+7.ATO\r
+\r
+"$MYNETCLOSE: X"\r
+\r
+or "ERROR: 913"?\r
+\r
+7. 返回透传\r
+\r
+- 若返回"CONNECT",则进入下一步:继续发送数据。\r
+- 若返回其他错误回码、或者未返回任何回码,则继续重发,最多3次。\r
+\r
+若重发3次仍未收到"OK",则重启模块。\r
+\r
+关闭<3次?\r
+\r
+关闭<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+No\r
+Yes\r
+\r
+2. 查询PPP连接建立情况\r
+\r
+- 若回码中包含"$MYNETACT: 0,1"字段,则进入下一步。\r
+- 若回码中包含"$MYNETACT: 0,0"则重新设置参数进行PPP拨号。\r
+- 若返回其他错误回码,则循环查询,1s/次,最多3次。若查询3次后仍\r
+\r
+未返回"$MYNETACT: 0,1",则重新设置参数进行PPP拨号。\r
+• 若未返回任何回码,则继续查询,1s/次,最多3次。若查询继续3次\r
+\r
+后,仍未返回任何回码,则重启模块。\r
+\r
+3. 关闭待用Socket\r
+- 若返回"$MYNETCLOSE: X"或"ERROR: 913",则进入下一步。该指令等待返\r
+\r
+- 回"$MYNETCLOSE: X"或"ERROR: 913"的超时时间为30s。\r
+- 若返回"ERROR: 901"则重新设置参数进行PPP拨号。\r
+- 若超时未收到回码或收到其他错误回码,则继续关闭,最多3次。若重\r
+\r
+复关闭3次后,仍未收到"$MYNETCLOSE: X"或"ERROR: 913"则重启模块。\r
+\r
+"CONNECT" ?\r
+\r
+8. AT$MYNETCLOSE=X\r
+\r
+No\r
+查询<3次?\r
+\r
+Yes\r
+\r
+No\r
+\r
+关闭<3次?\r
+\r
+Yes\r
+No\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+Yes\r
+\r
+6. 查询数据发送情况\r
+- 若返回"$MYNETACK: X,0,",则进入下一步:继续发送或者接收数据;\r
+- 若返回"ERROR: 901",则返回重新PPP拨号;\r
+- 若返回"ERROR: 913",则重新建立TCP连接;\r
+- 若回码中"<unAcked_dataLen>"字段不为0,则继续查询,1s/次,最多5\r
+\r
+次。若查询5次后,"<unAcked_dataLen>"字段不为0,则返回关闭当前连\r
+接,重新建立连接;\r
+• 若返回其他错误回码、或者未返回任何回码,则继续重发,最多3次。\r
+\r
+若重发3次仍返回其他错误回码、或者未返回任何回码,则重启模块。\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+No\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+40\r
+\r
+###### 4.3.1 异常处理\r
+\r
+###### 模块附着PS 域失败\r
+\r
+1. \r
+检查模块串口通信是否正常。若串口不通,可参考以下方式排查解决:\r
+\r
+− \r
+确保模块已经开机:若模块不开机需要检查电源设计和开机时序\r
+\r
+− \r
+确保串口电平变换正常:若电平变换不正常需要检查串口电路设计和器件选型\r
+\r
+− \r
+确保软件上串口配置正常,软件处理是否有误。比如自适应逻辑、是否带回车换行发送AT\r
+\r
+2. \r
+检查模块是否已经识别到SIM 卡。若模块无法识别SIM 卡,可参考以下方式排查解决(涉及到的\r
+\r
+AT 命令,请参考功能 AT 指令集):\r
+\r
+− \r
+确保SIM 卡是否插好、SIM 卡本身完好无损、与卡座接触是否良好\r
+\r
+− \r
+确保SIM 卡座相关电路无设计问题,是否有虚假焊等\r
+\r
+− \r
+确保SIM 卡没有被锁卡\r
+\r
+若通过以上排查模块仍无法识别到SIM 卡,可以联系运营商处理或者更换SIM 卡解决。\r
+\r
+3. \r
+查询当前模块的接收信号是否满足通信条件。我们建议信号强度值为12(-83dbm)再进行通信。\r
+\r
+若现场信号弱,可通过以下方式排查:\r
+\r
+− \r
+确保天线接好、天线本身完好无损(断路、短路)\r
+\r
+− \r
+确认模块注册的网络制式(AT$MYSYSINFO)等信息。\r
+\r
+若为现场网络环境问题,则联系运营商增强网络覆盖,或者更换其他运营商SIM 卡解决。\r
+\r
+4. \r
+若现场信号良好,模块能识别到SIM 卡且SIM 卡未欠费,但模块不能附着上PS 域,则需要联系\r
+\r
+运营商确定SIM 卡配置:\r
+\r
+− \r
+SIM 卡是否开通数据业务\r
+\r
+− \r
+SIM 卡配置鉴权参数是否正常\r
+\r
+− \r
+SIM 卡是否有绑定设备\r
+\r
+− \r
+SIM 卡数据业务相关的APN、用户名、密码、鉴权方式是否正常\r
+\r
+###### 模块激活PDP 失败\r
+\r
+若模块正常注册上PS 域,但是无法激活PPP 连接。可以参考以下方式排查解决(涉及到的AT 命\r
+\r
+令,请参考功能 AT 指令集):\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+41\r
+\r
+⚫ \r
+确保SIM 未欠费。\r
+\r
+⚫ \r
+使用AT+$MYNETCON?查询APN、用户名、密码,确保这些参数与运营商提供的一致。\r
+\r
+若以上参数都没有问题,模块仍无法激活PPP 连接,请联系当地运营商协助检查SIM 卡和后台服\r
+\r
+务器参数配置。\r
+\r
+###### 模块建立TCP 透传客户端连接失败\r
+\r
+若模块正常获取到IP,但是建立TCP 非透传客户端连接。可以参考以下方式排查解决(涉及到的\r
+\r
+AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认服务器IP 和端口都已配置好,端口已经建立服务器端侦听,IP 能被客户端PING 通。\r
+\r
+可以通过用其他客户端设备来连接该服务器IP 和端口确认。\r
+\r
+2. \r
+确认模块获取到的IP 是否能访问服务器。\r
+\r
+可通过发送AT+PING=<IP/域名>给模块,操作模块PING 服务器来确认。\r
+\r
+若模块能PING 通服务器,则需要检查服务器软件和端口配置。\r
+\r
+若模块不能PING 通服务器,则需要检查APN、用户名、密码设置是否正确,SIM 卡配置是否正\r
+\r
+确。\r
+\r
+3. \r
+确认SIM 卡配置是否有问题。\r
+\r
+可以直接联系运营商确认SIM 卡是否可以访问目的IP 或者让运营商直接跟踪后台消息排查原因。\r
+\r
+###### 模块发送TCP 数据失败\r
+\r
+若模块正常建立TCP 非透传客户端连接,但是发送TCP 数据失败。可以参考以下方式排查解决\r
+\r
+(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认是否是服务器问题。\r
+\r
+可通过更换服务器对比确认是否是服务器配置问题。\r
+\r
+2. \r
+确认SIM 卡是否也有欠费。\r
+\r
+可联系运营商确认。\r
+\r
+3. \r
+确认是否是长时间没有发送数据导致的假连接。\r
+\r
+可通过缩短心跳周期或者是提高数据发送频率来确认。\r
+\r
+若缩短心跳周期或者是提高数据发送频率后,数据发送正常则可能是假连接引起的问题。\r
+\r
+4. \r
+确认模块底层协议栈是否收到服务器端TCP 协议层的ACK。\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+42\r
+\r
+可通过”AT$MYNETACK =<SocketID>”命令的返回值来确认。\r
+\r
+− \r
+若返回值中<unAcked_dataLen>字段数值为0,则表示数据已经发送到服务器且服务器已经\r
+\r
+确认。\r
+\r
+− \r
+若返回值中<unAcked_dataLen>字段数值不为0,则有可能是数据没有发送到服务器,或者\r
+\r
+是服务器接收有延时还未收到数据,需要在服务器和模块同时抓取TCP 层数据来排查,可以\r
+\r
+联系我司FAE 跟进排查。\r
+\r
+###### 模块接收TCP 数据失败\r
+\r
+若模块正常建立TCP 非透传客户端连接,但是接收TCP 数据失败。可以参考以下方式排查解决\r
+\r
+(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+1. \r
+确认是否是服务器问题。\r
+\r
+可通过更换服务器对比确认是否是服务器配置问题。\r
+\r
+2. \r
+确认SIM 卡是否也有欠费。\r
+\r
+可联系运营商确认。\r
+\r
+3. \r
+确认TCP 连接是否还存在。\r
+\r
+可通过”AT$MYNETOPEN?”命令的返回值来确认。\r
+\r
+− \r
+若返回值中含有“$MYNETOPEN:”字符串,则表示TCP 连接存在。\r
+\r
+− \r
+若返回字段只有OK,则表示TCP 连接不存在。\r
+\r
+4. \r
+确认是否是长时间没有发送数据导致的假连接。\r
+\r
+可通过缩短心跳周期或者是提高数据发送频率来确认。\r
+\r
+− \r
+若缩短心跳周期或者是提高数据发送频率后,数据接收正常则可能是假连接引起的问题。\r
+\r
+− \r
+若缩短心跳或提高数据发送频率仍无法确认接收不到数据的原因,需要在服务器和模块同时\r
+\r
+抓取TCP 层数据来排查,可以联系我司FAE 跟进排查。\r
+\r
+###### 4.3.2 示例\r
+\r
+1. \r
+检查并确保模块已成功注册上PS 域\r
+\r
+AT \r
+//确认模块能正常响应AT 命令 \r
+OK \r
+AT+CGSN\r
+\r
++CGSN: "865324037375316" \r
+OK\r
+\r
+AT+CGMR \r
++CGMR: N720_DAC0CM_DL_V004 \r
+OK\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+43\r
+\r
+AT+CPIN? \r
++CPIN: READY \r
+//确认模块能识别到SIM 卡 \r
+OK \r
+AT+CIMI \r
+460016657520885 \r
+OK \r
+AT$MYNETCON=0,"APN","CMNET" \r
+ \r
+ \r
+//设置APN \r
+OK\r
+\r
+AT$MYNETCON=0,"USERPWD","CARD,CARD" \r
+ \r
+//设置用户名和密码 \r
+OK \r
+AT$MYNETCON=0,"AUTH",1 \r
+ \r
+ \r
+ \r
+//设置鉴权方式 \r
+OK \r
+AT+CSQ \r
++CSQ: 31, 99 \r
+//确认信号良好 \r
+OK \r
+AT+CREG=2 \r
+OK\r
+\r
+AT+CREG? \r
++CREG: 2,1,"FFFE","61C7E47",7 //确认模块注册上CS 域\r
+\r
+OK \r
+AT+CREG=0 \r
+OK\r
+\r
+AT+CGATT? \r
++CGATT: 1 \r
+//确认模块附着上PS 域 \r
+OK\r
+\r
+AT$MYSYSINFO\r
+\r
+$MYSYSINFO: 4,02 \r
+//确认模块注册的网络制式 \r
+OK\r
+\r
+2. \r
+激活网络连接\r
+\r
+AT$MYNETURC=1 \r
+OK \r
+AT$MYNETACT=0,1 \r
+ \r
+ \r
+ \r
+ \r
+//激活PPP 连接 \r
+OK \r
+$MYURCACT: 0,1,"10.51.228.13"\r
+\r
+AT$MYNETACT? \r
+ \r
+ \r
+ \r
+ \r
+//查询PPP 连接是否正常 \r
+$MYNETACT: 0,1,"10.51.228.13" \r
+OK\r
+\r
+3. \r
+建立TCP 非透传客户端连接并收发数据\r
+\r
+AT$MYNETCLOSE=0 \r
+ \r
+ERROR: 913\r
+\r
+AT$MYNETCREATE=0,0,0,"183.239.240.40",5008\r
+\r
+CONNECT \r
+ \r
+ \r
+//收发数据后,发送+++数据模式 \r
+OK \r
+AT$MYNETACK=0 \r
+$MYNETACK: 0,0,2047 \r
+OK \r
+ATO \r
+ \r
+ \r
+ \r
+ \r
+ \r
+//切换回数据模式 \r
+CONNECT\r
+\r
+OK \r
+AT$MYNETCLOSE=0 \r
+ \r
+ \r
+ \r
+//关闭链路\r
+\r
+功能模组TCP 应用指南 \r
+第4 章国网指令TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+44\r
+\r
+$MYNETCLOSE: 0 \r
+OK \r
+AT+CGATT=0 \r
+OK \r
+$MYURCACT: 0,0\r
+\r
+功能模组TCP 应用指南 \r
+第5 章外部协议栈TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+45\r
+\r
+#### 5 外部协议栈TCP 应用\r
+\r
+图 5-1 连接流程\r
+\r
+模块初始化流程\r
+\r
+"CONNECT"?\r
+\r
+ATD*99#\r
+\r
+进入外部协议栈成功\r
+\r
+进入外部协议栈\r
+• 若返回 CONNECT ,则模块进入外部协议栈模式;该指令\r
+\r
+等待返回"CONNECT"的超时时间为30s。\r
+• 若返回 NO CARRIER ,则继续执行ATD*99#,最多5次。若\r
+\r
+执行5次后仍未成功,则返回执行模块初始化流程查询网络\r
+注册情况。\r
+• 若超时未收到任何回码或者收到其他错误回码,则继续执行\r
+\r
+ATD*99#,最多3次。若执行3次后仍未成功,则重启模块。\r
+Yes\r
+\r
+No\r
+\r
+返回"NO CARRIER\r
+\r
+继续执行"\r
+\r
+其它情况\r
+重启模块\r
+\r
+##### 5.1 异常处理\r
+\r
+###### 模块附着PS 域失败\r
+\r
+使用AT+CPIN?检查模块是否已经识别到SIM 卡。若模块无法识别SIM 卡,可参考以下方式排查\r
+\r
+解决(涉及到的AT 命令,请参考功能 AT 指令集):\r
+\r
+− \r
+确保SIM 卡是否插好;\r
+\r
+− \r
+确保SIM 卡本身完好无损;\r
+\r
+− \r
+确保SIM 卡没有被锁卡;\r
+\r
+− \r
+确保SIM 卡卡座完好,不存在假焊;\r
+\r
+− \r
+擦拭SIM 卡及卡座的触点(该触点发生氧化、脏污也会产生读不到SIM 卡的现象),确保SI\r
+\r
+卡与模块卡座接触良好。\r
+\r
+若通过以上排查模块仍无法识别到SIM 卡,可以更换SIM 卡解决。\r
+\r
+1. \r
+检查天线是否完好,使用AT+CSQ 命令查询当前模块的接收信号是否满足通信条件。\r
+\r
+我们建议信号强度值为15(-83dbm)再进行通信。\r
+\r
+2. \r
+若现场信号良好,模块能识别到SIM 卡且SIM 卡未欠费,但模块不能附着上PS 域,可通过\r
+\r
+```\r
+AT+CGATT=1 命令,主动附着PS 域。\r
+```\r
+\r
+功能模组TCP 应用指南 \r
+第5 章外部协议栈TCP 应用\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+46\r
+\r
+##### 5.2 示例\r
+\r
+1. \r
+设置APN、用户名、密码,检查并确保模块已成功注册上PS 域\r
+\r
+AT \r
+OK\r
+\r
+AT+CGSN \r
++CGSN: "865324037375316" \r
+OK \r
+AT+CGMR \r
++CGMR: N720_EAB0CM_BZ_V008 \r
+OK \r
+AT+CPIN? \r
++CPIN: READY \r
+OK \r
+AT+CIMI\r
+\r
+460016657520885 \r
+OK \r
+AT+CGDCONT=1,"IP","UNINET" \r
+//设置APN \r
+OK \r
+AT+XGAUTH=1,1, "CARD","CARD" \r
+//设置用户名、密码、鉴权方式 \r
+OK \r
+AT+CSQ \r
++CSQ: 31, 99 \r
+OK\r
+\r
+AT+CREG=2 \r
+OK \r
+AT+CREG? \r
++CREG: 2,1,"FFFE","61C7E47",7 \r
+OK \r
+AT+CREG=0 \r
+OK \r
+AT+CGATT? \r
++CGATT: 1 \r
+OK\r
+\r
+AT$MYSYSINFO \r
+$MYSYSINFO: 4,02 \r
+OK\r
+\r
+2. \r
+进入外部协议栈\r
+\r
+ATD*99# \r
+ //进入外部协议栈 \r
+CONNECT\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+47\r
+\r
+#### A 关键AT 命令\r
+\r
+##### A.1 标准AT 指令\r
+\r
+###### A.1.1 AT+CGDCONT–设置PDP 格式\r
+\r
+设置GPRS 的PDP(Packet Data Protocol,分组数据协议)格式。目前仅支持设置一个APN。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+设置\r
+\r
+AT+CGDCONT=<cid>[,<PDP_type>[,<APN>[\r
+,<PDP_addr>[,<d_comp>[,<h_comp>[,<pd1> \r
+[,…[,<pdN>]]]]]]]]<CR>\r
+\r
+<CR><LF>OK<CR><LF> \r
+Or \r
+<CR><LF>ERROR<CR><LF>\r
+\r
+查询 \r
+AT+CGDCONT?<CR>\r
+\r
+<CR><LF>+CGDCONT: \r
+<cid>,<PDP_type>,<APN>,<PDP_ad\r
+dr>,<d_comp>,<h_comp>,<pd1> ,…,\r
+<pdN><CR><LF> \r
+<CR><LF>OK<CR><LF>\r
+\r
+测试 \r
+AT+CGDCONT=?<CR>\r
+\r
+<CR><LF>+CGDCONT: value range \r
+of <cid>, value range of <PDP_type>, \r
+value range of <APN>, value range of \r
+<PDP_addr>, \r
+value \r
+range \r
+of \r
+<d_comp>, value range of <h_comp>, \r
+value range of <pd1> ,…, value range \r
+of pdN \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<cid> \r
+(PDP Context Identifier)一个数字参数,指定一个PDP 上下文定义,这个参数是\r
+\r
+当地的TE-MT 接口并且被应用到其他PDP 上下文相关的命令当中,使用查询命\r
+\r
+令可以查询到允许的值(最小值为1)\r
+\r
+<PDP_type> \r
+(Packet Data Protocol type)字符串参数,用于指定分组数据协议的类型\r
+\r
+“IP”网络协议(Internet Protocol)(IETFSTD 5)\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+48\r
+\r
+“PPP”点对点协议(Point to Point Protocol)(IETF STD 51)\r
+\r
+“IPv6”网络协议V6\r
+\r
+<APN> \r
+(Access Point Name)字符串形式,是一个逻辑名称,用来选择GGSN 或者外部\r
+\r
+分组数据网。\r
+\r
+<PDP_address> \r
+字符串形式,用来在地址空间中区分MT。\r
+\r
+如果不写这个参数,则在PDP 的启动过程当中由TE 提供这个值。\r
+\r
+如果TE 提供失败,就请求动态地址,即使在PDP 的启动过程当中分配了地址,\r
+\r
+在使用这条指令查询的时候仍然会返回空。\r
+\r
+<d_comp> \r
+数字参数用来控制PDP 数据压缩(仅适用于SNDCP)\r
+\r
+0 - off (缺省情况下默认值)\r
+\r
+<h_comp> \r
+数字参数用来控制PDP 头部压缩0 - off (缺省情况下默认值)\r
+\r
+<pd1>, … <pdN> 0 到N,字符串类型,意义与<PDP_type>有关\r
+\r
+###### 示例\r
+\r
+AT+CGDCONT=1,"IP","CMNET" \r
+OK \r
+设置PDP 格式,PDP 类型为IP,APN 名称为CMNET\r
+\r
+AT+CGDCONT? \r
++CGDCONT: 1,"IP","CMNET","0.0.0.0",0,0,0,0\r
+\r
+OK\r
+\r
+查询当前PDP 格式\r
+\r
+AT+CGDCONT=? \r
++CGDCONT: (1-24),"IP",,,(0-2),(0-4),(0-1),(0-1) \r
++CGDCONT: (1-24),"PPP",,,(0-2),(0-4),(0-1),(0-1) \r
++CGDCONT: (1-24),"IPV6",,,(0-2),(0-4),(0-1),(0-1) \r
++CGDCONT: (1-24),"IPV4V6",,,(0-2),(0-4),(0-1),(0-1) \r
+OK\r
+\r
+查询设置PDP 格式的取值范围\r
+\r
+###### A.1.2 AT+XGAUTH–用户认证\r
+\r
+PDP 认证。\r
+\r
+该指令要放在AT+CGDCONT 这条指令后面。目前在专网中各个地方逐渐增加了用户身份认证需\r
+\r
+求,使用内部协议栈,需要使用到这条指令,因此,请在代码流程上加上这条指令。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+设置 \r
+AT+XGAUTH=<cid>,<auth>[,<name>,\r
+<pwd>]<CR>\r
+\r
+<CR><LF>OK<CR><LF> \r
+Or \r
+<CR><LF>ERROR<CR><LF>\r
+\r
+测试 \r
+AT+XGAUTH=?\r
+\r
+<CR><LF>+XGAUTH: \r
+(list \r
+of \r
+supported \r
+<cid>),(value \r
+range \r
+of<auth>),(length \r
+of \r
+<name>),(length of <pwd>)\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+49\r
+\r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<cid> \r
+(PDP Context Identifier)一个数字参数,指定一个PDP 上下文定义\r
+\r
+<cid>对应+CGDCONT 中的<cid>。\r
+\r
+<auth> \r
+鉴权类型,默认为1\r
+\r
+0:NONE\r
+\r
+1:PAP\r
+\r
+2:CHAP\r
+\r
+3:PAP or CHAP\r
+\r
+鉴权类型为非NONE 时,需带<name>和<pwd>参数\r
+\r
+<name> \r
+用户名\r
+\r
+<pwd> \r
+密码\r
+\r
+###### 示例\r
+\r
+AT+XGAUTH=1,1,”gsm”,”1234” \r
+OK \r
+设置第一个PDP 认证\r
+\r
+AT+XGAUTH=? \r
++XGAUTH: (1),(0-3),32,32 \r
+OK\r
+\r
+查询参数值范围\r
+\r
+###### A.1.3 AT+XIIC–建立PPP 链接\r
+\r
+建立PPP 连接,获取IP 地址。\r
+\r
+在使用AT+XIIC=1 建立PPP 连接之前,确保模块已经注册上网络。可使用AT+CEREG?来判断,\r
+\r
+如果返回+CEREG: 0,1 或+CEREG: 0,5,都表明已注册上网络。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+XIIC=<act><CR>\r
+\r
+<CR><LF>OK<CR><LF> \r
+Or \r
+<CR><LF>ERROR<CR><LF>\r
+\r
+查询 \r
+AT+XIIC?<CR> \r
+<CR><LF>+XIIC: <act>,<ip><CR><LF> \r
+<CR><LF>OK<CR><LF>\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+50\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+###### 参数\r
+\r
+<act> \r
+0:去激活PPP 连接\r
+\r
+1:激活PPP 连接\r
+\r
+<ip> \r
+IP 地址\r
+\r
+###### 示例\r
+\r
+AT+XIIC=1 \r
+OK \r
+建立第一个ppp 连接\r
+\r
+AT+XIIC? \r
++XIIC: 1,10.107.216.162 \r
+OK\r
+\r
+第一个PPP 链路建立成功,IP 地址是10.107.216.162。\r
+\r
+(1 前面有4 个空格)\r
+\r
+AT+XIIC? \r
++XIIC: 0,0.0.0.0 \r
+OK\r
+\r
+PPP 链路还未建立成功,\r
+\r
+(0 前面有4 个空格)\r
+\r
+###### A.1.4 AT+TCPSETUP–建立TCP 连接\r
+\r
+建立TCP 连接。\r
+\r
+请先使用AT+XIIC=1 建立PPP 链接。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+TCPSETUP=<n>,<ip>,<port\r
+>[,<recv_mode>]<CR>\r
+\r
+<CR><LF>OK<CR><LF> \r
+<CR><LF>+TCPSETUP:<n>,<result><CR><LF> \r
+Or \r
+<CR><LF>ERROR<CR><LF> \r
+Or \r
+<CR><LF>+TCPSETUP:<n>,ERROR1<CR><LF> \r
+Or \r
+<CR><LF>+TCPSETUP:ERRPR<CR><LF>\r
+\r
+###### 响应时间\r
+\r
+响应超时时间为60 秒。\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+51\r
+\r
+###### 参数\r
+\r
+<n> \r
+链路编号,只能为0~5\r
+\r
+<ip> \r
+目的IP 地址,必须是形如xx.xx.xx.xx 的输入,或者形如www.china.com(域名)\r
+\r
+<port> \r
+目的端口号,必须是十进制的ASCII 码\r
+\r
+<recv_mode> \r
+模式选择,范围0-1\r
+\r
+0:接收到的TCP、UDP 数据直接从串口输出(默认)\r
+\r
+1:接收到的TCP、UDP 数据缓存起来,输出来数据提示,需要外部MCU 主动发命\r
+\r
+令读取数据\r
+\r
+<result> \r
+结果码\r
+\r
+OK 建立成功\r
+\r
+FAIL 建立失败\r
+\r
+LINK ALREADY OPENED 连接已存在\r
+\r
+###### 示例\r
+\r
+AT+TCPSETUP=0,220.199.66.56,6800 \r
+OK\r
+\r
++TCPSETUP: 0,OK\r
+\r
+在链路0 上建立到 \r
+220.199.66.56,6800 的连接,成功\r
+\r
+AT+TCPSETUP=0,neowayjsr.oicp.net,60010 \r
+OK \r
+ \r
++TCPSETUP: 0,OK\r
+\r
+在链路0 上建立到 \r
+neowayjsr.oicp.net,60010 的连接,成功\r
+\r
++TCPCLOSE: 0,Link Closed \r
+链路被动断开后返回的回码 \r
+AT+TCPSETUP=1,192.168.20.6,7000 \r
+OK \r
+ \r
++TCPSETUP: 1,FAIL\r
+\r
+在链路1 上建立到192.168.20.6,7000 的连接,失\r
+败;可能是服务器未开通,IP 地址或端口不正确,或\r
+者是SIM 卡欠费,等等\r
+\r
+AT+TCPSETUP=0,neowayjsr.oicp.net,60010 \r
+OK \r
+ \r
++TCPSETUP:0,ERROR1\r
+\r
+当前链路0 的TCP/UDP 链接已存在\r
+\r
+AT+TCPSETUP=6,192.168.20.6,7000 \r
+ERROR \r
+AT 指令格式错误,链路号错误\r
+\r
+AT+TCPSETUP=0.58.60.184.213.10012 \r
+ERROR \r
+AT 指令格式错误,符号错误\r
+\r
+```\r
+AT+TCPSET=0,58.60.184.213,10012\r
+```\r
+\r
+ERROR \r
+AT 指令格式错误,指令不完整\r
+\r
+###### A.1.5 AT+TCPSEND–发送TCP 数据\r
+\r
+发送TCP 数据的命令,支持buffer 模式与命令模式,支持ASCII 与hex 发送。\r
+\r
+这条命令发送完毕后,会接收到大于号">",这时候请延迟50ms-100ms,然后发送数据。\r
+\r
+在发送TCP 数据之前,必须确保TCP 链路已经建立;建议在发送数据之前,先使用AT+IPSTATUS\r
+\r
+查看可用的buffer 大小。\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+52\r
+\r
+命令设置参数不保存,每次发送需要设置模式。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+TCPSEND=<n>,<length>\r
+[[,<content>][,mode]]<CR>\r
+\r
+<CR><LF>><content> \r
+<CR><LF>OK<CR><LF> \r
+<CR><LF>+TCPSEND: <n>,<length><CR><LF> \r
+Or \r
+<CR><LF>> \r
+<CR><LF>+TCPSEND: ERROR<CR><LF> \r
+Or \r
+<CR><LF>> \r
+<CR><LF>+TCPSEND: \r
+<n>,OPERATION \r
+EXPIRED<CR><LF> \r
+Or \r
+<CR><LF>+TCPSEND: <result><CR><LF>\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+###### 参数\r
+\r
+<n> \r
+链路编号,只能为0~5,且该链路已建立了TCP 连接\r
+\r
+<length> \r
+要发送的数据长度,以字节为单位,取值范围:\r
+\r
+buffer 模式发送HEX 数据:1~4096;\r
+\r
+buffer 模式发送ASCII 数据:1~4096;\r
+\r
+命令模式发送HEX 数据:1~512;\r
+\r
+命令模式发送ASCII 数据:1~1024;\r
+\r
+<content> \r
+命令模式下发送的数据,支持转义模式,content 长度范围0~1024\r
+\r
+Content 内容的逗号个数必须小于15 个,若需要传输大量逗号的数据,建议使用\r
+\r
+buffer 模式。\r
+\r
+<mode> \r
+HEX 发送启用开关\r
+\r
+0:ASCII 模式发送,支持转义字符(默认)\r
+\r
+1:HEX 模式发送\r
+\r
+<result> \r
+SOCKET ID OPEN FAILED \r
+DATA LENGTH ERROR\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+53\r
+\r
+###### 示例\r
+\r
+AT+TCPSEND=0,1 \r
+> \r
+OK \r
+ \r
++TCPSEND: 0,1\r
+\r
+在链路0 上发送1 字节的数据,成 \r
+功\r
+\r
+AT+TCPSEND=0,1024,,1 \r
+> \r
+OK \r
+ \r
++TCPSEND: 0,1024\r
+\r
+Buffer 模式,HEX 模式,发送1024 长度数据,发送成功\r
+\r
+AT+TCPSEND=0,27,"\"3~!@#$%\\^&*()_+;'332\",\"32\"" \r
+OK \r
+ \r
++TCPSEND: 0,27\r
+\r
+命令模式,转义模式,发送数据,发送成功\r
+\r
+```\r
+AT+TCPSEND=0,3,”313233”,1\r
+```\r
+\r
+OK \r
+ \r
++TCPSEND: 0,3\r
+\r
+命令模式,HEX 模式,发送数据,发送成功\r
+\r
+AT+TCPSEND=0,10 \r
+>\r
+\r
++TCPSEND:0,OPERATION EXPIRED\r
+\r
+输入发送命令出现“>”后,不输入数据,30 秒后提示超时\r
+\r
+AT+TCPSEND=0,1 \r
++TCPSEND: SOCKET ID OPEN FAILED\r
+\r
+在链路0 上发送1 字节的数据,该 \r
+链路尚未建立,发送失败\r
+\r
+###### A.1.6 AT+IPSTATUS–查询TCP/UDP 链路状态\r
+\r
+非透传下TCP/UDP 链路状态。由于UDP 的特点,该指令仅能查询是否已经通过指令建立链接,\r
+\r
+并不代表链接的真实情况。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+IPSTATUS=<n><CR>\r
+\r
+<CR><LF>+IPSTATUS: \r
+<n>,<STATUS>[,<TYPE>, \r
+<send-buffer-size>] \r
+Or \r
+<CR><LF>ERROR<CR><LF>\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+###### 参数\r
+\r
+<n> \r
+链路编号,范围 0~5\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+54\r
+\r
+<STATUS> \r
+该链路的状态,取值:\r
+\r
+CONNECT \r
+DISCONNECT \r
+CONNECTING \r
+DISCONNECTING \r
+<TYPE> \r
+链路类型,取值为 TCP 或者 UDP\r
+\r
+<send-buffer-size> \r
+模块内部可用的send buffer 的大小,十进制ASCII 码表示,单位为字节\r
+\r
+UDP 链路上不支持查询<send-buffer-size>参数\r
+\r
+###### 示例\r
+\r
+```\r
+AT+IPSTATUS=0\r
+```\r
+\r
++IPSTATUS: 0,CONNECT,TCP,4096 \r
+链路0,已建TCP 连接,可用buffer 为4096 字节\r
+\r
+AT+IPSTATUS=0 \r
++IPSTATUS: 0,CONNECT,UDP,0 \r
+链路0,已建UDP 连接\r
+\r
+AT+IPSTATUS=1 \r
++IPSTATUS: 1,DISCONNECT \r
+链路1,未建立任何“TCP/UDP”连接\r
+\r
+AT+IPSTATU \r
+ERROR \r
+AT 指令格式错误,指令不完整\r
+\r
+```\r
+AT+IPSTATUS=6\r
+```\r
+\r
+ERROR \r
+指令链路编号错误\r
+\r
+###### A.1.7 AT+TCPACK–查询TCP 链路发送数据状态\r
+\r
+查询TCP 链路发送成功的数据大小、接收方成功接收该链路的数据大小。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+TCPACK=<n><CR>\r
+\r
+<CR><LF>+TCPACK: \r
+<n>,<data_sent>,<acked_recv><CR><LF> \r
+Or \r
+<CR><LF>ERROR<CR><LF> \r
+Or \r
+<CR><LF>+TCPACK: <n>,DISCONNECT<CR><LF> \r
+Or \r
+<CR><LF>+TCPACK: NO TCP LINK<CR><LF>\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+55\r
+\r
+###### 参数\r
+\r
+<n> \r
+链路编号,只能为0~5\r
+\r
+<data_sent> \r
+该链路发送成功的链路数据大小,无符号64 位整型数,十进制ASCII 码表示,单位\r
+\r
+为字节。\r
+\r
+<acked_recv> \r
+接收方成功接收的链路数据大小,无符号64 位整型数,十进制ASCII 码表示,单位\r
+\r
+为字节。\r
+\r
+###### 示例\r
+\r
+AT+TCPACK=0 \r
++TCPACK: 0,20,20 \r
+链路0,发送成功20 个字节数据,接收方成功接收20 个字节数据\r
+\r
+```\r
+AT+TCPACK=0\r
+```\r
+\r
++TCPACK: 0,128,120 \r
+链路0,发送成功128 个字节数据,接收方成功接收120 个字节数据\r
+\r
+AT+TCPACK=1 \r
++TCPACK: 1,DISCONNECT \r
+链路1,未建立任何连接\r
+\r
+```\r
+AT+TCPACK=2\r
+```\r
+\r
++TCPACK: NO TCP LINK \r
+链路2 建立的是UDP 连接\r
+\r
+AT+TCPACK=6 \r
+ERROR \r
+指令链路编号错误\r
+\r
+###### A.1.8 AT+TCPLISTEN–设置服务器侦听功能\r
+\r
+设置服务器侦听功能。\r
+\r
+使用联通卡或者移动的专网卡可以进行调试使用,移动的公网卡不能作为服务器调试;\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+设置 \r
+AT+TCPLISTEN=<port><CR>\r
+\r
+<CR><LF>+TCPLISTEN:<socket>,OK \r
+Or \r
+<CR><LF>+TCPLISTEN:bind error \r
+Or \r
+<CR><LF>Listening... <CR><LF> \r
+Or \r
+<CR><LF>ERROR<CR><LF>\r
+\r
+查询 \r
+AT+TCPLISTEN?<CR>\r
+\r
+<CR><LF>+TCPLISTEN:listening status<CR><LF> \r
+Or \r
+<CR><LF>+TCPLISTEN:not listening<CR><LF>\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+56\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+###### 参数\r
+\r
+<port> \r
+端口号\r
+\r
+<socket> \r
+socket 号\r
+\r
+###### 示例\r
+\r
+AT+TCPLISTEN=6800 \r
++TCPLISTEN: 0,OK\r
+\r
+侦听端口号6800 \r
+服务器侦听开始启动 \r
+AT+TCPLISTEN=6800 \r
++TCPLISTEN: bind error\r
+\r
+侦听端口号6800 \r
+绑定失败 \r
+AT+TCPLISTEN=6800\r
+\r
+Listening...\r
+\r
+如果已经设置了侦听,再设置的话,会提示\r
+\r
+Listening... \r
+AT+TCPLISTEN=0 \r
+ERROR \r
+侦听非法端口号\r
+\r
+AT+TCPLISTEN? \r
++TCPLISTEN: listening status \r
+查询侦听状态,表示当前处于侦听\r
+\r
+AT+TCPLISTEN? \r
++TCPLISTEN: not listening \r
+查询侦听状态,表示当前没有侦听\r
+\r
+Connect \r
+AcceptSocket=1,ClientAddr=119.123.77.133,ClientPort=8000\r
+\r
+收到主站连接请求,其AcceptSocket 是主站跟模块\r
+建立的socket,119.123.77.133 是主站的IP 地\r
+址,8000 是主站的端口号\r
+\r
+###### A.1.9 AT+CLOSELISTEN–关闭侦听链接\r
+\r
+关闭侦听链接。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+CLOSELISTEN<CR>\r
+\r
+<CR><LF>+CLOSELISTEN:<Socket>,local link closed \r
+Or \r
+<CR><LF>+CLOSELISTEN:Transparent local<CR><LF>\r
+\r
+主动上报 \r
++CLOSELISTEN:<Socket>,local link closed\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+57\r
+\r
+###### 参数\r
+\r
+无。\r
+\r
+###### 示例\r
+\r
++CLOSELISTEN: 0,local link closed \r
+主站关闭链接或网络异常时,会主动上报该回码 \r
+AT+CLOSELISTEN \r
++CLOSELISTEN: 0,local link closed \r
+如果有主站链接,会同时把主站链接关闭\r
+\r
+AT+CLOSELISTEN \r
++CLOSELISTEN:Transparent local \r
+linkclosed\r
+\r
+使用透传方式时,会接收到这些信息\r
+\r
+###### A.1.10 AT+CLOSECLIENT–关闭主站链接\r
+\r
+关闭主站链接。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+CLOSECLIENT[=<socket>]<CR>\r
+\r
+<CR><LF>+CLOSECLIENT: \r
+<socket>,remote \r
+link closed<CR><LF> \r
+Or \r
+<CR><LF>ERROR<CR><LF> \r
+Or \r
+<CR><LF>+CLOSECLIENT: All remote link \r
+closed<CR><LF>\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+###### 参数\r
+\r
+<socket> \r
+socket 号\r
+\r
+###### 示例\r
+\r
+AT+CLOSECLIENT \r
++CLOSECLIENT: 1,remote link closed \r
+ \r
++CLOSECLIENT: 2,remote link closed\r
+\r
+不带参数,多个关闭\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+58\r
+\r
+AT+CLOSECLIENT=1 \r
++CLOSECLIENT: 1,remote link closed \r
+带参数,单个关闭\r
+\r
+AT+CLOSECLIENT=1 \r
+ERROR \r
+1 链路不存在远程客户端\r
+\r
+AT+CLOSECLIENT \r
++CLOSECLIENT: All remote link closed \r
+所有远程客户端已关闭\r
+\r
+###### A.1.11 +TCPRECV(S)–接收到主站的数据\r
+\r
+接收到主站的数据。\r
+\r
+跟客户端模式的接收格式不同,多了符号“(S)”;跟客户端的参数有所区别,请注意。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令\r
+\r
+主动上报 \r
++TCPRECV(S): <n>,<length>,<data><CR>\r
+\r
+###### 响应时间\r
+\r
+无。\r
+\r
+###### 参数\r
+\r
+<n> \r
+链路编号,只能为0~5\r
+\r
+<length> \r
+接收到的数据长度\r
+\r
+<data> \r
+接收到的数据。尾部追加0x0d 0x0a。用户可根据<length>参数来判断结尾\r
+\r
+###### 示例\r
+\r
++TCPRECV(S): 1,10,1234567899 \r
+链路1 接收到主站发过来的10 个字节的数据,接收字符格式为字符类型\r
+\r
+###### A.1.12 AT+TCPREADS–读取到主站的数据\r
+\r
+读取到主站的数据\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+TCPREADS=<n>,<length><CR> \r
+<CR><LF>+TCPREADS: <n>,<len>,<data>\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+59\r
+\r
+<CR><LF>OK<CR><LF> \r
+Or \r
+<CR><LF>ERROR<CR><LF>\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+###### 参数\r
+\r
+<n> \r
+链路编号,只能为0~5\r
+\r
+<length> \r
+本次允许读取的最大数据长度,范围1-2048 字节\r
+\r
+<len> \r
+读取到的数据长度\r
+\r
+<data> \r
+读取到的数据\r
+\r
+###### 示例\r
+\r
++TCPRECV(S): 1 \r
+AT+TCPREADS=1,100 \r
++TCPREADS: 1,10,1234567890 \r
+OK\r
+\r
+RECVMODE=0 \r
+链路1 接收到主站发过来的10 个字节的数据\r
+\r
+###### A.1.13 AT+TCPSENDS–发送给主站的数据\r
+\r
+发送给主站的数据。\r
+\r
+在发送TCP 数据之前,必须确保TCP 链路已经建立。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+TCPSENDS=<socket>[\r
+,<length>]<CR>\r
+\r
+<CR><LF>> \r
+<CR><LF>OK<CR><LF> \r
+<CR><LF>+TCPSENDS:<socket>,<length><CR><LF> \r
+Or \r
+<CR><LF>> \r
+<CR><LF>+TCPSENDS: <result><CR><LF> \r
+Or \r
+<CR><LF>> \r
+<CR><LF>+TCPSENDS: \r
+<socket>, \r
+OPERATION \r
+EXPIRED<CR><LF>\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+60\r
+\r
+Or \r
+<CR><LF>+TCPSENDS: <socket> is not link<CR><LF>\r
+\r
+###### 响应时间\r
+\r
+出现“>”后,不输入数据,60s 后提示超时。\r
+\r
+###### 参数\r
+\r
+<socket> \r
+侦听到的AcceptSocket 值,即主站跟模块的建立的socket ,参考\r
+\r
+```\r
+AT+TCPLISTEN 指令的说明\r
+```\r
+\r
+<length> \r
+要发送的数据长度,以字节为单位,取值范围1~2000\r
+\r
+<result> \r
+ERROR \r
+Buffer not enough,439\r
+\r
+###### 示例\r
+\r
+AT+TCPSENDS=0,10 \r
+> \r
+OK \r
+ \r
++TCPSENDS: 0,10\r
+\r
+在sokcet 0 上发送10 字节的数据(如:1234567890 或\r
+31323334353637383930),发送成功\r
+\r
+AT+TCPSENDS=0,536 \r
+> \r
++TCPSENDS: Buffer not enough,439\r
+\r
+在sokcet 0 上发送536 字节的数据(如:1234567890…),内部buffer\r
+不足,发送失败\r
+\r
+```\r
+AT+TCPSENDS=0\r
+```\r
+\r
+>\r
+\r
+OK \r
+ \r
++TCPSENDS: 0,21\r
+\r
+在链路 0 上发送 21 字节的数据(如:012345678901234567890 或\r
+\r
+303132333435363738393031323334353637383930),发送成功 \r
+(不带数据长度时以Ctrl+Z 为结束标志,最长不能超过2000)\r
+\r
+AT+TCPSENDS=0,1024 \r
+> \r
++TCPSENDS: ERROR\r
+\r
+服务器模块发送TCP 数据塞包\r
+\r
+AT+TCPSENDS=0,10 \r
++TCPSENDS: 0 is not link \r
+服务器模块未建立链路0 的连接\r
+\r
+```\r
+AT+TCPSENDS=0,5\r
+```\r
+\r
+> \r
++TCPSENDS: 0,OPERATION EXPIRED\r
+\r
+输入发送命令出现“>”后,不输入数据,30s 后提示超时\r
+\r
+###### A.1.14 AT+CLIENTSTATUS–查询主站链路的状态\r
+\r
+查询主站链路的状态\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+61\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+CLIENTSTATUS=<socket><CR>\r
+\r
+<CR><LF>+CLIENTSTATUS: <socket>,<status>, \r
+<type>,<send-buffer-size><CR><LF> \r
+Or \r
+<CR><LF>ERROR<CR><LF>\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+###### 参数\r
+\r
+<socket> \r
+侦听到的AcceptSocket 值,即主站跟模块的建立的socket ,参考\r
+\r
+```\r
+AT+TCPLISTEN 指令的说明\r
+```\r
+\r
+<status> \r
+该链路的状态,取值为CONNECT 或者DISCONNECT\r
+\r
+<type> \r
+链路类型,取值为TCP 或者INVALID\r
+\r
+INVALID 表示该链路不是侦听到的TCP 连接,可能为TCP/UDP 客户端或者\r
+\r
+服务器侦听链路。\r
+\r
+<send-buffer-size> \r
+模块内部可用的send buffer 的大小,十进制ASCII 码表示,单位为字节\r
+\r
+###### 示例\r
+\r
+AT+CLIENTSTATUS=0 \r
++CLIENTSTATUS: 0,CONNECT,TCP,61440\r
+\r
+主站socket 0,已建立TCP 连接,可用buffer 为\r
+61440 字节 \r
+AT+CLIENTSTATUS=4 \r
++CLIENTSTATUS: 4,DISCONNECT\r
+\r
+Socket 4,没有建立连接\r
+\r
+AT+CLIENTSTATUS=1 \r
++CLIENTSTATUS: 1,CONNECT,INVALID\r
+\r
+Socket 1 作为服务器侦听,链路类型返回INVALID\r
+\r
+###### A.1.15 AT+TCPACKS–查询TCP 服务器发送数据状态\r
+\r
+查询 TCP 服务器链路发送成功的数据大小、接收方成功接收该链路的数据大小。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+TCPACKS=<socket><CR> <CR><LF>+TCPACKS:\r
+\r
+<socket>,<data_sent>,<acked_recv>\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+62\r
+\r
+Or \r
+<CR><LF>+TCPACKS: <n>,DISCONNECT<CR><LF>\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+###### 参数\r
+\r
+<socket> \r
+socket 链路编号,只能为0~5\r
+\r
+<data_sent> \r
+该链路发送成功的链路数据大小,无符号64 位整型数,十进制ASCII 码表示,单位\r
+\r
+为字节。。\r
+\r
+<acked_recv> \r
+接收方成功接收的链路数据大小,无符号64 位整型数,十进制ASCII 码表示,单位\r
+\r
+为字节。\r
+\r
+###### 示例\r
+\r
+AT+TCPACKS=0 \r
++TCPACKS: 0,20,20\r
+\r
+链路0,发送成功20 个字节数据,接收方成功接收20 个字节数据\r
+\r
+AT+TCPACKS=0 \r
++TCPACKS:0,20,20\r
+\r
+链路0,发送成功128 个字节数据,接收方成功接收120 个字节数据\r
+\r
+AT+TCPACKS=1 \r
++TCPACKS:1,DISCONNECT\r
+\r
+链路1,未建立任何连接\r
+\r
+AT+TCPACK=6 \r
+ERROR\r
+\r
+指令链路编号错误\r
+\r
+###### A.1.16 AT+TCPTRANS–建立TCP 透传链接\r
+\r
+建立TCP 透传链接的命令。\r
+\r
+建立TCP 透传链接成功后,向服务器发送数据,串口不显示发送的数据;\r
+\r
+使用“+++”指令(不带回车换行)切换到命令模式;“ATO”指令切换到数据模式;\r
+\r
+建议透传方式一次最多收发4096 字节数据。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+TCPTRANS=<ip>,<port> \r
+[,<ip_type>]<CR>\r
+\r
+[<CR><LF>OK<CR><LF>] \r
+<CR><LF>+TCPTRANS: <result><CR><LF> \r
+Or\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+63\r
+\r
+<CR><LF>ERROR<CR><LF>\r
+\r
+###### 响应时间\r
+\r
+最大响应时间60s。\r
+\r
+###### 参数\r
+\r
+<ip> \r
+目的IP 地址,必须是形如xx.xx.xx.xx 的输入,或者形如www.china.com(域名)\r
+\r
+<port> \r
+目的端口号,必须是十进制的ASCII 码\r
+\r
+<result> \r
+OK \r
+FAIL \r
+ERROR\r
+\r
+###### 示例\r
+\r
+```\r
+AT+TCPTRANS=220.199.66.56,6800\r
+```\r
+\r
+OK \r
+ \r
++TCPTRANS:OK\r
+\r
+建立TCP 透传链接,成功\r
+\r
+AT+TCPTRANS=neowayjsr.oicp.net,60010 \r
+OK \r
+ \r
++TCPTRANS: OK\r
+\r
+用域名建立TCP 透传链接,成功\r
+\r
+AT+TCPTRANS=220.199.66.56, \r
++TCPTRANS: ERROR \r
+AT 指令格式错误\r
+\r
+```\r
+AT+TCPTRANS=220.199.66.56,6800\r
+```\r
+\r
+OK \r
+ \r
++TCPTRANS: FAIL\r
+\r
+建立TCP 透传链接,失败\r
+\r
+AT+TCPTRANS=220.199.66.56,6800 \r
+ERROR\r
+\r
+若已建立透传(TCP、UDP、TCP 服务器)链接,再发送该\r
+指令会返回:ERROR\r
+\r
+###### A.1.17 AT+TRANSCLOSE–关闭透传方式链接\r
+\r
+关闭透明传输方式链接。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT+TRANSCLOSE<CR>\r
+\r
+<CR><LF>+TRANSCLOSE: <n>,OK \r
+Or \r
+<CR><LF>ERROR<CR><LF>\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+64\r
+\r
+主动上报 \r
++TCPTRANS: Link Closed \r
++UDPTRANS: Link Closed\r
+\r
+###### 响应时间\r
+\r
+最大响应时间300ms。\r
+\r
+###### 参数\r
+\r
+无。\r
+\r
+###### 示例\r
+\r
+AT+TRANSCLOSE \r
++TRANSCLOSE: 0,OK \r
+主动关闭TCP 透传方式链接,成功\r
+\r
+AT+TRANSCLOSE \r
+ERROR \r
+未建立TCP/UDP 透传方式链接,失败\r
+\r
+```\r
+AT+TRANSCLOSE\r
+```\r
+\r
++TRANSCLOSE: 1,OK \r
+主动关闭UDP 透传方式链接,成功\r
+\r
++TCPTRANS: Link Closed \r
+被动关闭TCP 透传方式链接 \r
++UDPTRANS: Link Closed \r
+被动关闭UDP 透传方式链接\r
+\r
+###### A.1.18 ATD*99#–GPRS\r
+\r
+使用外部协议栈,进行GPRS 拨号连接\r
+\r
+进行拨号之前一定要确保CREG 已经注册成功,并且设置了APN。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+ATD*99#<CR> \r
+<CR><LF>CONNECT<CR><LF>\r
+\r
+###### 参数\r
+\r
+无\r
+\r
+###### 示例\r
+\r
+ATD*99# \r
+开始拨号连接\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+65\r
+\r
+CONNECT \r
+拨号成功的返回值\r
+\r
+##### A.2 国网AT 指令\r
+\r
+###### A.2.1 AT$MYNETCON–设置网络连接初始化参数\r
+\r
+该命令实现网络连接前的各种参数设置,是使用TCPIP 功能前必须完成的一步操作。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+设置 \r
+AT$MYNETCON=<channel>,\r
+<type>,<type_name><CR>\r
+\r
+<CR><LF>OK<CR><LF> \r
+<CR><LF>ERROR: <err><CR><LF>\r
+\r
+查询 \r
+AT$MYNETCON?<CR>\r
+\r
+<CR><LF>$MYNETCON: <Channel>,<Type>, \r
+<type_name> \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<channel> \r
+通道号,0-5。\r
+\r
+<type>,<type\r
+_name>\r
+\r
+<type>与<type_name>对应的取值如下:\r
+\r
+USERPWD:用户名和密码,格式为"user,passwd"\r
+\r
+APN:字符串类型\r
+\r
+CFGT:每包发送时等待的时间,1-65535,单位ms,默认值100,用于透明数据传\r
+\r
+输模式\r
+\r
+CFGP:数据包被传送的门限值,1-1460,默认值为1024,用于透明数据传输模式\r
+\r
+AUTH:鉴权类型,默认为1\r
+\r
+0:NONE\r
+\r
+1:PAP\r
+\r
+2:CHAP\r
+\r
+3:PAP or CHAP\r
+\r
+###### 示例\r
+\r
+AT$MYNETCON=0,"USERPWD","user,pwd" \r
+OK\r
+\r
+AT$MYNETCON?\r
+\r
+$MYNETCON: 0,"USERPWD","" \r
+$MYNETCON: 0,"APN",""\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+66\r
+\r
+$MYNETCON: 0,"CFGT",1000 \r
+$MYNETCON: 0,"CFGP",1024 \r
+$MYNETCON: 0,"AUTH",0 \r
+OK\r
+\r
+###### A.2.2 AT$MYNETACT–激活/去激活网络连接\r
+\r
+实现GPRS 网络的连接和断开,是使用TCP/IP 功能前必须完成的一步操作。\r
+\r
+命令执行成功,获取到IP 会主动上报本地IP。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT$MYNETACT=<channel>,<a\r
+ction><CR>\r
+\r
+<CR><LF>OK<CR><LF> \r
+Or \r
+<CR><LF>ERROR: <err><CR><LF>\r
+\r
+查询 \r
+AT$MYNETACT?<CR> \r
+<CR><LF>$MYNETACT: <channel>,<status>,<IP> \r
+<CR><LF>OK<CR><LF>\r
+\r
+测试 \r
+AT$MYNETACT=?<CR> \r
+<CR><LF>$MYNETACT: <status>,<Channel> \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<action> \r
+0:去激活网络连接\r
+\r
+1:激活网络连接\r
+\r
+<channel> \r
+通道号(0-5)\r
+\r
+<status> \r
+0:网络未激活\r
+\r
+1:网络已激活\r
+\r
+<IP> \r
+IP 地址;在网络已激活的情况下,显示本机IP\r
+\r
+###### 示例\r
+\r
+AT$MYNETACT=0,1 \r
+OK \r
+$MYURCACT: 0,1,"10.92.220.73" \r
+AT$MYNETACT=0,1 \r
+OK \r
+$MYURCACT: 0,1,"10.67.163.14"\r
+\r
+$MYURCACT:\r
+\r
+0,1,"24e0:bf:d206:6ed:1:2:fd94:4982"\r
+\r
+激活网络连接 \r
+执行成功 \r
+主动上报获取的IP \r
+双栈激活\r
+\r
+AT$MYNETACT? \r
+$MYNETACT: 0,1," 10.92.220.73" \r
+OK\r
+\r
+查询本地IP\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+67\r
+\r
+AT$MYNETACT=0,1 \r
+OK \r
+$MYURCACT: 0,1,"10.67.163.14" \r
+AT$MYNETACT=0,1 \r
+ERROR: 902\r
+\r
+重复激活提示ERROR\r
+\r
+###### A.2.3 AT$MYNETSRV–设置非透明传输服务参数\r
+\r
+设置TCP/IP 的服务参数。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行\r
+\r
+AT$MYNETSRV=<Channel>,<\r
+SocketID>,<nettype>,<viewMo\r
+de>,<ip:port>[,<ip_type>]<CR>\r
+\r
+<CR><LF>OK<CR><LF> \r
+Or \r
+<CR><LF>ERROR: <err><CR><LF>\r
+\r
+查询 \r
+AT$MYNETSRV?<CR>\r
+\r
+<CR><LF>$MYNETSRV: <Channel>,<SocketID>, \r
+<nettype>,<viewMode>,<ip:port> \r
+<CR><LF>OK<CR><LF>\r
+\r
+测试 \r
+AT$MYNETSRV=?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<Channel> \r
+整数类型,0-5,通道号,每个通道2K Bytes 的发送缓冲区和10K Bytes 的接收缓\r
+\r
+冲区\r
+\r
+<SocketID> \r
+整数类型,0-14,本命令使用的链接号\r
+\r
+<nettype> \r
+网络类型\r
+\r
+0:TCP Client\r
+\r
+1:TCP Server,本模式下,最多可建立4 个侦听\r
+\r
+2:UDP\r
+\r
+<viewMode> \r
+非透明数据传输模式下数据显示方式\r
+\r
+0:HEX(默认)\r
+\r
+1:TEXT\r
+\r
+<ip:port> \r
+ \r
+<ip_type>\r
+\r
+address 所对应的值,如:59.40.29.34:4988。address 中的IP 地址为127.0.0.1\r
+\r
+的时候,表示使用该SocketID 建立侦听服务。\r
+\r
+域名解析方式\r
+\r
+0:IPv4(默认)\r
+\r
+1:IPv6\r
+\r
+<err> \r
+错误代码\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+68\r
+\r
+###### 示例\r
+\r
+AT$MYNETSRV=0,0,0,0,"59.40.29.34:4988" \r
+OK\r
+\r
+通道0 设置TCP Client 服务参数 \r
+(远端服务器IP 地址为59.40.29.34,端口号为4988)\r
+\r
+###### A.2.4 AT$MYNETOPEN–开启服务\r
+\r
+打开TCP/UDP 连接。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT$MYNETOPEN=<SocketID><CR>\r
+\r
+<CR><LF>$MYNETOPEN: <SocketID> \r
+[,<Remote_MSS>] \r
+<CR><LF>OK<CR><LF> \r
+Or \r
+<CR><LF>ERROR: <err><CR><LF>\r
+\r
+查询 \r
+AT$MYNETOPEN?<CR>\r
+\r
+<CR><LF>$MYNETOPEN:<SocketID>,<LocalIP>,<l\r
+ocal_port>,<gate>,<DNS1>,<DNS2>,<type>,<dest_i\r
+p>,<dest_port> \r
+<CR><LF>OK<CR><LF>\r
+\r
+测试 \r
+AT$MYNETOPEN=?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<SocketID> \r
+整数类型,0-14,本命令使用的链接号。\r
+\r
+<Remote_MSS> \r
+整数类型,TCP 连接打开时候,client 和server 协商成功的MSS 值。\r
+\r
+<LocalIP> \r
+字符类型,本地IP 地址。\r
+\r
+<local_port> \r
+本地端口(每个通道可能不一样)。\r
+\r
+<gate> \r
+网关\r
+\r
+<DNS1> \r
+首选域名解析服务器\r
+\r
+<DNS2> \r
+备用域名解析服务器\r
+\r
+<type> \r
+TCP Client/TCP Server/UDP \r
+ \r
+0:TCP Client\r
+\r
+1:TCP Server\r
+\r
+2:UDP\r
+\r
+<dest_ip> \r
+需要连接的IP 地址\r
+\r
+<dest_port> \r
+需要连接的端口号\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+69\r
+\r
+###### 示例\r
+\r
+AT$MYNETOPEN=0 \r
+$MYNETOPEN: 0,2000 \r
+OK\r
+\r
+打开0 号socket。\r
+\r
+AT$MYNETOPEN? \r
+$MYNETOPEN:0,10.92.220.73,5000,0,dns1,dns2,0,59.40.29.34,4988\r
+\r
+OK\r
+\r
+OPEN 的socket 查询,查询到0 号\r
+socket 为打开状态。\r
+\r
+AT$MYNETSRV=5,1,0,0,"58.60.184.213:10188" \r
+OK \r
+AT$MYNETOPEN=2 \r
+ERROR: 913\r
+\r
+打开失败。\r
+\r
+###### A.2.5 AT$MYNETREAD–读取数据\r
+\r
+用于读取接收到的数据。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT$MYNETREAD=<SocketID>,\r
+<data_len><CR>\r
+\r
+<CR><LF>$MYNETREAD: <SocketID>,<data_length> \r
+<CR><LF><data> \r
+<CR><LF>OK<CR><LF>\r
+\r
+测试 \r
+AT$MYNETREAD=?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<SocketID> \r
+整数类型,0-14,本命令使用的链接号。\r
+\r
+<data_len> \r
+需要读取的最大数据长度,1-2048。\r
+\r
+<data_length> \r
+实际读取到的数据长度,0-2048。\r
+\r
+<data> \r
+数据,当data_length=0 时,无<data>显示。\r
+\r
+###### 示例\r
+\r
+AT$MYNETREAD=0,2048 \r
+$MYNETREAD:0,0 \r
+OK\r
+\r
+从0 号socket 读取2048 个数据 \r
+没有数据在缓冲区或数据已经全部读取完毕\r
+\r
+AT$MYNETREAD=0,2048 \r
+$MYNETREAD: 0,10 \r
+1234567890 \r
+OK\r
+\r
+从0 号socket 读取2048 个数据 \r
+实际读到10 个数据 \r
+数据内容为1234567890\r
+\r
+###### A.2.6 AT$MYNETWRITE–发送数据\r
+\r
+用于发送数据。\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+70\r
+\r
+UDP 链路,建议客户每次发送数据不大于1472 字节,可降低丢包概率。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT$MYNETWRITE=<SocketID>,\r
+<data_len><CR>\r
+\r
+<CR><LF>$MYNETWRITE: <SocketID>,<data_len> \r
+<CR><LF><input_data> \r
+<CR><LF>OK<CR><LF> \r
+Or \r
+<CR><LF>ERROR: <err><CR><LF>\r
+\r
+测试 \r
+AT$MYNETWRITE=?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<SocketID> \r
+整数类型,0-14,本命令使用的链接号。\r
+\r
+<data_len> \r
+要发送的数据长度,1-2048。\r
+\r
+<input_data> \r
+本次发送的数据内容。\r
+\r
+<err> \r
+错误代码。\r
+\r
+###### 示例\r
+\r
+AT$MYNETWRITE=0,10 \r
+$MYNETWRITE: 0,10 \r
+1234567890\r
+\r
+OK\r
+\r
+0 号socket 发送10 个数据 \r
+表示可以输入要发送数据 \r
+写入要发送数据1234567890(输入的数据不会回显在串口上)\r
+\r
+命令执行成功\r
+\r
+###### A.2.7 AT$MYNETCLOSE–关闭连接\r
+\r
+用于关闭一条socket 连接。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT$MYNETCLOSE=<SocketID><CR>\r
+\r
+<CR><LF>$MYNETCLOSE: <SocketID> \r
+<CR><LF>OK<CR><LF> \r
+<CR><LF>ERROR: <err><CR><LF>\r
+\r
+查询 \r
+AT$MYNETCLOSE?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+测试 \r
+AT$MYNETCLOSE=?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+71\r
+\r
+###### 参数\r
+\r
+<SocketID> \r
+整数类型,0-14,需要断开的socket 链接号。\r
+\r
+###### 示例\r
+\r
+AT$MYNETCLOSE=0 \r
+$MYNETCLOSE: 0 \r
+OK\r
+\r
+关闭0 号socket。\r
+\r
+###### A.2.8 AT$MYNETACK–查询TCPACK 信息\r
+\r
+用于查询终端已发送但未被对端确认的TCP 数据数量,模块内置协议栈剩余缓冲区的大小。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT$MYNETACK=<SocketID><CR>\r
+\r
+<CR><LF>$MYNETACK:<SocketID>, \r
+<unAcked_dataLen>,<rest_bufferLen> \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<SocketID> \r
+整数类型,0-14,需要查询的链接号。\r
+\r
+<unAcked_dataLen> \r
+数据发送但未收到对方确认信息的数量,最大值为2^32-1。\r
+\r
+<rest_bufferLen> \r
+模块内置协议栈剩余缓冲区的大小,0-10240。\r
+\r
+###### 示例\r
+\r
+AT$MYNETACK=0 \r
+$MYNETACK: 0,0,10240 \r
+OK\r
+\r
+查询0 号socket 发送数据的ACK \r
+发送了的未被对端确认ACK 的数量为0 \r
+模块内置协议栈剩余缓冲区为10240\r
+\r
+###### A.2.9 AT$MYNETACCEPT–接受侦听请求\r
+\r
+该命令是用于当通信模块工作在TCP 服务器模式下,接收客户端的侦听请求。\r
+\r
+在透明数据传输模式下,如果要关闭Server 的连接,首先要+++退出数据模式,进入命令模式,然\r
+\r
+后运行AT$MYNETCLOSE=SocketID,这样将一起关闭Server 侦听的SocketID 和这个SocketID 下建\r
+\r
+立的其他链接。\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+72\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行 \r
+AT$MYNETACCEPT=<SocketID>,\r
+<action>,<transportMode><CR>\r
+\r
+<transportMode>=0:\r
+\r
+<CR><LF>OK<CR><LF> \r
+<transportMode>=1:\r
+\r
+<CR><LF>CONNECT<CR><LF> \r
+Or \r
+<CR><LF>ERROR: <err><CR><LF>\r
+\r
+查询 \r
+AT$MYNETACCEPT?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+测试 \r
+AT$MYNETACCEPT=?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<SocketID> \r
+0-14,新接入的socket id,由主动上报$MYURCCLIENT 提供。\r
+\r
+<action> \r
+是否接受侦听。\r
+\r
+0:接受\r
+\r
+1:拒绝\r
+\r
+<transportMode> \r
+侦听成功后模块的传输模式。\r
+\r
+0:非透明数据传输模式\r
+\r
+1:透明数据传输模式\r
+\r
+###### 示例\r
+\r
+AT$MYNETSRV=0,0,1,0,"127.0.0.1:5000" \r
+OK \r
+AT$MYNETOPEN=0 \r
+$MYNETOPEN: 0 \r
+OK \r
+ \r
+$MYURCCLIENT: 1,"10.90.72.235",51614\r
+\r
+AT$MYNETACCEPT=1,0,0 \r
+OK\r
+\r
+设置0 号socket 侦听本地端口5000。 \r
+ \r
+打开0 号socket 开启服务器模式。 \r
+ \r
+ \r
+ \r
+客户端1 连接上报\r
+\r
+接受客户端1 连接\r
+\r
+$MYURCCLIENT: 2,"10.90.72.235",55469 \r
+AT$MYNETACCEPT=2,1,0 \r
+OK\r
+\r
+客户端2 连接上报 \r
+拒绝客户端2 连接\r
+\r
+###### A.2.10 AT$MYNETCREATE–开启透明传输服务\r
+\r
+开启通信模块透明数据传输服务。\r
+\r
+TCP/UDP Client 模式下,执行本条命令后通信模块开始连接服务器,连接服务器成功后,通信模块\r
+\r
+进入透明数据传输模式。\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+73\r
+\r
+TCPServer 模式下,执行本条命令,模块返回OK,仅代表通信模块建立侦听socket 成功,但模块\r
+\r
+不立即进入数据模式,仍旧在命令模式;只有在客户端连接成功以后,通信模块才进入透明数据传输模\r
+\r
+式。\r
+\r
+透明数据传输模式的挂断,必须首先使用“+++”切入命令模式后,方可执行AT$MYNETCLOSE\r
+\r
+命令。+++要以字符串的形式输入才能退出透传模式,单个+输入,倘若+与+之间相差50ms,+会当做\r
+\r
+数据发送出去。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令 \r
+响应格式\r
+\r
+执行\r
+\r
+AT$MYNETCREATE=<channel>,<mode>,<\r
+SocketID>,<ip>,<port>[,<local_port>][,<ip_ty\r
+pe>]<CR>\r
+\r
+<mode>=1:\r
+\r
+<CR><LF>OK<CR><LF>\r
+\r
+<mode>=0 或2:\r
+\r
+<CR><LF>CONNECT<CR><LF> \r
+Or \r
+<CR><LF>ERROR: <err><CR><LF>\r
+\r
+查询 \r
+AT$MYNETCREATE?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+测试 \r
+AT$MYNETCREATE=?<CR> \r
+<CR><LF>OK<CR><LF>\r
+\r
+###### 参数\r
+\r
+<channel> \r
+通道号0-5\r
+\r
+<mode> \r
+TCP Client/TCP Server/UDP,0-2\r
+\r
+0:TCPClient\r
+\r
+1:TCPServer\r
+\r
+2:UDP\r
+\r
+<SocketID> \r
+本次的链接号,整数类型,0-14\r
+\r
+<ip> \r
+需要连接的IP\r
+\r
+<port> \r
+客户端模式则为需要连接的端口,服务器模式则为侦听端口\r
+\r
+<local_port> \r
+<ip_type>\r
+\r
+可缺省\r
+\r
+域名解析方式,可缺省\r
+\r
+0:IPv4(默认)\r
+\r
+1:IPv6\r
+\r
+###### 示例\r
+\r
+AT$MYNETCREATE=0,0,0,"59.40.29.34",4988,3000 \r
+CONNECT\r
+\r
+建立TCP 透明数据传输通道 \r
+目的IP 和PORT:"59.40.29.34",4988\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+74\r
+\r
+OK \r
+ \r
+AT$MYNETCREATE=0,0,0,"2400:da00:2::29",4988,3000 \r
+CONNECT \r
+ \r
+OK\r
+\r
+CONNECT 表示可以开始透明传输 \r
+输入+++ \r
+模块返回OK 表示回到命令模式 \r
+ \r
+建立TCPIPV6 透明数据传输通道\r
+\r
+###### A.2.11 $MYURCREAD–数据到来主动上报\r
+\r
+该主动上报命令表达模块接收到了数据。\r
+\r
+每收到一包数据,上报一次。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令\r
+\r
+主动上报 \r
+<CR><LF>$MYURCREAD: <SocketID><CR><LF>\r
+\r
+###### 参数\r
+\r
+<SocketID> \r
+整数类型,0-14,本次上报的链接号。\r
+\r
+###### 示例\r
+\r
+AT$MYNETSRV=0,0,0,0,"59.40.29.34:4988"\r
+\r
+OK \r
+AT$MYNETOPEN=0\r
+\r
+$MYNETOPEN: 0,2000 \r
+OK\r
+\r
+$MYURCREAD: 0 \r
+AT$MYNETREAD=0,2048 \r
+$MYNETREAD: 0,10 \r
+0123456789\r
+\r
+OK\r
+\r
+0 号socket 设置为TCP 客户端\r
+\r
+打开0 号socket 进行TCP 链接\r
+\r
+0 号socket 数据到来主动上报 \r
+按最大长度2048 读取 \r
+0 号socket 读取到10 个字节 \r
+数据内容为0123456789\r
+\r
+###### A.2.12 $MYURCCLOSE–链路断开主动上报\r
+\r
+该主动上报命令表达当前SocketID 对应的链接已断开\r
+\r
+上报了本命令后,SocketID 对应的缓冲区内的收发数据会被清空。\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+75\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令\r
+\r
+主动上报 \r
+<CR><LF>$MYURCCLOSE: <SocketID><CR><LF>\r
+\r
+###### 参数\r
+\r
+<SocketID> \r
+整数类型,0-14,本次上报的链接号。\r
+\r
+###### 示例\r
+\r
+AT$MYNETSRV=0,0,0,0,"59.40.29.34:4988" \r
+OK \r
+AT$MYNETOPEN=0 \r
+$MYNETOPEN: 0,2000 \r
+OK\r
+\r
+$MYURCCLOSE: 0\r
+\r
+0 号socket 设置为TCP 客户端 \r
+ \r
+打开0 号socket 进行TCP 链接\r
+\r
+主动上报:0 号socket 断开\r
+\r
+###### A.2.13 $MYURCACT–网络连接状态主动上报\r
+\r
+该主动上报命令表达网络连接建立或者断开\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令\r
+\r
+主动上报 \r
+<CR><LF>$MYURCACT: <channel>,<type>,<IP><CR><LF>\r
+\r
+###### 参数\r
+\r
+<channel> \r
+使用的Channel 通道\r
+\r
+<type> \r
+网络连接状态\r
+\r
+0:网络连接断开\r
+\r
+1:网络连接建立\r
+\r
+<IP> \r
+本机IP 地址\r
+\r
+###### 示例\r
+\r
+AT$MYNETACT=0,1 \r
+OK\r
+\r
+激活开网络连接 \r
+网络连接状态上报\r
+\r
+功能模组TCP 应用指南\r
+\r
+A 关键AT 命令\r
+\r
+深圳市有方科技股份有限公司版权所有 \r
+76\r
+\r
+$MYURCACT: 0,1,"10.91.102.62" \r
+网络激活成功,获取的本地IP 为"10.91.102.62" \r
+AT$MYNETACT=0,1 \r
+OK \r
+$MYURCACT: 0,1,"10.67.163.14"\r
+\r
+激活开网络连接 \r
+网络连接状态上报 \r
+网络激活成功,获取的本地IP 为"10.91.102.62"\r
+\r
+###### A.2.14 $MYURCCLIENT–客户端连接主动上报\r
+\r
+当开启侦听服务后,客户端连接上报。\r
+\r
+该上报表示有客户端申请连接。\r
+\r
+###### 命令格式\r
+\r
+类型 \r
+命令\r
+\r
+主动上报 \r
+<CR><LF>$MYURCCLIENT: <SocketID>,<IP>,<port><CR><LF>\r
+\r
+###### 参数\r
+\r
+<SocketID> \r
+新Client socket id\r
+\r
+<IP> \r
+客户端IP 地址\r
+\r
+<port> \r
+客户端端口号\r
+\r
+###### 示例\r
+\r
+AT$MYNETSRV=0,0,1,0,"127.0.0.1:5000"\r
+\r
+OK\r
+\r
+AT$MYNETOPEN=0 \r
+$MYNETOPEN: 0 \r
+OK \r
+ \r
+$MYURCCLIENT: 1,"10.90.72.235",51614 \r
+AT$MYNETACCEPT=1,0,0 \r
+OK\r
+\r
+0 号socket 设置为TCP server,侦听端口为5000\r
+\r
+打开0 号socket 开始侦听\r
+\r
+客户端连接主动上报\r
diff --git a/code/sdk/zhihui/ppp/doc/Neoway_功能模块TCP应用指南_V1.5.pdf b/code/sdk/zhihui/ppp/doc/Neoway_功能模块TCP应用指南_V1.5.pdf
new file mode 100644 (file)
index 0000000..ef0683a
Binary files /dev/null and "b/code/sdk/zhihui/ppp/doc/Neoway_\345\212\237\350\203\275\346\250\241\345\235\227TCP\345\272\224\347\224\250\346\214\207\345\215\227_V1.5.pdf" differ
index 2f98361..4f8891d 100644 (file)
@@ -83,10 +83,9 @@ extern "C" {
 
 /* 调试配置 */
 #define PPP_DEBUG_ENABLE            1                   // 启用调试输出
-// #define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_DEBUG // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
+#define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_DEBUG // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
 // #define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_INFO // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
-#define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_WARN // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
-
+// #define PPP_DEBUG_LEVEL             PPP_DEBUG_LEVEL_WARN // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
 /*
  * 调试级别说明:
  * PPP_DEBUG_LEVEL_ERROR (1)  - 只显示错误信息
index 4c14cce..709679c 100644 (file)
@@ -2957,15 +2957,17 @@ static errcode_t ppp_parse_cclk_and_convert_to_timestamp(const char *response,
  * @param timezone_quarters 时区(quarters,15分钟为单位)
  * @return ERRCODE_SUCC-成功,其他-失败
  * 
- * 时间设置逻辑说明:
+ * æ\97¶é\97´è®¾ç½®é\80»è¾\91说æ\98\8eï¼\88å\8f\82ç\85§ cat1_porting.c ç\9a\84 sync_time_utc å®\9eç\8e°ï¼\89ï¼\9a
  * - 输入:UTC时间戳 + 时区偏移
- * - 设置:本地时间 = UTC时间 + 时区偏移
+ * - 设置:tv.tv_sec 设置为本地时间戳(UTC + 时区偏移),tz_minuteswest 设置时区偏移
+ * - 系统时间直接设置为本地时间,tz_minuteswest 用于时区信息记录
  * 
  * 示例(UTC+8):
- * - UTC时间:02:59:43
+ * - UTC时间:15:06:28
  * - 时区:+32 quarters = +8 hours
- * - 本地时间:02:59:43 + 8小时 = 10:59:43
- * - settimeofday 会将系统时间设置为 10:59:43
+ * - tv.tv_sec:本地时间戳(UTC时间戳 + 8小时 = 对应23:06:28)
+ * - tz_minuteswest:+480分钟
+ * - 系统显示:23:06:28
  */
 static errcode_t ppp_set_system_time(uint32_t utc_timestamp, int timezone_quarters)
 {
@@ -2981,12 +2983,12 @@ static errcode_t ppp_set_system_time(uint32_t utc_timestamp, int timezone_quarte
         return ERRCODE_FAIL;
     }
     
-    // 准备时间结构(参照 cat1_porting.c 的实现)
+    // 准备时间结构(参照 cat1_porting.c 的 sync_time_utc 实现)
     struct timeval tv = {0};
     struct timezone tzone = {0};
     
-    // 设置本地时间 = UTC时间 + 时区偏移
-    // settimeofday 的 tv_sec 应该设置为本地时间戳
+    // settimeofday 的 tv_sec 设置为本地时间戳(UTC + 时区偏移)
+    // 这样系统时间就是本地时间,tz_minuteswest 用于时区信息记录
     tv.tv_sec = utc_timestamp + timezone_quarters * MINUTES_PER_QUARTER * 60;
     tv.tv_usec = 0;
     
@@ -2996,9 +2998,9 @@ static errcode_t ppp_set_system_time(uint32_t utc_timestamp, int timezone_quarte
     
     PPP_DEBUG_INFO("Setting system time:");
     PPP_DEBUG_INFO("  UTC timestamp: %u", utc_timestamp);
+    PPP_DEBUG_INFO("  Local timestamp: %ld (UTC + %d hours)", tv.tv_sec, timezone_quarters / 4);
     PPP_DEBUG_INFO("  Timezone: %+d minutes (%+d quarters)", 
                    tzone.tz_minuteswest, timezone_quarters);
-    PPP_DEBUG_INFO("  Local timestamp: %ld", tv.tv_sec);
     
     // 调用系统API设置时间
     int ret = settimeofday(&tv, &tzone);
index cb663ad..147398b 100644 (file)
@@ -932,21 +932,21 @@ static bool ppp_at_parse_response(const char *response, const char *expected)
         return false;
     }
 
-
-     // 检查期望响应 ,优先检测期望响应
-    if (expected && strstr(response, expected)) {
-        return true;
+    // 优先检查ABORT条件(ERROR、BUSY、NO CARRIER),即使响应中包含期望的字符串,如果有ERROR也应该返回失败
+    if (strstr(response, "ERROR") != NULL) {
+        PPP_DEBUG_ERROR("AT command response contains ERROR: %s", response);
+        return false;
     }
-
-    // 检查ABORT条件 (对应chat文件中的ABORT指令)
-    if (strstr(response, "BUSY") || 
-        strstr(response, "ERROR") || 
-        strstr(response, "NO CARRIER")) {
+    
+    if (strstr(response, "BUSY") != NULL || strstr(response, "NO CARRIER") != NULL) {
         PPP_DEBUG_ERROR("AT command aborted: %s", response);
         return false;
     }
 
-   
+    // 检查期望响应
+    if (expected && strstr(response, expected)) {
+        return true;
+    }
 
     return false;
 }
index b567a58..19e7386 100644 (file)
@@ -26,6 +26,7 @@
 #include "tcxo.h"
 #include <stdint.h>
 #include "pm_veto.h"
+#include "pm.h"
 #include <sys/stat.h>
 // #include "config/include/ConfigModelC.h"
 #include "upg_porting.h"
@@ -4607,6 +4608,28 @@ void uapi_cat1_add_cat1_vote(void)
 #endif
 }
 
+void uapi_net_app_remove_net_app_vote(void)
+{
+    uint8_t cnt = pm_veto_get_id_cnt(PM_ID_NET_APP);
+    if (cnt == 1) {
+        uapi_pm_remove_sleep_veto(PM_ID_NET_APP);
+    } else {
+        // WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"NET_APP has delete pm_id_net_app vote\r\n");
+    }
+    // WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"\r\nNET_APP SLEEP SUCCESS\r\n");
+}
+
+void uapi_net_app_add_net_app_vote(void)
+{
+    uint8_t cnt = pm_veto_get_id_cnt(PM_ID_NET_APP);
+    if (cnt == 0) {
+        uapi_pm_add_sleep_veto(PM_ID_NET_APP);
+    } else {
+        // WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"NET_APP has got pm_id_net_app vote\r\n");
+    }
+    // WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"\r\nNET_APP WAKEUP SUCCESS\r\n");
+}
+
 void uapi_get_lcd_id(uint32_t *id)
 {
     lcd_drv_common *lcd_drv_comm_api = NULL;
index 1fe7b61..6fa7f47 100644 (file)
@@ -470,6 +470,9 @@ uint8_t calculate_lrc(uint8_t *data,uint16_t len,uint8_t _lrcValue);
 void uapi_cat1_remove_cat1_vote(void);
 void uapi_cat1_add_cat1_vote(void);
 
+void uapi_net_app_remove_net_app_vote(void);
+void uapi_net_app_add_net_app_vote(void);
+
 void uapi_get_lcd_id(uint32_t *id);
 
 void uapi_lcd_backlight_on(void);
diff --git a/code/sdk/zhihui/websocket/websockets_py/src/websocket_console_server.py b/code/sdk/zhihui/websocket/websockets_py/src/websocket_console_server.py
new file mode 100644 (file)
index 0000000..2fb78dc
--- /dev/null
@@ -0,0 +1,446 @@
+#!/usr/bin/env python3
+"""
+WebSocket控制台服务器
+支持:
+- 正常接收和回复 ping/pong 指令(自动处理)
+- 打印收到的所有数据
+- 通过控制台发送消息给指定的连接的客户端
+"""
+
+import asyncio
+import logging
+import argparse
+import time
+import sys
+from typing import Dict, Set, Optional
+import json
+
+# 导入 websockets
+try:
+    import websockets
+    from websockets.asyncio.server import serve
+    from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+except ImportError as e:
+    print(f"错误: 无法导入 websockets 模块: {e}")
+    print("请确保已安装 websockets: pip install websockets")
+    sys.exit(1)
+
+# 配置日志
+logging.basicConfig(
+    level=logging.INFO,
+    format='%(asctime)s - %(levelname)s - %(message)s'
+)
+logger = logging.getLogger(__name__)
+
+
+class WebSocketConsoleServer:
+    def __init__(self, host: str, port: int):
+        self.host = host
+        self.port = port
+        self.clients: Dict[int, websockets.WebSocketServerProtocol] = {}  # client_id -> websocket
+        self.client_info: Dict[int, dict] = {}  # client_id -> info dict
+        self.message_queue = asyncio.Queue()  # 用于控制台输入的消息队列
+        self.running = True
+        
+    def get_client_id(self, websocket) -> int:
+        """获取客户端ID"""
+        return id(websocket)
+    
+    def get_client_address(self, websocket) -> tuple:
+        """安全获取客户端地址"""
+        remote_address = getattr(websocket, 'remote_address', None)
+        if isinstance(remote_address, tuple) and len(remote_address) >= 2:
+            return remote_address[0], remote_address[1]
+        return 'unknown', -1
+    
+    async def handle_client(self, websocket):
+        """处理客户端连接"""
+        # 从 websocket 对象获取 path(兼容不同版本的 websockets 库)
+        path = getattr(websocket, 'path', '')
+        
+        client_id = self.get_client_id(websocket)
+        client_ip, client_port = self.get_client_address(websocket)
+        
+        # 保存客户端信息
+        self.clients[client_id] = websocket
+        self.client_info[client_id] = {
+            'id': client_id,
+            'ip': client_ip,
+            'port': client_port,
+            'path': path,
+            'connect_time': time.time(),
+            'messages_received': 0,
+            'messages_sent': 0
+        }
+        
+        logger.info(f"[客户端连接] ID={client_id}, 地址={client_ip}:{client_port}, 路径={path}")
+        logger.info(f"[客户端列表] 当前连接数: {len(self.clients)}")
+        
+        # 发送欢迎消息
+        try:
+            welcome_msg = json.dumps({
+                "type": "welcome",
+                "message": "连接成功",
+                "client_id": client_id,
+                "server_time": time.strftime("%Y-%m-%d %H:%M:%S")
+            }, ensure_ascii=False)
+            await websocket.send(welcome_msg)
+            self.client_info[client_id]['messages_sent'] += 1
+            logger.info(f"[发送] 客户端 {client_id}: 欢迎消息")
+        except Exception as e:
+            logger.error(f"发送欢迎消息失败: {e}")
+        
+        try:
+            # 处理消息循环
+            async for message in websocket:
+                await self.process_message(websocket, message, client_id)
+                
+        except ConnectionClosedOK:
+            logger.info(f"[客户端断开] ID={client_id} 正常关闭")
+        except ConnectionClosedError as e:
+            logger.warning(f"[客户端断开] ID={client_id} 异常关闭: {e}")
+        except (asyncio.CancelledError, AssertionError) as e:
+            # keepalive 相关的取消和断言错误(连接可能已断开)
+            logger.info(f"[客户端断开] ID={client_id} 任务被取消或 keepalive 超时: {type(e).__name__}")
+        except Exception as e:
+            # 记录其他异常,但不中断清理流程
+            logger.error(f"[错误] 处理客户端 {client_id} 消息时出错: {type(e).__name__}: {e}")
+        finally:
+            # 清理客户端
+            if client_id in self.clients:
+                del self.clients[client_id]
+            if client_id in self.client_info:
+                info = self.client_info[client_id]
+                duration = time.time() - info['connect_time']
+                logger.info(f"[客户端统计] ID={client_id}, 连接时长={duration:.2f}秒, "
+                          f"接收={info['messages_received']}, 发送={info['messages_sent']}")
+                del self.client_info[client_id]
+            logger.info(f"[客户端列表] 当前连接数: {len(self.clients)}")
+    
+    async def process_message(self, websocket, message, client_id: int):
+        """处理接收到的消息"""
+        self.client_info[client_id]['messages_received'] += 1
+        
+        # 打印收到的所有数据
+        if isinstance(message, bytes):
+            # 二进制消息
+            hex_str = message.hex()
+            ascii_str = ''.join(chr(b) if 32 <= b < 127 else '.' for b in message[:64])
+            logger.info(f"[接收-二进制] 客户端 {client_id}: {len(message)} bytes")
+            logger.info(f"  HEX: {hex_str[:128]}{'...' if len(hex_str) > 128 else ''}")
+            logger.info(f"  ASCII预览: {ascii_str}")
+            
+            # 尝试解析为文本(如果是UTF-8编码)
+            try:
+                text = message.decode('utf-8')
+                logger.info(f"  UTF-8文本: {text[:200]}{'...' if len(text) > 200 else ''}")
+            except UnicodeDecodeError:
+                pass
+        else:
+            # 文本消息
+            logger.info(f"[接收-文本] 客户端 {client_id}: {message}")
+            
+            # 尝试解析为JSON
+            try:
+                data = json.loads(message)
+                logger.info(f"  JSON解析: {json.dumps(data, ensure_ascii=False, indent=2)}")
+            except (json.JSONDecodeError, TypeError):
+                pass
+    
+    async def console_input_handler(self):
+        """处理控制台输入"""
+        loop = asyncio.get_event_loop()
+        
+        print("\n" + "="*60)
+        print("WebSocket 控制台服务器")
+        print("="*60)
+        print("可用命令:")
+        print("  list              - 列出所有连接的客户端")
+        print("  send <id> <msg>   - 向指定客户端发送文本消息")
+        print("  sendall <msg>     - 向所有客户端发送文本消息")
+        print("  close <id>        - 关闭指定客户端连接")
+        print("  stats             - 显示统计信息")
+        print("  help              - 显示帮助信息")
+        print("  quit/exit         - 退出服务器")
+        print("="*60 + "\n")
+        
+        while self.running:
+            try:
+                # 使用 asyncio 读取 stdin
+                line = await loop.run_in_executor(None, sys.stdin.readline)
+                if not line:
+                    break
+                
+                line = line.strip()
+                if not line:
+                    continue
+                
+                # 处理命令
+                await self.handle_console_command(line)
+                
+            except EOFError:
+                break
+            except Exception as e:
+                logger.error(f"处理控制台输入时出错: {e}")
+    
+    async def handle_console_command(self, command: str):
+        """处理控制台命令"""
+        parts = command.split(None, 2)
+        cmd = parts[0].lower() if parts else ""
+        
+        if cmd == "list":
+            await self.cmd_list_clients()
+        elif cmd == "send" and len(parts) >= 3:
+            client_id = int(parts[1])
+            message = parts[2]
+            await self.cmd_send_to_client(client_id, message)
+        elif cmd == "sendall" and len(parts) >= 2:
+            message = parts[1]
+            await self.cmd_send_to_all(message)
+        elif cmd == "close" and len(parts) >= 2:
+            client_id = int(parts[1])
+            await self.cmd_close_client(client_id)
+        elif cmd == "stats":
+            await self.cmd_show_stats()
+        elif cmd in ["help", "h", "?"]:
+            self.print_help()
+        elif cmd in ["quit", "exit", "q"]:
+            logger.info("收到退出命令,正在关闭服务器...")
+            self.running = False
+            # 关闭所有连接
+            for client_id, websocket in list(self.clients.items()):
+                try:
+                    await websocket.close()
+                except Exception as e:
+                    logger.error(f"关闭客户端 {client_id} 时出错: {e}")
+        else:
+            logger.warning(f"未知命令: {command}")
+            logger.info("输入 'help' 查看可用命令")
+    
+    async def cmd_list_clients(self):
+        """列出所有客户端"""
+        if not self.clients:
+            logger.info("[客户端列表] 当前没有连接的客户端")
+            return
+        
+        logger.info(f"[客户端列表] 当前连接数: {len(self.clients)}")
+        for client_id, info in self.client_info.items():
+            duration = time.time() - info['connect_time']
+            logger.info(f"  ID={client_id}, 地址={info['ip']}:{info['port']}, "
+                      f"路径={info['path']}, 连接时长={duration:.2f}秒, "
+                      f"接收={info['messages_received']}, 发送={info['messages_sent']}")
+    
+    async def cmd_send_to_client(self, client_id: int, message: str):
+        """向指定客户端发送消息"""
+        if client_id not in self.clients:
+            logger.error(f"[发送失败] 客户端 {client_id} 不存在")
+            await self.cmd_list_clients()
+            return
+        
+        websocket = self.clients[client_id]
+        
+        # 检查连接状态(兼容不同版本的 websockets 库)
+        try:
+            if getattr(websocket, 'closed', False):
+                logger.error(f"[发送失败] 客户端 {client_id} 连接已关闭")
+                if client_id in self.clients:
+                    del self.clients[client_id]
+                if client_id in self.client_info:
+                    del self.client_info[client_id]
+                return
+        except Exception:
+            pass  # 如果属性不存在,继续尝试发送
+        
+        try:
+            await websocket.send(message)
+            self.client_info[client_id]['messages_sent'] += 1
+            logger.info(f"[发送成功] 客户端 {client_id}: {message}")
+        except (ConnectionClosedOK, ConnectionClosedError) as e:
+            logger.warning(f"[发送失败] 客户端 {client_id} 连接已关闭: {e}")
+            # 清理客户端
+            if client_id in self.clients:
+                del self.clients[client_id]
+            if client_id in self.client_info:
+                del self.client_info[client_id]
+        except (asyncio.CancelledError, AssertionError) as e:
+            # 忽略 keepalive 相关的取消和断言错误(连接可能已断开)
+            logger.warning(f"[发送失败] 客户端 {client_id} keepalive 错误(连接可能已断开): {type(e).__name__}")
+            # 清理客户端
+            if client_id in self.clients:
+                del self.clients[client_id]
+            if client_id in self.client_info:
+                del self.client_info[client_id]
+        except Exception as e:
+            logger.error(f"[发送失败] 客户端 {client_id}: {type(e).__name__}: {e}")
+            # 如果是连接相关错误,清理客户端
+            if "connection" in str(e).lower() or "closed" in str(e).lower():
+                if client_id in self.clients:
+                    del self.clients[client_id]
+                if client_id in self.client_info:
+                    del self.client_info[client_id]
+    
+    async def cmd_send_to_all(self, message: str):
+        """向所有客户端发送消息"""
+        if not self.clients:
+            logger.warning("[发送失败] 当前没有连接的客户端")
+            return
+        
+        success_count = 0
+        failed_count = 0
+        clients_to_remove = []
+        
+        for client_id, websocket in list(self.clients.items()):
+            # 检查连接状态(兼容不同版本的 websockets 库)
+            try:
+                if getattr(websocket, 'closed', False):
+                    logger.warning(f"[发送跳过] 客户端 {client_id} 连接已关闭")
+                    clients_to_remove.append(client_id)
+                    failed_count += 1
+                    continue
+            except Exception:
+                pass  # 如果属性不存在,继续尝试发送
+            
+            try:
+                await websocket.send(message)
+                self.client_info[client_id]['messages_sent'] += 1
+                success_count += 1
+            except (ConnectionClosedOK, ConnectionClosedError) as e:
+                logger.warning(f"[发送失败] 客户端 {client_id} 连接已关闭: {e}")
+                clients_to_remove.append(client_id)
+                failed_count += 1
+            except (asyncio.CancelledError, AssertionError) as e:
+                # 忽略 keepalive 相关的取消和断言错误(连接可能已断开)
+                logger.warning(f"[发送失败] 客户端 {client_id} keepalive 错误(连接可能已断开): {type(e).__name__}")
+                clients_to_remove.append(client_id)
+                failed_count += 1
+            except Exception as e:
+                logger.error(f"[发送失败] 客户端 {client_id}: {type(e).__name__}: {e}")
+                # 如果是连接相关错误,标记为需要移除
+                if "connection" in str(e).lower() or "closed" in str(e).lower():
+                    clients_to_remove.append(client_id)
+                failed_count += 1
+        
+        # 清理已断开的客户端
+        for client_id in clients_to_remove:
+            if client_id in self.clients:
+                del self.clients[client_id]
+            if client_id in self.client_info:
+                del self.client_info[client_id]
+        
+        logger.info(f"[发送完成] 成功={success_count}, 失败={failed_count}, 消息={message}")
+    
+    async def cmd_close_client(self, client_id: int):
+        """关闭指定客户端连接"""
+        if client_id not in self.clients:
+            logger.error(f"[关闭失败] 客户端 {client_id} 不存在")
+            await self.cmd_list_clients()
+            return
+        
+        websocket = self.clients[client_id]
+        try:
+            await websocket.close()
+            logger.info(f"[关闭成功] 客户端 {client_id}")
+        except Exception as e:
+            logger.error(f"[关闭失败] 客户端 {client_id}: {e}")
+    
+    async def cmd_show_stats(self):
+        """显示统计信息"""
+        total_received = sum(info['messages_received'] for info in self.client_info.values())
+        total_sent = sum(info['messages_sent'] for info in self.client_info.values())
+        
+        logger.info("=== 服务器统计信息 ===")
+        logger.info(f"当前连接数: {len(self.clients)}")
+        logger.info(f"总接收消息数: {total_received}")
+        logger.info(f"总发送消息数: {total_sent}")
+        logger.info("====================")
+    
+    def print_help(self):
+        """打印帮助信息"""
+        print("\n可用命令:")
+        print("  list              - 列出所有连接的客户端")
+        print("  send <id> <msg>   - 向指定客户端发送文本消息")
+        print("  sendall <msg>     - 向所有客户端发送文本消息")
+        print("  close <id>        - 关闭指定客户端连接")
+        print("  stats             - 显示统计信息")
+        print("  help              - 显示帮助信息")
+        print("  quit/exit         - 退出服务器")
+        print()
+    
+    async def start_server(self):
+        """启动WebSocket服务器"""
+        logger.info(f"启动WebSocket控制台服务器: ws://{self.host}:{self.port}")
+        
+        # 启动服务器
+        # 注意:ping_interval 和 ping_timeout 设置
+        # - ping_interval: 发送 ping 的间隔(秒),None 表示禁用
+        # - ping_timeout: 等待 pong 的超时时间(秒),None 表示不超时
+        # 如果客户端响应慢,可以增加 ping_timeout 或设置为 None
+        async with serve(
+            self.handle_client,
+            self.host,
+            self.port,
+            ping_interval=30,   # 每30秒发送ping
+            ping_timeout=20,    # ping超时20秒(增加超时时间,避免网络延迟导致误判)
+            close_timeout=10    # 关闭超时10秒
+        ) as server:
+            logger.info("WebSocket服务器启动成功")
+            logger.info("注意: ping/pong 由 websockets 库自动处理")
+            logger.info("输入 'help' 查看可用命令\n")
+            
+            # 启动控制台输入处理任务
+            console_task = asyncio.create_task(self.console_input_handler())
+            
+            # 等待服务器关闭或控制台退出
+            try:
+                await asyncio.gather(
+                    server.wait_closed(),
+                    console_task,
+                    return_exceptions=True
+                )
+            except KeyboardInterrupt:
+                logger.info("收到中断信号,正在关闭服务器...")
+                self.running = False
+            finally:
+                # 关闭所有连接
+                for client_id, websocket in list(self.clients.items()):
+                    try:
+                        await websocket.close()
+                    except Exception:
+                        pass
+                console_task.cancel()
+                try:
+                    await console_task
+                except asyncio.CancelledError:
+                    pass
+
+
+async def main():
+    """主函数"""
+    parser = argparse.ArgumentParser(description='WebSocket控制台服务器')
+    parser.add_argument('--host', default='0.0.0.0', help='服务器地址 (默认: 0.0.0.0)')
+    parser.add_argument('--port', type=int, default=8910, help='服务器端口 (默认: 8910)')
+    parser.add_argument('--verbose', '-v', action='store_true', help='详细日志输出')
+    
+    args = parser.parse_args()
+    
+    if args.verbose:
+        logging.getLogger().setLevel(logging.DEBUG)
+    
+    # 创建服务器实例
+    server = WebSocketConsoleServer(args.host, args.port)
+    
+    # 启动服务器
+    try:
+        await server.start_server()
+    except Exception as e:
+        logger.error(f"服务器运行出错: {e}")
+    finally:
+        logger.info("服务器已关闭")
+
+
+if __name__ == "__main__":
+    try:
+        asyncio.run(main())
+    except KeyboardInterrupt:
+        print("\n服务器已停止")
+
index eced455..7fbf74e 100644 (file)
@@ -13,8 +13,7 @@
  * - 未定义 SUPPORT_N706A_NET → 使用 OpenHarmony 模式(PPP拨号 + lwIP)
  *
  * SSL实现(仅影响N706A内部):
- * - N706A_SSL_USE_MBEDTLS → n706a_net 内部使用 mbedtls 实现SSL
- * - N706A_SSL_USE_AT_COMMAND → n706a_net 内部使用 AT 指令实现SSL
+ * - N706A_SSL_USE_AT_COMMAND → n706a_net 内部使用 AT 指令实现SSL(由N706A模块内置SSL处理)
  *
  * @version 5.1.0
  * @date 2025-10-31
index 3160789..9c48340 100644 (file)
@@ -87,6 +87,159 @@ void ZhihuiHttpClientSetCustomHeader(HttpClient *client, char *header);
  */
 int ZhihuiHttpClientGetRequest(HttpClient *client, const char *url, HttpClientData *clientData);
 
+/* ========== HTTPS 证书和 SSL 配置接口(可选,高级接口)========== */
+
+/**
+ * @brief 初始化HTTPS证书(仅N706A模式需要,可选接口)
+ * @return 0成功,非0失败
+ * @note 
+ *   - 在OpenHarmony模式下,此函数不做任何操作,直接返回成功
+ *   - 默认情况下,HTTPS 连接时会自动初始化证书,无需手动调用此函数
+ *   - 此接口仅用于需要手动控制证书初始化的高级场景
+ */
+int ZhihuiHttpClientInitCertificate(void);
+
+/**
+ * @brief 配置HTTPS SSL参数(仅N706A模式需要,可选接口)
+ * @param ssl_version SSL版本:0=SSL3.0, 1=TLS1.0, 2=TLS1.1, 3=TLS1.2, 4=TLS1.0/1.1/1.2, 5=TLS1.3
+ * @param auth_mode 认证模式:0=不认证, 1=单向认证, 2=双向认证
+ * @param ca_cert CA证书文件名(NULL表示使用默认证书)
+ * @param client_cert 客户端证书文件名(双向认证需要,单向认证为NULL)
+ * @param client_key 客户端密钥文件名(双向认证需要,单向认证为NULL)
+ * @return 0成功,非0失败
+ * @note 
+ *   - 在OpenHarmony模式下,此函数不做任何操作,直接返回成功
+ *   - 默认情况下,HTTPS 连接时会自动配置 SSL 参数(TLS1.2,单向认证),无需手动调用此函数
+ *   - 此接口仅用于需要自定义 SSL 配置的高级场景(如双向认证、不同 SSL 版本等)
+ */
+int ZhihuiHttpClientConfigHttps(int ssl_version,
+                                int auth_mode,
+                                const char *ca_cert,
+                                const char *client_cert,
+                                const char *client_key);
+
+/* ========== 高级 HTTP 客户端接口(类似 n706a_http_client 的接口)========== */
+
+/**
+ * @brief HTTP客户端句柄(不透明类型,内部实现相关)
+ */
+typedef void* ZhihuiHttpClientHandle;
+
+/**
+ * @brief 初始化HTTP客户端(高级接口)
+ * @param handle 输出:HTTP客户端句柄
+ * @return 0成功,非0失败
+ */
+int ZhihuiHttpClientInitAdvanced(ZhihuiHttpClientHandle *handle);
+
+/**
+ * @brief 设置HTTP/HTTPS请求URL(高级接口)
+ * @param handle HTTP客户端句柄
+ * @param url 完整URL(如"http://example.com/path"或"https://example.com/path")
+ * @param keepalive 是否启用长连接,0=关闭,1=启用
+ * @return 0成功,非0失败
+ */
+int ZhihuiHttpClientSetUrl(ZhihuiHttpClientHandle handle, const char *url, int keepalive);
+
+/**
+ * @brief 建立HTTP/HTTPS连接(高级接口)
+ * @param handle HTTP客户端句柄
+ * @return 0成功,非0失败
+ */
+int ZhihuiHttpClientSetupConnection(ZhihuiHttpClientHandle handle);
+
+/**
+ * @brief 设置HTTP请求头(高级接口)
+ * @param handle HTTP客户端句柄
+ * @param header 请求头字符串(如"Content-Type: application/json\r\n")
+ * @return 0成功,非0失败
+ */
+int ZhihuiHttpClientSetHeader(ZhihuiHttpClientHandle handle, const char *header);
+
+/**
+ * @brief 发送HTTP GET请求(高级接口)
+ * @param handle HTTP客户端句柄
+ * @param path 请求路径(如"/api/data",NULL表示使用URL中设置的路径)
+ * @param clientData 响应数据(responseBuf和responseBufLen必须已设置)
+ * @return 0成功,非0失败
+ */
+int ZhihuiHttpClientGet(ZhihuiHttpClientHandle handle, const char *path, HttpClientData *clientData);
+
+/**
+ * @brief 发送HTTP POST请求(高级接口)
+ * @param handle HTTP客户端句柄
+ * @param path 请求路径(如"/api/data",NULL表示使用URL中设置的路径)
+ * @param content_type 内容类型(如"application/json")
+ * @param body 请求体数据
+ * @param body_len 请求体长度
+ * @param clientData 响应数据(responseBuf和responseBufLen必须已设置)
+ * @return 0成功,非0失败
+ */
+int ZhihuiHttpClientPost(ZhihuiHttpClientHandle handle, 
+                         const char *path,
+                         const char *content_type,
+                         const void *body,
+                         size_t body_len,
+                         HttpClientData *clientData);
+
+/**
+ * @brief 获取HTTP响应码(高级接口)
+ * @param handle HTTP客户端句柄
+ * @return HTTP响应码(如200, 404等),-1表示失败
+ */
+int ZhihuiHttpClientGetResponseCode(ZhihuiHttpClientHandle handle);
+
+/**
+ * @brief HTTP流式接收数据回调函数类型
+ * @param user_data 用户数据指针(由应用层传入)
+ * @param data 响应体数据块指针
+ * @param length 数据块长度
+ * @param is_finished 是否是最后一个数据块(true=接收完成,false=还有更多数据)
+ * @return 0表示成功,非0表示错误(会导致接收中断)
+ * 
+ * @note 此回调在URC到达时立即调用,应用层可以在此回调中直接写入文件
+ *       回调函数应该尽快返回,避免阻塞后续数据接收
+ */
+typedef int (*ZhihuiHttpStreamCallback)(void *user_data, 
+                                        const uint8_t *data, 
+                                        size_t length, 
+                                        bool is_finished);
+
+/**
+ * @brief 设置HTTP流式接收回调(用于大文件下载等场景)
+ * @param handle HTTP客户端句柄
+ * @param callback 流式接收回调函数(NULL表示禁用流式模式)
+ * @param user_data 回调函数的用户数据指针
+ * @return 0成功,非0失败
+ * 
+ * @note 启用流式模式后,响应体数据会通过回调函数立即传递给应用层
+ *       应用层可以在回调中直接写入文件,无需等待所有数据接收完成
+ *       流式模式与传统的累积模式互斥,启用流式模式后,responseBuf将不再累积数据
+ */
+int ZhihuiHttpClientSetStreamCallback(ZhihuiHttpClientHandle handle,
+                                      ZhihuiHttpStreamCallback callback,
+                                      void *user_data);
+
+/**
+ * @brief 流式接收HTTP响应数据(高级接口,用于文件下载等场景)
+ * @param handle HTTP客户端句柄
+ * @param clientData 响应数据(responseBuf和responseBufLen必须已设置)
+ * @return HTTP_SUCCESS成功,HTTP_EAGAIN还有更多数据,其他值表示错误
+ * @note 此接口用于在发送GET/POST请求后,循环接收响应数据块
+ *       需要配合 isMore 字段判断是否还有更多数据
+ * @note 如果启用了流式模式(通过ZhihuiHttpClientSetStreamCallback),
+ *       数据会通过回调函数传递,此函数只用于等待响应头解析完成
+ * @note 此函数与旧接口同名但参数不同,为避免冲突,使用不同的函数名
+ */
+int ZhihuiHttpClientRecvResponseAdvanced(ZhihuiHttpClientHandle handle, HttpClientData *clientData);
+
+/**
+ * @brief 关闭HTTP客户端(高级接口)
+ * @param handle HTTP客户端句柄
+ * @return 0成功,非0失败
+ */
+int ZhihuiHttpClientClose(ZhihuiHttpClientHandle handle);
+
 #ifdef __cplusplus
 }
 #endif
index 8487b1f..1f6bd38 100644 (file)
@@ -141,7 +141,7 @@ int ZhihuiHttpClientSend(HttpClient *client, const char *url, int method, HttpCl
             return -1;
         }
         
-        n706a_httpc_result_t ret = n706a_http_client_init(ppp_client, -1);  /* 自动分配socket_id,避免与WebSocket冲突 */
+        n706a_httpc_result_t ret = n706a_http_client_init(ppp_client);  /* 自动分配socket_id,避免与WebSocket冲突 */
         if (ret != N706A_HTTP_SUCCESS) {
             free(ppp_client);
             return -1;
@@ -274,3 +274,332 @@ int ZhihuiHttpClientGetRequest(HttpClient *client, const char *url, HttpClientDa
     }
 }
 
+/* ========== HTTPS 证书和 SSL 配置接口实现 ========== */
+
+int ZhihuiHttpClientInitCertificate(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:不需要证书初始化,直接返回成功 */
+        return 0;
+    } else {
+        /* N706A 模式:调用 n706a_http_client_init_certificate */
+        n706a_httpc_result_t ret = n706a_http_client_init_certificate();
+        return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+    }
+}
+
+int ZhihuiHttpClientConfigHttps(int ssl_version,
+                                int auth_mode,
+                                const char *ca_cert,
+                                const char *client_cert,
+                                const char *client_key)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:不需要SSL配置,直接返回成功 */
+        return 0;
+    } else {
+        /* N706A 模式:调用 n706a_http_client_config_https */
+        n706a_httpc_result_t ret = n706a_http_client_config_https(
+            ssl_version,
+            auth_mode,
+            ca_cert,
+            client_cert,
+            client_key
+        );
+        return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+    }
+}
+
+/* ========== 高级 HTTP 客户端接口实现 ========== */
+
+/* N706A 模式下的客户端句柄结构 */
+typedef struct {
+    n706a_http_client_t *n706a_client;
+} zhihui_http_client_handle_t;
+
+int ZhihuiHttpClientInitAdvanced(ZhihuiHttpClientHandle *handle)
+{
+    if (!handle) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持高级接口,返回错误 */
+        printf("[ZhihuiNet][HTTP] Advanced HTTP client interface not supported in OpenHarmony mode\n");
+        return -1;
+    } else {
+        /* N706A 模式:创建客户端句柄 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)malloc(sizeof(zhihui_http_client_handle_t));
+        if (!h) {
+            return -1;
+        }
+        
+        h->n706a_client = (n706a_http_client_t *)malloc(sizeof(n706a_http_client_t));
+        if (!h->n706a_client) {
+            free(h);
+            return -1;
+        }
+        
+        memset(h->n706a_client, 0, sizeof(n706a_http_client_t));
+        
+        n706a_httpc_result_t ret = n706a_http_client_init(h->n706a_client);
+        if (ret != N706A_HTTP_SUCCESS) {
+            free(h->n706a_client);
+            free(h);
+            return -1;
+        }
+        
+        *handle = (ZhihuiHttpClientHandle)h;
+        return 0;
+    }
+}
+
+int ZhihuiHttpClientSetUrl(ZhihuiHttpClientHandle handle, const char *url, int keepalive)
+{
+    if (!handle || !url) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持高级接口 */
+        return -1;
+    } else {
+        /* N706A 模式 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)handle;
+        n706a_httpc_result_t ret = n706a_http_client_set_url(h->n706a_client, url, keepalive);
+        return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+    }
+}
+
+int ZhihuiHttpClientSetupConnection(ZhihuiHttpClientHandle handle)
+{
+    if (!handle) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持高级接口 */
+        return -1;
+    } else {
+        /* N706A 模式 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)handle;
+        n706a_httpc_result_t ret = n706a_http_client_setup_connection(h->n706a_client);
+        return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+    }
+}
+
+int ZhihuiHttpClientSetHeader(ZhihuiHttpClientHandle handle, const char *header)
+{
+    if (!handle || !header) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持高级接口 */
+        return -1;
+    } else {
+        /* N706A 模式 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)handle;
+        n706a_httpc_result_t ret = n706a_http_client_set_header(h->n706a_client, header);
+        return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+    }
+}
+
+int ZhihuiHttpClientGet(ZhihuiHttpClientHandle handle, const char *path, HttpClientData *clientData)
+{
+    if (!handle || !clientData) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持高级接口 */
+        return -1;
+    } else {
+        /* N706A 模式 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)handle;
+        
+        /* 准备 n706a_http_client_data_t 结构 */
+        n706a_http_client_data_t n706a_data = {0};
+        n706a_data.responseBuf = clientData->responseBuf;
+        n706a_data.responseBufLen = clientData->responseBufLen;
+        n706a_data.headerBuf = clientData->headerBuf;
+        n706a_data.headerBufLen = clientData->headerBufLen;
+        
+        n706a_httpc_result_t ret = n706a_http_client_get(h->n706a_client, path, &n706a_data);
+        
+        /* 更新 clientData(注意:n706a_http_client_data_t 和 HttpClientData 字段名可能不同) */
+        if (clientData) {
+            clientData->responseContentLen = n706a_data.responseContentLen;
+            clientData->contentBlockLen = n706a_data.responseContentLen;
+            clientData->isMore = n706a_data.isMore;
+            /* 注意:responseBufLen 在 n706a 中可能被更新为实际接收的长度 */
+            if (n706a_data.responseBufLen > 0 && n706a_data.responseBufLen != clientData->responseBufLen) {
+                clientData->responseBufLen = n706a_data.responseBufLen;
+            }
+        }
+        
+        return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+    }
+}
+
+int ZhihuiHttpClientPost(ZhihuiHttpClientHandle handle, 
+                         const char *path,
+                         const char *content_type,
+                         const void *body,
+                         size_t body_len,
+                         HttpClientData *clientData)
+{
+    if (!handle || !clientData) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持高级接口 */
+        return -1;
+    } else {
+        /* N706A 模式 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)handle;
+        
+        /* 准备 n706a_http_client_data_t 结构 */
+        n706a_http_client_data_t n706a_data = {0};
+        n706a_data.responseBuf = clientData->responseBuf;
+        n706a_data.responseBufLen = clientData->responseBufLen;
+        n706a_data.headerBuf = clientData->headerBuf;
+        n706a_data.headerBufLen = clientData->headerBufLen;
+        
+        n706a_httpc_result_t ret = n706a_http_client_post(h->n706a_client, 
+                                                          path,
+                                                          content_type,
+                                                          (const char *)body,
+                                                          body_len,
+                                                          &n706a_data);
+        
+        /* 更新 clientData */
+        if (clientData) {
+            clientData->responseContentLen = n706a_data.responseContentLen;
+            clientData->contentBlockLen = n706a_data.responseContentLen;
+            clientData->isMore = n706a_data.isMore;
+            /* 注意:responseBufLen 在 n706a 中可能被更新为实际接收的长度 */
+            if (n706a_data.responseBufLen > 0 && n706a_data.responseBufLen != clientData->responseBufLen) {
+                clientData->responseBufLen = n706a_data.responseBufLen;
+            }
+        }
+        
+        return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+    }
+}
+
+int ZhihuiHttpClientGetResponseCode(ZhihuiHttpClientHandle handle)
+{
+    if (!handle) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持高级接口 */
+        return -1;
+    } else {
+        /* N706A 模式 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)handle;
+        return n706a_http_client_get_response_code(h->n706a_client);
+    }
+}
+
+int ZhihuiHttpClientSetStreamCallback(ZhihuiHttpClientHandle handle,
+                                      ZhihuiHttpStreamCallback callback,
+                                      void *user_data)
+{
+    if (!handle) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持流式接收 */
+        return -1;
+    } else {
+        /* N706A 模式 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)handle;
+        
+        /* 将回调函数类型转换为 n706a_http_stream_callback_t */
+        n706a_http_stream_callback_t n706a_callback = (n706a_http_stream_callback_t)callback;
+        
+        n706a_httpc_result_t ret = n706a_http_client_set_stream_callback(h->n706a_client, 
+                                                                         n706a_callback, 
+                                                                         user_data);
+        return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+    }
+}
+
+int ZhihuiHttpClientRecvResponseAdvanced(ZhihuiHttpClientHandle handle, HttpClientData *clientData)
+{
+    if (!handle || !clientData) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持高级接口 */
+        return -1;
+    } else {
+        /* N706A 模式 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)handle;
+        
+        /* 准备 n706a_http_client_data_t 结构 */
+        n706a_http_client_data_t n706a_data = {0};
+        n706a_data.responseBuf = clientData->responseBuf;
+        n706a_data.responseBufLen = clientData->responseBufLen;
+        n706a_data.headerBuf = clientData->headerBuf;
+        n706a_data.headerBufLen = clientData->headerBufLen;
+        n706a_data.isMore = clientData->isMore;
+        n706a_data.responseContentLen = clientData->responseContentLen;
+        
+        /* 如果启用了流式模式,使用流式接收接口 */
+        n706a_httpc_result_t ret;
+        if (h->n706a_client && 
+            ((n706a_http_client_t *)h->n706a_client)->stream_mode_enabled) {
+            /* 流式模式:等待后续URC数据到达 */
+            ret = n706a_http_client_recv_response_stream(h->n706a_client, &n706a_data, 5000);
+        } else {
+            /* 传统模式:调用 http_receive_response(但它是静态函数,无法直接调用) */
+            /* 实际上,传统模式下,数据已经在 http_receive_response 中处理了 */
+            /* 这里只需要返回状态 */
+            ret = (n706a_data.isMore) ? N706A_HTTP_EAGAIN : N706A_HTTP_SUCCESS;
+        }
+        
+        /* 更新 clientData */
+        clientData->isMore = n706a_data.isMore;
+        clientData->responseContentLen = n706a_data.responseContentLen;
+        clientData->contentBlockLen = n706a_data.contentBlockLen;
+        
+        if (ret == N706A_HTTP_EAGAIN) {
+            return HTTP_EAGAIN;  /* 还有更多数据 */
+        } else if (ret == N706A_HTTP_SUCCESS) {
+            return HTTP_SUCCESS;  /* 接收完成 */
+        } else {
+            return -1;  /* 错误 */
+        }
+    }
+}
+
+int ZhihuiHttpClientClose(ZhihuiHttpClientHandle handle)
+{
+    if (!handle) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* OpenHarmony 模式:暂不支持高级接口 */
+        return -1;
+    } else {
+        /* N706A 模式 */
+        zhihui_http_client_handle_t *h = (zhihui_http_client_handle_t *)handle;
+        
+        if (h->n706a_client) {
+            n706a_http_client_close(h->n706a_client);
+            free(h->n706a_client);
+        }
+        
+        free(h);
+        return 0;
+    }
+}
+
index 174c11c..484d48a 100644 (file)
 #include <string.h>
 #include <stdlib.h>
 #include "soc_osal.h"
+#include "custom_utils.h"  /* 用于 app_task_definition_t, USE_EXTEND_MEM */
+#include "thread_init.h"  /* 确保包含 app_task_definition_t, USE_EXTEND_MEM, osPriorityNormal, osThreadFunc_t */
 
 /* 包含 N706A 网络层的头文件 */
 #include "n706a_websocket.h"
+#include "n706a_tcp_client.h"
 
 /* 外部函数:获取当前实现类型 */
 extern zhihui_net_impl_type_t ZhihuiNetGetImplementation(void);
@@ -31,7 +34,7 @@ typedef struct {
     bool initialized;
     bool running;
     int client_id;
-    osal_task *worker_thread;
+    uint32_t worker_thread;  /* 使用 uint32_t 以配合 uapi_create_osThread_custom */
     
     /* 保存的回调 */
     ws_call_backs_t callbacks;
@@ -40,6 +43,7 @@ typedef struct {
     bool stop_requested;
     bool reconnect_requested;
     bool sleep_mode;
+    bool is_connected;  /* 连接状态标志 */
     
 } n706a_ws_adapter_t;
 
@@ -54,6 +58,9 @@ static void n706a_on_open_callback(int client_id)
 {
     printf("[ZhihuiNet][N706A][WS] Connection opened (client_id=%d)\n", client_id);
     
+    /* 标记为已连接(异步确认) */
+    g_adapter.is_connected = true;
+    
     if (g_adapter.callbacks.event_callback) {
         g_adapter.callbacks.event_callback(WS_EVENT_CONNECTED);
     }
@@ -83,6 +90,10 @@ static void n706a_on_message_callback(int client_id, const char *data, size_t le
         if (is_finished) {
             /* 完整消息 */
             if (g_adapter.callbacks.message_callback) {
+                /* 注意:data 应该已经是以 \0 结尾的字符串
+                 * (由 n706a_websocket.c 在解析层保证,使用 recv_buffer 作为临时缓冲区)
+                 * 这里直接传递即可,不需要再次创建副本
+                 */
                 g_adapter.callbacks.message_callback(data, 0);
             }
         } else {
@@ -102,6 +113,9 @@ static void n706a_on_error_callback(int client_id, int error_code, const char *e
     printf("[ZhihuiNet][N706A][WS] Error occurred: code=%d, msg=%s\n", 
            error_code, error_msg ? error_msg : "unknown");
     
+    /* 标记为未连接,worker线程会在下次循环时尝试重连 */
+    g_adapter.is_connected = false;
+    
     if (g_adapter.callbacks.event_callback) {
         g_adapter.callbacks.event_callback(WS_EVENT_DISCONNECTED);
     }
@@ -115,6 +129,9 @@ static void n706a_on_close_callback(int client_id, int close_code, const char *r
     printf("[ZhihuiNet][N706A][WS] Connection closed: code=%d, reason=%s\n", 
            close_code, reason ? reason : "unknown");
     
+    /* 标记为未连接,worker线程会在下次循环时尝试重连 */
+    g_adapter.is_connected = false;
+    
     if (g_adapter.callbacks.event_callback) {
         g_adapter.callbacks.event_callback(WS_EVENT_DISCONNECTED);
     }
@@ -127,19 +144,18 @@ static void* ws_worker_thread(void *arg)
 {
     printf("[ZhihuiNet][N706A][WS] Worker thread started\n");
     
-    /* 获取URL */
-    char url[512] = {0};
-    char header[1024] = {0};
+    /* 动态分配大缓冲区,避免栈溢出 */
+    char *url = (char *)malloc(512);
+    char *header = (char *)malloc(1024);
     
-    if (g_adapter.callbacks.url_callback) {
-        g_adapter.callbacks.url_callback(url, sizeof(url), header, sizeof(header));
-    } else {
-        printf("[ZhihuiNet][N706A][WS] ERROR: No URL callback provided\n");
+    if (!url || !header) {
+        printf("[ZhihuiNet][N706A][WS] ERROR: Failed to allocate memory for url/header\n");
+        if (url) free(url);
+        if (header) free(header);
+        g_adapter.running = false;
         return NULL;
     }
     
-    printf("[ZhihuiNet][N706A][WS] Connecting to: %s\n", url);
-    
     /* 创建WebSocket客户端 */
     g_adapter.client_id = n706a_websocket_constructor(
         n706a_on_open_callback,
@@ -150,39 +166,158 @@ static void* ws_worker_thread(void *arg)
     
     if (g_adapter.client_id < 0) {
         printf("[ZhihuiNet][N706A][WS] ERROR: Failed to create WebSocket client\n");
+        free(url);
+        free(header);
         g_adapter.running = false;
         return NULL;
     }
     
     printf("[ZhihuiNet][N706A][WS] WebSocket client created (id=%d)\n", g_adapter.client_id);
     
-    /* 连接到服务器 */
-    if (g_adapter.callbacks.event_callback) {
-        g_adapter.callbacks.event_callback(WS_EVENT_CONNECTING);
-    }
-    
-    int ret = n706a_websocket_connect(g_adapter.client_id, url, 
-                                      strlen(header) > 0 ? header : NULL);
-    if (ret != N706A_WS_SUCCESS) {
-        printf("[ZhihuiNet][N706A][WS] ERROR: Failed to connect (ret=%d)\n", ret);
-        n706a_websocket_close(g_adapter.client_id);
-        g_adapter.running = false;
-        return NULL;
-    }
-    
     /* 主循环 */
     uint32_t interval = g_adapter.callbacks.main_thread_interval_ms;
     if (interval == 0) {
         interval = 1000; // 默认1秒
     }
     
+    g_adapter.is_connected = false;
+    
+    /* 重试相关变量 */
+    uint32_t retry_count = 0;
+    uint32_t retry_delay_ms = 1000;  /* 初始重试延迟:1秒 */
+    const uint32_t max_retry_delay_ms = 30000;  /* 最大重试延迟:30秒 */
+    const uint32_t max_retry_count = 10;  /* 最大重试次数(超过后重置延迟) */
+    
     while (g_adapter.running && !g_adapter.stop_requested) {
-        /* 处理心跳 */
-        if (g_adapter.callbacks.heartbeat_callback) {
+        /* 检查连接状态 */
+        if (!g_adapter.is_connected) {
+            /* 未连接状态:先检查token是否存在 */
+            memset(url, 0, 512);
+            memset(header, 0, 1024);
+            
+            if (g_adapter.callbacks.url_callback) {
+                g_adapter.callbacks.url_callback(url, 512, header, 1024);
+            } else {
+                printf("[ZhihuiNet][N706A][WS] ERROR: No URL callback provided\n");
+                osal_msleep(interval);
+                continue;
+            }
+            
+            /* 检查URL中是否包含有效的token(检查deviceToken参数是否为空) */
+            bool has_valid_token = false;
+            const char *token_param = strstr(url, "deviceToken=");
+            if (token_param) {
+                token_param += strlen("deviceToken=");
+                const char *token_end = strchr(token_param, '&');
+                if (token_end) {
+                    size_t token_len = token_end - token_param;
+                    has_valid_token = (token_len > 0);
+                } else {
+                    /* token在URL末尾 */
+                    has_valid_token = (strlen(token_param) > 0);
+                }
+            }
+            
+            if (!has_valid_token) {
+                /* Token不存在,sleep后继续循环,不进行网络连接 */
+                printf("[ZhihuiNet][N706A][WS] Token not available, waiting...\n");
+                osal_msleep(interval);
+                continue;
+            }
+            
+            /* Token存在,尝试连接 */
+            printf("[ZhihuiNet][N706A][WS] Connecting to: %s\n", url);
+            
+            if (g_adapter.callbacks.event_callback) {
+                g_adapter.callbacks.event_callback(WS_EVENT_CONNECTING);
+            }
+            
+            int ret = n706a_websocket_connect(g_adapter.client_id, url, 
+                                              strlen(header) > 0 ? header : NULL);
+            
+            if (ret == N706A_WS_SUCCESS) {
+                g_adapter.is_connected = true;
+                retry_count = 0;  /* 连接成功,重置重试计数 */
+                retry_delay_ms = 1000;  /* 重置重试延迟 */
+                printf("[ZhihuiNet][N706A][WS] Connected successfully\n");
+            } else {
+                /* 连接失败,根据错误类型处理 */
+                if (ret == N706A_WS_ERROR_ALREADY_OPEN) {
+                    /* 这个错误不应该出现,因为 n706a_websocket_connect 已经处理了 OPEN 状态 */
+                    /* 如果仍然出现,说明状态异常,强制关闭连接 */
+                    printf("[ZhihuiNet][N706A][WS] Unexpected ALREADY_OPEN error (ret=%d), closing connection...\n", ret);
+                    n706a_websocket_close(g_adapter.client_id);
+                    g_adapter.is_connected = false;
+                    osal_msleep(200);  /* 等待关闭完成 */
+                } else {
+                    printf("[ZhihuiNet][N706A][WS] Failed to connect (ret=%d), will retry after %u ms (retry #%u)\n", 
+                           ret, retry_delay_ms, retry_count + 1);
+                }
+                
+                /* 增加重试计数 */
+                retry_count++;
+                
+                /* 使用指数退避策略 */
+                if (retry_count >= max_retry_count) {
+                    /* 达到最大重试次数,重置计数和延迟 */
+                    retry_count = 0;
+                    retry_delay_ms = 1000;
+                } else {
+                    /* 指数退避:每次重试延迟翻倍,但不超过最大值 */
+                    retry_delay_ms *= 2;
+                    if (retry_delay_ms > max_retry_delay_ms) {
+                        retry_delay_ms = max_retry_delay_ms;
+                    }
+                }
+                
+                /* 等待重试延迟 */
+                osal_msleep(retry_delay_ms);
+                continue;
+            }
+        }
+
+
+        
+        /* 处理心跳(仅在已连接时) */
+        if (g_adapter.is_connected && g_adapter.callbacks.heartbeat_callback) {
             int need_heartbeat = g_adapter.callbacks.heartbeat_callback();
             if (need_heartbeat) {
                 // 发送心跳(可以是Ping帧或自定义消息)
-                n706a_websocket_send_ping(g_adapter.client_id, NULL, 0);
+                int ret = n706a_websocket_send_ping(g_adapter.client_id, NULL, 0);
+                if (ret != N706A_WS_SUCCESS) {
+                    /* 发送失败,检查TCP连接状态 */
+                    printf("[ZhihuiNet][N706A][WS] Ping failed (ret=%d), checking TCP connection state...\n", ret);
+                    
+                    /* 获取WebSocket客户端的socket_id并检查TCP连接状态 */
+                    int socket_id = n706a_websocket_get_socket_id(g_adapter.client_id);
+                    if (socket_id >= 0) {
+                        /* 查询TCP连接状态 */
+                        bool is_connected = false;
+                        bool is_ssl = false;
+                        n706a_tcp_result_t query_ret = n706a_tcp_query_socket_status(socket_id, &is_connected, &is_ssl);
+                        
+                        if (query_ret == N706A_TCP_SUCCESS) {
+                            if (is_connected) {
+                                /* 连接仍然有效,可能是发送失败但连接未断开 */
+                                printf("[ZhihuiNet][N706A][WS] Ping failed but TCP connection is still active, will retry\n");
+                                /* 不设置 g_adapter.is_connected = false,继续使用当前连接 */
+                            } else {
+                                /* 连接已断开 */
+                                printf("[ZhihuiNet][N706A][WS] Ping failed and TCP connection is disconnected\n");
+                                g_adapter.is_connected = false;
+                            }
+                        } else {
+                            /* 查询失败,保守处理:判定为连接断开 */
+                            printf("[ZhihuiNet][N706A][WS] Ping failed and failed to query socket status, assuming disconnected\n");
+                            g_adapter.is_connected = false;
+                        }
+                    } else {
+                        /* 无法获取socket_id,判定为连接断开 */
+                        printf("[ZhihuiNet][N706A][WS] Ping failed and cannot get socket ID, assuming disconnected\n");
+                        g_adapter.is_connected = false;
+                    }
+                    continue;
+                }
             }
         }
         
@@ -190,15 +325,11 @@ static void* ws_worker_thread(void *arg)
         if (g_adapter.reconnect_requested) {
             printf("[ZhihuiNet][N706A][WS] Reconnecting...\n");
             n706a_websocket_close(g_adapter.client_id);
+            g_adapter.is_connected = false;
             osal_msleep(1000);
-            
-            /* 重新连接 */
-            ret = n706a_websocket_connect(g_adapter.client_id, url, 
-                                         strlen(header) > 0 ? header : NULL);
-            if (ret == N706A_WS_SUCCESS) {
-                printf("[ZhihuiNet][N706A][WS] Reconnection successful\n");
-            }
             g_adapter.reconnect_requested = false;
+            /* 继续循环,让连接逻辑处理重连 */
+            continue;
         }
         
         /* 休眠模式处理 */
@@ -212,7 +343,11 @@ static void* ws_worker_thread(void *arg)
     
     /* 清理 */
     printf("[ZhihuiNet][N706A][WS] Worker thread exiting\n");
-    n706a_websocket_close(g_adapter.client_id);
+    if (g_adapter.client_id >= 0) {
+        n706a_websocket_close(g_adapter.client_id);
+    }
+    free(url);
+    free(header);
     g_adapter.running = false;
     
     return NULL;
@@ -247,23 +382,28 @@ int ZhihuiWsStart(const ws_call_backs_t *callbacks)
         g_adapter.sleep_mode = false;
         g_adapter.client_id = -1;
         
-        /* 创建worker线程 */
-        osal_kthread_lock();
-        g_adapter.worker_thread = osal_kthread_create(
-            (osal_kthread_handler)ws_worker_thread,
-            NULL,
-            "ws_worker",
-            8192
-        );
-        osal_kthread_unlock();
+        /* 创建worker线程 - 使用 uapi_create_osThread_custom 在 PSRAM 中分配栈 */
+        /* ✅ 优化:通过 USE_EXTEND_MEM 将线程栈分配到 PSRAM,节省珍贵的 DTCM */
+        app_task_definition_t ws_worker_task_cfg = {0};
+        ws_worker_task_cfg.attr.name = "ws_worker";
+        ws_worker_task_cfg.attr.priority = osPriorityNormal;
+        ws_worker_task_cfg.attr.stack_size = 16384;  // 16KB栈(增加栈大小以避免在HTTP和WebSocket同时运行时的栈溢出)
+        ws_worker_task_cfg.func = (osThreadFunc_t)ws_worker_thread;
+        ws_worker_task_cfg.task_handle = &g_adapter.worker_thread;
+        ws_worker_task_cfg.use_mem = USE_EXTEND_MEM;  // ⭐ 关键:使用 PSRAM 分配栈
+        ws_worker_task_cfg.create_queue_handle = NULL;
         
-        if (!g_adapter.worker_thread) {
-            printf("[ZhihuiNet][N706A][WS] ERROR: Failed to create worker thread\n");
+        errcode_t ret = uapi_create_osThread_custom(&ws_worker_task_cfg);
+        if (ret != ERRCODE_SUCC) {
+            printf("[ZhihuiNet][N706A][WS] ERROR: Failed to create worker thread: 0x%x\n", ret);
             g_adapter.initialized = false;
             g_adapter.running = false;
             return -1;
         }
         
+        printf("[ZhihuiNet][N706A][WS] Worker thread created with PSRAM stack (handle=0x%X)\n", 
+               g_adapter.worker_thread);
+        
         printf("[ZhihuiNet][N706A][WS] Service started successfully\n");
         return 0;
     }