把雷鸟的网络接口都抽取到公共接口,增加openharmon和n706a AT模式的支持。默认openharmon的lwip模式已经可以正常工作。n706a的at模式还需要重构。
author孙春阳 <snickersun@163.com>
Fri, 31 Oct 2025 07:21:50 +0000 (15:21 +0800)
committer孙春阳 <snickersun@163.com>
Fri, 31 Oct 2025 07:21:50 +0000 (15:21 +0800)
54 files changed:
code/sdk/build/config/target_config/brandy/config.py
code/sdk/zhihui/CMakeLists.txt
code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
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_speech_chat.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_choose.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_exit.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_finish.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_random.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_web_ack.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c
code/sdk/zhihui/n706a_net/CMakeLists.txt [new file with mode: 0644]
code/sdk/zhihui/n706a_net/include/n706a_http_client.h [new file with mode: 0644]
code/sdk/zhihui/n706a_net/include/n706a_tcp_client.h [new file with mode: 0644]
code/sdk/zhihui/n706a_net/include/n706a_websocket.h [new file with mode: 0644]
code/sdk/zhihui/n706a_net/src/n706a_http_client.c [new file with mode: 0644]
code/sdk/zhihui/n706a_net/src/n706a_tcp_client.c [new file with mode: 0644]
code/sdk/zhihui/n706a_net/src/n706a_websocket.c [new file with mode: 0644]
code/sdk/zhihui/ppp/example/n706a_http_tcp_example.c [new file with mode: 0644]
code/sdk/zhihui/ppp/example/n706a_ssl_comparison_example.c [new file with mode: 0644]
code/sdk/zhihui/ppp/example/n706a_websocket_example.c [new file with mode: 0644]
code/sdk/zhihui/ppp/example/ppp_at_cmd.c
code/sdk/zhihui/ppp/include/n706a_http_client.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/n706a_tcp_client.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/n706a_websocket.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_api.h
code/sdk/zhihui/ppp/include/ppp_at_handler.h
code/sdk/zhihui/ppp/src/n706a_http_client.c [new file with mode: 0644]
code/sdk/zhihui/ppp/src/n706a_tcp_client.c [new file with mode: 0644]
code/sdk/zhihui/ppp/src/n706a_websocket.c [new file with mode: 0644]
code/sdk/zhihui/ppp/src/ppp_api.c
code/sdk/zhihui/ppp/src/ppp_at_handler.c
code/sdk/zhihui/ppp/src/ppp_interrupt.c
code/sdk/zhihui/ppp/src/ppp_lwip.c
code/sdk/zhihui/ppp/src/ppp_power_ctl.c
code/sdk/zhihui/zhihui_net/CMakeLists.txt [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/adapter/zhihui_net_openharmony_adapter.c [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/adapter/zhihui_net_openharmony_adapter.h [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/adapter/zhihui_net_ppp_adapter.c [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/adapter/zhihui_net_ppp_adapter.h [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/example/zhihui_net_http_example.c [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/example/zhihui_net_ws_example.c [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/include/zhihui_net.h [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/include/zhihui_net_http.h [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/include/zhihui_net_websocket.h [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/src/zhihui_net.c [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/src/zhihui_net_http.c [new file with mode: 0644]
code/sdk/zhihui/zhihui_net/src/zhihui_net_websocket.c [new file with mode: 0644]

index 28c9282..5b68f44 100644 (file)
@@ -1491,6 +1491,11 @@ target = {
                         "tcp_test",
                         #websocket 相关配置
                         "zhihui_websocket",
+
+                        #zhihui_net 相关配置
+                        "zhihui_net",
+                        "n706a_net",
+                    
                         #ai 相关配置
                         "aiMediaService",       
                         "aiService",
index 8908022..8195aac 100644 (file)
@@ -31,3 +31,5 @@ add_subdirectory_if_exist(ai_new)
 add_subdirectory_if_exist(imsl)
 add_subdirectory_if_exist(minimp3)
 add_subdirectory_if_exist(shake)
+add_subdirectory_if_exist(n706a_net)
+add_subdirectory_if_exist(zhihui_net)
index 2db97e4..d4da6c3 100644 (file)
@@ -30,8 +30,7 @@ set(PUBLIC_HEADER
     ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include
     ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include
     ${ROOT_DIR}/open_source/openharmony/third_party/cJSON/
-    ${ROOT_DIR}/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include
-    ${ROOT_DIR}/zhihui/websocket/os_ws
+    ${ROOT_DIR}/zhihui/zhihui_net/include
 )
 
 # 公共宏定义(可选)
index cfd1db2..f559259 100644 (file)
@@ -1,6 +1,6 @@
 #include "ln_api.h"
-/* for https/http client */
-#include "netstack/http/include/httpclient.h"
+/* 智慧网络统一接口(已包含 OpenHarmony 类型定义) */
+#include "zhihui_net.h"
 /* for HMAC-SHA256 */
 #include "mbedtls/md.h"
 /* for MD5 file verify */
@@ -479,24 +479,24 @@ int uapi_ln_download_file(const char *url, const char *save_path)
     }
 
     // 建立连接
-    ret = HttpClientConn(&client, url);
+    ret = ZhihuiHttpClientConn(&client, url);
     if (ret != 0)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] connection failed, ret=0x%x", ret);
         fclose(fd);
-        HttpClientUnprepare(&client_data);
+        ZhihuiHttpClientUnprepare(&client_data);
         g_ln_is_file_downloading = 0;
         return ERRCODE_FAIL;
     }
 
     // 发送GET请求
-    ret = HttpClientSend(&client, url, HTTP_GET, &client_data);
+    ret = ZhihuiHttpClientSend(&client, url, HTTP_GET, &client_data);
     if (ret != 0)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] send request failed, ret=0x%x", ret);
         fclose(fd);
-        HttpClientClse(&client);
-        HttpClientUnprepare(&client_data);
+        ZhihuiHttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&client_data);
         g_ln_is_file_downloading = 0;
         return ERRCODE_FAIL;
     }
@@ -504,7 +504,7 @@ int uapi_ln_download_file(const char *url, const char *save_path)
     // 接收响应数据
     do
     {
-        HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data);
+        HTTPC_RESULT recv_res = ZhihuiHttpClientRecvResponse(&client, &client_data);
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] recv_res = %d", recv_res);
         if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
         {
@@ -517,8 +517,8 @@ int uapi_ln_download_file(const char *url, const char *save_path)
                     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] write failed, written=%zu, expected=%d",
                                   written, client_data.contentBlockLen);
                     fclose(fd);
-                    HttpClientClse(&client);
-                    HttpClientUnprepare(&client_data);
+                    ZhihuiHttpClientClse(&client);
+                    ZhihuiHttpClientUnprepare(&client_data);
                     return ERRCODE_FAIL;
                 }
                 total_written += written;
@@ -557,8 +557,8 @@ int uapi_ln_download_file(const char *url, const char *save_path)
         }
     }
 
-    HttpClientUnprepare(&client_data);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&client_data);
+    ZhihuiHttpClientClse(&client);
     fclose(fd);
     g_ln_is_file_downloading = 0;
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 下载过程结束,ret=%d", ret);
@@ -655,7 +655,7 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path)
         header_buf = (char *)malloc(128);
         if (!header_buf)
         {
-            HttpClientUnprepare(&client_data);
+            ZhihuiHttpClientUnprepare(&client_data);
             fclose(fd);
             return ERRCODE_FAIL;
         }
@@ -663,7 +663,7 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path)
         if (hdr <= 0)
         {
             free(header_buf);
-            HttpClientUnprepare(&client_data);
+            ZhihuiHttpClientUnprepare(&client_data);
             fclose(fd);
             return ERRCODE_FAIL;
         }
@@ -672,37 +672,37 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path)
     }
 
     /* 建立连接并发送请求 */
-    ret = HttpClientConn(&client, url);
+    ret = ZhihuiHttpClientConn(&client, url);
     if (ret != 0)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] connection failed, ret=0x%x", ret);
         if (header_buf) free(header_buf);
         fclose(fd);
-        HttpClientUnprepare(&client_data);
+        ZhihuiHttpClientUnprepare(&client_data);
         return ERRCODE_FAIL;
     }
 
-    ret = HttpClientSend(&client, url, HTTP_GET, &client_data);
+    ret = ZhihuiHttpClientSend(&client, url, HTTP_GET, &client_data);
     if (ret != 0)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] send request failed, ret=0x%x", ret);
         if (header_buf) free(header_buf);
         fclose(fd);
-        HttpClientClse(&client);
-        HttpClientUnprepare(&client_data);
+        ZhihuiHttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&client_data);
         return ERRCODE_FAIL;
     }
 
     /* 首次接收,判定状态码(200则改为覆盖写)*/
     {
-        HTTPC_RESULT first_recv = HttpClientRecvResponse(&client, &client_data);
+        HTTPC_RESULT first_recv = ZhihuiHttpClientRecvResponse(&client, &client_data);
         if (!(first_recv == HTTP_SUCCESS || first_recv == HTTP_EAGAIN))
         {
             WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] first recv failed: %d", first_recv);
             if (header_buf) free(header_buf);
             fclose(fd);
-            HttpClientClse(&client);
-            HttpClientUnprepare(&client_data);
+            ZhihuiHttpClientClse(&client);
+            ZhihuiHttpClientUnprepare(&client_data);
             return ERRCODE_FAIL;
         }
 
@@ -716,8 +716,8 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path)
             if (!fd)
             {
                 if (header_buf) free(header_buf);
-                HttpClientClse(&client);
-                HttpClientUnprepare(&client_data);
+                ZhihuiHttpClientClse(&client);
+                ZhihuiHttpClientUnprepare(&client_data);
                 return ERRCODE_FAIL;
             }
             resume_offset = 0;
@@ -733,8 +733,8 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path)
                               written, client_data.contentBlockLen);
                 if (header_buf) free(header_buf);
                 fclose(fd);
-                HttpClientClse(&client);
-                HttpClientUnprepare(&client_data);
+                ZhihuiHttpClientClse(&client);
+                ZhihuiHttpClientUnprepare(&client_data);
                 return ERRCODE_FAIL;
             }
             total_written += written;
@@ -744,7 +744,7 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path)
     /* 后续接收循环 */
     while (client_data.isMore)
     {
-        HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data);
+        HTTPC_RESULT recv_res = ZhihuiHttpClientRecvResponse(&client, &client_data);
         if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
         {
             if (client_data.contentBlockLen > 0)
@@ -756,8 +756,8 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path)
                                   written, client_data.contentBlockLen);
                     if (header_buf) free(header_buf);
                     fclose(fd);
-                    HttpClientClse(&client);
-                    HttpClientUnprepare(&client_data);
+                    ZhihuiHttpClientClse(&client);
+                    ZhihuiHttpClientUnprepare(&client_data);
                     return ERRCODE_FAIL;
                 }
                 total_written += written;
@@ -785,8 +785,8 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path)
     }
 
     if (header_buf) free(header_buf);
-    HttpClientUnprepare(&client_data);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&client_data);
+    ZhihuiHttpClientClse(&client);
     fclose(fd);
     return ret;
 }
@@ -829,13 +829,13 @@ int uapi_ln_download_file_resume_with_retry(const char *url, const char *save_pa
     return ret;
 }
 /* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */
-HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size)
+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 = HttpClientPrepare(clientData, header_size, resp_size);
+    HTTPC_RESULT ret = ZhihuiHttpClientPrepare(clientData, header_size, resp_size);
     if (ret == HTTP_SUCCESS)
         return ret;
     int tries = 0;
@@ -845,7 +845,7 @@ HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, i
         resp_size = resp_size < (16 * 1024) ? (16 * 1024) : resp_size * 2;
         if (resp_size > (256 * 1024))
             resp_size = (256 * 1024);
-        ret = HttpClientPrepare(clientData, header_size, resp_size);
+        ret = ZhihuiHttpClientPrepare(clientData, header_size, resp_size);
         tries++;
     }
     return ret; 
@@ -1112,28 +1112,28 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
     }
 
     // 建立连接
-    ret = HttpClientConn(&client, audio_url);
+    ret = ZhihuiHttpClientConn(&client, audio_url);
     if (ret != 0)
     {
         printf("[SSE] connection failed, ret=0x%x\n", ret);
 #if TEST_MP3_DOWNLOAD
         fclose(fd);
 #endif
-        HttpClientUnprepare(&client_data);
+        ZhihuiHttpClientUnprepare(&client_data);
         free(save_path);
         return ERRCODE_FAIL;
     }
 
     // 发送GET请求
-    ret = HttpClientSend(&client, audio_url, HTTP_GET, &client_data);
+    ret = ZhihuiHttpClientSend(&client, audio_url, HTTP_GET, &client_data);
     if (ret != 0)
     {
         printf("[SSE] send request failed, ret=0x%x\n", ret);
 #if TEST_MP3_DOWNLOAD
         fclose(fd);
 #endif
-        HttpClientClse(&client);
-        HttpClientUnprepare(&client_data);
+        ZhihuiHttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&client_data);
         free(save_path);
         return ERRCODE_FAIL;
     }
@@ -1141,7 +1141,7 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
     // 接收响应数据(支持取消)
     do
     {
-        HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data);
+        HTTPC_RESULT recv_res = ZhihuiHttpClientRecvResponse(&client, &client_data);
         if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
         {
             if (client_data.contentBlockLen > 0)
@@ -1155,8 +1155,8 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
                     printf("[SSE] write failed, written=%zu, expected=%d\n",
                            written, client_data.contentBlockLen);
                     fclose(fd);
-                    HttpClientClse(&client);
-                    HttpClientUnprepare(&client_data);
+                    ZhihuiHttpClientClse(&client);
+                    ZhihuiHttpClientUnprepare(&client_data);
                     return ERRCODE_FAIL;
                 }
                 total_written += written;
@@ -1196,8 +1196,8 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
 #if TEST_MP3_DOWNLOAD
     fclose(fd);
 #endif
-    HttpClientUnprepare(&client_data);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&client_data);
+    ZhihuiHttpClientClse(&client);
     free(save_path);
     return ERRCODE_SUCC;
 }
@@ -1739,6 +1739,22 @@ errcode_t uapi_ln_http_server_init(void)
 {
     printf("uapi_ln_http_server_init !\r\n");
 
+    /* 初始化 zhihui_net 
+     * 说明:
+     * - PPP拨号模式:使用 OpenHarmony (PPP + lwIP + haisi_websocket)
+     * - 非PPP模式:使用 N706A AT指令(纯AT,不拨号)
+     */
+    #if defined(SUPPORT_PPP_SERVICE_INTERRUPT) || defined(SUPPORT_PPP_SERVICE_POLLING)
+    /* PPP拨号模式:使用OpenHarmony实现(基于lwIP) */
+    ZhihuiNetSetImplementation(ZHIHUI_NET_IMPL_OPENHARMONY);
+    printf("[LeiNiao] Using OpenHarmony (PPP + lwIP) network implementation\r\n");
+    #else
+    /* 非PPP模式:使用N706A AT指令 */
+    ZhihuiNetSetImplementation(ZHIHUI_NET_IMPL_N706A_AT);
+    printf("[LeiNiao] Using N706A AT command network implementation\r\n");
+    #endif
+    ZhihuiNetInit();
+
     ln_send_taskcfg.create_queue_handle = create_ln_http_send_queue;
     ln_send_taskcfg.attr.name = "ln_http_server_send_task";
     ln_send_taskcfg.attr.priority = osPriorityNormal;
index baf5b55..b14b0f1 100644 (file)
@@ -8,8 +8,8 @@ extern "C"
 
 #include "stdint.h"
 #include "errcode.h"
-/* HTTP types for helper function prototypes */
-#include "netstack/http/include/httpclient.h"
+/* 智慧网络统一接口 - 替换原 OpenHarmony netstack */
+#include "zhihui_net.h"
 #include <unistd.h>
 #include "wearable_log.h"
 #include "ln_api_interface.h"
@@ -281,7 +281,8 @@ extern ScheduleReminderConfig g_schedule_reminder_config;
 
 /* ============================== 公共工具函数(供拆分后的模块复用) ============================== */
 
-HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size);
+/* 使用 zhihui_net 接口 */
+int ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size);
 uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_type);
 
 /* Token 本地缓存接口 */
index 50ce014..4c7b505 100644 (file)
@@ -500,7 +500,7 @@ uint32_t ln_device_get_character_http(const char *device_id,
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
     printf("device getCharacter header %s\n", header_buf);
 
@@ -511,7 +511,7 @@ uint32_t ln_device_get_character_http(const char *device_id,
         return ERRCODE_FAIL;
     }
 
-    HTTPC_RESULT res = HttpClientGetRequest(&client, LN_DEVICE_GET_CHARACTER_URL, &clientData);
+    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_DEVICE_GET_CHARACTER_URL, &clientData);
     if (res != HTTP_SUCCESS)
     {
         printf("device getCharacter get failed %d\n", res);
@@ -531,8 +531,8 @@ uint32_t ln_device_get_character_http(const char *device_id,
     }
 
 end:
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
     free(header_buf);
     return ERRCODE_SUCC;
 }
@@ -569,7 +569,7 @@ uint32_t ln_device_get_user_id_http(const char *device_id,
         free(header_buf2);
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf2);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf2);
 
     printf("device getUserId header %s\n", header_buf2);
 
@@ -580,7 +580,7 @@ uint32_t ln_device_get_user_id_http(const char *device_id,
         return ERRCODE_FAIL;
     }
 
-    HTTPC_RESULT res = HttpClientGetRequest(&client, LN_DEVICE_GET_USER_ID_URL, &clientData);
+    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_DEVICE_GET_USER_ID_URL, &clientData);
     if (res != HTTP_SUCCESS)
     {
         printf("device getUserId get failed %d\n", res);
@@ -637,8 +637,8 @@ uint32_t ln_device_get_user_id_http(const char *device_id,
     }
 
 end:
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
     free(header_buf2);
     return ERRCODE_SUCC;
 }
@@ -680,7 +680,7 @@ 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;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    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);
@@ -691,12 +691,12 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
         return ERRCODE_FAIL;
     }
 
-    HTTPC_RESULT res = HttpClientGetRequest(&client, LN_DEVICE_GET_CHAT_STAMINA_URL, &clientData);
+    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_DEVICE_GET_CHAT_STAMINA_URL, &clientData);
     if (res != HTTP_SUCCESS)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] request failed: %d", res);
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -706,8 +706,8 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
     if (body_len <= 0 || !clientData.responseBuf)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] empty body, code=%d", code);
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -715,8 +715,8 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
     char *json_text = (char *)malloc((size_t)body_len + 1);
     if (!json_text)
     {
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -736,8 +736,8 @@ 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);
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -754,8 +754,8 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
 
         cJSON_Delete(root);
         free(json_text);
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -766,8 +766,8 @@ 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);
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -795,8 +795,8 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
 
     cJSON_Delete(root);
     free(json_text);
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
     free(header_buf);
     return ERRCODE_SUCC;
 }
\ No newline at end of file
index b2f4146..abb8e1e 100644 (file)
@@ -1047,7 +1047,7 @@ static uint32_t ln_emoji_report_status_http(const char *device_id,
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] build header failed");
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
     /* 组装 JSON */
     cJSON *root = cJSON_CreateObject();
@@ -1093,7 +1093,7 @@ static uint32_t ln_emoji_report_status_http(const char *device_id,
     if (res != HTTP_SUCCESS)
     {
         free(body);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] request failed");
         return ERRCODE_FAIL;
@@ -1103,7 +1103,7 @@ static uint32_t ln_emoji_report_status_http(const char *device_id,
     if (code != 200)
     {
         free(body);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response code not 200");
         return ERRCODE_FAIL;
@@ -1131,7 +1131,7 @@ static uint32_t ln_emoji_report_status_http(const char *device_id,
 
 
     free(body);
-    HttpClientUnprepare(&clientData);
+    ZhihuiHttpClientUnprepare(&clientData);
     free(header_buf);
     return ERRCODE_SUCC;
 }
@@ -1169,7 +1169,7 @@ static uint32_t ln_emoji_report_all_http(const char *device_id,
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
     /* 组装 JSON 数组 */
     cJSON *root = cJSON_CreateObject();
@@ -1218,7 +1218,7 @@ static uint32_t ln_emoji_report_all_http(const char *device_id,
     if (res != HTTP_SUCCESS)
     {
         free(body);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -1228,7 +1228,7 @@ static uint32_t ln_emoji_report_all_http(const char *device_id,
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] HTTP %d", code);
         free(body);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -1248,7 +1248,7 @@ static uint32_t ln_emoji_report_all_http(const char *device_id,
     }
 
     free(body);
-    HttpClientUnprepare(&clientData);
+    ZhihuiHttpClientUnprepare(&clientData);
     free(header_buf);
     return ERRCODE_SUCC;
 }
index eac265b..7d99321 100644 (file)
@@ -270,7 +270,7 @@ uint32_t ln_ota_get_task_http(const char *device_id,
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
     HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024);
     if (prep != HTTP_SUCCESS)
@@ -284,7 +284,7 @@ uint32_t ln_ota_get_task_http(const char *device_id,
     char *url_with_params = (char *)malloc(url_cap);
     if (!url_with_params)
     {
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         return ERRCODE_FAIL;
     }
@@ -293,7 +293,7 @@ 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)
     {
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         free(url_with_params);
         return ERRCODE_FAIL;
@@ -301,7 +301,7 @@ uint32_t ln_ota_get_task_http(const char *device_id,
 
     printf("[OTA] GET %s\n", url_with_params);
 
-    HTTPC_RESULT res = HttpClientGetRequest(&client, url_with_params, &clientData);
+    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, url_with_params, &clientData);
     if (res != HTTP_SUCCESS)
     {
         printf("[OTA] HttpClientGetRequest error %d\n", res);
@@ -327,8 +327,8 @@ uint32_t ln_ota_get_task_http(const char *device_id,
     }
 
 end:
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
     if (header_buf)
         free(header_buf);
     if (url_with_params)
@@ -418,7 +418,7 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
         header_buf = (char *)malloc(128);
         if (!header_buf)
         {
-            HttpClientUnprepare(&client_data);
+            ZhihuiHttpClientUnprepare(&client_data);
             fclose(fd);
             return -1;
         }
@@ -426,46 +426,46 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
         if (hdr <= 0)
         {
             free(header_buf);
-            HttpClientUnprepare(&client_data);
+            ZhihuiHttpClientUnprepare(&client_data);
             fclose(fd);
             return -1;
         }
-        HttpClientSetCustomHeader(&client, header_buf);
+        ZhihuiHttpClientSetCustomHeader(&client, header_buf);
     }
 
     // 建立连接(与http_download流程一致)
-    ret = HttpClientConn(&client, full_url);
+    ret = ZhihuiHttpClientConn(&client, full_url);
     if (ret != 0)
     {
         printf("[OTA] connection failed, ret=0x%x\n", ret);
         if (header_buf) free(header_buf);
         fclose(fd);
-        HttpClientUnprepare(&client_data);
+        ZhihuiHttpClientUnprepare(&client_data);
         return ret;
     }
 
     // 发送GET请求
-    ret = HttpClientSend(&client, full_url, HTTP_GET, &client_data);
+    ret = ZhihuiHttpClientSend(&client, full_url, HTTP_GET, &client_data);
     if (ret != 0)
     {
         printf("[OTA] send request failed, ret=0x%x\n", ret);
         if (header_buf) free(header_buf);
-        HttpClientClse(&client);
+        ZhihuiHttpClientClse(&client);
         fclose(fd);
-        HttpClientUnprepare(&client_data);
+        ZhihuiHttpClientUnprepare(&client_data);
         return ret;
     }
 
     printf("[OTA] starting to receive data...\n");
     // 先接收首包以判定状态码;如服务端未按 Range 返回(200),需覆盖写
-    HTTPC_RESULT first_recv = HttpClientRecvResponse(&client, &client_data);
+    HTTPC_RESULT first_recv = ZhihuiHttpClientRecvResponse(&client, &client_data);
     if (!(first_recv == HTTP_SUCCESS || first_recv == HTTP_EAGAIN))
     {
         printf("[OTA] first recv failed: %d\n", first_recv);
         if (header_buf) free(header_buf);
-        HttpClientClse(&client);
+        ZhihuiHttpClientClse(&client);
         fclose(fd);
-        HttpClientUnprepare(&client_data);
+        ZhihuiHttpClientUnprepare(&client_data);
         return -1;
     }
 
@@ -479,8 +479,8 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
         if (!fd)
         {
             if (header_buf) free(header_buf);
-            HttpClientClse(&client);
-            HttpClientUnprepare(&client_data);
+            ZhihuiHttpClientClse(&client);
+            ZhihuiHttpClientUnprepare(&client_data);
             return -1;
         }
         resume_offset = 0;
@@ -497,8 +497,8 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
             printf("[OTA] write failed on first block, written=%zu, expected=%d\n", written, client_data.contentBlockLen);
             if (header_buf) free(header_buf);
             fclose(fd);
-            HttpClientClse(&client);
-            HttpClientUnprepare(&client_data);
+            ZhihuiHttpClientClse(&client);
+            ZhihuiHttpClientUnprepare(&client_data);
             return -1;
         }
         total_written += written;
@@ -520,7 +520,7 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
     // 后续接收循环
     while (client_data.isMore)
     {
-        HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data);
+        HTTPC_RESULT recv_res = ZhihuiHttpClientRecvResponse(&client, &client_data);
         if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
         {
             if (client_data.contentBlockLen > 0)
@@ -531,8 +531,8 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
                     printf("[OTA] write failed, written=%zu, expected=%d\n", written, client_data.contentBlockLen);
                     if (header_buf) free(header_buf);
                     fclose(fd);
-                    HttpClientClse(&client);
-                    HttpClientUnprepare(&client_data);
+                    ZhihuiHttpClientClse(&client);
+                    ZhihuiHttpClientUnprepare(&client_data);
                     return -1;
                 }
                 total_written += written;
@@ -554,8 +554,8 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
                     printf("[OTA] received more than expected! current_total=%lld > total_size=%d\n", current_total, total_size);
                     if (header_buf) free(header_buf);
                     fclose(fd);
-                    HttpClientClse(&client);
-                    HttpClientUnprepare(&client_data);
+                    ZhihuiHttpClientClse(&client);
+                    ZhihuiHttpClientUnprepare(&client_data);
                     return -1;
                 }
             }
@@ -590,8 +590,8 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
     // 资源清理
     if (header_buf) free(header_buf);
     fclose(fd);
-    HttpClientClse(&client);
-    HttpClientUnprepare(&client_data);
+    ZhihuiHttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&client_data);
 
     printf("[OTA] download process finished\n");
 
index a7d9829..d2df595 100644 (file)
@@ -172,7 +172,7 @@ uint32_t ln_speech_chat_stream_http(const char *device_id,
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
     printf("chatstream header_buf: %s\n", header_buf);
 
     /* 准备缓冲区(自适应) */
@@ -191,22 +191,22 @@ uint32_t ln_speech_chat_stream_http(const char *device_id,
     clientData.isChunked = true;
 
     int ret = -1;
-    ret = HttpClientConn(&client, LN_CHAT_URL);
+    ret = ZhihuiHttpClientConn(&client, LN_CHAT_URL);
     if (ret != 0)
     {
         printf("connection failed, ret=0x%x\n", ret);
         free(body);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientUnprepare(&clientData);
         return ret;
     }
 
-    ret = HttpClientSend(&client, LN_CHAT_URL, HTTP_POST, &clientData);
+    ret = ZhihuiHttpClientSend(&client, LN_CHAT_URL, HTTP_POST, &clientData);
     if (ret != 0)
     {
         printf("send request failed, ret=0x%x\n", ret);
         free(body);
-        HttpClientClse(&client);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
         return ret;
     }
 
@@ -215,15 +215,15 @@ uint32_t ln_speech_chat_stream_http(const char *device_id,
     {
         printf("chatstream: fopen failed for %s:\n", speech_file_path);
         free(body);
-        HttpClientClse(&client);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
         return ERRCODE_FAIL;
     }
 
     size_t total_written = 0;
     do
     {
-        HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &clientData);
+        HTTPC_RESULT recv_res = ZhihuiHttpClientRecvResponse(&client, &clientData);
         printf("chatstream: recv response result: %d\n", recv_res);
         if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
         {
@@ -237,8 +237,8 @@ uint32_t ln_speech_chat_stream_http(const char *device_id,
                     printf("chatstream: write failed, written=%zu, expected=%d\n",
                            written, clientData.contentBlockLen);
                     free(body);
-                    HttpClientClse(&client);
-                    HttpClientUnprepare(&clientData);
+                    ZhihuiHttpClientClse(&client);
+                    ZhihuiHttpClientUnprepare(&clientData);
                     fclose(fd);
                     return ERRCODE_FAIL;
                 }
@@ -257,8 +257,8 @@ uint32_t ln_speech_chat_stream_http(const char *device_id,
         }
     } while (clientData.isMore);
 
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
     free(body);
     fclose(fd);
 
index 98c3a6b..0127275 100644 (file)
@@ -170,7 +170,7 @@ uint32_t ln_story_choose_http(const char *device_id,
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
     printf("story choose header_buf: %s\n", header_buf);
 
     /* 自适应准备 HTTP 缓冲区 */
@@ -189,22 +189,22 @@ uint32_t ln_story_choose_http(const char *device_id,
     clientData.isChunked = true;
 
     int ret = -1;
-    ret = HttpClientConn(&client, LN_STORY_CHOOSE_URL);
+    ret = ZhihuiHttpClientConn(&client, LN_STORY_CHOOSE_URL);
     if (ret != 0)
     {
         printf("connection failed, ret=0x%x\n", ret);
         free(body);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientUnprepare(&clientData);
         return ret;
     }
 
-    ret = HttpClientSend(&client, LN_STORY_CHOOSE_URL, HTTP_POST, &clientData);
+    ret = ZhihuiHttpClientSend(&client, LN_STORY_CHOOSE_URL, HTTP_POST, &clientData);
     if (ret != 0)
     {
         printf("send request failed, ret=0x%x\n", ret);
         free(body);
-        HttpClientClse(&client);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
         return ret;
     }
 
@@ -213,15 +213,15 @@ uint32_t ln_story_choose_http(const char *device_id,
     {
         printf("story choose: fopen failed for %s:\n", story_choose_file_path);
         free(body);
-        HttpClientClse(&client);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
         return ERRCODE_FAIL;
     }
 
     size_t total_written = 0;
     do
     {
-        HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &clientData);
+        HTTPC_RESULT recv_res = ZhihuiHttpClientRecvResponse(&client, &clientData);
         printf("story choose: recv response result: %d\n", recv_res);
         if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
         {
@@ -234,8 +234,8 @@ uint32_t ln_story_choose_http(const char *device_id,
                     printf("story choose: write failed, written=%zu, expected=%d\n",
                            written, clientData.contentBlockLen);
                     free(body);
-                    HttpClientClse(&client);
-                    HttpClientUnprepare(&clientData);
+                    ZhihuiHttpClientClse(&client);
+                    ZhihuiHttpClientUnprepare(&clientData);
                     fclose(fd);
                     return ERRCODE_FAIL;
                 }
@@ -255,8 +255,8 @@ uint32_t ln_story_choose_http(const char *device_id,
     } while (clientData.isMore);
 
     free(body);
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
     fclose(fd);
 
     if (total_written > 0)
index 483e203..0fe192d 100644 (file)
@@ -31,7 +31,7 @@ uint32_t ln_story_exit_inquiry_http(const char *device_id,
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
     HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024);
     if (prep != HTTP_SUCCESS)
@@ -63,8 +63,8 @@ uint32_t ln_story_exit_inquiry_http(const char *device_id,
     }
 
 end:
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
     if (header_buf)
         free(header_buf);
     return ERRCODE_SUCC;
index 7c9a79c..8dfcd5b 100644 (file)
@@ -29,7 +29,7 @@ uint32_t ln_story_finish_http(const char *device_id,
     {
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
     HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024);
     if (prep != HTTP_SUCCESS)
@@ -62,8 +62,8 @@ uint32_t ln_story_finish_http(const char *device_id,
     }
 
 end:
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
     free(header_buf);
     return ERRCODE_SUCC;
 }
index 727f93d..641e614 100644 (file)
@@ -55,7 +55,7 @@ uint32_t ln_story_random_http(const char *device_id,
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
     /* 自适应准备 HTTP 缓冲区 */
     HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE * 2);
@@ -111,24 +111,24 @@ uint32_t ln_story_random_http(const char *device_id,
     clientData.postContentType = (char *)"application/json";
 
     int ret = -1;
-    ret = HttpClientConn(&client, LN_STORY_RANDOM_URL);
+    ret = ZhihuiHttpClientConn(&client, LN_STORY_RANDOM_URL);
     if (ret != 0)
     {
         printf("[OTA] connection failed, ret=0x%x\n", ret);
         free(header_buf);
         free(json_body);
         free(escaped_theme);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientUnprepare(&clientData);
         return ret;
     }
 
     // 发送GET请求
-    ret = HttpClientSend(&client, LN_STORY_RANDOM_URL, HTTP_POST, &clientData);
+    ret = ZhihuiHttpClientSend(&client, LN_STORY_RANDOM_URL, HTTP_POST, &clientData);
     if (ret != 0)
     {
         printf("[OTA] send request failed, ret=0x%x\n", ret);
-        HttpClientClse(&client);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         free(json_body);
         free(escaped_theme);
@@ -139,8 +139,8 @@ uint32_t ln_story_random_http(const char *device_id,
     if (!fd)
     {
         printf("story choose: fopen failed for %s:\n", story_random_file_path);
-        HttpClientClse(&client);
-        HttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
         free(header_buf);
         free(json_body);
         free(escaped_theme);
@@ -150,7 +150,7 @@ uint32_t ln_story_random_http(const char *device_id,
     size_t total_written = 0;
     do
     {
-        HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &clientData);
+        HTTPC_RESULT recv_res = ZhihuiHttpClientRecvResponse(&client, &clientData);
         printf("story random: recv response result: %d\n", recv_res);
         if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
         {
@@ -162,8 +162,8 @@ uint32_t ln_story_random_http(const char *device_id,
                 {
                     printf("story random: write failed, written=%zu, expected=%d\n",
                            written, clientData.contentBlockLen);
-                    HttpClientClse(&client);
-                    HttpClientUnprepare(&clientData);
+                    ZhihuiHttpClientClse(&client);
+                    ZhihuiHttpClientUnprepare(&clientData);
                     fclose(fd);
                     free(header_buf);
                     free(json_body);
@@ -185,8 +185,8 @@ uint32_t ln_story_random_http(const char *device_id,
         }
     } while (clientData.isMore);
 
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
 
     if (total_written > 0)
     {
index 286758f..42604fe 100644 (file)
@@ -91,7 +91,7 @@ uint32_t ln_timeline_get_status_http(const char *device_id,
                uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
                return ERRCODE_FAIL;
        }
-       HttpClientSetCustomHeader(&client, header_buf);
+       ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
        HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE*2);
        if (prep != HTTP_SUCCESS)
@@ -101,7 +101,7 @@ uint32_t ln_timeline_get_status_http(const char *device_id,
                return ERRCODE_FAIL;
        }
 
-       HTTPC_RESULT res = HttpClientGetRequest(&client, LN_TIMELINE_GET_STATUS_URL, &clientData);
+       HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_TIMELINE_GET_STATUS_URL, &clientData);
        if (res != HTTP_SUCCESS)
        {
                goto end;
@@ -118,8 +118,8 @@ uint32_t ln_timeline_get_status_http(const char *device_id,
        }
 
 end:
-       HttpClientUnprepare(&clientData);
-       HttpClientClse(&client);
+       ZhihuiHttpClientUnprepare(&clientData);
+       ZhihuiHttpClientClse(&client);
        free(header_buf);
        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
        return ERRCODE_SUCC;
index 898b952..bbfb369 100644 (file)
@@ -294,9 +294,9 @@ uint32_t ln_get_device_token_http(const char *device_id,
     /* 5. 发起HTTPS GET */
     HttpClient client = (HttpClient){0};
     HttpClientData clientData = (HttpClientData){0};
-    HttpClientSetCustomHeader(&client, header_buf);
+    ZhihuiHttpClientSetCustomHeader(&client, header_buf);
 
-    HTTPC_RESULT prep = HttpClientPrepare(&clientData, 2048, 4096);
+    HTTPC_RESULT prep = ZhihuiHttpClientPrepare(&clientData, 2048, 4096);
     if (prep != HTTP_SUCCESS)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: HttpClientPrepare failed %d", prep);
@@ -306,12 +306,12 @@ uint32_t ln_get_device_token_http(const char *device_id,
         return ERRCODE_FAIL;
     }
 
-    HTTPC_RESULT res = HttpClientGetRequest(&client, LN_FULL_URL, &clientData);
+    HTTPC_RESULT res = ZhihuiHttpClientGetRequest(&client, LN_FULL_URL, &clientData);
     if (res != HTTP_SUCCESS)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: request failed %d", res);
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(sign_data);
         free(header_buf);
                uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
@@ -322,8 +322,8 @@ uint32_t ln_get_device_token_http(const char *device_id,
     if (code != 200)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: HTTP %d", code);
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(sign_data);
         free(header_buf);
                uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
@@ -334,8 +334,8 @@ uint32_t ln_get_device_token_http(const char *device_id,
     if (clientData.responseBuf == NULL || clientData.responseBufLen <= 0)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: empty body");
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(sign_data);
         free(header_buf);
                uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
@@ -349,8 +349,8 @@ uint32_t ln_get_device_token_http(const char *device_id,
     char *json_text = (char *)malloc((size_t)body_len + 1);
     if (json_text == NULL)
     {
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(sign_data);
         free(header_buf);
                uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
@@ -364,8 +364,8 @@ uint32_t ln_get_device_token_http(const char *device_id,
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: JSON parse failed");
         free(json_text);
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
         free(sign_data);
         free(header_buf);
                uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
@@ -381,8 +381,8 @@ uint32_t ln_get_device_token_http(const char *device_id,
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_get_device_token_http: failed, message=%s", msg && cJSON_IsString(msg) ? msg->valuestring : "unknown");
         cJSON_Delete(root);
         free(json_text);
-        HttpClientUnprepare(&clientData);
-        HttpClientClse(&client);
+        ZhihuiHttpClientUnprepare(&clientData);
+        ZhihuiHttpClientClse(&client);
                uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
         return ERRCODE_FAIL;
     }
@@ -425,8 +425,8 @@ uint32_t ln_get_device_token_http(const char *device_id,
 
     cJSON_Delete(root);
     free(json_text);
-    HttpClientUnprepare(&clientData);
-    HttpClientClse(&client);
+    ZhihuiHttpClientUnprepare(&clientData);
+    ZhihuiHttpClientClse(&client);
     free(sign_data);
     free(header_buf);
        uapi_pm_remove_sleep_veto(PM_ID_DEBUG);
index 5c621a0..d0b1d98 100644 (file)
@@ -66,7 +66,7 @@ int ln_ws_send_device_bind_ack(const char *user_id, bool success, const char *ms
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
 
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent DeviceBindAck: %s\n", json_string);
 
@@ -117,7 +117,7 @@ int ln_ws_send_device_unbind_ack(const char *user_id, bool success, const char *
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
 
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent DeviceUnbindAck: %s\n", json_string);
 
@@ -168,7 +168,7 @@ int ln_ws_send_character_change_ack(const char *character_id, bool success, cons
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent CharacterChangeAck: %s\n", json_string);
     cJSON_free(json_string);
     cJSON_Delete(json);
@@ -217,7 +217,7 @@ int ln_ws_send_character_config_change_ack(const char *character_id, bool succes
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent CharacterConfigChangeAck: %s\n", json_string);
     cJSON_free(json_string);
     cJSON_Delete(json);
@@ -265,7 +265,7 @@ int ln_ws_send_sleep_mode_start_ack(bool success, const char *msg)
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent SleepModeStartAck: %s\n", json_string);
     cJSON_free(json_string);
     cJSON_Delete(json);
@@ -313,7 +313,7 @@ int ln_ws_send_sleep_mode_end_ack(bool success, const char *msg)
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent SleepModeEndAck: %s\n", json_string);
     cJSON_free(json_string);
     cJSON_Delete(json);
@@ -362,7 +362,7 @@ int ln_ws_send_schedule_reminder_ack(int schedule_id, bool success, const char *
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent ScheduleReminderAck: %s\n", json_string);
     cJSON_free(json_string);
     cJSON_Delete(json);
@@ -411,7 +411,7 @@ int ln_ws_send_schedule_reminder_config_ack(int schedule_id, bool success, const
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent ScheduleReminderConfigAck: %s\n", json_string);
     cJSON_free(json_string);
     cJSON_Delete(json);
@@ -462,7 +462,7 @@ int ln_ws_send_delete_emoji_ack(const char *character_id, const char *eyesColor,
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent DeleteEmojiAck: %s\n", json_string);
     cJSON_free(json_string);
     cJSON_Delete(json);
@@ -513,7 +513,7 @@ int ln_ws_send_download_emoji_ack(const char *character_id, const char *eyesColo
         return -1;
     }
 
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent DownloadEmojiAck: %s\n", json_string);
     cJSON_free(json_string);
     cJSON_Delete(json);
index 4f54746..1a0f9a7 100644 (file)
@@ -1,6 +1,6 @@
 #include "ln_api.h"
-#include "net_websocket.h"
-#include "haisi_websocket.h"
+/* 智慧网络统一接口(已包含 OpenHarmony 类型定义) */
+#include "zhihui_net.h"
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -396,7 +396,7 @@ int ln_ws_send_ping(void)
     }
     
     // 发送 JSON 字符串
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     
     LN_WS_DEBUG("Sent ping: %s\n", json_string);
     
@@ -631,7 +631,7 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f
     }
     
     // 发送JSON字符串
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     
     LN_WS_DEBUG("Sent audio data: chatId=%s, messageId=%s, size=%u, finished=%s\n", 
            g_audio_chat_ctx.chat_id, message_id, audio_size, finished ? "true" : "false");
@@ -1028,9 +1028,9 @@ int ln_ws_start(ws_message_callback_t_ln callback, uint32_t audio_timeout_ticks)
     g_ws_callbacks.reconnect_success_callback = ln_ws_reconnect_success_callback;
     g_ws_callbacks.main_thread_interval_ms = LN_WS_MAIN_THREAD_INTERVAL_MS;
     // 启动WebSocket服务
-    int ret = uapi_ws_start(&g_ws_callbacks);
+    int ret = ZhihuiWsStart(&g_ws_callbacks);
     if (ret != 0) {
-        LN_WS_WARN("uapi_ws_start failed: %d\n", ret);
+        LN_WS_WARN("ZhihuiWsStart failed: %d\n", ret);
         g_ws_running = 0;
         return ret;
     }
@@ -1271,7 +1271,7 @@ int ln_ws_send_text(const char *text)
         LN_WS_WARN("Cannot send text, connection state: %d\n", g_ws_connected);
         return -1;
     }
-    return uapi_ws_send_text(text);
+    return ZhihuiWsSendText(text);
 }
 
 /* 发送随机故事请求 */
@@ -1336,7 +1336,7 @@ int ln_ws_send_random_story_request(const char *story_theme)
     LN_WS_DEBUG("Sending RandomStoryRequest: %s\n", json_string);
     
     // 发送消息
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     
     // 清理资源
     cJSON_free(json_string);
@@ -1414,7 +1414,7 @@ int ln_ws_send_voice_chat_interrupt(const char *reason)
     LN_WS_DEBUG("Sending VoiceChatInterrupt: %s\n", json_string);
     
     // 发送消息
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     
     // 清理资源
     cJSON_free(json_string);
@@ -1450,7 +1450,7 @@ int ln_ws_stop(void)
     // 停止音频服务(与 ln_ws_start 中的 ln_ws_audio_init 对应)
     ln_ws_audio_deinit();
     
-    int ret = uapi_ws_stop();
+    int ret = ZhihuiWsStop();
     g_ws_connected = WS_STATE_DISCONNECTED;
     
     // 清理Base64缓存
@@ -2031,7 +2031,7 @@ int ln_ws_enter_sleep(void)
     }
     
     // 使用新的接口进入休眠
-    int ret = uapi_ws_enter_sleep();
+    int ret = ZhihuiWsEnterSleep();
     if (ret == 0) {
     g_ws_connected = WS_STATE_SLEEP;
     LN_WS_DEBUG("Sleep mode activated, all operations paused\n");
@@ -2057,7 +2057,7 @@ int ln_ws_exit_sleep(void)
     }
     
     // 使用新的接口退出休眠
-    int ret = uapi_ws_exit_sleep();
+    int ret = ZhihuiWsExitSleep();
     if (ret == 0) {
     g_ws_connected = WS_STATE_DISCONNECTED;
     LN_WS_DEBUG("Sleep mode deactivated, resuming normal operations\n");
@@ -2446,7 +2446,7 @@ static int ln_ws_reconnect(void)
     g_ws_connected = WS_STATE_RECONNECTING;
     
     // 使用强制重连API
-    int ret = uapi_ws_force_reconnect();
+    int ret = ZhihuiWsForceReconnect();
     if (ret != 0) {
         LN_WS_ERROR("WebSocket force reconnection failed: %d\n", ret);
         g_ws_connected = WS_STATE_DISCONNECTED;
@@ -2795,7 +2795,7 @@ static int ln_ws_send_audio_data_direct(const uint8_t *audio_data, uint32_t audi
     }
     
     // 发送JSON字符串
-    int ret = uapi_ws_send_text(json_string);
+    int ret = ZhihuiWsSendText(json_string);
     
     LN_WS_ERROR("Sent audio data: chatId=%s, messageId=%s, size=%u, finished=%s\n", 
            g_audio_chat_ctx.chat_id, message_id, audio_size, finished ? "true" : "false");
diff --git a/code/sdk/zhihui/n706a_net/CMakeLists.txt b/code/sdk/zhihui/n706a_net/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9d74e56
--- /dev/null
@@ -0,0 +1,50 @@
+#===============================================================================
+# @brief    N706A PPP网络接口层
+# Copyright (c) @CompanyNameMagicTag 2023-2025. All rights reserved.
+#===============================================================================
+set(COMPONENT_NAME "n706a_net")
+
+set(SOURCES ${SOURCES}
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/n706a_tcp_client.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/n706a_http_client.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/n706a_websocket.c
+)
+
+set(PUBLIC_HEADER
+    ${CMAKE_CURRENT_SOURCE_DIR}/include
+)
+
+set(PUBLIC_DEFINES
+    SUPPORT_N706A_NET
+)
+
+set(PRIVATE_HEADER
+)
+
+set(PRIVATE_DEFINES
+# N706A_SSL_USE_MBEDTLS
+N706A_SSL_USE_AT_COMMAND
+)
+
+# use this when you want to add ccflags like -include xxx
+set(COMPONENT_PUBLIC_CCFLAGS
+)
+
+set(COMPONENT_CCFLAGS
+    -Wno-incompatible-function-pointer-types
+    -Wno-unused-function
+    -Wno-format
+)
+
+set(WHOLE_LINK
+    true
+)
+
+set(MAIN_COMPONENT
+    false
+)
+
+build_component()
+
+install_sdk(${CMAKE_CURRENT_SOURCE_DIR} "*")
+
diff --git a/code/sdk/zhihui/n706a_net/include/n706a_http_client.h b/code/sdk/zhihui/n706a_net/include/n706a_http_client.h
new file mode 100644 (file)
index 0000000..fe6b150
--- /dev/null
@@ -0,0 +1,271 @@
+/**
+ * @file n706a_http_client.h
+ * @brief N706A HTTP/HTTPS客户端接口(基于TCP实现)
+ * 
+ * 本实现采用统一的TCP底层架构:
+ * - HTTP通过普通TCP连接实现
+ * - HTTPS通过SSL TCP连接实现
+ * - 手动构造HTTP协议,突破AT+HTTP*单连接限制
+ * - 支持6个并发HTTP/HTTPS连接
+ * 
+ * @version 3.0
+ * @date 2025-10-30
+ */
+
+#ifndef PPP_N706A_HTTP_CLIENT_H
+#define PPP_N706A_HTTP_CLIENT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "n706a_tcp_client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief N706A HTTP请求类型
+ */
+typedef enum {
+    N706A_HTTP_DELETE,
+    N706A_HTTP_GET,
+    N706A_HTTP_HEAD,
+    N706A_HTTP_POST,
+    N706A_HTTP_PUT
+} n706a_http_request_type_t;
+
+/**
+ * @brief N706A HTTP错误码
+ */
+typedef enum {
+    N706A_HTTP_EAGAIN   =  1,  /**< 还有更多数据需要获取 */
+    N706A_HTTP_SUCCESS  =  0,  /**< 操作成功      */
+    N706A_HTTP_ENOBUFS  = -1,  /**< 缓冲区错误           */
+    N706A_HTTP_EARG     = -2,  /**< 非法参数       */
+    N706A_HTTP_ENOTSUPP = -3,  /**< 不支持            */
+    N706A_HTTP_EDNS     = -4,  /**< DNS失败               */
+    N706A_HTTP_ECONN    = -5,  /**< 连接失败           */
+    N706A_HTTP_ESEND    = -6,  /**< 发送数据包失败       */
+    N706A_HTTP_ECLSD    = -7,  /**< 连接已关闭         */
+    N706A_HTTP_ERECV    = -8,  /**< 接收数据包失败       */
+    N706A_HTTP_EPARSE   = -9,  /**< URL解析错误        */
+    N706A_HTTP_EPROTO   = -10, /**< 协议错误         */
+    N706A_HTTP_EUNKOWN  = -11, /**< 未知错误          */
+    N706A_HTTP_ETIMEOUT = -12, /**< 超时                */
+} n706a_httpc_result_t;
+
+/**
+ * @brief N706A HTTP客户端结构(基于TCP实现)
+ */
+typedef struct {
+    n706a_tcp_socket_t tcp_sock;    /**< 底层TCP socket */
+    int socket_id;                   /**< TCP socket编号(0-5) */
+    bool is_https;                   /**< HTTPS标志(决定是否使用SSL) */
+    uint16_t remote_port;            /**< 远程端口 */
+    int response_code;               /**< HTTP响应码 */
+    char *header;                    /**< 自定义请求头 */
+    char *auth_user;                 /**< 基本认证用户名 */
+    char *auth_password;             /**< 基本认证密码 */
+    char host[256];                  /**< 主机名 */
+    bool connected;                  /**< 连接状态 */
+    bool keepalive;                  /**< 长连接模式 */
+    uint32_t timeout_ms;             /**< 超时时间(毫秒) */
+} n706a_http_client_t;
+
+/**
+ * @brief N706A HTTP数据结构
+ */
+typedef struct {
+    bool isMore;            /**< 是否还有更多数据需要获取 */
+    bool isChunked;         /**< 响应数据是否分块编码 */
+    int method;             /**< HTTP方法 */
+    int retrieveLen;        /**< 需要获取的内容长度 */
+    int responseContentLen; /**< 响应内容总长度 */
+    int contentBlockLen;    /**< 一个块的内容长度 */
+    int postBufLen;         /**< POST数据长度 */
+    int responseBufLen;     /**< 响应体缓冲区长度 */
+    int headerBufLen;       /**< 响应头缓冲区长度 */
+    char *postContentType;  /**< POST数据的内容类型 */
+    char *postBuf;          /**< 要POST的用户数据 */
+    char *responseBuf;      /**< 存储响应体数据的缓冲区 */
+    char *headerBuf;        /**< 存储响应头数据的缓冲区 */
+    bool isRedirected;      /**< 是否重定向 */
+    char *redirectUrl;      /**< 重定向URL */
+} n706a_http_client_data_t;
+
+/**
+ * @brief 初始化N706A HTTP客户端
+ * @param client HTTP客户端结构指针
+ * @param socket_id TCP socket编号(0-5)
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client, int socket_id);
+
+/**
+ * @brief 连接到HTTP/HTTPS服务器
+ * @param client HTTP客户端结构指针
+ * @param host 主机名或IP地址
+ * @param port 端口号(HTTP默认80,HTTPS默认443)
+ * @param use_ssl 是否使用SSL(HTTPS)
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_connect(n706a_http_client_t *client,
+                                               const char *host,
+                                               uint16_t port,
+                                               bool use_ssl);
+
+/**
+ * @brief 发送HTTP GET请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径(如"/api/data")
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_get(n706a_http_client_t *client,
+                                           const char *path,
+                                           n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 发送HTTP POST请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径
+ * @param content_type 内容类型(如"application/json")
+ * @param body 请求体数据
+ * @param body_len 请求体长度
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_post(n706a_http_client_t *client,
+                                            const char *path,
+                                            const char *content_type,
+                                            const char *body,
+                                            size_t body_len,
+                                            n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 发送HTTP PUT请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径
+ * @param content_type 内容类型
+ * @param body 请求体数据
+ * @param body_len 请求体长度
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_put(n706a_http_client_t *client,
+                                           const char *path,
+                                           const char *content_type,
+                                           const char *body,
+                                           size_t body_len,
+                                           n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 发送HTTP DELETE请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_delete(n706a_http_client_t *client,
+                                              const char *path,
+                                              n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 发送HTTP HEAD请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_head(n706a_http_client_t *client,
+                                            const char *path,
+                                            n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 设置自定义HTTP请求头
+ * @param client HTTP客户端结构指针
+ * @param header 自定义请求头(如"X-API-Key: abc123\r\n")
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_set_header(n706a_http_client_t *client,
+                                                  const char *header);
+
+/**
+ * @brief 设置HTTP基本认证
+ * @param client HTTP客户端结构指针
+ * @param username 用户名
+ * @param password 密码
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_set_auth(n706a_http_client_t *client,
+                                                const char *username,
+                                                const char *password);
+
+/**
+ * @brief 设置Keep-Alive模式
+ * @param client HTTP客户端结构指针
+ * @param keepalive true启用长连接,false关闭
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_set_keepalive(n706a_http_client_t *client,
+                                                     bool keepalive);
+
+/**
+ * @brief 关闭HTTP连接
+ * @param client HTTP客户端结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_close(n706a_http_client_t *client);
+
+/**
+ * @brief 检查HTTP连接状态
+ * @param client HTTP客户端结构指针
+ * @return true已连接,false未连接
+ */
+bool n706a_http_client_is_connected(n706a_http_client_t *client);
+
+/**
+ * @brief 获取最后的HTTP响应码
+ * @param client HTTP客户端结构指针
+ * @return HTTP响应码(如200, 404等)
+ */
+int n706a_http_client_get_response_code(n706a_http_client_t *client);
+
+/* ========== 辅助函数 ========== */
+
+/**
+ * @brief 解析完整URL并发起GET请求(便捷函数)
+ * @param client HTTP客户端结构指针
+ * @param url 完整URL(如"http://api.example.com/data"或"https://secure.com/")
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 此函数会自动解析URL、建立连接并发送GET请求
+ */
+n706a_httpc_result_t n706a_http_client_get_url(n706a_http_client_t *client,
+                                               const char *url,
+                                               n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 解析完整URL并发起POST请求(便捷函数)
+ * @param client HTTP客户端结构指针
+ * @param url 完整URL
+ * @param content_type 内容类型
+ * @param body 请求体数据
+ * @param body_len 请求体长度
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_post_url(n706a_http_client_t *client,
+                                                const char *url,
+                                                const char *content_type,
+                                                const char *body,
+                                                size_t body_len,
+                                                n706a_http_client_data_t *client_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // N706A_HTTP_CLIENT_H
+
diff --git a/code/sdk/zhihui/n706a_net/include/n706a_tcp_client.h b/code/sdk/zhihui/n706a_net/include/n706a_tcp_client.h
new file mode 100644 (file)
index 0000000..227cace
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
+ *
+ * N706A TCP客户端接口
+ * 基于N706A AT指令实现TCP socket功能
+ * 
+ * SSL实现方式:
+ *   - N706A_SSL_USE_AT_COMMAND: 使用AT+SSLTCP*指令(默认,推荐)
+ *   - N706A_SSL_USE_MBEDTLS: 使用mbedtls库(需要额外库支持)
+ */
+
+#ifndef PPP_N706A_TCP_CLIENT_H
+#define PPP_N706A_TCP_CLIENT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* ========== SSL实现方式选择 ========== */
+/**
+ * 编译时选择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)
+    /* 默认使用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
+
+/* N706A TCP错误码 */
+typedef enum {
+    N706A_TCP_SUCCESS  = 0,      /**< 操作成功 */
+    N706A_TCP_ERROR    = -1,     /**< 通用错误 */
+    N706A_TCP_EINVAL   = -2,     /**< 参数无效 */
+    N706A_TCP_ENOBUFS  = -3,     /**< 缓冲区不足 */
+    N706A_TCP_ENOTCONN = -4,     /**< 未连接 */
+    N706A_TCP_ECONNREF = -5,     /**< 连接被拒绝 */
+    N706A_TCP_ETIMEDOUT= -6,     /**< 超时 */
+    N706A_TCP_EINPROGRESS = -7,  /**< 操作进行中 */
+} n706a_tcp_result_t;
+
+/* TCP socket状态 */
+typedef enum {
+    N706A_TCP_STATE_CLOSED = 0,    /**< 关闭 */
+    N706A_TCP_STATE_CONNECTING,    /**< 连接中 */
+    N706A_TCP_STATE_CONNECTED,     /**< 已连接 */
+    N706A_TCP_STATE_ERROR          /**< 错误状态 */
+} n706a_tcp_state_t;
+
+/* TCP socket结构 */
+typedef struct {
+    int socket_id;                  /**< socket编号(0-5) */
+    n706a_tcp_state_t state;        /**< 连接状态 */
+    char remote_ip[64];             /**< 远程IP或域名 */
+    uint16_t remote_port;           /**< 远程端口 */
+    bool is_ssl;                    /**< 是否SSL连接 */
+    uint32_t timeout_ms;            /**< 超时时间 */
+    
+    /* SSL相关字段(根据编译宏不同而不同) */
+#ifdef N706A_SSL_USE_AT_COMMAND
+    /* 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;
+
+/**
+ * @brief 创建TCP socket
+ * @param sock TCP socket指针
+ * @param socket_id socket编号(0-5)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_socket_create(n706a_tcp_socket_t *sock, int socket_id);
+
+/**
+ * @brief 连接到TCP服务器
+ * @param sock TCP socket指针
+ * @param host 主机地址(IP或域名)
+ * @param port 端口号
+ * @param use_ssl 是否使用SSL连接(用于WSS)
+ * @param timeout_ms 超时时间(毫秒)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ * @note 使用SSL时会调用AT+SSLTCPSETUP,否则调用AT+TCPSETUP
+ */
+n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock, const char *host, 
+                                     uint16_t port, bool use_ssl, uint32_t timeout_ms);
+
+/**
+ * @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格式字符串)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_config_ssl(n706a_tcp_socket_t *sock, 
+                                        int ssl_version, 
+                                        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指针
+ * @param data 要发送的数据
+ * @param len 数据长度
+ * @param sent 实际发送的字节数(输出)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_send(n706a_tcp_socket_t *sock, const uint8_t *data, 
+                                  size_t len, size_t *sent);
+
+/**
+ * @brief 接收TCP数据
+ * @param sock TCP socket指针
+ * @param buffer 接收缓冲区
+ * @param len 缓冲区大小
+ * @param received 实际接收的字节数(输出)
+ * @param timeout_ms 超时时间(毫秒)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_recv(n706a_tcp_socket_t *sock, uint8_t *buffer, 
+                                  size_t len, size_t *received, uint32_t timeout_ms);
+
+/**
+ * @brief 关闭TCP连接
+ * @param sock TCP socket指针
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_close(n706a_tcp_socket_t *sock);
+
+/**
+ * @brief 检查socket是否已连接
+ * @param sock TCP socket指针
+ * @return true已连接,false未连接
+ */
+bool n706a_tcp_is_connected(const n706a_tcp_socket_t *sock);
+
+/**
+ * @brief 获取socket状态
+ * @param sock TCP socket指针
+ * @return socket状态
+ */
+n706a_tcp_state_t n706a_tcp_get_state(const n706a_tcp_socket_t *sock);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_N706A_TCP_CLIENT_H */
+
diff --git a/code/sdk/zhihui/n706a_net/include/n706a_websocket.h b/code/sdk/zhihui/n706a_net/include/n706a_websocket.h
new file mode 100644 (file)
index 0000000..a710a84
--- /dev/null
@@ -0,0 +1,310 @@
+/**
+ * @file n706a_websocket.h
+ * @brief N706A WebSocket客户端接口定义
+ * 
+ * 基于TCP/SSL TCP实现完整的WebSocket协议(RFC 6455),支持:
+ * - ws://协议(普通TCP)
+ * - wss://协议(SSL TCP)
+ * - 文本和二进制消息
+ * - 自动握手和帧封装
+ * - 事件回调机制
+ * 
+ * @version 1.0
+ * @date 2025-10-30
+ */
+
+#ifndef PPP_N706A_WEBSOCKET_H
+#define PPP_N706A_WEBSOCKET_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "n706a_tcp_client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* WebSocket最大客户端数量 */
+#define N706A_WS_MAX_CLIENTS 6
+
+/* WebSocket URL最大长度 */
+#define N706A_WS_MAX_URL_LEN 512
+
+/* WebSocket帧操作码 */
+typedef enum {
+    N706A_WS_OPCODE_CONTINUATION = 0x0,  /**< 继续帧 */
+    N706A_WS_OPCODE_TEXT = 0x1,          /**< 文本帧 */
+    N706A_WS_OPCODE_BINARY = 0x2,        /**< 二进制帧 */
+    N706A_WS_OPCODE_CLOSE = 0x8,         /**< 关闭帧 */
+    N706A_WS_OPCODE_PING = 0x9,          /**< Ping帧 */
+    N706A_WS_OPCODE_PONG = 0xA           /**< Pong帧 */
+} n706a_ws_opcode_t;
+
+/* WebSocket状态 */
+typedef enum {
+    N706A_WS_STATE_CLOSED = 0,      /**< 已关闭 */
+    N706A_WS_STATE_CONNECTING,      /**< 连接中 */
+    N706A_WS_STATE_OPEN,            /**< 已打开 */
+    N706A_WS_STATE_CLOSING          /**< 关闭中 */
+} n706a_ws_state_t;
+
+/* WebSocket关闭状态码 */
+typedef enum {
+    N706A_WS_CLOSE_NORMAL = 1000,           /**< 正常关闭 */
+    N706A_WS_CLOSE_GOING_AWAY = 1001,       /**< 端点离开 */
+    N706A_WS_CLOSE_PROTOCOL_ERROR = 1002,   /**< 协议错误 */
+    N706A_WS_CLOSE_UNSUPPORTED = 1003,      /**< 不支持的数据 */
+    N706A_WS_CLOSE_NO_STATUS = 1005,        /**< 无状态码 */
+    N706A_WS_CLOSE_ABNORMAL = 1006,         /**< 异常关闭 */
+    N706A_WS_CLOSE_INVALID_DATA = 1007,     /**< 无效数据 */
+    N706A_WS_CLOSE_POLICY = 1008,           /**< 策略违反 */
+    N706A_WS_CLOSE_TOO_LARGE = 1009,        /**< 消息过大 */
+    N706A_WS_CLOSE_EXTENSION = 1010,        /**< 扩展协商失败 */
+    N706A_WS_CLOSE_SERVER_ERROR = 1011      /**< 服务器错误 */
+} n706a_ws_close_code_t;
+
+/* WebSocket错误码 */
+typedef enum {
+    N706A_WS_SUCCESS = 0,               /**< 成功 */
+    N706A_WS_ERROR_INVALID_PARAM = -1,  /**< 参数无效 */
+    N706A_WS_ERROR_NO_MEMORY = -2,      /**< 内存不足 */
+    N706A_WS_ERROR_NOT_CONNECTED = -3,  /**< 未连接 */
+    N706A_WS_ERROR_ALREADY_OPEN = -4,   /**< 已经打开 */
+    N706A_WS_ERROR_HANDSHAKE = -5,      /**< 握手失败 */
+    N706A_WS_ERROR_SEND_FAIL = -6,      /**< 发送失败 */
+    N706A_WS_ERROR_RECV_FAIL = -7,      /**< 接收失败 */
+    N706A_WS_ERROR_PROTOCOL = -8,       /**< 协议错误 */
+    N706A_WS_ERROR_TIMEOUT = -9,        /**< 超时 */
+    N706A_WS_ERROR_CLOSED = -10         /**< 连接已关闭 */
+} n706a_ws_result_t;
+
+/* 前置声明 */
+struct n706a_websocket_client;
+
+/**
+ * @brief WebSocket连接打开回调
+ * @param client_id 客户端ID
+ */
+typedef void (*n706a_ws_on_open_callback_t)(int client_id);
+
+/**
+ * @brief WebSocket消息接收回调
+ * @param client_id 客户端ID
+ * @param data 消息数据
+ * @param length 数据长度
+ * @param is_finished 是否是最后一帧
+ * @param is_binary 是否是二进制数据
+ */
+typedef void (*n706a_ws_on_message_callback_t)(int client_id, 
+                                                const char *data, 
+                                                size_t length,
+                                                bool is_finished, 
+                                                bool is_binary);
+
+/**
+ * @brief WebSocket错误回调
+ * @param client_id 客户端ID
+ * @param error_code 错误码
+ * @param error_msg 错误消息
+ */
+typedef void (*n706a_ws_on_error_callback_t)(int client_id, 
+                                              int error_code, 
+                                              const char *error_msg);
+
+/**
+ * @brief WebSocket连接关闭回调
+ * @param client_id 客户端ID
+ * @param close_code 关闭状态码
+ * @param reason 关闭原因
+ */
+typedef void (*n706a_ws_on_close_callback_t)(int client_id, 
+                                              int close_code, 
+                                              const char *reason);
+
+/**
+ * @brief N706A WebSocket客户端结构
+ */
+typedef struct n706a_websocket_client {
+    int client_id;                      /**< 客户端ID (0-5) */
+    n706a_tcp_socket_t tcp_sock;        /**< 底层TCP socket */
+    n706a_ws_state_t state;             /**< WebSocket状态 */
+    bool is_ssl;                        /**< 是否WSS连接 */
+    
+    /* URL信息 */
+    char url[N706A_WS_MAX_URL_LEN];     /**< 完整URL */
+    char host[128];                     /**< 主机名 */
+    uint16_t port;                      /**< 端口号 */
+    char path[256];                     /**< 路径 */
+    
+    /* WebSocket协议 */
+    char sec_key[32];                   /**< Sec-WebSocket-Key */
+    char sec_accept[32];                /**< Sec-WebSocket-Accept */
+    
+    /* 接收缓冲区 */
+    uint8_t *recv_buffer;               /**< 接收缓冲区 */
+    size_t recv_buffer_size;            /**< 缓冲区大小 */
+    size_t recv_buffer_len;             /**< 已接收数据长度 */
+    
+    /* 回调函数 */
+    n706a_ws_on_open_callback_t on_open;
+    n706a_ws_on_message_callback_t on_message;
+    n706a_ws_on_error_callback_t on_error;
+    n706a_ws_on_close_callback_t on_close;
+    
+    /* 运行控制 */
+    bool running;                       /**< 接收线程运行标志 */
+    void *recv_thread;                  /**< 接收线程句柄 */
+    
+} n706a_websocket_client_t;
+
+/**
+ * @brief 创建WebSocket客户端
+ * @param on_open 连接打开回调
+ * @param on_message 消息接收回调
+ * @param on_error 错误回调
+ * @param on_close 连接关闭回调
+ * @return 客户端ID (0-5),失败返回-1
+ */
+int n706a_websocket_constructor(n706a_ws_on_open_callback_t on_open,
+                                n706a_ws_on_message_callback_t on_message,
+                                n706a_ws_on_error_callback_t on_error,
+                                n706a_ws_on_close_callback_t on_close);
+
+/**
+ * @brief 创建WebSocket客户端并指定socket ID
+ * @param socket_id TCP socket ID (0-5)
+ * @param on_open 连接打开回调
+ * @param on_message 消息接收回调
+ * @param on_error 错误回调
+ * @param on_close 连接关闭回调
+ * @return 客户端ID,失败返回-1
+ */
+int n706a_websocket_constructor_with_socket(int socket_id,
+                                            n706a_ws_on_open_callback_t on_open,
+                                            n706a_ws_on_message_callback_t on_message,
+                                            n706a_ws_on_error_callback_t on_error,
+                                            n706a_ws_on_close_callback_t on_close);
+
+/**
+ * @brief 连接到WebSocket服务器
+ * @param client_id 客户端ID
+ * @param url WebSocket URL (ws://或wss://)
+ * @param option 可选参数(如子协议等),可为NULL
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_connect(int client_id, const char *url, const char *option);
+
+/**
+ * @brief 发送文本消息
+ * @param client_id 客户端ID
+ * @param data 文本数据
+ * @param length 数据长度
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_send_text(int client_id, const char *data, size_t length);
+
+/**
+ * @brief 发送二进制消息
+ * @param client_id 客户端ID
+ * @param data 二进制数据
+ * @param length 数据长度
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_send_binary(int client_id, const uint8_t *data, size_t length);
+
+/**
+ * @brief 发送Ping帧
+ * @param client_id 客户端ID
+ * @param data Ping数据(可为NULL)
+ * @param length 数据长度
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_send_ping(int client_id, const char *data, size_t length);
+
+/**
+ * @brief 发送Pong帧
+ * @param client_id 客户端ID
+ * @param data Pong数据(可为NULL)
+ * @param length 数据长度
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_send_pong(int client_id, const char *data, size_t length);
+
+/**
+ * @brief 关闭WebSocket连接
+ * @param client_id 客户端ID
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_close(int client_id);
+
+/**
+ * @brief 关闭WebSocket连接(带状态码和原因)
+ * @param client_id 客户端ID
+ * @param close_code 关闭状态码
+ * @param reason 关闭原因(可为NULL)
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_close_with_reason(int client_id, 
+                                      n706a_ws_close_code_t close_code,
+                                      const char *reason);
+
+/**
+ * @brief 获取WebSocket连接状态
+ * @param client_id 客户端ID
+ * @return WebSocket状态
+ */
+n706a_ws_state_t n706a_websocket_get_state(int client_id);
+
+/**
+ * @brief 检查WebSocket是否已连接
+ * @param client_id 客户端ID
+ * @return true已连接,false未连接
+ */
+bool n706a_websocket_is_connected(int client_id);
+
+/**
+ * @brief 销毁WebSocket客户端
+ * @param client_id 客户端ID
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_destructor(int client_id);
+
+/* ========== 内部辅助函数(高级用户可选使用) ========== */
+
+/**
+ * @brief 解析WebSocket URL
+ * @param url 完整URL
+ * @param protocol 协议(输出,"ws"或"wss")
+ * @param host 主机名(输出)
+ * @param port 端口号(输出)
+ * @param path 路径(输出)
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_parse_url(const char *url, 
+                              char *protocol, 
+                              char *host, 
+                              uint16_t *port, 
+                              char *path);
+
+/**
+ * @brief 生成WebSocket Sec-WebSocket-Key
+ * @param key 输出缓冲区(至少25字节)
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_generate_key(char *key);
+
+/**
+ * @brief 计算WebSocket Sec-WebSocket-Accept
+ * @param key Sec-WebSocket-Key
+ * @param accept 输出缓冲区(至少29字节)
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_calculate_accept(const char *key, char *accept);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_N706A_WEBSOCKET_H */
+
diff --git a/code/sdk/zhihui/n706a_net/src/n706a_http_client.c b/code/sdk/zhihui/n706a_net/src/n706a_http_client.c
new file mode 100644 (file)
index 0000000..649e370
--- /dev/null
@@ -0,0 +1,785 @@
+/**
+ * @file n706a_http_client.c
+ * @brief N706A HTTP/HTTPS客户端实现(基于TCP)
+ * 
+ * 基于TCP手动构造HTTP协议,支持:
+ * - HTTP (普通TCP)
+ * - HTTPS (SSL TCP)
+ * - 6个并发连接
+ * - 所有标准HTTP方法
+ */
+
+#include "n706a_http_client.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* 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);
+
+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,
+                          char *host,
+                          uint16_t *port,
+                          char *path);
+
+/* ========== 公共函数实现 ========== */
+
+/**
+ * @brief 初始化HTTP客户端
+ */
+n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client, int socket_id)
+{
+    if (!client || socket_id < 0 || socket_id > 5) {
+        return N706A_HTTP_EARG;
+    }
+    
+    memset(client, 0, sizeof(n706a_http_client_t));
+    
+    client->socket_id = socket_id;
+    client->is_https = false;
+    client->remote_port = 0;
+    client->response_code = 0;
+    client->connected = false;
+    client->keepalive = false;
+    client->timeout_ms = 10000;  // 默认10秒
+    
+    /* 初始化TCP socket */
+    n706a_tcp_socket_create(&client->tcp_sock, socket_id);
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 连接到HTTP/HTTPS服务器
+ */
+n706a_httpc_result_t n706a_http_client_connect(n706a_http_client_t *client,
+                                               const char *host,
+                                               uint16_t port,
+                                               bool use_ssl)
+{
+    if (!client || !host) {
+        return N706A_HTTP_EARG;
+    }
+    
+    /* 保存连接信息 */
+    strncpy(client->host, host, sizeof(client->host) - 1);
+    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握手需要更长时间
+    
+    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;
+    }
+}
+
+/**
+ * @brief 发送HTTP GET请求
+ */
+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) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    /* 构造GET请求 */
+    char request_buf[2048];
+    int request_len = http_build_request(client, "GET", path,
+                                        NULL, 0, NULL,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        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);
+    
+    if (ret != N706A_TCP_SUCCESS || sent != (size_t)request_len) {
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 接收响应 */
+    uint8_t response_buf[8192];
+    size_t received;
+    
+    ret = n706a_tcp_recv(&client->tcp_sock,
+                        response_buf,
+                        sizeof(response_buf) - 1,
+                        &received,
+                        client->timeout_ms);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        return N706A_HTTP_ERECV;
+    }
+    
+    response_buf[received] = '\0';
+    
+    /* 解析响应 */
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 发送HTTP POST请求
+ */
+n706a_httpc_result_t n706a_http_client_post(n706a_http_client_t *client,
+                                            const char *path,
+                                            const char *content_type,
+                                            const char *body,
+                                            size_t body_len,
+                                            n706a_http_client_data_t *client_data)
+{
+    if (!client || !path || !client_data) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    /* 构造POST请求 */
+    char request_buf[4096];
+    int request_len = http_build_request(client, "POST", path,
+                                        body, body_len, content_type,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        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);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 接收响应 */
+    uint8_t response_buf[8192];
+    size_t received;
+    
+    ret = n706a_tcp_recv(&client->tcp_sock,
+                        response_buf,
+                        sizeof(response_buf) - 1,
+                        &received,
+                        client->timeout_ms);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        return N706A_HTTP_ERECV;
+    }
+    
+    response_buf[received] = '\0';
+    
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 发送HTTP PUT请求
+ */
+n706a_httpc_result_t n706a_http_client_put(n706a_http_client_t *client,
+                                           const char *path,
+                                           const char *content_type,
+                                           const char *body,
+                                           size_t body_len,
+                                           n706a_http_client_data_t *client_data)
+{
+    if (!client || !path || !client_data) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    char request_buf[4096];
+    int request_len = http_build_request(client, "PUT", path,
+                                        body, body_len, content_type,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        return N706A_HTTP_ENOBUFS;
+    }
+    
+    size_t sent;
+    n706a_tcp_send(&client->tcp_sock, (uint8_t*)request_buf, request_len, &sent);
+    
+    uint8_t response_buf[8192];
+    size_t received;
+    n706a_tcp_recv(&client->tcp_sock, response_buf, sizeof(response_buf) - 1,
+                   &received, client->timeout_ms);
+    
+    response_buf[received] = '\0';
+    
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 发送HTTP DELETE请求
+ */
+n706a_httpc_result_t n706a_http_client_delete(n706a_http_client_t *client,
+                                              const char *path,
+                                              n706a_http_client_data_t *client_data)
+{
+    if (!client || !path || !client_data) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    char request_buf[2048];
+    int request_len = http_build_request(client, "DELETE", path,
+                                        NULL, 0, NULL,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        return N706A_HTTP_ENOBUFS;
+    }
+    
+    size_t sent;
+    n706a_tcp_send(&client->tcp_sock, (uint8_t*)request_buf, request_len, &sent);
+    
+    uint8_t response_buf[8192];
+    size_t received;
+    n706a_tcp_recv(&client->tcp_sock, response_buf, sizeof(response_buf) - 1,
+                   &received, client->timeout_ms);
+    
+    response_buf[received] = '\0';
+    
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 发送HTTP HEAD请求
+ */
+n706a_httpc_result_t n706a_http_client_head(n706a_http_client_t *client,
+                                            const char *path,
+                                            n706a_http_client_data_t *client_data)
+{
+    if (!client || !path || !client_data) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    char request_buf[2048];
+    int request_len = http_build_request(client, "HEAD", path,
+                                        NULL, 0, NULL,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        return N706A_HTTP_ENOBUFS;
+    }
+    
+    size_t sent;
+    n706a_tcp_send(&client->tcp_sock, (uint8_t*)request_buf, request_len, &sent);
+    
+    uint8_t response_buf[8192];
+    size_t received;
+    n706a_tcp_recv(&client->tcp_sock, response_buf, sizeof(response_buf) - 1,
+                   &received, client->timeout_ms);
+    
+    response_buf[received] = '\0';
+    
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 设置自定义HTTP请求头
+ */
+n706a_httpc_result_t n706a_http_client_set_header(n706a_http_client_t *client,
+                                                  const char *header)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (client->header) {
+        free(client->header);
+    }
+    
+    if (header) {
+        client->header = strdup(header);
+    } else {
+        client->header = NULL;
+    }
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 设置HTTP基本认证
+ */
+n706a_httpc_result_t n706a_http_client_set_auth(n706a_http_client_t *client,
+                                                const char *username,
+                                                const char *password)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (client->auth_user) {
+        free(client->auth_user);
+    }
+    if (client->auth_password) {
+        free(client->auth_password);
+    }
+    
+    if (username) {
+        client->auth_user = strdup(username);
+    } else {
+        client->auth_user = NULL;
+    }
+    
+    if (password) {
+        client->auth_password = strdup(password);
+    } else {
+        client->auth_password = NULL;
+    }
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 设置Keep-Alive模式
+ */
+n706a_httpc_result_t n706a_http_client_set_keepalive(n706a_http_client_t *client,
+                                                     bool keepalive)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    client->keepalive = keepalive;
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 关闭HTTP连接
+ */
+n706a_httpc_result_t n706a_http_client_close(n706a_http_client_t *client)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (client->connected) {
+        n706a_tcp_close(&client->tcp_sock);
+        client->connected = false;
+    }
+    
+    if (client->header) {
+        free(client->header);
+        client->header = NULL;
+    }
+    
+    if (client->auth_user) {
+        free(client->auth_user);
+        client->auth_user = NULL;
+    }
+    
+    if (client->auth_password) {
+        free(client->auth_password);
+        client->auth_password = NULL;
+    }
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 检查HTTP连接状态
+ */
+bool n706a_http_client_is_connected(n706a_http_client_t *client)
+{
+    if (!client) {
+        return false;
+    }
+    
+    return client->connected && n706a_tcp_is_connected(&client->tcp_sock);
+}
+
+/**
+ * @brief 获取最后的HTTP响应码
+ */
+int n706a_http_client_get_response_code(n706a_http_client_t *client)
+{
+    if (!client) {
+        return 0;
+    }
+    
+    return client->response_code;
+}
+
+/**
+ * @brief 解析完整URL并发起GET请求
+ */
+n706a_httpc_result_t n706a_http_client_get_url(n706a_http_client_t *client,
+                                               const char *url,
+                                               n706a_http_client_data_t *client_data)
+{
+    if (!client || !url || !client_data) {
+        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;
+    }
+    
+    bool use_ssl = (strcmp(protocol, "https") == 0);
+    
+    n706a_httpc_result_t ret = n706a_http_client_connect(client, host, port, use_ssl);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return ret;
+    }
+    
+    return n706a_http_client_get(client, path, client_data);
+}
+
+/**
+ * @brief 解析完整URL并发起POST请求
+ */
+n706a_httpc_result_t n706a_http_client_post_url(n706a_http_client_t *client,
+                                                const char *url,
+                                                const char *content_type,
+                                                const char *body,
+                                                size_t body_len,
+                                                n706a_http_client_data_t *client_data)
+{
+    if (!client || !url || !client_data) {
+        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;
+    }
+    
+    bool use_ssl = (strcmp(protocol, "https") == 0);
+    
+    n706a_httpc_result_t ret = n706a_http_client_connect(client, host, port, use_ssl);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return ret;
+    }
+    
+    return n706a_http_client_post(client, path, content_type, body, body_len, client_data);
+}
+
+/* ========== 私有函数实现 ========== */
+
+/**
+ * @brief 构造HTTP请求
+ */
+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)
+{
+    int len = 0;
+    
+    /* 请求行 */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "%s %s HTTP/1.1\r\n", method, path);
+    
+    /* Host头 */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "Host: %s\r\n", client->host);
+    
+    /* User-Agent */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "User-Agent: N706A-HTTP-Client/1.0\r\n");
+    
+    /* Accept */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "Accept: */*\r\n");
+    
+    /* Connection */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "Connection: %s\r\n",
+                   client->keepalive ? "keep-alive" : "close");
+    
+    /* 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);
+    }
+    
+    /* Basic认证 */
+    if (client->auth_user && client->auth_password) {
+        char auth_str[256];
+        char auth_b64[512];
+        
+        snprintf(auth_str, sizeof(auth_str), "%s:%s",
+                client->auth_user, client->auth_password);
+        
+        http_base64_encode(auth_str, strlen(auth_str), auth_b64);
+        
+        len += snprintf(request_buf + len, buf_size - len,
+                       "Authorization: Basic %s\r\n", auth_b64);
+    }
+    
+    /* 自定义请求头 */
+    if (client->header) {
+        len += snprintf(request_buf + len, buf_size - len,
+                       "%s", client->header);
+    }
+    
+    /* 空行 */
+    len += snprintf(request_buf + len, buf_size - len, "\r\n");
+    
+    /* 请求体 */
+    if (body && body_len > 0) {
+        if (len + body_len < buf_size) {
+            memcpy(request_buf + len, body, body_len);
+            len += body_len;
+        } else {
+            return -1;  // 缓冲区不足
+        }
+    }
+    
+    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;
+    }
+    
+    /* 解析状态行 */
+    int major, minor, status_code;
+    if (sscanf(response, "HTTP/%d.%d %d", &major, &minor, &status_code) != 3) {
+        return N706A_HTTP_EPROTO;
+    }
+    
+    client->response_code = status_code;
+    
+    /* 查找头尾分界 */
+    const char *header_end = strstr(response, "\r\n\r\n");
+    if (!header_end) {
+        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';
+    }
+    
+    /* 提取Content-Length */
+    const char *content_len_str = strcasestr(response, "Content-Length:");
+    if (content_len_str && content_len_str < header_end) {
+        data->responseContentLen = atoi(content_len_str + 15);
+    } else {
+        data->responseContentLen = 0;
+    }
+    
+    /* 检查是否chunked */
+    if (strcasestr(response, "Transfer-Encoding: chunked")) {
+        data->isChunked = true;
+    } else {
+        data->isChunked = false;
+    }
+    
+    /* 提取响应体 */
+    const char *body = header_end + 4;
+    size_t body_len = response_len - (body - response);
+    
+    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 (data->responseContentLen > 0 && body_len < (size_t)data->responseContentLen) {
+        data->isMore = true;
+    } else {
+        data->isMore = false;
+    }
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief Base64编码
+ */
+static void http_base64_encode(const char *input, size_t len, char *output)
+{
+    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;
+        }
+    }
+    
+    if (i) {
+        for (size_t k = i; k < 3; k++) {
+            char_array_3[k] = '\0';
+        }
+        
+        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);
+        
+        for (size_t k = 0; k < i + 1; k++) {
+            output[j++] = base64_table[char_array_4[k]];
+        }
+        
+        while (i++ < 3) {
+            output[j++] = '=';
+        }
+    }
+    
+    output[j] = '\0';
+}
+
+/**
+ * @brief 解析URL
+ */
+static int http_parse_url(const char *url,
+                          char *protocol,
+                          char *host,
+                          uint16_t *port,
+                          char *path)
+{
+    if (!url || !protocol || !host || !port || !path) {
+        return -1;
+    }
+    
+    const char *p = url;
+    
+    /* 解析协议 */
+    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;
+    }
+    
+    /* 解析host和port */
+    const char *path_start = strchr(p, '/');
+    const char *port_start = strchr(p, ':');
+    
+    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, "/");
+        }
+    }
+    
+    return 0;
+}
+
diff --git a/code/sdk/zhihui/n706a_net/src/n706a_tcp_client.c b/code/sdk/zhihui/n706a_net/src/n706a_tcp_client.c
new file mode 100644 (file)
index 0000000..2d9639d
--- /dev/null
@@ -0,0 +1,660 @@
+/**
+ * @file n706a_tcp_client.c
+ * @brief N706A TCP客户端实现
+ * 
+ * 支持两种SSL实现方式:
+ * - AT指令模式:使用AT+SSLTCP*指令
+ * - mbedtls模式:使用mbedtls库
+ */
+
+#include "n706a_tcp_client.h"
+#include "ppp_at_handler.h"
+#include "ppp_types.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* ========== 私有函数声明 ========== */
+
+#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);
+static n706a_tcp_result_t tcp_mbedtls_init_ssl(n706a_tcp_socket_t *sock);
+static void tcp_mbedtls_cleanup_ssl(n706a_tcp_socket_t *sock);
+#endif
+
+/* ========== 公共函数实现 ========== */
+
+/**
+ * @brief 创建TCP socket
+ */
+n706a_tcp_result_t n706a_tcp_socket_create(n706a_tcp_socket_t *sock, int socket_id)
+{
+    if (!sock || socket_id < 0 || socket_id > 5) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    memset(sock, 0, sizeof(n706a_tcp_socket_t));
+    
+    sock->socket_id = socket_id;
+    sock->state = N706A_TCP_STATE_CLOSED;
+    sock->is_ssl = false;
+    sock->timeout_ms = 15000;  // 默认15秒超时
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    sock->ssl_version = 3;  // 默认TLS 1.2
+    sock->auth_mode = 1;    // 默认服务器认证
+    memset(sock->ca_cert_file, 0, sizeof(sock->ca_cert_file));
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    sock->ssl_version = MBEDTLS_SSL_MINOR_VERSION_3;  // 默认TLS 1.2
+    sock->auth_mode = MBEDTLS_SSL_VERIFY_REQUIRED;    // 默认服务器认证
+    sock->ssl_initialized = false;
+#endif
+    
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 配置SSL参数
+ */
+n706a_tcp_result_t n706a_tcp_config_ssl(n706a_tcp_socket_t *sock,
+                                        int ssl_version,
+                                        int auth_mode,
+                                        const char *ca_cert)
+{
+    if (!sock) {
+        return N706A_TCP_EINVAL;
+    }
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    /* AT指令模式:保存配置参数 */
+    sock->ssl_version = ssl_version;
+    sock->auth_mode = auth_mode;
+    
+    if (ca_cert) {
+        strncpy(sock->ca_cert_file, ca_cert, sizeof(sock->ca_cert_file) - 1);
+        
+        /* 配置AT指令 */
+        char at_cmd[256];
+        
+        /* 设置SSL版本 */
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCFG=\"sslversion\",%d", ssl_version);
+        ppp_at_response_t response;
+        ppp_at_send_command(at_cmd, NULL, 5000, &response);
+        
+        /* 设置认证模式 */
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCFG=\"authmode\",%d", auth_mode);
+        ppp_at_send_command(at_cmd, NULL, 5000, &response);
+        
+        /* 设置CA证书 */
+        if (ca_cert && strlen(ca_cert) > 0) {
+            snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCFG=\"cacert\",\"%s\"", ca_cert);
+            ppp_at_send_command(at_cmd, NULL, 5000, &response);
+        }
+    }
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:解析CA证书 */
+    if (ca_cert && strlen(ca_cert) > 0) {
+        mbedtls_x509_crt_init(&sock->cacert);
+        
+        int ret = mbedtls_x509_crt_parse(&sock->cacert,
+                                        (const unsigned char *)ca_cert,
+                                        strlen(ca_cert) + 1);
+        if (ret != 0) {
+            return N706A_TCP_ERROR;
+        }
+    }
+    
+    sock->auth_mode = auth_mode;
+#endif
+    
+    return N706A_TCP_SUCCESS;
+}
+
+#ifdef N706A_SSL_USE_MBEDTLS
+/**
+ * @brief 配置客户端证书和私钥(mbedtls专用)
+ */
+n706a_tcp_result_t n706a_tcp_config_ssl_client_cert(n706a_tcp_socket_t *sock,
+                                                     const char *client_cert,
+                                                     const char *client_key)
+{
+    if (!sock || !client_cert || !client_key) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    /* 解析客户端证书 */
+    mbedtls_x509_crt_init(&sock->clicert);
+    int ret = mbedtls_x509_crt_parse(&sock->clicert,
+                                    (const unsigned char *)client_cert,
+                                    strlen(client_cert) + 1);
+    if (ret != 0) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 解析私钥 */
+    mbedtls_pk_init(&sock->pkey);
+    ret = mbedtls_pk_parse_key(&sock->pkey,
+                              (const unsigned char *)client_key,
+                              strlen(client_key) + 1,
+                              NULL, 0
+#if MBEDTLS_VERSION_MAJOR >= 3
+                              , mbedtls_ctr_drbg_random, &sock->ctr_drbg
+#endif
+                              );
+    if (ret != 0) {
+        mbedtls_x509_crt_free(&sock->clicert);
+        return N706A_TCP_ERROR;
+    }
+    
+    return N706A_TCP_SUCCESS;
+}
+#endif
+
+/**
+ * @brief 连接到TCP服务器
+ */
+n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock,
+                                     const char *host,
+                                     uint16_t port,
+                                     bool use_ssl,
+                                     uint32_t timeout_ms)
+{
+    if (!sock || !host) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    if (sock->state == N706A_TCP_STATE_CONNECTED) {
+        return N706A_TCP_SUCCESS;  // 已连接
+    }
+    
+    /* 保存连接信息 */
+    strncpy(sock->remote_ip, host, sizeof(sock->remote_ip) - 1);
+    sock->remote_port = port;
+    sock->is_ssl = use_ssl;
+    sock->timeout_ms = timeout_ms;
+    
+    char at_cmd[256];
+    ppp_at_response_t response;
+    ppp_error_t ret;
+    
+    sock->state = N706A_TCP_STATE_CONNECTING;
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    if (use_ssl) {
+        /* AT指令SSL模式:使用AT+SSLTCPSETUP */
+        snprintf(at_cmd, sizeof(at_cmd),
+                "AT+SSLTCPSETUP=%d,%s,%d,%d",
+                sock->socket_id,
+                host,
+                port,
+                sock->auth_mode);
+        
+        ret = ppp_at_send_command(at_cmd, NULL, timeout_ms, &response);
+        
+        if (ret == PPP_ERR_OK && response.length > 0 &&
+            strstr(response.response, "+SSLTCPSETUP:") &&
+            strstr(response.response, ",OK")) {
+            sock->state = N706A_TCP_STATE_CONNECTED;
+            return N706A_TCP_SUCCESS;
+        }
+    } else {
+        /* 普通TCP */
+        snprintf(at_cmd, sizeof(at_cmd),
+                "AT+TCPSETUP=%d,%s,%d",
+                sock->socket_id,
+                host,
+                port);
+        
+        ret = ppp_at_send_command(at_cmd, NULL, timeout_ms, &response);
+        
+        if (ret == PPP_ERR_OK && response.length > 0 &&
+            strstr(response.response, "+TCPSETUP:") &&
+            strstr(response.response, ",OK")) {
+            sock->state = N706A_TCP_STATE_CONNECTED;
+            return N706A_TCP_SUCCESS;
+        }
+    }
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:先建立TCP,再进行SSL握手 */
+    
+    /* 1. 先建立普通TCP连接 */
+    snprintf(at_cmd, sizeof(at_cmd),
+            "AT+TCPSETUP=%d,%s,%d",
+            sock->socket_id,
+            host,
+            port);
+    
+    ret = ppp_at_send_command(at_cmd, NULL, timeout_ms, &response);
+    
+    if (ret != PPP_ERR_OK || response.length == 0 ||
+        !strstr(response.response, "+TCPSETUP:") ||
+        !strstr(response.response, ",OK")) {
+        sock->state = N706A_TCP_STATE_ERROR;
+        return N706A_TCP_ECONNREF;
+    }
+    
+    /* 2. 如果使用SSL,进行SSL握手 */
+    if (use_ssl) {
+        n706a_tcp_result_t ssl_ret = tcp_mbedtls_init_ssl(sock);
+        if (ssl_ret != N706A_TCP_SUCCESS) {
+            n706a_tcp_close(sock);
+            return ssl_ret;
+        }
+        
+        /* 设置主机名(用于SNI) */
+        mbedtls_ssl_set_hostname(&sock->ssl, host);
+        
+        /* 执行SSL握手 */
+        int handshake_ret;
+        while ((handshake_ret = mbedtls_ssl_handshake(&sock->ssl)) != 0) {
+            if (handshake_ret != MBEDTLS_ERR_SSL_WANT_READ &&
+                handshake_ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+                /* 握手失败 */
+                tcp_mbedtls_cleanup_ssl(sock);
+                n706a_tcp_close(sock);
+                sock->state = N706A_TCP_STATE_ERROR;
+                return N706A_TCP_ERROR;
+            }
+        }
+        
+        /* 验证证书 */
+        uint32_t flags = mbedtls_ssl_get_verify_result(&sock->ssl);
+        if (flags != 0 && sock->auth_mode != MBEDTLS_SSL_VERIFY_NONE) {
+            /* 证书验证失败 */
+            tcp_mbedtls_cleanup_ssl(sock);
+            n706a_tcp_close(sock);
+            sock->state = N706A_TCP_STATE_ERROR;
+            return N706A_TCP_ERROR;
+        }
+    }
+#endif
+    
+    sock->state = N706A_TCP_STATE_CONNECTED;
+    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)
+{
+    if (!sock || !data || len == 0 || !sent) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    if (sock->state != N706A_TCP_STATE_CONNECTED) {
+        return N706A_TCP_ENOTCONN;
+    }
+    
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:通过SSL发送 */
+    if (sock->is_ssl && sock->ssl_initialized) {
+        int ret = mbedtls_ssl_write(&sock->ssl, data, len);
+        if (ret > 0) {
+            *sent = ret;
+            return N706A_TCP_SUCCESS;
+        } else {
+            return N706A_TCP_ERROR;
+        }
+    }
+#endif
+    
+    /* AT指令模式或普通TCP */
+    char at_cmd[64];
+    ppp_at_response_t response;
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    if (sock->is_ssl) {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPSEND=%d,%zu",
+                sock->socket_id, len);
+    } else
+#endif
+    {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu",
+                sock->socket_id, len);
+    }
+    
+    /* 发送命令,等待'>'提示符 */
+    ppp_error_t ret = ppp_at_send_command(at_cmd, NULL, 5000, &response);
+    if (ret != PPP_ERR_OK) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 发送实际数据 */
+    ret = ppp_at_send_raw_data(data, len, sock->timeout_ms);
+    if (ret != PPP_ERR_OK) {
+        return N706A_TCP_ERROR;
+    }
+    
+    *sent = len;
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 接收TCP数据
+ */
+n706a_tcp_result_t n706a_tcp_recv(n706a_tcp_socket_t *sock,
+                                  uint8_t *buffer,
+                                  size_t len,
+                                  size_t *received,
+                                  uint32_t timeout_ms)
+{
+    if (!sock || !buffer || len == 0 || !received) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    if (sock->state != N706A_TCP_STATE_CONNECTED) {
+        return N706A_TCP_ENOTCONN;
+    }
+    
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:通过SSL接收 */
+    if (sock->is_ssl && sock->ssl_initialized) {
+        mbedtls_ssl_conf_read_timeout(&sock->conf, timeout_ms);
+        
+        int ret = mbedtls_ssl_read(&sock->ssl, buffer, len);
+        if (ret > 0) {
+            *received = ret;
+            return N706A_TCP_SUCCESS;
+        } else if (ret == 0 || ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
+            *received = 0;
+            return N706A_TCP_SUCCESS;
+        } else {
+            return N706A_TCP_ERROR;
+        }
+    }
+#endif
+    
+    /* AT指令模式或普通TCP */
+    char at_cmd[64];
+    ppp_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);
+    }
+    
+    ppp_error_t ret = ppp_at_send_command(at_cmd, NULL, timeout_ms, &response);
+    if (ret != PPP_ERR_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);
+            
+            if (data_len > len) {
+                data_len = len;
+            }
+            
+            memcpy(buffer, data_start, data_len);
+            *received = data_len;
+            return N706A_TCP_SUCCESS;
+        }
+    }
+    
+    *received = 0;
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 关闭TCP连接
+ */
+n706a_tcp_result_t n706a_tcp_close(n706a_tcp_socket_t *sock)
+{
+    if (!sock) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    if (sock->state == N706A_TCP_STATE_CLOSED) {
+        return N706A_TCP_SUCCESS;
+    }
+    
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:清理SSL上下文 */
+    if (sock->is_ssl && sock->ssl_initialized) {
+        mbedtls_ssl_close_notify(&sock->ssl);
+        tcp_mbedtls_cleanup_ssl(sock);
+    }
+#endif
+    
+    /* 发送关闭命令 */
+    char at_cmd[32];
+    ppp_at_response_t response;
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    if (sock->is_ssl) {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCLOSE=%d", sock->socket_id);
+    } else
+#endif
+    {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+TCPCLOSE=%d", sock->socket_id);
+    }
+    
+    ppp_at_send_command(at_cmd, NULL, 5000, &response);
+    
+    sock->state = N706A_TCP_STATE_CLOSED;
+    sock->is_ssl = false;
+    
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 检查socket是否已连接
+ */
+bool n706a_tcp_is_connected(const n706a_tcp_socket_t *sock)
+{
+    if (!sock) {
+        return false;
+    }
+    
+    return (sock->state == N706A_TCP_STATE_CONNECTED);
+}
+
+/**
+ * @brief 获取socket状态
+ */
+n706a_tcp_state_t n706a_tcp_get_state(const n706a_tcp_socket_t *sock)
+{
+    if (!sock) {
+        return N706A_TCP_STATE_ERROR;
+    }
+    
+    return sock->state;
+}
+
+/* ========== mbedtls私有函数实现 ========== */
+
+#ifdef N706A_SSL_USE_MBEDTLS
+
+/**
+ * @brief 初始化mbedtls SSL
+ */
+static n706a_tcp_result_t tcp_mbedtls_init_ssl(n706a_tcp_socket_t *sock)
+{
+    if (!sock) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    /* 初始化SSL上下文 */
+    mbedtls_ssl_init(&sock->ssl);
+    mbedtls_ssl_config_init(&sock->conf);
+    mbedtls_entropy_init(&sock->entropy);
+    mbedtls_ctr_drbg_init(&sock->ctr_drbg);
+    
+    /* 初始化随机数生成器 */
+    const char *pers = "n706a_tcp_client";
+    int ret = mbedtls_ctr_drbg_seed(&sock->ctr_drbg,
+                                   mbedtls_entropy_func,
+                                   &sock->entropy,
+                                   (const unsigned char *)pers,
+                                   strlen(pers));
+    if (ret != 0) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 配置SSL */
+    ret = mbedtls_ssl_config_defaults(&sock->conf,
+                                     MBEDTLS_SSL_IS_CLIENT,
+                                     MBEDTLS_SSL_TRANSPORT_STREAM,
+                                     MBEDTLS_SSL_PRESET_DEFAULT);
+    if (ret != 0) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 设置认证模式 */
+    mbedtls_ssl_conf_authmode(&sock->conf, sock->auth_mode);
+    
+    /* 设置随机数生成器 */
+    mbedtls_ssl_conf_rng(&sock->conf,
+                        mbedtls_ctr_drbg_random,
+                        &sock->ctr_drbg);
+    
+    /* 设置CA证书 */
+    mbedtls_ssl_conf_ca_chain(&sock->conf, &sock->cacert, NULL);
+    
+    /* 设置客户端证书(如果有) */
+    if (sock->clicert.version != 0) {
+        mbedtls_ssl_conf_own_cert(&sock->conf, &sock->clicert, &sock->pkey);
+    }
+    
+    /* 应用配置 */
+    ret = mbedtls_ssl_setup(&sock->ssl, &sock->conf);
+    if (ret != 0) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 设置发送/接收回调 */
+    mbedtls_ssl_set_bio(&sock->ssl,
+                       sock,
+                       tcp_mbedtls_send_callback,
+                       tcp_mbedtls_recv_callback,
+                       NULL);
+    
+    sock->ssl_initialized = true;
+    
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 清理mbedtls SSL
+ */
+static void tcp_mbedtls_cleanup_ssl(n706a_tcp_socket_t *sock)
+{
+    if (!sock || !sock->ssl_initialized) {
+        return;
+    }
+    
+    mbedtls_ssl_free(&sock->ssl);
+    mbedtls_ssl_config_free(&sock->conf);
+    mbedtls_ctr_drbg_free(&sock->ctr_drbg);
+    mbedtls_entropy_free(&sock->entropy);
+    
+    if (sock->cacert.version != 0) {
+        mbedtls_x509_crt_free(&sock->cacert);
+    }
+    
+    if (sock->clicert.version != 0) {
+        mbedtls_x509_crt_free(&sock->clicert);
+    }
+    
+    /* 检查私钥是否已初始化,在mbedtls 2.x和3.x中都有效 */
+    mbedtls_pk_free(&sock->pkey);
+    
+    sock->ssl_initialized = false;
+}
+
+/**
+ * @brief mbedtls发送回调(通过AT命令发送)
+ */
+static int tcp_mbedtls_send_callback(void *ctx, const unsigned char *buf, size_t len)
+{
+    n706a_tcp_socket_t *sock = (n706a_tcp_socket_t *)ctx;
+    
+    if (!sock || !buf || len == 0) {
+        return MBEDTLS_ERR_NET_SEND_FAILED;
+    }
+    
+    /* 通过AT+TCPSEND发送加密后的数据 */
+    char at_cmd[64];
+    snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu",
+            sock->socket_id, len);
+    
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command(at_cmd, NULL, 5000, &response);
+    if (ret != PPP_ERR_OK) {
+        return MBEDTLS_ERR_NET_SEND_FAILED;
+    }
+    
+    /* 发送数据 */
+    ret = ppp_at_send_raw_data(buf, len, sock->timeout_ms);
+    if (ret != PPP_ERR_OK) {
+        return MBEDTLS_ERR_NET_SEND_FAILED;
+    }
+    
+    return len;
+}
+
+/**
+ * @brief mbedtls接收回调(通过AT命令接收)
+ */
+static int tcp_mbedtls_recv_callback(void *ctx, unsigned char *buf, size_t len)
+{
+    n706a_tcp_socket_t *sock = (n706a_tcp_socket_t *)ctx;
+    
+    if (!sock || !buf || len == 0) {
+        return MBEDTLS_ERR_NET_RECV_FAILED;
+    }
+    
+    /* 通过AT+TCPREAD接收加密数据 */
+    char at_cmd[64];
+    snprintf(at_cmd, sizeof(at_cmd), "AT+TCPREAD=%d,%zu",
+            sock->socket_id, len);
+    
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command(at_cmd, NULL, sock->timeout_ms, &response);
+    if (ret != PPP_ERR_OK) {
+        return MBEDTLS_ERR_NET_RECV_FAILED;
+    }
+    
+    /* 解析数据 */
+    if (response.length > 0) {
+        const char *data_start = strstr(response.response, "\r\n");
+        if (data_start) {
+            data_start += 2;
+            size_t data_len = response.length - (data_start - response.response);
+            
+            if (data_len > len) {
+                data_len = len;
+            }
+            
+            memcpy(buf, data_start, data_len);
+            return data_len;
+        }
+    }
+    
+    return 0;  // 没有数据
+}
+
+#endif /* N706A_SSL_USE_MBEDTLS */
+
diff --git a/code/sdk/zhihui/n706a_net/src/n706a_websocket.c b/code/sdk/zhihui/n706a_net/src/n706a_websocket.c
new file mode 100644 (file)
index 0000000..70b86a2
--- /dev/null
@@ -0,0 +1,843 @@
+/**
+ * @file n706a_websocket.c
+ * @brief N706A WebSocket客户端实现(基于TCP)
+ * 
+ * 完整实现RFC 6455 WebSocket协议:
+ * - ws:// (普通TCP)
+ * - wss:// (SSL TCP)
+ * - 握手
+ * - 帧封装/解封装
+ * - Ping/Pong
+ * - 分片传输
+ */
+
+#include "n706a_websocket.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+/* WebSocket GUID(RFC 6455) */
+#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+/* WebSocket帧标志 */
+#define WS_FIN_BIT      0x80
+#define WS_MASK_BIT     0x80
+#define WS_OPCODE_MASK  0x0F
+#define WS_PAYLOAD_MASK 0x7F
+
+/* 全局WebSocket客户端数组 */
+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_send_frame(n706a_websocket_client_t *client,
+                        n706a_ws_opcode_t opcode,
+                        const uint8_t *data,
+                        size_t length,
+                        bool fin);
+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(void *arg);
+static int ws_parse_url(const char *url, char *protocol, char *host,
+                       uint16_t *port, char *path);
+
+/* SHA-1哈希(简化实现,用于WebSocket握手) */
+static void sha1_simple(const unsigned char *input, size_t len, unsigned char output[20]);
+
+/* Base64编码 */
+static void base64_encode(const unsigned char *input, size_t len, char *output);
+
+/* ========== 公共函数实现 ========== */
+
+/**
+ * @brief 创建WebSocket客户端
+ */
+int n706a_websocket_constructor(n706a_ws_on_open_callback_t on_open,
+                                n706a_ws_on_message_callback_t on_message,
+                                n706a_ws_on_error_callback_t on_error,
+                                n706a_ws_on_close_callback_t on_close)
+{
+    /* 查找空闲slot */
+    int client_id = -1;
+    for (int i = 0; i < N706A_WS_MAX_CLIENTS; i++) {
+        if (g_ws_clients[i] == NULL) {
+            client_id = i;
+            break;
+        }
+    }
+    
+    if (client_id < 0) {
+        return -1;  // 没有可用slot
+    }
+    
+    /* 分配客户端结构 */
+    n706a_websocket_client_t *client = 
+        (n706a_websocket_client_t *)malloc(sizeof(n706a_websocket_client_t));
+    
+    if (!client) {
+        return -1;
+    }
+    
+    memset(client, 0, sizeof(n706a_websocket_client_t));
+    
+    client->client_id = client_id;
+    client->state = N706A_WS_STATE_CLOSED;
+    client->is_ssl = false;
+    
+    /* 初始化TCP socket */
+    n706a_tcp_socket_create(&client->tcp_sock, client_id);
+    
+    /* 设置回调 */
+    client->on_open = on_open;
+    client->on_message = on_message;
+    client->on_error = on_error;
+    client->on_close = on_close;
+    
+    /* 分配接收缓冲区 */
+    client->recv_buffer_size = 8192;
+    client->recv_buffer = (uint8_t *)malloc(client->recv_buffer_size);
+    if (!client->recv_buffer) {
+        free(client);
+        return -1;
+    }
+    
+    client->recv_buffer_len = 0;
+    client->running = false;
+    
+    g_ws_clients[client_id] = client;
+    
+    return client_id;
+}
+
+/**
+ * @brief 创建WebSocket客户端(指定socket ID)
+ */
+int n706a_websocket_constructor_with_socket(int socket_id,
+                                            n706a_ws_on_open_callback_t on_open,
+                                            n706a_ws_on_message_callback_t on_message,
+                                            n706a_ws_on_error_callback_t on_error,
+                                            n706a_ws_on_close_callback_t on_close)
+{
+    if (socket_id < 0 || socket_id >= N706A_WS_MAX_CLIENTS) {
+        return -1;
+    }
+    
+    if (g_ws_clients[socket_id] != NULL) {
+        return -1;  // 已被占用
+    }
+    
+    n706a_websocket_client_t *client = 
+        (n706a_websocket_client_t *)malloc(sizeof(n706a_websocket_client_t));
+    
+    if (!client) {
+        return -1;
+    }
+    
+    memset(client, 0, sizeof(n706a_websocket_client_t));
+    
+    client->client_id = socket_id;
+    client->state = N706A_WS_STATE_CLOSED;
+    
+    n706a_tcp_socket_create(&client->tcp_sock, socket_id);
+    
+    client->on_open = on_open;
+    client->on_message = on_message;
+    client->on_error = on_error;
+    client->on_close = on_close;
+    
+    client->recv_buffer_size = 8192;
+    client->recv_buffer = (uint8_t *)malloc(client->recv_buffer_size);
+    if (!client->recv_buffer) {
+        free(client);
+        return -1;
+    }
+    
+    g_ws_clients[socket_id] = client;
+    
+    return socket_id;
+}
+
+/**
+ * @brief 连接到WebSocket服务器
+ */
+int n706a_websocket_connect(int client_id, const char *url, const char *option)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    if (client->state != N706A_WS_STATE_CLOSED) {
+        return N706A_WS_ERROR_ALREADY_OPEN;
+    }
+    
+    /* 解析URL */
+    char protocol[8];
+    
+    if (ws_parse_url(url, protocol, client->host, &client->port, client->path) != 0) {
+        if (client->on_error) {
+            client->on_error(client_id, N706A_WS_ERROR_INVALID_PARAM, "Invalid URL");
+        }
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    strncpy(client->url, url, sizeof(client->url) - 1);
+    
+    /* 判断是否WSS */
+    client->is_ssl = (strcmp(protocol, "wss") == 0);
+    
+    client->state = N706A_WS_STATE_CONNECTING;
+    
+    /* 建立TCP连接 */
+    n706a_tcp_result_t ret = n706a_tcp_connect(&client->tcp_sock,
+                                               client->host,
+                                               client->port,
+                                               client->is_ssl,
+                                               30000);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        client->state = N706A_WS_STATE_CLOSED;
+        if (client->on_error) {
+            client->on_error(client_id, N706A_WS_ERROR_HANDSHAKE, "TCP connection failed");
+        }
+        return N706A_WS_ERROR_HANDSHAKE;
+    }
+    
+    /* WebSocket握手 */
+    if (ws_perform_handshake(client) != 0) {
+        n706a_tcp_close(&client->tcp_sock);
+        client->state = N706A_WS_STATE_CLOSED;
+        if (client->on_error) {
+            client->on_error(client_id, N706A_WS_ERROR_HANDSHAKE, "WebSocket handshake failed");
+        }
+        return N706A_WS_ERROR_HANDSHAKE;
+    }
+    
+    client->state = N706A_WS_STATE_OPEN;
+    
+    /* 启动接收线程 */
+    client->running = true;
+    // TODO: 创建接收线程
+    // pthread_create(&client->recv_thread, NULL, ws_recv_thread_func, client);
+    
+    /* 调用on_open回调 */
+    if (client->on_open) {
+        client->on_open(client_id);
+    }
+    
+    return N706A_WS_SUCCESS;
+}
+
+/**
+ * @brief 发送文本消息
+ */
+int n706a_websocket_send_text(int client_id, const char *data, size_t length)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client || client->state != N706A_WS_STATE_OPEN) {
+        return N706A_WS_ERROR_NOT_CONNECTED;
+    }
+    
+    return ws_send_frame(client, N706A_WS_OPCODE_TEXT, (const uint8_t *)data, length, true);
+}
+
+/**
+ * @brief 发送二进制消息
+ */
+int n706a_websocket_send_binary(int client_id, const uint8_t *data, size_t length)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client || client->state != N706A_WS_STATE_OPEN) {
+        return N706A_WS_ERROR_NOT_CONNECTED;
+    }
+    
+    return ws_send_frame(client, N706A_WS_OPCODE_BINARY, data, length, true);
+}
+
+/**
+ * @brief 发送Ping帧
+ */
+int n706a_websocket_send_ping(int client_id, const char *data, size_t length)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client || client->state != N706A_WS_STATE_OPEN) {
+        return N706A_WS_ERROR_NOT_CONNECTED;
+    }
+    
+    return ws_send_frame(client, N706A_WS_OPCODE_PING, (const uint8_t *)data, length, true);
+}
+
+/**
+ * @brief 发送Pong帧
+ */
+int n706a_websocket_send_pong(int client_id, const char *data, size_t length)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client || client->state != N706A_WS_STATE_OPEN) {
+        return N706A_WS_ERROR_NOT_CONNECTED;
+    }
+    
+    return ws_send_frame(client, N706A_WS_OPCODE_PONG, (const uint8_t *)data, length, true);
+}
+
+/**
+ * @brief 关闭WebSocket连接
+ */
+int n706a_websocket_close(int client_id)
+{
+    return n706a_websocket_close_with_reason(client_id, N706A_WS_CLOSE_NORMAL, NULL);
+}
+
+/**
+ * @brief 关闭WebSocket连接(带原因)
+ */
+int n706a_websocket_close_with_reason(int client_id,
+                                      n706a_ws_close_code_t close_code,
+                                      const char *reason)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    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);
+        
+        client->state = N706A_WS_STATE_CLOSING;
+    }
+    
+    /* 停止接收线程 */
+    client->running = false;
+    // TODO: 等待线程结束
+    // pthread_join(client->recv_thread, NULL);
+    
+    /* 关闭TCP连接 */
+    n706a_tcp_close(&client->tcp_sock);
+    
+    client->state = N706A_WS_STATE_CLOSED;
+    
+    /* 调用on_close回调 */
+    if (client->on_close) {
+        client->on_close(client_id, close_code, reason);
+    }
+    
+    return N706A_WS_SUCCESS;
+}
+
+/**
+ * @brief 获取WebSocket状态
+ */
+n706a_ws_state_t n706a_websocket_get_state(int client_id)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_STATE_CLOSED;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client) {
+        return N706A_WS_STATE_CLOSED;
+    }
+    
+    return client->state;
+}
+
+/**
+ * @brief 检查WebSocket是否已连接
+ */
+bool n706a_websocket_is_connected(int client_id)
+{
+    return (n706a_websocket_get_state(client_id) == N706A_WS_STATE_OPEN);
+}
+
+/**
+ * @brief 销毁WebSocket客户端
+ */
+int n706a_websocket_destructor(int client_id)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client) {
+        return N706A_WS_SUCCESS;
+    }
+    
+    /* 确保连接已关闭 */
+    if (client->state != N706A_WS_STATE_CLOSED) {
+        n706a_websocket_close(client_id);
+    }
+    
+    /* 释放资源 */
+    if (client->recv_buffer) {
+        free(client->recv_buffer);
+    }
+    
+    free(client);
+    g_ws_clients[client_id] = NULL;
+    
+    return N706A_WS_SUCCESS;
+}
+
+/* ========== 私有函数实现 ========== */
+
+/**
+ * @brief 执行WebSocket握手
+ */
+static int ws_perform_handshake(n706a_websocket_client_t *client)
+{
+    char handshake_req[1024];
+    char sec_key[25];
+    
+    /* 生成Sec-WebSocket-Key */
+    n706a_websocket_generate_key(sec_key);
+    strncpy(client->sec_key, sec_key, sizeof(client->sec_key) - 1);
+    
+    /* 构造握手请求 */
+    int len = snprintf(handshake_req, sizeof(handshake_req),
+        "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
+    );
+    
+    /* 发送握手请求 */
+    size_t sent;
+    n706a_tcp_result_t ret = n706a_tcp_send(&client->tcp_sock,
+                                            (uint8_t *)handshake_req,
+                                            len,
+                                            &sent);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        return -1;
+    }
+    
+    /* 接收握手响应 */
+    uint8_t response[2048];
+    size_t received;
+    
+    ret = n706a_tcp_recv(&client->tcp_sock,
+                        response,
+                        sizeof(response) - 1,
+                        &received,
+                        10000);
+    
+    if (ret != N706A_TCP_SUCCESS || received == 0) {
+        return -1;
+    }
+    
+    response[received] = '\0';
+    
+    /* 验证响应 */
+    if (!strstr((char *)response, "HTTP/1.1 101") ||
+        !strstr((char *)response, "Upgrade: websocket") ||
+        !strstr((char *)response, "Connection: Upgrade")) {
+        return -1;
+    }
+    
+    /* 验证Sec-WebSocket-Accept */
+    const char *accept_start = strstr((char *)response, "Sec-WebSocket-Accept:");
+    if (accept_start) {
+        accept_start += 21;  // 跳过"Sec-WebSocket-Accept:"
+        while (*accept_start == ' ') accept_start++;
+        
+        char expected_accept[29];
+        n706a_websocket_calculate_accept(sec_key, expected_accept);
+        
+        if (strncmp(accept_start, expected_accept, strlen(expected_accept)) != 0) {
+            return -1;  // Accept值不匹配
+        }
+    }
+    
+    return 0;
+}
+
+/**
+ * @brief 发送WebSocket帧
+ */
+static int ws_send_frame(n706a_websocket_client_t *client,
+                        n706a_ws_opcode_t opcode,
+                        const uint8_t *data,
+                        size_t length,
+                        bool fin)
+{
+    uint8_t frame[14];  // 最大帧头长度
+    size_t frame_len = 0;
+    
+    /* 第一字节:FIN + RSV + Opcode */
+    frame[frame_len++] = (fin ? WS_FIN_BIT : 0) | (opcode & WS_OPCODE_MASK);
+    
+    /* 第二字节:MASK + Payload length */
+    if (length < 126) {
+        frame[frame_len++] = WS_MASK_BIT | (length & WS_PAYLOAD_MASK);
+    } else if (length < 65536) {
+        frame[frame_len++] = WS_MASK_BIT | 126;
+        frame[frame_len++] = (length >> 8) & 0xFF;
+        frame[frame_len++] = length & 0xFF;
+    } else {
+        frame[frame_len++] = WS_MASK_BIT | 127;
+        for (int i = 7; i >= 0; i--) {
+            frame[frame_len++] = (length >> (i * 8)) & 0xFF;
+        }
+    }
+    
+    /* Masking key */
+    uint8_t mask_key[4];
+    ws_generate_mask_key(mask_key);
+    memcpy(frame + frame_len, mask_key, 4);
+    frame_len += 4;
+    
+    /* 发送帧头 */
+    size_t sent;
+    n706a_tcp_send(&client->tcp_sock, frame, frame_len, &sent);
+    
+    /* 发送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);
+        }
+    }
+    
+    return N706A_WS_SUCCESS;
+}
+
+/**
+ * @brief 接收WebSocket帧
+ */
+static int ws_recv_frame(n706a_websocket_client_t *client)
+{
+    uint8_t header[2];
+    size_t received;
+    
+    /* 接收帧头前2字节 */
+    n706a_tcp_result_t ret = n706a_tcp_recv(&client->tcp_sock,
+                                            header,
+                                            2,
+                                            &received,
+                                            5000);
+    
+    if (ret != N706A_TCP_SUCCESS || received < 2) {
+        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];
+        }
+    }
+    
+    /* 接收payload */
+    if (payload_len > 0) {
+        uint8_t *payload = (uint8_t *)malloc(payload_len + 1);
+        if (!payload) {
+            return -1;
+        }
+        
+        n706a_tcp_recv(&client->tcp_sock, payload, payload_len, &received, 10000);
+        payload[payload_len] = '\0';
+        
+        /* 处理不同类型的帧 */
+        switch (opcode) {
+            case N706A_WS_OPCODE_TEXT:
+            case N706A_WS_OPCODE_BINARY:
+                if (client->on_message) {
+                    client->on_message(client->client_id,
+                                     (char *)payload,
+                                     payload_len,
+                                     fin,
+                                     opcode == N706A_WS_OPCODE_BINARY);
+                }
+                break;
+                
+            case N706A_WS_OPCODE_PING:
+                /* 自动回复Pong */
+                ws_send_frame(client, N706A_WS_OPCODE_PONG, payload, payload_len, true);
+                break;
+                
+            case N706A_WS_OPCODE_PONG:
+                /* 忽略Pong */
+                break;
+                
+            case N706A_WS_OPCODE_CLOSE:
+                /* 服务器关闭连接 */
+                client->state = N706A_WS_STATE_CLOSING;
+                
+                uint16_t close_code = N706A_WS_CLOSE_NO_STATUS;
+                if (payload_len >= 2) {
+                    close_code = (payload[0] << 8) | payload[1];
+                }
+                
+                if (client->on_close) {
+                    client->on_close(client->client_id, close_code,
+                                   payload_len > 2 ? (char *)(payload + 2) : NULL);
+                }
+                break;
+                
+            default:
+                break;
+        }
+        
+        free(payload);
+    }
+    
+    return 0;
+}
+
+/**
+ * @brief 生成mask key
+ */
+static void ws_generate_mask_key(uint8_t *mask_key)
+{
+    for (int i = 0; i < 4; i++) {
+        mask_key[i] = rand() & 0xFF;
+    }
+}
+
+/**
+ * @brief 掩码数据
+ */
+static void ws_mask_data(uint8_t *data, size_t len, const uint8_t *mask_key)
+{
+    for (size_t i = 0; i < len; i++) {
+        data[i] ^= mask_key[i % 4];
+    }
+}
+
+/**
+ * @brief 解析WebSocket URL
+ */
+static int ws_parse_url(const char *url, char *protocol, char *host,
+                       uint16_t *port, char *path)
+{
+    if (!url || !protocol || !host || !port || !path) {
+        return -1;
+    }
+    
+    const char *p = url;
+    
+    /* 解析协议 */
+    if (strncmp(p, "wss://", 6) == 0) {
+        strcpy(protocol, "wss");
+        *port = 443;
+        p += 6;
+    } else if (strncmp(p, "ws://", 5) == 0) {
+        strcpy(protocol, "ws");
+        *port = 80;
+        p += 5;
+    } else {
+        return -1;
+    }
+    
+    /* 解析host */
+    const char *path_start = strchr(p, '/');
+    const char *port_start = strchr(p, ':');
+    
+    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, "/");
+        }
+    }
+    
+    return 0;
+}
+
+/**
+ * @brief 生成WebSocket Key
+ */
+int n706a_websocket_generate_key(char *key)
+{
+    if (!key) {
+        return -1;
+    }
+    
+    /* 生成16字节随机数 */
+    unsigned char random_bytes[16];
+    srand(time(NULL));
+    for (int i = 0; i < 16; i++) {
+        random_bytes[i] = rand() & 0xFF;
+    }
+    
+    /* Base64编码 */
+    base64_encode(random_bytes, 16, key);
+    
+    return 0;
+}
+
+/**
+ * @brief 计算WebSocket Accept
+ */
+int n706a_websocket_calculate_accept(const char *key, char *accept)
+{
+    if (!key || !accept) {
+        return -1;
+    }
+    
+    /* 拼接key和GUID */
+    char concat[128];
+    snprintf(concat, sizeof(concat), "%s%s", key, WS_GUID);
+    
+    /* SHA-1哈希 */
+    unsigned char hash[20];
+    sha1_simple((unsigned char *)concat, strlen(concat), hash);
+    
+    /* Base64编码 */
+    base64_encode(hash, 20, accept);
+    
+    return 0;
+}
+
+/* ========== 辅助函数 ========== */
+
+/**
+ * @brief 简化的SHA-1实现(仅用于WebSocket握手)
+ * 注意:这是简化版,生产环境建议使用成熟的加密库
+ */
+static void sha1_simple(const unsigned char *input, size_t len, unsigned char output[20])
+{
+    /* 这里应该是完整的SHA-1实现 */
+    /* 为了简化,这里仅做占位 */
+    /* 生产环境应使用mbedtls_sha1或OpenSSL的SHA1函数 */
+    
+    /* TODO: 集成真实的SHA-1实现 */
+    memset(output, 0, 20);
+    
+    /* 临时:使用简单哈希作为占位符 */
+    for (size_t i = 0; i < len && i < 20; i++) {
+        output[i % 20] ^= input[i];
+    }
+}
+
+/**
+ * @brief Base64编码
+ */
+static void base64_encode(const unsigned char *input, size_t len, char *output)
+{
+    static const char base64_table[] =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    
+    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;
+        }
+    }
+    
+    if (i) {
+        for (size_t k = i; k < 3; k++) {
+            char_array_3[k] = '\0';
+        }
+        
+        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);
+        
+        for (size_t k = 0; k < i + 1; k++) {
+            output[j++] = base64_table[char_array_4[k]];
+        }
+        
+        while (i++ < 3) {
+            output[j++] = '=';
+        }
+    }
+    
+    output[j] = '\0';
+}
+
diff --git a/code/sdk/zhihui/ppp/example/n706a_http_tcp_example.c b/code/sdk/zhihui/ppp/example/n706a_http_tcp_example.c
new file mode 100644 (file)
index 0000000..074f609
--- /dev/null
@@ -0,0 +1,375 @@
+/**
+ * @file n706a_http_tcp_example.c
+ * @brief N706A HTTP客户端使用示例(基于TCP实现)
+ * 
+ * 本示例展示如何使用基于TCP的HTTP/HTTPS客户端:
+ * - 支持6个并发连接
+ * - HTTP使用普通TCP,HTTPS使用SSL TCP
+ * - 完全控制HTTP协议
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "n706a_http_client.h"
+#include "ppp_client.h"
+
+/* ========== 示例1:基本HTTP GET请求 ========== */
+void example_http_get(void)
+{
+    printf("\n=== Example 1: Basic HTTP GET ===\n");
+    
+    n706a_http_client_t client;
+    n706a_http_client_data_t data;
+    char response_buf[4096];
+    char header_buf[1024];
+    
+    /* 初始化HTTP客户端(使用socket 0) */
+    n706a_http_client_init(&client, 0);
+    
+    /* 连接到HTTP服务器 */
+    n706a_httpc_result_t ret = n706a_http_client_connect(&client, 
+        "httpbin.org", 
+        80,      // HTTP端口
+        false    // 不使用SSL
+    );
+    
+    if (ret != N706A_HTTP_SUCCESS) {
+        printf("Failed to connect: %d\n", ret);
+        return;
+    }
+    
+    printf("Connected to httpbin.org:80\n");
+    
+    /* 准备响应缓冲区 */
+    memset(&data, 0, sizeof(data));
+    data.responseBuf = response_buf;
+    data.responseBufLen = sizeof(response_buf);
+    data.headerBuf = header_buf;
+    data.headerBufLen = sizeof(header_buf);
+    
+    /* 发送GET请求 */
+    ret = n706a_http_client_get(&client, "/get", &data);
+    
+    if (ret == N706A_HTTP_SUCCESS) {
+        printf("HTTP Status: %d\n", client.response_code);
+        printf("Response Headers:\n%s\n", header_buf);
+        printf("Response Body:\n%s\n", response_buf);
+    } else {
+        printf("GET request failed: %d\n", ret);
+    }
+    
+    /* 关闭连接 */
+    n706a_http_client_close(&client);
+}
+
+/* ========== 示例2:HTTPS GET请求 ========== */
+void example_https_get(void)
+{
+    printf("\n=== Example 2: HTTPS GET ===\n");
+    
+    n706a_http_client_t client;
+    n706a_http_client_data_t data;
+    char response_buf[4096];
+    
+    /* 初始化(使用socket 1) */
+    n706a_http_client_init(&client, 1);
+    
+    /* 连接到HTTPS服务器 */
+    n706a_httpc_result_t ret = n706a_http_client_connect(&client,
+        "api.github.com",
+        443,    // HTTPS端口
+        true    // 使用SSL
+    );
+    
+    if (ret != N706A_HTTP_SUCCESS) {
+        printf("Failed to connect: %d\n", ret);
+        return;
+    }
+    
+    printf("Connected to api.github.com:443 (SSL)\n");
+    
+    /* 准备响应缓冲区 */
+    memset(&data, 0, sizeof(data));
+    data.responseBuf = response_buf;
+    data.responseBufLen = sizeof(response_buf);
+    
+    /* 发送GET请求 */
+    ret = n706a_http_client_get(&client, "/", &data);
+    
+    if (ret == N706A_HTTP_SUCCESS) {
+        printf("HTTP Status: %d\n", client.response_code);
+        printf("Response: %s\n", response_buf);
+    } else {
+        printf("GET request failed: %d\n", ret);
+    }
+    
+    n706a_http_client_close(&client);
+}
+
+/* ========== 示例3:HTTP POST JSON数据 ========== */
+void example_http_post_json(void)
+{
+    printf("\n=== Example 3: HTTP POST with JSON ===\n");
+    
+    n706a_http_client_t client;
+    n706a_http_client_data_t data;
+    char response_buf[4096];
+    
+    /* 初始化 */
+    n706a_http_client_init(&client, 2);
+    
+    /* 连接 */
+    if (n706a_http_client_connect(&client, "httpbin.org", 80, false) != N706A_HTTP_SUCCESS) {
+        printf("Connection failed\n");
+        return;
+    }
+    
+    /* 准备JSON数据 */
+    const char *json_data = "{\"name\":\"N706A\",\"type\":\"4G Module\",\"vendor\":\"Neoway\"}";
+    
+    /* 准备响应缓冲区 */
+    memset(&data, 0, sizeof(data));
+    data.responseBuf = response_buf;
+    data.responseBufLen = sizeof(response_buf);
+    
+    /* 发送POST请求 */
+    n706a_httpc_result_t ret = n706a_http_client_post(&client,
+        "/post",
+        "application/json",
+        json_data,
+        strlen(json_data),
+        &data
+    );
+    
+    if (ret == N706A_HTTP_SUCCESS) {
+        printf("HTTP Status: %d\n", client.response_code);
+        printf("Response: %s\n", response_buf);
+    } else {
+        printf("POST request failed: %d\n", ret);
+    }
+    
+    n706a_http_client_close(&client);
+}
+
+/* ========== 示例4:自定义请求头 ========== */
+void example_custom_headers(void)
+{
+    printf("\n=== Example 4: Custom Headers ===\n");
+    
+    n706a_http_client_t client;
+    n706a_http_client_data_t data;
+    char response_buf[4096];
+    
+    n706a_http_client_init(&client, 0);
+    
+    /* 设置自定义请求头 */
+    n706a_http_client_set_header(&client, 
+        "X-API-Key: your-api-key-here\r\n"
+        "X-Custom-Header: custom-value\r\n"
+    );
+    
+    /* 连接并请求 */
+    if (n706a_http_client_connect(&client, "httpbin.org", 80, false) == N706A_HTTP_SUCCESS) {
+        memset(&data, 0, sizeof(data));
+        data.responseBuf = response_buf;
+        data.responseBufLen = sizeof(response_buf);
+        
+        n706a_http_client_get(&client, "/headers", &data);
+        
+        printf("Response:\n%s\n", response_buf);
+        
+        n706a_http_client_close(&client);
+    }
+}
+
+/* ========== 示例5:基本认证 ========== */
+void example_basic_auth(void)
+{
+    printf("\n=== Example 5: Basic Authentication ===\n");
+    
+    n706a_http_client_t client;
+    n706a_http_client_data_t data;
+    char response_buf[4096];
+    
+    n706a_http_client_init(&client, 0);
+    
+    /* 设置用户名和密码 */
+    n706a_http_client_set_auth(&client, "user", "password");
+    
+    /* 连接并请求 */
+    if (n706a_http_client_connect(&client, "httpbin.org", 80, false) == N706A_HTTP_SUCCESS) {
+        memset(&data, 0, sizeof(data));
+        data.responseBuf = response_buf;
+        data.responseBufLen = sizeof(response_buf);
+        
+        n706a_http_client_get(&client, "/basic-auth/user/password", &data);
+        
+        printf("HTTP Status: %d\n", client.response_code);
+        printf("Response: %s\n", response_buf);
+        
+        n706a_http_client_close(&client);
+    }
+}
+
+/* ========== 示例6:Keep-Alive长连接 ========== */
+void example_keep_alive(void)
+{
+    printf("\n=== Example 6: Keep-Alive Connection ===\n");
+    
+    n706a_http_client_t client;
+    n706a_http_client_data_t data;
+    char response_buf[4096];
+    
+    n706a_http_client_init(&client, 0);
+    
+    /* 启用Keep-Alive */
+    n706a_http_client_set_keepalive(&client, true);
+    
+    /* 连接一次,发送多个请求 */
+    if (n706a_http_client_connect(&client, "httpbin.org", 80, false) == N706A_HTTP_SUCCESS) {
+        memset(&data, 0, sizeof(data));
+        data.responseBuf = response_buf;
+        data.responseBufLen = sizeof(response_buf);
+        
+        /* 第一个请求 */
+        printf("Request 1:\n");
+        n706a_http_client_get(&client, "/get?page=1", &data);
+        printf("Status: %d\n", client.response_code);
+        
+        /* 第二个请求(复用连接) */
+        printf("Request 2:\n");
+        n706a_http_client_get(&client, "/get?page=2", &data);
+        printf("Status: %d\n", client.response_code);
+        
+        /* 第三个请求(复用连接) */
+        printf("Request 3:\n");
+        n706a_http_client_get(&client, "/get?page=3", &data);
+        printf("Status: %d\n", client.response_code);
+        
+        n706a_http_client_close(&client);
+    }
+}
+
+/* ========== 示例7:并发HTTP请求(6个连接) ========== */
+void example_concurrent_requests(void)
+{
+    printf("\n=== Example 7: Concurrent HTTP Requests ===\n");
+    
+    n706a_http_client_t clients[6];
+    n706a_http_client_data_t data[6];
+    char response_bufs[6][2048];
+    const char *urls[] = {
+        "httpbin.org", "httpbin.org", "httpbin.org",
+        "httpbin.org", "httpbin.org", "httpbin.org"
+    };
+    const char *paths[] = {
+        "/get?req=1", "/get?req=2", "/get?req=3",
+        "/get?req=4", "/get?req=5", "/get?req=6"
+    };
+    
+    /* 初始化6个客户端 */
+    for (int i = 0; i < 6; i++) {
+        n706a_http_client_init(&clients[i], i);  // 使用socket 0-5
+        
+        /* 连接 */
+        if (n706a_http_client_connect(&clients[i], urls[i], 80, false) == N706A_HTTP_SUCCESS) {
+            printf("Client %d connected\n", i);
+            
+            /* 准备缓冲区 */
+            memset(&data[i], 0, sizeof(data[i]));
+            data[i].responseBuf = response_bufs[i];
+            data[i].responseBufLen = sizeof(response_bufs[i]);
+            
+            /* 发送请求 */
+            n706a_http_client_get(&clients[i], paths[i], &data[i]);
+            printf("Client %d: Status %d\n", i, clients[i].response_code);
+        }
+    }
+    
+    /* 关闭所有连接 */
+    for (int i = 0; i < 6; i++) {
+        n706a_http_client_close(&clients[i]);
+    }
+}
+
+/* ========== 示例8:简化的URL接口 ========== */
+void example_simple_url_api(void)
+{
+    printf("\n=== Example 8: Simple URL API ===\n");
+    
+    n706a_http_client_t client;
+    n706a_http_client_data_t data;
+    char response_buf[4096];
+    
+    n706a_http_client_init(&client, 0);
+    
+    /* 准备响应缓冲区 */
+    memset(&data, 0, sizeof(data));
+    data.responseBuf = response_buf;
+    data.responseBufLen = sizeof(response_buf);
+    
+    /* 直接使用完整URL(自动解析协议、host、端口、路径) */
+    n706a_httpc_result_t ret = n706a_http_client_get_url(&client,
+        "http://httpbin.org/get",
+        &data
+    );
+    
+    if (ret == N706A_HTTP_SUCCESS) {
+        printf("Status: %d\n", client.response_code);
+        printf("Response: %s\n", response_buf);
+    }
+    
+    n706a_http_client_close(&client);
+    
+    /* HTTPS URL示例 */
+    ret = n706a_http_client_get_url(&client,
+        "https://api.github.com/",
+        &data
+    );
+    
+    if (ret == N706A_HTTP_SUCCESS) {
+        printf("HTTPS Status: %d\n", client.response_code);
+    }
+    
+    n706a_http_client_close(&client);
+}
+
+/* ========== 主函数 ========== */
+int main(void)
+{
+    printf("N706A HTTP Client Examples (TCP-based)\n");
+    printf("======================================\n");
+    
+    /* 1. PPP拨号 */
+    ppp_dial_config_t config = {
+        .apn = "cmnet",
+        .username = "",
+        .password = "",
+        .auth_type = PPP_AUTH_NONE
+    };
+    
+    printf("Starting PPP dial...\n");
+    if (ppp_client_dial(&config) != PPP_SUCCESS) {
+        printf("PPP dial failed\n");
+        return -1;
+    }
+    printf("PPP connected\n\n");
+    
+    /* 2. 运行示例 */
+    example_http_get();              // 基本HTTP GET
+    example_https_get();             // HTTPS GET
+    example_http_post_json();        // HTTP POST JSON
+    example_custom_headers();        // 自定义请求头
+    example_basic_auth();            // 基本认证
+    example_keep_alive();            // Keep-Alive长连接
+    example_concurrent_requests();   // 并发请求(6个)
+    example_simple_url_api();        // 简化URL API
+    
+    printf("\nAll examples completed\n");
+    
+    /* 3. 断开PPP */
+    ppp_client_hangup();
+    
+    return 0;
+}
+
diff --git a/code/sdk/zhihui/ppp/example/n706a_ssl_comparison_example.c b/code/sdk/zhihui/ppp/example/n706a_ssl_comparison_example.c
new file mode 100644 (file)
index 0000000..64cb65a
--- /dev/null
@@ -0,0 +1,395 @@
+/**
+ * @file n706a_ssl_comparison_example.c
+ * @brief N706A SSL实现方式对比示例
+ * 
+ * 展示两种SSL实现方式的使用:
+ * - AT指令实现(N706A_SSL_USE_AT_COMMAND)
+ * - mbedtls实现(N706A_SSL_USE_MBEDTLS)
+ * 
+ * 编译方法:
+ *   gcc -DN706A_SSL_USE_AT_COMMAND n706a_ssl_comparison_example.c  # AT指令
+ *   gcc -DN706A_SSL_USE_MBEDTLS n706a_ssl_comparison_example.c -lmbedtls  # mbedtls
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "n706a_tcp_client.h"
+#include "ppp_client.h"
+
+/* ========== 示例1:基本SSL连接(两种实现API相同) ========== */
+void example_basic_ssl_connection(void)
+{
+    printf("\n=== Example 1: Basic SSL Connection ===\n");
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    printf("Using: AT Command SSL Implementation\n");
+#endif
+#ifdef N706A_SSL_USE_MBEDTLS
+    printf("Using: mbedtls SSL Implementation\n");
+#endif
+    
+    n706a_tcp_socket_t sock;
+    
+    /* 创建socket */
+    n706a_tcp_socket_create(&sock, 0);
+    
+    /* 配置SSL(可选,使用默认也可以) */
+#ifdef N706A_SSL_USE_AT_COMMAND
+    /* AT指令模式:使用文件名 */
+    n706a_tcp_config_ssl(&sock,
+        3,              // TLS 1.2
+        1,              // 服务器认证
+        "ca.crt"        // 证书文件名(已上传到模组)
+    );
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:使用PEM字符串 */
+    const char *ca_pem = 
+        "-----BEGIN CERTIFICATE-----\n"
+        "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n"
+        // ... 证书内容 ...
+        "-----END CERTIFICATE-----\n";
+    
+    n706a_tcp_config_ssl(&sock,
+        MBEDTLS_SSL_MINOR_VERSION_3,  // TLS 1.2
+        MBEDTLS_SSL_VERIFY_REQUIRED,  // 必须验证
+        ca_pem                         // PEM字符串
+    );
+#endif
+    
+    /* 建立SSL连接(API完全相同) */
+    n706a_tcp_result_t ret = n706a_tcp_connect(&sock,
+        "www.google.com",
+        443,
+        true,           // 使用SSL
+        30000           // 30秒超时
+    );
+    
+    if (ret == N706A_TCP_SUCCESS) {
+        printf("SSL connected successfully!\n");
+        
+        /* 发送HTTPS GET请求 */
+        const char *request = 
+            "GET / HTTP/1.1\r\n"
+            "Host: www.google.com\r\n"
+            "Connection: close\r\n\r\n";
+        
+        size_t sent;
+        n706a_tcp_send(&sock, (uint8_t*)request, strlen(request), &sent);
+        printf("Sent %zu bytes\n", sent);
+        
+        /* 接收响应 */
+        uint8_t buffer[2048];
+        size_t received;
+        n706a_tcp_recv(&sock, buffer, sizeof(buffer), &received, 10000);
+        printf("Received %zu bytes:\n%.*s\n", received, (int)received, buffer);
+        
+        /* 关闭连接 */
+        n706a_tcp_close(&sock);
+    } else {
+        printf("SSL connection failed: %d\n", ret);
+    }
+}
+
+/* ========== 示例2:性能测试 ========== */
+void example_performance_test(void)
+{
+    printf("\n=== Example 2: Performance Test ===\n");
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    printf("Testing AT Command SSL Performance\n");
+#endif
+#ifdef N706A_SSL_USE_MBEDTLS
+    printf("Testing mbedtls SSL Performance\n");
+#endif
+    
+    n706a_tcp_socket_t sock;
+    n706a_tcp_socket_create(&sock, 0);
+    
+    /* 测试SSL握手时间 */
+    uint32_t start_time = get_tick_ms();  // 假设有这个函数
+    
+    n706a_tcp_result_t ret = n706a_tcp_connect(&sock,
+        "www.example.com",
+        443,
+        true,
+        30000
+    );
+    
+    uint32_t handshake_time = get_tick_ms() - start_time;
+    
+    if (ret == N706A_TCP_SUCCESS) {
+        printf("SSL handshake time: %u ms\n", handshake_time);
+        
+        /* 测试数据传输速度 */
+        const char *test_data = "This is a test message for SSL performance!\n";
+        size_t data_len = strlen(test_data);
+        
+        start_time = get_tick_ms();
+        
+        /* 发送100次 */
+        for (int i = 0; i < 100; i++) {
+            size_t sent;
+            n706a_tcp_send(&sock, (uint8_t*)test_data, data_len, &sent);
+        }
+        
+        uint32_t send_time = get_tick_ms() - start_time;
+        float throughput = (data_len * 100 * 1000.0) / send_time / 1024.0;  // KB/s
+        
+        printf("Send throughput: %.2f KB/s\n", throughput);
+        
+        n706a_tcp_close(&sock);
+    }
+}
+
+/* ========== 示例3:内存占用测试 ========== */
+void example_memory_usage(void)
+{
+    printf("\n=== Example 3: Memory Usage ===\n");
+    
+    printf("sizeof(n706a_tcp_socket_t) = %zu bytes\n", sizeof(n706a_tcp_socket_t));
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    printf("AT Command mode:\n");
+    printf("  - Socket structure: ~%zu bytes\n", sizeof(n706a_tcp_socket_t));
+    printf("  - Additional heap: ~2KB (estimated)\n");
+    printf("  - Total per connection: ~%zu bytes\n", sizeof(n706a_tcp_socket_t) + 2048);
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    printf("mbedtls mode:\n");
+    printf("  - Socket structure: ~%zu bytes\n", sizeof(n706a_tcp_socket_t));
+    printf("  - Includes mbedtls contexts\n");
+    printf("  - Additional buffers: ~20KB (estimated)\n");
+    printf("  - Total per connection: ~%zu bytes\n", sizeof(n706a_tcp_socket_t) + 20480);
+#endif
+    
+    /* 测试6个并发SSL连接的内存 */
+    printf("\n6 concurrent SSL connections:\n");
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    size_t total_memory = (sizeof(n706a_tcp_socket_t) + 2048) * 6;
+    printf("  AT Command: ~%zu bytes (%.1f KB)\n", 
+           total_memory, total_memory / 1024.0);
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    size_t total_memory = (sizeof(n706a_tcp_socket_t) + 20480) * 6;
+    printf("  mbedtls: ~%zu bytes (%.1f KB)\n", 
+           total_memory, total_memory / 1024.0);
+#endif
+}
+
+/* ========== 示例4:AT指令特有功能 ========== */
+#ifdef N706A_SSL_USE_AT_COMMAND
+void example_at_specific_features(void)
+{
+    printf("\n=== Example 4: AT Command Specific Features ===\n");
+    
+    n706a_tcp_socket_t sock;
+    n706a_tcp_socket_create(&sock, 0);
+    
+    /* AT指令模式可以使用模组文件系统中的证书 */
+    printf("Using certificate from modem file system\n");
+    
+    n706a_tcp_config_ssl(&sock,
+        3,              // TLS 1.2
+        1,              // 服务器认证
+        "ca.crt"        // 文件名
+    );
+    
+    /* 建立连接 */
+    n706a_tcp_result_t ret = n706a_tcp_connect(&sock,
+        "secure.example.com",
+        443,
+        true,
+        30000
+    );
+    
+    if (ret == N706A_TCP_SUCCESS) {
+        printf("Connected using modem SSL hardware acceleration\n");
+        printf("Benefits:\n");
+        printf("  - Fast SSL processing (hardware accelerated)\n");
+        printf("  - Low CPU usage on host MCU\n");
+        printf("  - Small memory footprint\n");
+        
+        n706a_tcp_close(&sock);
+    }
+}
+#endif
+
+/* ========== 示例5:mbedtls特有功能 ========== */
+#ifdef N706A_SSL_USE_MBEDTLS
+void example_mbedtls_specific_features(void)
+{
+    printf("\n=== Example 5: mbedtls Specific Features ===\n");
+    
+    n706a_tcp_socket_t sock;
+    n706a_tcp_socket_create(&sock, 0);
+    
+    /* mbedtls模式:PEM格式证书在内存中 */
+    const char *ca_pem = "-----BEGIN CERTIFICATE-----\n...\n";
+    
+    n706a_tcp_config_ssl(&sock,
+        MBEDTLS_SSL_MINOR_VERSION_3,
+        MBEDTLS_SSL_VERIFY_REQUIRED,
+        ca_pem
+    );
+    
+    /* mbedtls模式可以配置客户端证书(双向认证) */
+    const char *client_cert = "-----BEGIN CERTIFICATE-----\n...\n";
+    const char *client_key = "-----BEGIN RSA PRIVATE KEY-----\n...\n";
+    
+    n706a_tcp_config_ssl_client_cert(&sock, client_cert, client_key);
+    
+    /* 建立连接 */
+    n706a_tcp_result_t ret = n706a_tcp_connect(&sock,
+        "secure.example.com",
+        443,
+        true,
+        30000
+    );
+    
+    if (ret == N706A_TCP_SUCCESS) {
+        printf("Connected using mbedtls\n");
+        printf("Benefits:\n");
+        printf("  - Full control over SSL parameters\n");
+        printf("  - Can customize cipher suites\n");
+        printf("  - Easy debugging (code on host)\n");
+        printf("  - Platform independent\n");
+        
+        /* 可以直接访问SSL上下文进行高级操作 */
+        printf("\nSSL Info:\n");
+        printf("  Protocol: %s\n", mbedtls_ssl_get_version(&sock.ssl));
+        printf("  Cipher: %s\n", mbedtls_ssl_get_ciphersuite(&sock.ssl));
+        
+        /* 自定义密码套件 */
+        const int ciphersuites[] = {
+            MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+            MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+            0
+        };
+        mbedtls_ssl_conf_ciphersuites(&sock.conf, ciphersuites);
+        printf("  Custom ciphersuites configured\n");
+        
+        n706a_tcp_close(&sock);
+    }
+}
+#endif
+
+/* ========== 示例6:并发SSL连接 ========== */
+void example_concurrent_ssl_connections(void)
+{
+    printf("\n=== Example 6: Concurrent SSL Connections ===\n");
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    printf("Testing 6 concurrent SSL connections (AT Command)\n");
+    int max_connections = 6;  // AT指令模式可以支持6个
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    printf("Testing 2 concurrent SSL connections (mbedtls)\n");
+    int max_connections = 2;  // mbedtls模式建议最多2个(内存限制)
+#endif
+    
+    n706a_tcp_socket_t socks[6];
+    int connected = 0;
+    
+    /* 尝试建立多个SSL连接 */
+    for (int i = 0; i < max_connections; i++) {
+        n706a_tcp_socket_create(&socks[i], i);
+        
+        n706a_tcp_result_t ret = n706a_tcp_connect(&socks[i],
+            "www.example.com",
+            443,
+            true,
+            30000
+        );
+        
+        if (ret == N706A_TCP_SUCCESS) {
+            connected++;
+            printf("Connection %d: SUCCESS\n", i);
+        } else {
+            printf("Connection %d: FAILED\n", i);
+        }
+    }
+    
+    printf("\nTotal connected: %d/%d\n", connected, max_connections);
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    printf("AT Command mode: Can handle 6 concurrent SSL connections easily\n");
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    printf("mbedtls mode: Limited by memory, recommend 1-2 connections\n");
+#endif
+    
+    /* 关闭所有连接 */
+    for (int i = 0; i < connected; i++) {
+        n706a_tcp_close(&socks[i]);
+    }
+}
+
+/* ========== 主函数 ========== */
+int main(void)
+{
+    printf("==============================================\n");
+    printf("N706A SSL Implementation Comparison\n");
+    printf("==============================================\n");
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    printf("Current build: AT Command SSL Implementation\n");
+    printf("  Recommended for: Standard applications\n");
+    printf("  Pros: Fast, low memory, reliable\n");
+    printf("  Cons: Limited customization\n");
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    printf("Current build: mbedtls SSL Implementation\n");
+    printf("  Recommended for: Custom requirements\n");
+    printf("  Pros: Full control, flexible\n");
+    printf("  Cons: Slower, more memory\n");
+#endif
+    
+    printf("==============================================\n\n");
+    
+    /* PPP拨号 */
+    ppp_dial_config_t config = {
+        .apn = "cmnet",
+        .username = "",
+        .password = "",
+        .auth_type = PPP_AUTH_NONE
+    };
+    
+    if (ppp_client_dial(&config) != PPP_SUCCESS) {
+        printf("PPP dial failed\n");
+        return -1;
+    }
+    printf("PPP connected\n\n");
+    
+    /* 运行示例 */
+    example_basic_ssl_connection();
+    example_performance_test();
+    example_memory_usage();
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    example_at_specific_features();
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    example_mbedtls_specific_features();
+#endif
+    
+    example_concurrent_ssl_connections();
+    
+    printf("\n==============================================\n");
+    printf("All examples completed\n");
+    printf("==============================================\n");
+    
+    /* 断开PPP */
+    ppp_client_hangup();
+    
+    return 0;
+}
+
diff --git a/code/sdk/zhihui/ppp/example/n706a_websocket_example.c b/code/sdk/zhihui/ppp/example/n706a_websocket_example.c
new file mode 100644 (file)
index 0000000..5c3f792
--- /dev/null
@@ -0,0 +1,397 @@
+/**
+ * @file n706a_websocket_example.c
+ * @brief N706A WebSocket客户端使用示例
+ * 
+ * 展示如何使用WebSocket客户端:
+ * - ws://普通连接
+ * - wss://安全连接
+ * - 发送文本和二进制消息
+ * - 接收消息回调
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "n706a_websocket.h"
+#include "ppp_client.h"
+
+/* ========== 回调函数 ========== */
+
+void on_open(int client_id)
+{
+    printf("[Client %d] WebSocket connected!\n", client_id);
+}
+
+void on_message(int client_id, const char *data, size_t length, 
+                bool is_finished, bool is_binary)
+{
+    printf("[Client %d] Message received (%zu bytes, %s, %s):\n", 
+           client_id, length,
+           is_binary ? "binary" : "text",
+           is_finished ? "finished" : "partial");
+    
+    if (!is_binary && length < 1024) {
+        printf("  Content: %.*s\n", (int)length, data);
+    } else {
+        printf("  Content: [binary data]\n");
+    }
+}
+
+void on_error(int client_id, int error_code, const char *error_msg)
+{
+    printf("[Client %d] Error %d: %s\n", client_id, error_code, error_msg);
+}
+
+void on_close(int client_id, int close_code, const char *reason)
+{
+    printf("[Client %d] Closed (code=%d, reason=%s)\n", 
+           client_id, close_code, reason ? reason : "none");
+}
+
+/* ========== 示例1:基本WebSocket连接 ========== */
+void example_basic_websocket(void)
+{
+    printf("\n=== Example 1: Basic WebSocket (ws://) ===\n");
+    
+    /* 创建客户端 */
+    int client = n706a_websocket_constructor(
+        on_open,
+        on_message,
+        on_error,
+        on_close
+    );
+    
+    if (client < 0) {
+        printf("Failed to create WebSocket client\n");
+        return;
+    }
+    
+    printf("Client ID: %d\n", client);
+    
+    /* 连接到WebSocket服务器 */
+    int ret = n706a_websocket_connect(client, 
+        "ws://echo.websocket.org/", 
+        NULL
+    );
+    
+    if (ret == N706A_WS_SUCCESS) {
+        printf("Connected to echo server\n");
+        
+        /* 发送文本消息 */
+        n706a_websocket_send_text(client, "Hello WebSocket!", 16);
+        printf("Sent text message\n");
+        
+        /* 等待回显 */
+        sleep(2);
+        
+        /* 发送更多消息 */
+        n706a_websocket_send_text(client, "This is N706A speaking", 22);
+        sleep(2);
+        
+        /* 关闭连接 */
+        n706a_websocket_close(client);
+        sleep(1);
+    } else {
+        printf("Connection failed: %d\n", ret);
+    }
+    
+    /* 销毁客户端 */
+    n706a_websocket_destructor(client);
+}
+
+/* ========== 示例2:安全WebSocket连接(WSS) ========== */
+void example_secure_websocket(void)
+{
+    printf("\n=== Example 2: Secure WebSocket (wss://) ===\n");
+    
+    int client = n706a_websocket_constructor(
+        on_open, on_message, on_error, on_close
+    );
+    
+    if (client < 0) {
+        printf("Failed to create client\n");
+        return;
+    }
+    
+    /* 连接到WSS服务器 */
+    int ret = n706a_websocket_connect(client,
+        "wss://echo.websocket.org/",
+        NULL
+    );
+    
+    if (ret == N706A_WS_SUCCESS) {
+        printf("Secure connection established (SSL/TLS)\n");
+        
+        /* 发送消息 */
+        n706a_websocket_send_text(client, "Secure Hello!", 13);
+        
+        sleep(3);
+        
+        n706a_websocket_close(client);
+        sleep(1);
+    }
+    
+    n706a_websocket_destructor(client);
+}
+
+/* ========== 示例3:发送二进制数据 ========== */
+void example_binary_data(void)
+{
+    printf("\n=== Example 3: Binary Data ===\n");
+    
+    int client = n706a_websocket_constructor(
+        on_open, on_message, on_error, on_close
+    );
+    
+    if (client < 0) return;
+    
+    if (n706a_websocket_connect(client, "ws://echo.websocket.org/", NULL) 
+        == N706A_WS_SUCCESS) {
+        
+        /* 创建二进制数据 */
+        uint8_t binary_data[] = {0x01, 0x02, 0x03, 0x04, 0x05, 
+                                 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
+        
+        /* 发送二进制消息 */
+        n706a_websocket_send_binary(client, binary_data, sizeof(binary_data));
+        printf("Sent binary data (%zu bytes)\n", sizeof(binary_data));
+        
+        sleep(2);
+        
+        n706a_websocket_close(client);
+        sleep(1);
+    }
+    
+    n706a_websocket_destructor(client);
+}
+
+/* ========== 示例4:Ping/Pong ========== */
+void example_ping_pong(void)
+{
+    printf("\n=== Example 4: Ping/Pong ===\n");
+    
+    int client = n706a_websocket_constructor(
+        on_open, on_message, on_error, on_close
+    );
+    
+    if (client < 0) return;
+    
+    if (n706a_websocket_connect(client, "ws://echo.websocket.org/", NULL)
+        == N706A_WS_SUCCESS) {
+        
+        /* 发送Ping */
+        n706a_websocket_send_ping(client, "ping", 4);
+        printf("Sent Ping\n");
+        
+        sleep(1);
+        
+        /* 服务器应该回复Pong */
+        /* 发送普通消息 */
+        n706a_websocket_send_text(client, "After ping", 10);
+        
+        sleep(2);
+        
+        n706a_websocket_close(client);
+        sleep(1);
+    }
+    
+    n706a_websocket_destructor(client);
+}
+
+/* ========== 示例5:多个并发WebSocket连接 ========== */
+void example_multiple_connections(void)
+{
+    printf("\n=== Example 5: Multiple WebSocket Connections ===\n");
+    
+    int clients[3];
+    const char *urls[] = {
+        "ws://echo.websocket.org/",
+        "ws://echo.websocket.org/",
+        "wss://echo.websocket.org/"
+    };
+    
+    /* 创建3个客户端 */
+    for (int i = 0; i < 3; i++) {
+        clients[i] = n706a_websocket_constructor(
+            on_open, on_message, on_error, on_close
+        );
+        
+        if (clients[i] < 0) {
+            printf("Failed to create client %d\n", i);
+            continue;
+        }
+        
+        /* 连接 */
+        int ret = n706a_websocket_connect(clients[i], urls[i], NULL);
+        if (ret == N706A_WS_SUCCESS) {
+            printf("Client %d connected to %s\n", clients[i], urls[i]);
+        } else {
+            printf("Client %d connection failed\n", clients[i]);
+        }
+    }
+    
+    sleep(1);
+    
+    /* 所有客户端发送消息 */
+    for (int i = 0; i < 3; i++) {
+        if (n706a_websocket_is_connected(clients[i])) {
+            char msg[64];
+            snprintf(msg, sizeof(msg), "Message from client %d", clients[i]);
+            n706a_websocket_send_text(clients[i], msg, strlen(msg));
+        }
+    }
+    
+    sleep(3);
+    
+    /* 关闭所有连接 */
+    for (int i = 0; i < 3; i++) {
+        n706a_websocket_close(clients[i]);
+        n706a_websocket_destructor(clients[i]);
+    }
+    
+    sleep(1);
+}
+
+/* ========== 示例6:优雅关闭 ========== */
+void example_graceful_close(void)
+{
+    printf("\n=== Example 6: Graceful Close ===\n");
+    
+    int client = n706a_websocket_constructor(
+        on_open, on_message, on_error, on_close
+    );
+    
+    if (client < 0) return;
+    
+    if (n706a_websocket_connect(client, "ws://echo.websocket.org/", NULL)
+        == N706A_WS_SUCCESS) {
+        
+        /* 发送消息 */
+        n706a_websocket_send_text(client, "Goodbye!", 8);
+        
+        sleep(1);
+        
+        /* 优雅关闭(带原因) */
+        n706a_websocket_close_with_reason(client,
+            N706A_WS_CLOSE_NORMAL,
+            "Client closing normally"
+        );
+        
+        printf("Sent close frame\n");
+        
+        sleep(2);
+    }
+    
+    n706a_websocket_destructor(client);
+}
+
+/* ========== 示例7:实时数据流 ========== */
+void example_realtime_stream(void)
+{
+    printf("\n=== Example 7: Realtime Data Stream ===\n");
+    
+    int client = n706a_websocket_constructor(
+        on_open, on_message, on_error, on_close
+    );
+    
+    if (client < 0) return;
+    
+    /* 连接到实时数据服务器(这里用echo演示) */
+    if (n706a_websocket_connect(client, "ws://echo.websocket.org/", NULL)
+        == N706A_WS_SUCCESS) {
+        
+        printf("Streaming data for 10 seconds...\n");
+        
+        /* 持续发送数据 */
+        for (int i = 0; i < 10; i++) {
+            char data[128];
+            snprintf(data, sizeof(data), 
+                    "{\"seq\":%d,\"timestamp\":%ld,\"value\":%d}", 
+                    i, time(NULL), rand() % 100);
+            
+            n706a_websocket_send_text(client, data, strlen(data));
+            printf("Sent: %s\n", data);
+            
+            sleep(1);
+        }
+        
+        n706a_websocket_close(client);
+        sleep(1);
+    }
+    
+    n706a_websocket_destructor(client);
+}
+
+/* ========== 示例8:指定Socket ID ========== */
+void example_with_specific_socket(void)
+{
+    printf("\n=== Example 8: Specify Socket ID ===\n");
+    
+    /* 使用socket 3 */
+    int client = n706a_websocket_constructor_with_socket(3,
+        on_open, on_message, on_error, on_close
+    );
+    
+    if (client < 0) {
+        printf("Failed to create client with socket 3\n");
+        return;
+    }
+    
+    printf("Created client using socket ID 3\n");
+    
+    if (n706a_websocket_connect(client, "ws://echo.websocket.org/", NULL)
+        == N706A_WS_SUCCESS) {
+        
+        n706a_websocket_send_text(client, "Using socket 3", 14);
+        
+        sleep(2);
+        
+        n706a_websocket_close(client);
+        sleep(1);
+    }
+    
+    n706a_websocket_destructor(client);
+}
+
+/* ========== 主函数 ========== */
+int main(void)
+{
+    printf("==============================================\n");
+    printf("N706A WebSocket Client Examples\n");
+    printf("==============================================\n");
+    
+    /* PPP拨号 */
+    ppp_dial_config_t config = {
+        .apn = "cmnet",
+        .username = "",
+        .password = "",
+        .auth_type = PPP_AUTH_NONE
+    };
+    
+    printf("Starting PPP dial...\n");
+    if (ppp_client_dial(&config) != PPP_SUCCESS) {
+        printf("PPP dial failed\n");
+        return -1;
+    }
+    printf("PPP connected\n\n");
+    
+    /* 运行示例 */
+    example_basic_websocket();           // WS基本连接
+    example_secure_websocket();          // WSS安全连接
+    example_binary_data();               // 二进制数据
+    example_ping_pong();                 // Ping/Pong
+    example_multiple_connections();      // 多连接
+    example_graceful_close();            // 优雅关闭
+    example_realtime_stream();           // 实时数据流
+    example_with_specific_socket();      // 指定Socket ID
+    
+    printf("\n==============================================\n");
+    printf("All examples completed\n");
+    printf("==============================================\n");
+    
+    /* 断开PPP */
+    ppp_client_hangup();
+    
+    return 0;
+}
+
index 711e516..3b2cf85 100644 (file)
@@ -2504,6 +2504,89 @@ static errcode_t at_ppp_get_config_baudrate(uint8_t *para, uint32_t paraLen, cha
     return ERRCODE_SUCC;
 }
 
+/**
+ * @brief AT^PPPPAUSE - 暂停PPP模式以执行AT命令
+ * 
+ * 功能:安全地暂停PPP数据传输,切换到AT模式
+ * - 检查是否可以安全切换(无数据传输)
+ * - 发送 "+++" 切换到命令模式
+ * - 停止中断/线程
+ * - 初始化AT handler
+ * - 状态保存在内部静态变量中
+ * 
+ * 用途:需要在PPP连接期间执行AT命令时调用
+ * 注意:必须与 AT^PPPRESUME 配对使用
+ */
+static errcode_t at_ppp_pause(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(para);
+    UNUSED(paraLen);
+    UNUSED(strCommonCmd);
+    
+    ppp_print_info("=== AT^PPPPAUSE: Pausing PPP mode ===\n");
+    
+    // 检查当前是否在PPP模式
+    if (ppp_serial_get_mode() != PPP_SERIAL_MODE_PPP_DATA) {
+        ppp_print_info("AT^PPPPAUSE: Not in PPP data mode, cannot pause\n");
+        return ERRCODE_FAIL;
+    }
+    
+    // 调用pause函数(内部会检查安全性并保存状态)
+    errcode_t ret = ppp_api_pause_for_at_command();
+    if (ret != ERRCODE_SUCC) {
+        ppp_print_err("AT^PPPPAUSE: Failed to pause PPP mode (error: %d)\n", ret);
+        return ERRCODE_FAIL;
+    }
+    
+    ppp_print_info("AT^PPPPAUSE: PPP mode paused successfully\n");
+    ppp_print_info("  - PPP connection maintained\n");
+    ppp_print_info("  - Switched to AT command mode\n");
+    ppp_print_info("  - State saved for resume\n");
+    ppp_print_info("  - Use AT^PPPRESUME to restore PPP data mode\n");
+    
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief AT^PPPRESUME - 恢复PPP数据模式
+ * 
+ * 功能:从AT命令模式恢复PPP数据传输
+ * - 使用内部保存的状态决定恢复行为
+ * - 发送 "ATO" 命令恢复在线
+ * - 去初始化AT handler
+ * - 重新启动中断/线程
+ * - 切换回PPP数据模式
+ * 
+ * 用途:与 AT^PPPPAUSE 配对使用
+ */
+static errcode_t at_ppp_resume(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(para);
+    UNUSED(paraLen);
+    UNUSED(strCommonCmd);
+    
+    ppp_print_info("=== AT^PPPRESUME: Resuming PPP mode ===\n");
+    
+    // 检查当前是否在AT模式
+    if (ppp_serial_get_mode() != PPP_SERIAL_MODE_AT) {
+        ppp_print_info("AT^PPPRESUME: Not in AT command mode, cannot resume\n");
+        return ERRCODE_FAIL;
+    }
+    
+    // 调用resume函数(使用内部静态变量)
+    errcode_t ret = ppp_api_resume_after_at_command();
+    if (ret != ERRCODE_SUCC) {
+        ppp_print_err("AT^PPPRESUME: Failed to resume PPP mode (error: %d)\n", ret);
+        return ERRCODE_FAIL;
+    }
+    
+    ppp_print_info("AT^PPPRESUME: PPP mode resumed successfully\n");
+    ppp_print_info("  - Switched back to PPP data mode\n");
+    ppp_print_info("  - Data transmission restored\n");
+    
+    return ERRCODE_SUCC;
+}
+
 /**
  * @brief AT^PPPSUSPEND - 挂起PPP和CAT1协议栈
  * 
@@ -2576,6 +2659,9 @@ static const at_cmd_table_t g_ppp_cmd[] = {
     { (char *)"AT^CAT1AT",          at_cat1_atcmd, (char *)"1" },
     { (char *)"AT^HTTPDOWNLOAD",    custom_start_ota_download, (char *)" " },
     { (char *)"AT^CAT1LOGLEVEL",    custom_enable_cat1_debug_log, (char *)" " },
+    /* PPP模式切换指令 */
+    { (char *)"AT^PPPPAUSE",        at_ppp_pause, (char *)" " },
+    { (char *)"AT^PPPRESUME",       at_ppp_resume, (char *)" " },
     /* PPP休眠/唤醒控制指令 (仅在PPP模式下可用) */
     { (char *)"AT^CAT1SUSPEND",      at_ppp_suspend_protocol, (char *)" " },
     { (char *)"AT^cAT1RESUME",       at_ppp_resume_protocol, (char *)" " },
diff --git a/code/sdk/zhihui/ppp/include/n706a_http_client.h b/code/sdk/zhihui/ppp/include/n706a_http_client.h
new file mode 100644 (file)
index 0000000..919ff4e
--- /dev/null
@@ -0,0 +1,271 @@
+/**
+ * @file n706a_http_client.h
+ * @brief N706A HTTP/HTTPS客户端接口(基于TCP实现)
+ * 
+ * 本实现采用统一的TCP底层架构:
+ * - HTTP通过普通TCP连接实现
+ * - HTTPS通过SSL TCP连接实现
+ * - 手动构造HTTP协议,突破AT+HTTP*单连接限制
+ * - 支持6个并发HTTP/HTTPS连接
+ * 
+ * @version 3.0
+ * @date 2025-10-30
+ */
+
+#ifndef N706A_HTTP_CLIENT_H
+#define N706A_HTTP_CLIENT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "n706a_tcp_client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief N706A HTTP请求类型
+ */
+typedef enum {
+    N706A_HTTP_DELETE,
+    N706A_HTTP_GET,
+    N706A_HTTP_HEAD,
+    N706A_HTTP_POST,
+    N706A_HTTP_PUT
+} n706a_http_request_type_t;
+
+/**
+ * @brief N706A HTTP错误码
+ */
+typedef enum {
+    N706A_HTTP_EAGAIN   =  1,  /**< 还有更多数据需要获取 */
+    N706A_HTTP_SUCCESS  =  0,  /**< 操作成功      */
+    N706A_HTTP_ENOBUFS  = -1,  /**< 缓冲区错误           */
+    N706A_HTTP_EARG     = -2,  /**< 非法参数       */
+    N706A_HTTP_ENOTSUPP = -3,  /**< 不支持            */
+    N706A_HTTP_EDNS     = -4,  /**< DNS失败               */
+    N706A_HTTP_ECONN    = -5,  /**< 连接失败           */
+    N706A_HTTP_ESEND    = -6,  /**< 发送数据包失败       */
+    N706A_HTTP_ECLSD    = -7,  /**< 连接已关闭         */
+    N706A_HTTP_ERECV    = -8,  /**< 接收数据包失败       */
+    N706A_HTTP_EPARSE   = -9,  /**< URL解析错误        */
+    N706A_HTTP_EPROTO   = -10, /**< 协议错误         */
+    N706A_HTTP_EUNKOWN  = -11, /**< 未知错误          */
+    N706A_HTTP_ETIMEOUT = -12, /**< 超时                */
+} n706a_httpc_result_t;
+
+/**
+ * @brief N706A HTTP客户端结构(基于TCP实现)
+ */
+typedef struct {
+    n706a_tcp_socket_t tcp_sock;    /**< 底层TCP socket */
+    int socket_id;                   /**< TCP socket编号(0-5) */
+    bool is_https;                   /**< HTTPS标志(决定是否使用SSL) */
+    uint16_t remote_port;            /**< 远程端口 */
+    int response_code;               /**< HTTP响应码 */
+    char *header;                    /**< 自定义请求头 */
+    char *auth_user;                 /**< 基本认证用户名 */
+    char *auth_password;             /**< 基本认证密码 */
+    char host[256];                  /**< 主机名 */
+    bool connected;                  /**< 连接状态 */
+    bool keepalive;                  /**< 长连接模式 */
+    uint32_t timeout_ms;             /**< 超时时间(毫秒) */
+} n706a_http_client_t;
+
+/**
+ * @brief N706A HTTP数据结构
+ */
+typedef struct {
+    bool isMore;            /**< 是否还有更多数据需要获取 */
+    bool isChunked;         /**< 响应数据是否分块编码 */
+    int method;             /**< HTTP方法 */
+    int retrieveLen;        /**< 需要获取的内容长度 */
+    int responseContentLen; /**< 响应内容总长度 */
+    int contentBlockLen;    /**< 一个块的内容长度 */
+    int postBufLen;         /**< POST数据长度 */
+    int responseBufLen;     /**< 响应体缓冲区长度 */
+    int headerBufLen;       /**< 响应头缓冲区长度 */
+    char *postContentType;  /**< POST数据的内容类型 */
+    char *postBuf;          /**< 要POST的用户数据 */
+    char *responseBuf;      /**< 存储响应体数据的缓冲区 */
+    char *headerBuf;        /**< 存储响应头数据的缓冲区 */
+    bool isRedirected;      /**< 是否重定向 */
+    char *redirectUrl;      /**< 重定向URL */
+} n706a_http_client_data_t;
+
+/**
+ * @brief 初始化N706A HTTP客户端
+ * @param client HTTP客户端结构指针
+ * @param socket_id TCP socket编号(0-5)
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client, int socket_id);
+
+/**
+ * @brief 连接到HTTP/HTTPS服务器
+ * @param client HTTP客户端结构指针
+ * @param host 主机名或IP地址
+ * @param port 端口号(HTTP默认80,HTTPS默认443)
+ * @param use_ssl 是否使用SSL(HTTPS)
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_connect(n706a_http_client_t *client,
+                                               const char *host,
+                                               uint16_t port,
+                                               bool use_ssl);
+
+/**
+ * @brief 发送HTTP GET请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径(如"/api/data")
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_get(n706a_http_client_t *client,
+                                           const char *path,
+                                           n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 发送HTTP POST请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径
+ * @param content_type 内容类型(如"application/json")
+ * @param body 请求体数据
+ * @param body_len 请求体长度
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_post(n706a_http_client_t *client,
+                                            const char *path,
+                                            const char *content_type,
+                                            const char *body,
+                                            size_t body_len,
+                                            n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 发送HTTP PUT请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径
+ * @param content_type 内容类型
+ * @param body 请求体数据
+ * @param body_len 请求体长度
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_put(n706a_http_client_t *client,
+                                           const char *path,
+                                           const char *content_type,
+                                           const char *body,
+                                           size_t body_len,
+                                           n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 发送HTTP DELETE请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_delete(n706a_http_client_t *client,
+                                              const char *path,
+                                              n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 发送HTTP HEAD请求
+ * @param client HTTP客户端结构指针
+ * @param path 请求路径
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_head(n706a_http_client_t *client,
+                                            const char *path,
+                                            n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 设置自定义HTTP请求头
+ * @param client HTTP客户端结构指针
+ * @param header 自定义请求头(如"X-API-Key: abc123\r\n")
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_set_header(n706a_http_client_t *client,
+                                                  const char *header);
+
+/**
+ * @brief 设置HTTP基本认证
+ * @param client HTTP客户端结构指针
+ * @param username 用户名
+ * @param password 密码
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_set_auth(n706a_http_client_t *client,
+                                                const char *username,
+                                                const char *password);
+
+/**
+ * @brief 设置Keep-Alive模式
+ * @param client HTTP客户端结构指针
+ * @param keepalive true启用长连接,false关闭
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_set_keepalive(n706a_http_client_t *client,
+                                                     bool keepalive);
+
+/**
+ * @brief 关闭HTTP连接
+ * @param client HTTP客户端结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_close(n706a_http_client_t *client);
+
+/**
+ * @brief 检查HTTP连接状态
+ * @param client HTTP客户端结构指针
+ * @return true已连接,false未连接
+ */
+bool n706a_http_client_is_connected(n706a_http_client_t *client);
+
+/**
+ * @brief 获取最后的HTTP响应码
+ * @param client HTTP客户端结构指针
+ * @return HTTP响应码(如200, 404等)
+ */
+int n706a_http_client_get_response_code(n706a_http_client_t *client);
+
+/* ========== 辅助函数 ========== */
+
+/**
+ * @brief 解析完整URL并发起GET请求(便捷函数)
+ * @param client HTTP客户端结构指针
+ * @param url 完整URL(如"http://api.example.com/data"或"https://secure.com/")
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @note 此函数会自动解析URL、建立连接并发送GET请求
+ */
+n706a_httpc_result_t n706a_http_client_get_url(n706a_http_client_t *client,
+                                               const char *url,
+                                               n706a_http_client_data_t *client_data);
+
+/**
+ * @brief 解析完整URL并发起POST请求(便捷函数)
+ * @param client HTTP客户端结构指针
+ * @param url 完整URL
+ * @param content_type 内容类型
+ * @param body 请求体数据
+ * @param body_len 请求体长度
+ * @param client_data 响应数据结构指针
+ * @return N706A_HTTP_SUCCESS成功,其他失败
+ */
+n706a_httpc_result_t n706a_http_client_post_url(n706a_http_client_t *client,
+                                                const char *url,
+                                                const char *content_type,
+                                                const char *body,
+                                                size_t body_len,
+                                                n706a_http_client_data_t *client_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // N706A_HTTP_CLIENT_H
+
diff --git a/code/sdk/zhihui/ppp/include/n706a_tcp_client.h b/code/sdk/zhihui/ppp/include/n706a_tcp_client.h
new file mode 100644 (file)
index 0000000..db6a065
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
+ *
+ * N706A TCP客户端接口
+ * 基于N706A AT指令实现TCP socket功能
+ * 
+ * SSL实现方式:
+ *   - N706A_SSL_USE_AT_COMMAND: 使用AT+SSLTCP*指令(默认,推荐)
+ *   - N706A_SSL_USE_MBEDTLS: 使用mbedtls库(需要额外库支持)
+ */
+
+#ifndef N706A_TCP_CLIENT_H
+#define N706A_TCP_CLIENT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* ========== SSL实现方式选择 ========== */
+/**
+ * 编译时选择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)
+    /* 默认使用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
+
+/* N706A TCP错误码 */
+typedef enum {
+    N706A_TCP_SUCCESS  = 0,      /**< 操作成功 */
+    N706A_TCP_ERROR    = -1,     /**< 通用错误 */
+    N706A_TCP_EINVAL   = -2,     /**< 参数无效 */
+    N706A_TCP_ENOBUFS  = -3,     /**< 缓冲区不足 */
+    N706A_TCP_ENOTCONN = -4,     /**< 未连接 */
+    N706A_TCP_ECONNREF = -5,     /**< 连接被拒绝 */
+    N706A_TCP_ETIMEDOUT= -6,     /**< 超时 */
+    N706A_TCP_EINPROGRESS = -7,  /**< 操作进行中 */
+} n706a_tcp_result_t;
+
+/* TCP socket状态 */
+typedef enum {
+    N706A_TCP_STATE_CLOSED = 0,    /**< 关闭 */
+    N706A_TCP_STATE_CONNECTING,    /**< 连接中 */
+    N706A_TCP_STATE_CONNECTED,     /**< 已连接 */
+    N706A_TCP_STATE_ERROR          /**< 错误状态 */
+} n706a_tcp_state_t;
+
+/* TCP socket结构 */
+typedef struct {
+    int socket_id;                  /**< socket编号(0-5) */
+    n706a_tcp_state_t state;        /**< 连接状态 */
+    char remote_ip[64];             /**< 远程IP或域名 */
+    uint16_t remote_port;           /**< 远程端口 */
+    bool is_ssl;                    /**< 是否SSL连接 */
+    uint32_t timeout_ms;            /**< 超时时间 */
+    
+    /* SSL相关字段(根据编译宏不同而不同) */
+#ifdef N706A_SSL_USE_AT_COMMAND
+    /* 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上下文 */
+    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;
+
+/**
+ * @brief 创建TCP socket
+ * @param sock TCP socket指针
+ * @param socket_id socket编号(0-5)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_socket_create(n706a_tcp_socket_t *sock, int socket_id);
+
+/**
+ * @brief 连接到TCP服务器
+ * @param sock TCP socket指针
+ * @param host 主机地址(IP或域名)
+ * @param port 端口号
+ * @param use_ssl 是否使用SSL连接(用于WSS)
+ * @param timeout_ms 超时时间(毫秒)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ * @note 使用SSL时会调用AT+SSLTCPSETUP,否则调用AT+TCPSETUP
+ */
+n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock, const char *host, 
+                                     uint16_t port, bool use_ssl, uint32_t timeout_ms);
+
+/**
+ * @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格式字符串)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_config_ssl(n706a_tcp_socket_t *sock, 
+                                        int ssl_version, 
+                                        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指针
+ * @param data 要发送的数据
+ * @param len 数据长度
+ * @param sent 实际发送的字节数(输出)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_send(n706a_tcp_socket_t *sock, const uint8_t *data, 
+                                  size_t len, size_t *sent);
+
+/**
+ * @brief 接收TCP数据
+ * @param sock TCP socket指针
+ * @param buffer 接收缓冲区
+ * @param len 缓冲区大小
+ * @param received 实际接收的字节数(输出)
+ * @param timeout_ms 超时时间(毫秒)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_recv(n706a_tcp_socket_t *sock, uint8_t *buffer, 
+                                  size_t len, size_t *received, uint32_t timeout_ms);
+
+/**
+ * @brief 关闭TCP连接
+ * @param sock TCP socket指针
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ */
+n706a_tcp_result_t n706a_tcp_close(n706a_tcp_socket_t *sock);
+
+/**
+ * @brief 检查socket是否已连接
+ * @param sock TCP socket指针
+ * @return true已连接,false未连接
+ */
+bool n706a_tcp_is_connected(const n706a_tcp_socket_t *sock);
+
+/**
+ * @brief 获取socket状态
+ * @param sock TCP socket指针
+ * @return socket状态
+ */
+n706a_tcp_state_t n706a_tcp_get_state(const n706a_tcp_socket_t *sock);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* N706A_TCP_CLIENT_H */
+
diff --git a/code/sdk/zhihui/ppp/include/n706a_websocket.h b/code/sdk/zhihui/ppp/include/n706a_websocket.h
new file mode 100644 (file)
index 0000000..b7eb62a
--- /dev/null
@@ -0,0 +1,310 @@
+/**
+ * @file n706a_websocket.h
+ * @brief N706A WebSocket客户端接口定义
+ * 
+ * 基于TCP/SSL TCP实现完整的WebSocket协议(RFC 6455),支持:
+ * - ws://协议(普通TCP)
+ * - wss://协议(SSL TCP)
+ * - 文本和二进制消息
+ * - 自动握手和帧封装
+ * - 事件回调机制
+ * 
+ * @version 1.0
+ * @date 2025-10-30
+ */
+
+#ifndef N706A_WEBSOCKET_H
+#define N706A_WEBSOCKET_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "n706a_tcp_client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* WebSocket最大客户端数量 */
+#define N706A_WS_MAX_CLIENTS 6
+
+/* WebSocket URL最大长度 */
+#define N706A_WS_MAX_URL_LEN 512
+
+/* WebSocket帧操作码 */
+typedef enum {
+    N706A_WS_OPCODE_CONTINUATION = 0x0,  /**< 继续帧 */
+    N706A_WS_OPCODE_TEXT = 0x1,          /**< 文本帧 */
+    N706A_WS_OPCODE_BINARY = 0x2,        /**< 二进制帧 */
+    N706A_WS_OPCODE_CLOSE = 0x8,         /**< 关闭帧 */
+    N706A_WS_OPCODE_PING = 0x9,          /**< Ping帧 */
+    N706A_WS_OPCODE_PONG = 0xA           /**< Pong帧 */
+} n706a_ws_opcode_t;
+
+/* WebSocket状态 */
+typedef enum {
+    N706A_WS_STATE_CLOSED = 0,      /**< 已关闭 */
+    N706A_WS_STATE_CONNECTING,      /**< 连接中 */
+    N706A_WS_STATE_OPEN,            /**< 已打开 */
+    N706A_WS_STATE_CLOSING          /**< 关闭中 */
+} n706a_ws_state_t;
+
+/* WebSocket关闭状态码 */
+typedef enum {
+    N706A_WS_CLOSE_NORMAL = 1000,           /**< 正常关闭 */
+    N706A_WS_CLOSE_GOING_AWAY = 1001,       /**< 端点离开 */
+    N706A_WS_CLOSE_PROTOCOL_ERROR = 1002,   /**< 协议错误 */
+    N706A_WS_CLOSE_UNSUPPORTED = 1003,      /**< 不支持的数据 */
+    N706A_WS_CLOSE_NO_STATUS = 1005,        /**< 无状态码 */
+    N706A_WS_CLOSE_ABNORMAL = 1006,         /**< 异常关闭 */
+    N706A_WS_CLOSE_INVALID_DATA = 1007,     /**< 无效数据 */
+    N706A_WS_CLOSE_POLICY = 1008,           /**< 策略违反 */
+    N706A_WS_CLOSE_TOO_LARGE = 1009,        /**< 消息过大 */
+    N706A_WS_CLOSE_EXTENSION = 1010,        /**< 扩展协商失败 */
+    N706A_WS_CLOSE_SERVER_ERROR = 1011      /**< 服务器错误 */
+} n706a_ws_close_code_t;
+
+/* WebSocket错误码 */
+typedef enum {
+    N706A_WS_SUCCESS = 0,               /**< 成功 */
+    N706A_WS_ERROR_INVALID_PARAM = -1,  /**< 参数无效 */
+    N706A_WS_ERROR_NO_MEMORY = -2,      /**< 内存不足 */
+    N706A_WS_ERROR_NOT_CONNECTED = -3,  /**< 未连接 */
+    N706A_WS_ERROR_ALREADY_OPEN = -4,   /**< 已经打开 */
+    N706A_WS_ERROR_HANDSHAKE = -5,      /**< 握手失败 */
+    N706A_WS_ERROR_SEND_FAIL = -6,      /**< 发送失败 */
+    N706A_WS_ERROR_RECV_FAIL = -7,      /**< 接收失败 */
+    N706A_WS_ERROR_PROTOCOL = -8,       /**< 协议错误 */
+    N706A_WS_ERROR_TIMEOUT = -9,        /**< 超时 */
+    N706A_WS_ERROR_CLOSED = -10         /**< 连接已关闭 */
+} n706a_ws_result_t;
+
+/* 前置声明 */
+struct n706a_websocket_client;
+
+/**
+ * @brief WebSocket连接打开回调
+ * @param client_id 客户端ID
+ */
+typedef void (*n706a_ws_on_open_callback_t)(int client_id);
+
+/**
+ * @brief WebSocket消息接收回调
+ * @param client_id 客户端ID
+ * @param data 消息数据
+ * @param length 数据长度
+ * @param is_finished 是否是最后一帧
+ * @param is_binary 是否是二进制数据
+ */
+typedef void (*n706a_ws_on_message_callback_t)(int client_id, 
+                                                const char *data, 
+                                                size_t length,
+                                                bool is_finished, 
+                                                bool is_binary);
+
+/**
+ * @brief WebSocket错误回调
+ * @param client_id 客户端ID
+ * @param error_code 错误码
+ * @param error_msg 错误消息
+ */
+typedef void (*n706a_ws_on_error_callback_t)(int client_id, 
+                                              int error_code, 
+                                              const char *error_msg);
+
+/**
+ * @brief WebSocket连接关闭回调
+ * @param client_id 客户端ID
+ * @param close_code 关闭状态码
+ * @param reason 关闭原因
+ */
+typedef void (*n706a_ws_on_close_callback_t)(int client_id, 
+                                              int close_code, 
+                                              const char *reason);
+
+/**
+ * @brief N706A WebSocket客户端结构
+ */
+typedef struct n706a_websocket_client {
+    int client_id;                      /**< 客户端ID (0-5) */
+    n706a_tcp_socket_t tcp_sock;        /**< 底层TCP socket */
+    n706a_ws_state_t state;             /**< WebSocket状态 */
+    bool is_ssl;                        /**< 是否WSS连接 */
+    
+    /* URL信息 */
+    char url[N706A_WS_MAX_URL_LEN];     /**< 完整URL */
+    char host[128];                     /**< 主机名 */
+    uint16_t port;                      /**< 端口号 */
+    char path[256];                     /**< 路径 */
+    
+    /* WebSocket协议 */
+    char sec_key[32];                   /**< Sec-WebSocket-Key */
+    char sec_accept[32];                /**< Sec-WebSocket-Accept */
+    
+    /* 接收缓冲区 */
+    uint8_t *recv_buffer;               /**< 接收缓冲区 */
+    size_t recv_buffer_size;            /**< 缓冲区大小 */
+    size_t recv_buffer_len;             /**< 已接收数据长度 */
+    
+    /* 回调函数 */
+    n706a_ws_on_open_callback_t on_open;
+    n706a_ws_on_message_callback_t on_message;
+    n706a_ws_on_error_callback_t on_error;
+    n706a_ws_on_close_callback_t on_close;
+    
+    /* 运行控制 */
+    bool running;                       /**< 接收线程运行标志 */
+    void *recv_thread;                  /**< 接收线程句柄 */
+    
+} n706a_websocket_client_t;
+
+/**
+ * @brief 创建WebSocket客户端
+ * @param on_open 连接打开回调
+ * @param on_message 消息接收回调
+ * @param on_error 错误回调
+ * @param on_close 连接关闭回调
+ * @return 客户端ID (0-5),失败返回-1
+ */
+int n706a_websocket_constructor(n706a_ws_on_open_callback_t on_open,
+                                n706a_ws_on_message_callback_t on_message,
+                                n706a_ws_on_error_callback_t on_error,
+                                n706a_ws_on_close_callback_t on_close);
+
+/**
+ * @brief 创建WebSocket客户端并指定socket ID
+ * @param socket_id TCP socket ID (0-5)
+ * @param on_open 连接打开回调
+ * @param on_message 消息接收回调
+ * @param on_error 错误回调
+ * @param on_close 连接关闭回调
+ * @return 客户端ID,失败返回-1
+ */
+int n706a_websocket_constructor_with_socket(int socket_id,
+                                            n706a_ws_on_open_callback_t on_open,
+                                            n706a_ws_on_message_callback_t on_message,
+                                            n706a_ws_on_error_callback_t on_error,
+                                            n706a_ws_on_close_callback_t on_close);
+
+/**
+ * @brief 连接到WebSocket服务器
+ * @param client_id 客户端ID
+ * @param url WebSocket URL (ws://或wss://)
+ * @param option 可选参数(如子协议等),可为NULL
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_connect(int client_id, const char *url, const char *option);
+
+/**
+ * @brief 发送文本消息
+ * @param client_id 客户端ID
+ * @param data 文本数据
+ * @param length 数据长度
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_send_text(int client_id, const char *data, size_t length);
+
+/**
+ * @brief 发送二进制消息
+ * @param client_id 客户端ID
+ * @param data 二进制数据
+ * @param length 数据长度
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_send_binary(int client_id, const uint8_t *data, size_t length);
+
+/**
+ * @brief 发送Ping帧
+ * @param client_id 客户端ID
+ * @param data Ping数据(可为NULL)
+ * @param length 数据长度
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_send_ping(int client_id, const char *data, size_t length);
+
+/**
+ * @brief 发送Pong帧
+ * @param client_id 客户端ID
+ * @param data Pong数据(可为NULL)
+ * @param length 数据长度
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_send_pong(int client_id, const char *data, size_t length);
+
+/**
+ * @brief 关闭WebSocket连接
+ * @param client_id 客户端ID
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_close(int client_id);
+
+/**
+ * @brief 关闭WebSocket连接(带状态码和原因)
+ * @param client_id 客户端ID
+ * @param close_code 关闭状态码
+ * @param reason 关闭原因(可为NULL)
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_close_with_reason(int client_id, 
+                                      n706a_ws_close_code_t close_code,
+                                      const char *reason);
+
+/**
+ * @brief 获取WebSocket连接状态
+ * @param client_id 客户端ID
+ * @return WebSocket状态
+ */
+n706a_ws_state_t n706a_websocket_get_state(int client_id);
+
+/**
+ * @brief 检查WebSocket是否已连接
+ * @param client_id 客户端ID
+ * @return true已连接,false未连接
+ */
+bool n706a_websocket_is_connected(int client_id);
+
+/**
+ * @brief 销毁WebSocket客户端
+ * @param client_id 客户端ID
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_destructor(int client_id);
+
+/* ========== 内部辅助函数(高级用户可选使用) ========== */
+
+/**
+ * @brief 解析WebSocket URL
+ * @param url 完整URL
+ * @param protocol 协议(输出,"ws"或"wss")
+ * @param host 主机名(输出)
+ * @param port 端口号(输出)
+ * @param path 路径(输出)
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_parse_url(const char *url, 
+                              char *protocol, 
+                              char *host, 
+                              uint16_t *port, 
+                              char *path);
+
+/**
+ * @brief 生成WebSocket Sec-WebSocket-Key
+ * @param key 输出缓冲区(至少25字节)
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_generate_key(char *key);
+
+/**
+ * @brief 计算WebSocket Sec-WebSocket-Accept
+ * @param key Sec-WebSocket-Key
+ * @param accept 输出缓冲区(至少29字节)
+ * @return N706A_WS_SUCCESS成功,其他失败
+ */
+int n706a_websocket_calculate_accept(const char *key, char *accept);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* N706A_WEBSOCKET_H */
+
index a9f3fc2..58d39e4 100644 (file)
@@ -62,6 +62,44 @@ errcode_t ppp_ppp_mode_pause(void);
  */
 errcode_t ppp_ppp_mode_resume(void);
 
+/**
+ * @brief 为执行AT命令暂停PPP模式(高级接口)
+ * @return 错误码
+ * 
+ * 功能说明:
+ * - 检查当前模式,如果在PPP模式则自动暂停
+ * - 检查是否安全切换(无数据传输)
+ * - 暂停PPP模式并初始化AT handler
+ * - 状态保存在内部静态变量中
+ * 
+ * 注意:必须与 ppp_api_resume_after_at_command() 配对使用
+ */
+errcode_t ppp_api_pause_for_at_command(void);
+
+/**
+ * @brief AT命令执行后恢复PPP模式(高级接口)
+ * @return 错误码
+ * 
+ * 功能说明:
+ * - 使用内部静态变量恢复状态
+ * - 去初始化AT handler
+ * - 恢复PPP模式
+ * - 如果恢复失败,强制重建PPP连接
+ * 
+ * 注意:必须与 ppp_api_pause_for_at_command() 配对使用
+ */
+errcode_t ppp_api_resume_after_at_command(void);
+
+/**
+ * @brief 查询PPP是否处于暂停状态
+ * @return true: PPP已暂停,数据输出被阻止; false: PPP正常运行
+ * 
+ * 用途:
+ * - 在PPP输出回调中检查是否允许数据输出
+ * - 在pause状态下阻止PPP协议栈向串口发送数据,保证AT命令独占串口
+ */
+bool ppp_api_is_paused(void);
+
 /**
  * @brief 设置PPP波特率
  * @param baudrate 波特率
index 901fbe6..e3c35c9 100644 (file)
@@ -7,6 +7,7 @@ extern "C" {
 
 #include <stdint.h>
 #include <stdbool.h>
+#include <stddef.h>
 #include "ppp_config.h"
 #include "ppp_types.h"
 
@@ -33,6 +34,16 @@ void ppp_at_handler_deinit(void);
 ppp_error_t ppp_at_send_command(const char *command, const char *expected_response, 
                                 uint32_t timeout, ppp_at_response_t *response);
 
+/**
+ * @brief 发送原始数据(不带AT命令格式)
+ * @param data 要发送的数据
+ * @param len 数据长度
+ * @param timeout 超时时间(ms)
+ * @return 错误码
+ * @note 用于 TCP/SSL 数据发送,在 AT+TCPSEND/AT+SSLTCPSEND 命令后使用
+ */
+ppp_error_t ppp_at_send_raw_data(const void *data, size_t len, uint32_t timeout);
+
 /**
  * @brief 测试AT连接
  * @return 错误码
diff --git a/code/sdk/zhihui/ppp/src/n706a_http_client.c b/code/sdk/zhihui/ppp/src/n706a_http_client.c
new file mode 100644 (file)
index 0000000..649e370
--- /dev/null
@@ -0,0 +1,785 @@
+/**
+ * @file n706a_http_client.c
+ * @brief N706A HTTP/HTTPS客户端实现(基于TCP)
+ * 
+ * 基于TCP手动构造HTTP协议,支持:
+ * - HTTP (普通TCP)
+ * - HTTPS (SSL TCP)
+ * - 6个并发连接
+ * - 所有标准HTTP方法
+ */
+
+#include "n706a_http_client.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* 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);
+
+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,
+                          char *host,
+                          uint16_t *port,
+                          char *path);
+
+/* ========== 公共函数实现 ========== */
+
+/**
+ * @brief 初始化HTTP客户端
+ */
+n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client, int socket_id)
+{
+    if (!client || socket_id < 0 || socket_id > 5) {
+        return N706A_HTTP_EARG;
+    }
+    
+    memset(client, 0, sizeof(n706a_http_client_t));
+    
+    client->socket_id = socket_id;
+    client->is_https = false;
+    client->remote_port = 0;
+    client->response_code = 0;
+    client->connected = false;
+    client->keepalive = false;
+    client->timeout_ms = 10000;  // 默认10秒
+    
+    /* 初始化TCP socket */
+    n706a_tcp_socket_create(&client->tcp_sock, socket_id);
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 连接到HTTP/HTTPS服务器
+ */
+n706a_httpc_result_t n706a_http_client_connect(n706a_http_client_t *client,
+                                               const char *host,
+                                               uint16_t port,
+                                               bool use_ssl)
+{
+    if (!client || !host) {
+        return N706A_HTTP_EARG;
+    }
+    
+    /* 保存连接信息 */
+    strncpy(client->host, host, sizeof(client->host) - 1);
+    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握手需要更长时间
+    
+    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;
+    }
+}
+
+/**
+ * @brief 发送HTTP GET请求
+ */
+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) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    /* 构造GET请求 */
+    char request_buf[2048];
+    int request_len = http_build_request(client, "GET", path,
+                                        NULL, 0, NULL,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        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);
+    
+    if (ret != N706A_TCP_SUCCESS || sent != (size_t)request_len) {
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 接收响应 */
+    uint8_t response_buf[8192];
+    size_t received;
+    
+    ret = n706a_tcp_recv(&client->tcp_sock,
+                        response_buf,
+                        sizeof(response_buf) - 1,
+                        &received,
+                        client->timeout_ms);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        return N706A_HTTP_ERECV;
+    }
+    
+    response_buf[received] = '\0';
+    
+    /* 解析响应 */
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 发送HTTP POST请求
+ */
+n706a_httpc_result_t n706a_http_client_post(n706a_http_client_t *client,
+                                            const char *path,
+                                            const char *content_type,
+                                            const char *body,
+                                            size_t body_len,
+                                            n706a_http_client_data_t *client_data)
+{
+    if (!client || !path || !client_data) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    /* 构造POST请求 */
+    char request_buf[4096];
+    int request_len = http_build_request(client, "POST", path,
+                                        body, body_len, content_type,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        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);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        return N706A_HTTP_ESEND;
+    }
+    
+    /* 接收响应 */
+    uint8_t response_buf[8192];
+    size_t received;
+    
+    ret = n706a_tcp_recv(&client->tcp_sock,
+                        response_buf,
+                        sizeof(response_buf) - 1,
+                        &received,
+                        client->timeout_ms);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        return N706A_HTTP_ERECV;
+    }
+    
+    response_buf[received] = '\0';
+    
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 发送HTTP PUT请求
+ */
+n706a_httpc_result_t n706a_http_client_put(n706a_http_client_t *client,
+                                           const char *path,
+                                           const char *content_type,
+                                           const char *body,
+                                           size_t body_len,
+                                           n706a_http_client_data_t *client_data)
+{
+    if (!client || !path || !client_data) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    char request_buf[4096];
+    int request_len = http_build_request(client, "PUT", path,
+                                        body, body_len, content_type,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        return N706A_HTTP_ENOBUFS;
+    }
+    
+    size_t sent;
+    n706a_tcp_send(&client->tcp_sock, (uint8_t*)request_buf, request_len, &sent);
+    
+    uint8_t response_buf[8192];
+    size_t received;
+    n706a_tcp_recv(&client->tcp_sock, response_buf, sizeof(response_buf) - 1,
+                   &received, client->timeout_ms);
+    
+    response_buf[received] = '\0';
+    
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 发送HTTP DELETE请求
+ */
+n706a_httpc_result_t n706a_http_client_delete(n706a_http_client_t *client,
+                                              const char *path,
+                                              n706a_http_client_data_t *client_data)
+{
+    if (!client || !path || !client_data) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    char request_buf[2048];
+    int request_len = http_build_request(client, "DELETE", path,
+                                        NULL, 0, NULL,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        return N706A_HTTP_ENOBUFS;
+    }
+    
+    size_t sent;
+    n706a_tcp_send(&client->tcp_sock, (uint8_t*)request_buf, request_len, &sent);
+    
+    uint8_t response_buf[8192];
+    size_t received;
+    n706a_tcp_recv(&client->tcp_sock, response_buf, sizeof(response_buf) - 1,
+                   &received, client->timeout_ms);
+    
+    response_buf[received] = '\0';
+    
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 发送HTTP HEAD请求
+ */
+n706a_httpc_result_t n706a_http_client_head(n706a_http_client_t *client,
+                                            const char *path,
+                                            n706a_http_client_data_t *client_data)
+{
+    if (!client || !path || !client_data) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (!client->connected) {
+        return N706A_HTTP_ECONN;
+    }
+    
+    char request_buf[2048];
+    int request_len = http_build_request(client, "HEAD", path,
+                                        NULL, 0, NULL,
+                                        request_buf, sizeof(request_buf));
+    
+    if (request_len < 0) {
+        return N706A_HTTP_ENOBUFS;
+    }
+    
+    size_t sent;
+    n706a_tcp_send(&client->tcp_sock, (uint8_t*)request_buf, request_len, &sent);
+    
+    uint8_t response_buf[8192];
+    size_t received;
+    n706a_tcp_recv(&client->tcp_sock, response_buf, sizeof(response_buf) - 1,
+                   &received, client->timeout_ms);
+    
+    response_buf[received] = '\0';
+    
+    return http_parse_response((char*)response_buf, received, client, client_data);
+}
+
+/**
+ * @brief 设置自定义HTTP请求头
+ */
+n706a_httpc_result_t n706a_http_client_set_header(n706a_http_client_t *client,
+                                                  const char *header)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (client->header) {
+        free(client->header);
+    }
+    
+    if (header) {
+        client->header = strdup(header);
+    } else {
+        client->header = NULL;
+    }
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 设置HTTP基本认证
+ */
+n706a_httpc_result_t n706a_http_client_set_auth(n706a_http_client_t *client,
+                                                const char *username,
+                                                const char *password)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (client->auth_user) {
+        free(client->auth_user);
+    }
+    if (client->auth_password) {
+        free(client->auth_password);
+    }
+    
+    if (username) {
+        client->auth_user = strdup(username);
+    } else {
+        client->auth_user = NULL;
+    }
+    
+    if (password) {
+        client->auth_password = strdup(password);
+    } else {
+        client->auth_password = NULL;
+    }
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 设置Keep-Alive模式
+ */
+n706a_httpc_result_t n706a_http_client_set_keepalive(n706a_http_client_t *client,
+                                                     bool keepalive)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    client->keepalive = keepalive;
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 关闭HTTP连接
+ */
+n706a_httpc_result_t n706a_http_client_close(n706a_http_client_t *client)
+{
+    if (!client) {
+        return N706A_HTTP_EARG;
+    }
+    
+    if (client->connected) {
+        n706a_tcp_close(&client->tcp_sock);
+        client->connected = false;
+    }
+    
+    if (client->header) {
+        free(client->header);
+        client->header = NULL;
+    }
+    
+    if (client->auth_user) {
+        free(client->auth_user);
+        client->auth_user = NULL;
+    }
+    
+    if (client->auth_password) {
+        free(client->auth_password);
+        client->auth_password = NULL;
+    }
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief 检查HTTP连接状态
+ */
+bool n706a_http_client_is_connected(n706a_http_client_t *client)
+{
+    if (!client) {
+        return false;
+    }
+    
+    return client->connected && n706a_tcp_is_connected(&client->tcp_sock);
+}
+
+/**
+ * @brief 获取最后的HTTP响应码
+ */
+int n706a_http_client_get_response_code(n706a_http_client_t *client)
+{
+    if (!client) {
+        return 0;
+    }
+    
+    return client->response_code;
+}
+
+/**
+ * @brief 解析完整URL并发起GET请求
+ */
+n706a_httpc_result_t n706a_http_client_get_url(n706a_http_client_t *client,
+                                               const char *url,
+                                               n706a_http_client_data_t *client_data)
+{
+    if (!client || !url || !client_data) {
+        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;
+    }
+    
+    bool use_ssl = (strcmp(protocol, "https") == 0);
+    
+    n706a_httpc_result_t ret = n706a_http_client_connect(client, host, port, use_ssl);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return ret;
+    }
+    
+    return n706a_http_client_get(client, path, client_data);
+}
+
+/**
+ * @brief 解析完整URL并发起POST请求
+ */
+n706a_httpc_result_t n706a_http_client_post_url(n706a_http_client_t *client,
+                                                const char *url,
+                                                const char *content_type,
+                                                const char *body,
+                                                size_t body_len,
+                                                n706a_http_client_data_t *client_data)
+{
+    if (!client || !url || !client_data) {
+        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;
+    }
+    
+    bool use_ssl = (strcmp(protocol, "https") == 0);
+    
+    n706a_httpc_result_t ret = n706a_http_client_connect(client, host, port, use_ssl);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return ret;
+    }
+    
+    return n706a_http_client_post(client, path, content_type, body, body_len, client_data);
+}
+
+/* ========== 私有函数实现 ========== */
+
+/**
+ * @brief 构造HTTP请求
+ */
+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)
+{
+    int len = 0;
+    
+    /* 请求行 */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "%s %s HTTP/1.1\r\n", method, path);
+    
+    /* Host头 */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "Host: %s\r\n", client->host);
+    
+    /* User-Agent */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "User-Agent: N706A-HTTP-Client/1.0\r\n");
+    
+    /* Accept */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "Accept: */*\r\n");
+    
+    /* Connection */
+    len += snprintf(request_buf + len, buf_size - len,
+                   "Connection: %s\r\n",
+                   client->keepalive ? "keep-alive" : "close");
+    
+    /* 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);
+    }
+    
+    /* Basic认证 */
+    if (client->auth_user && client->auth_password) {
+        char auth_str[256];
+        char auth_b64[512];
+        
+        snprintf(auth_str, sizeof(auth_str), "%s:%s",
+                client->auth_user, client->auth_password);
+        
+        http_base64_encode(auth_str, strlen(auth_str), auth_b64);
+        
+        len += snprintf(request_buf + len, buf_size - len,
+                       "Authorization: Basic %s\r\n", auth_b64);
+    }
+    
+    /* 自定义请求头 */
+    if (client->header) {
+        len += snprintf(request_buf + len, buf_size - len,
+                       "%s", client->header);
+    }
+    
+    /* 空行 */
+    len += snprintf(request_buf + len, buf_size - len, "\r\n");
+    
+    /* 请求体 */
+    if (body && body_len > 0) {
+        if (len + body_len < buf_size) {
+            memcpy(request_buf + len, body, body_len);
+            len += body_len;
+        } else {
+            return -1;  // 缓冲区不足
+        }
+    }
+    
+    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;
+    }
+    
+    /* 解析状态行 */
+    int major, minor, status_code;
+    if (sscanf(response, "HTTP/%d.%d %d", &major, &minor, &status_code) != 3) {
+        return N706A_HTTP_EPROTO;
+    }
+    
+    client->response_code = status_code;
+    
+    /* 查找头尾分界 */
+    const char *header_end = strstr(response, "\r\n\r\n");
+    if (!header_end) {
+        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';
+    }
+    
+    /* 提取Content-Length */
+    const char *content_len_str = strcasestr(response, "Content-Length:");
+    if (content_len_str && content_len_str < header_end) {
+        data->responseContentLen = atoi(content_len_str + 15);
+    } else {
+        data->responseContentLen = 0;
+    }
+    
+    /* 检查是否chunked */
+    if (strcasestr(response, "Transfer-Encoding: chunked")) {
+        data->isChunked = true;
+    } else {
+        data->isChunked = false;
+    }
+    
+    /* 提取响应体 */
+    const char *body = header_end + 4;
+    size_t body_len = response_len - (body - response);
+    
+    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 (data->responseContentLen > 0 && body_len < (size_t)data->responseContentLen) {
+        data->isMore = true;
+    } else {
+        data->isMore = false;
+    }
+    
+    return N706A_HTTP_SUCCESS;
+}
+
+/**
+ * @brief Base64编码
+ */
+static void http_base64_encode(const char *input, size_t len, char *output)
+{
+    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;
+        }
+    }
+    
+    if (i) {
+        for (size_t k = i; k < 3; k++) {
+            char_array_3[k] = '\0';
+        }
+        
+        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);
+        
+        for (size_t k = 0; k < i + 1; k++) {
+            output[j++] = base64_table[char_array_4[k]];
+        }
+        
+        while (i++ < 3) {
+            output[j++] = '=';
+        }
+    }
+    
+    output[j] = '\0';
+}
+
+/**
+ * @brief 解析URL
+ */
+static int http_parse_url(const char *url,
+                          char *protocol,
+                          char *host,
+                          uint16_t *port,
+                          char *path)
+{
+    if (!url || !protocol || !host || !port || !path) {
+        return -1;
+    }
+    
+    const char *p = url;
+    
+    /* 解析协议 */
+    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;
+    }
+    
+    /* 解析host和port */
+    const char *path_start = strchr(p, '/');
+    const char *port_start = strchr(p, ':');
+    
+    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, "/");
+        }
+    }
+    
+    return 0;
+}
+
diff --git a/code/sdk/zhihui/ppp/src/n706a_tcp_client.c b/code/sdk/zhihui/ppp/src/n706a_tcp_client.c
new file mode 100644 (file)
index 0000000..d7e0684
--- /dev/null
@@ -0,0 +1,659 @@
+/**
+ * @file n706a_tcp_client.c
+ * @brief N706A TCP客户端实现
+ * 
+ * 支持两种SSL实现方式:
+ * - AT指令模式:使用AT+SSLTCP*指令
+ * - mbedtls模式:使用mbedtls库
+ */
+
+#include "n706a_tcp_client.h"
+#include "ppp_at_handler.h"
+#include "ppp_types.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* ========== 私有函数声明 ========== */
+
+#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);
+static n706a_tcp_result_t tcp_mbedtls_init_ssl(n706a_tcp_socket_t *sock);
+static void tcp_mbedtls_cleanup_ssl(n706a_tcp_socket_t *sock);
+#endif
+
+/* ========== 公共函数实现 ========== */
+
+/**
+ * @brief 创建TCP socket
+ */
+n706a_tcp_result_t n706a_tcp_socket_create(n706a_tcp_socket_t *sock, int socket_id)
+{
+    if (!sock || socket_id < 0 || socket_id > 5) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    memset(sock, 0, sizeof(n706a_tcp_socket_t));
+    
+    sock->socket_id = socket_id;
+    sock->state = N706A_TCP_STATE_CLOSED;
+    sock->is_ssl = false;
+    sock->timeout_ms = 15000;  // 默认15秒超时
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    sock->ssl_version = 3;  // 默认TLS 1.2
+    sock->auth_mode = 1;    // 默认服务器认证
+    memset(sock->ca_cert_file, 0, sizeof(sock->ca_cert_file));
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    sock->ssl_initialized = false;
+#endif
+    
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 配置SSL参数
+ */
+n706a_tcp_result_t n706a_tcp_config_ssl(n706a_tcp_socket_t *sock,
+                                        int ssl_version,
+                                        int auth_mode,
+                                        const char *ca_cert)
+{
+    if (!sock) {
+        return N706A_TCP_EINVAL;
+    }
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    /* AT指令模式:保存配置参数 */
+    sock->ssl_version = ssl_version;
+    sock->auth_mode = auth_mode;
+    
+    if (ca_cert) {
+        strncpy(sock->ca_cert_file, ca_cert, sizeof(sock->ca_cert_file) - 1);
+        
+        /* 配置AT指令 */
+        char at_cmd[256];
+        
+        /* 设置SSL版本 */
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCFG=\"sslversion\",%d", ssl_version);
+        ppp_at_response_t response;
+        ppp_at_send_command(at_cmd, NULL, 5000, &response);
+        
+        /* 设置认证模式 */
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCFG=\"authmode\",%d", auth_mode);
+        ppp_at_send_command(at_cmd, NULL, 5000, &response);
+        
+        /* 设置CA证书 */
+        if (ca_cert && strlen(ca_cert) > 0) {
+            snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCFG=\"cacert\",\"%s\"", ca_cert);
+            ppp_at_send_command(at_cmd, NULL, 5000, &response);
+        }
+    }
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:解析CA证书 */
+    if (ca_cert && strlen(ca_cert) > 0) {
+        mbedtls_x509_crt_init(&sock->cacert);
+        
+        int ret = mbedtls_x509_crt_parse(&sock->cacert,
+                                        (const unsigned char *)ca_cert,
+                                        strlen(ca_cert) + 1);
+        if (ret != 0) {
+            return N706A_TCP_ERROR;
+        }
+    }
+    
+    sock->auth_mode = auth_mode;
+#endif
+    
+    return N706A_TCP_SUCCESS;
+}
+
+#ifdef N706A_SSL_USE_MBEDTLS
+/**
+ * @brief 配置客户端证书和私钥(mbedtls专用)
+ */
+n706a_tcp_result_t n706a_tcp_config_ssl_client_cert(n706a_tcp_socket_t *sock,
+                                                     const char *client_cert,
+                                                     const char *client_key)
+{
+    if (!sock || !client_cert || !client_key) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    /* 解析客户端证书 */
+    mbedtls_x509_crt_init(&sock->clicert);
+    int ret = mbedtls_x509_crt_parse(&sock->clicert,
+                                    (const unsigned char *)client_cert,
+                                    strlen(client_cert) + 1);
+    if (ret != 0) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 解析私钥 */
+    mbedtls_pk_init(&sock->pkey);
+    ret = mbedtls_pk_parse_key(&sock->pkey,
+                              (const unsigned char *)client_key,
+                              strlen(client_key) + 1,
+                              NULL, 0);
+    if (ret != 0) {
+        mbedtls_x509_crt_free(&sock->clicert);
+        return N706A_TCP_ERROR;
+    }
+    
+    return N706A_TCP_SUCCESS;
+}
+#endif
+
+/**
+ * @brief 连接到TCP服务器
+ */
+n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock,
+                                     const char *host,
+                                     uint16_t port,
+                                     bool use_ssl,
+                                     uint32_t timeout_ms)
+{
+    if (!sock || !host) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    if (sock->state == N706A_TCP_STATE_CONNECTED) {
+        return N706A_TCP_SUCCESS;  // 已连接
+    }
+    
+    /* 保存连接信息 */
+    strncpy(sock->remote_ip, host, sizeof(sock->remote_ip) - 1);
+    sock->remote_port = port;
+    sock->is_ssl = use_ssl;
+    sock->timeout_ms = timeout_ms;
+    
+    char at_cmd[256];
+    ppp_at_response_t response;
+    ppp_error_t ret;
+    
+    sock->state = N706A_TCP_STATE_CONNECTING;
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    if (use_ssl) {
+        /* AT指令SSL模式:使用AT+SSLTCPSETUP */
+        snprintf(at_cmd, sizeof(at_cmd),
+                "AT+SSLTCPSETUP=%d,%s,%d,%d",
+                sock->socket_id,
+                host,
+                port,
+                sock->auth_mode);
+        
+        ret = ppp_at_send_command(at_cmd, NULL, timeout_ms, &response);
+        
+        if (ret == PPP_SUCCESS && response.response &&
+            strstr(response.response, "+SSLTCPSETUP:") &&
+            strstr(response.response, ",OK")) {
+            sock->state = N706A_TCP_STATE_CONNECTED;
+            return N706A_TCP_SUCCESS;
+        }
+    } else {
+        /* 普通TCP */
+        snprintf(at_cmd, sizeof(at_cmd),
+                "AT+TCPSETUP=%d,%s,%d",
+                sock->socket_id,
+                host,
+                port);
+        
+        ret = ppp_at_send_command(at_cmd, NULL, timeout_ms, &response);
+        
+        if (ret == PPP_SUCCESS && response.response &&
+            strstr(response.response, "+TCPSETUP:") &&
+            strstr(response.response, ",OK")) {
+            sock->state = N706A_TCP_STATE_CONNECTED;
+            return N706A_TCP_SUCCESS;
+        }
+    }
+#endif
+
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:先建立TCP,再进行SSL握手 */
+    
+    /* 1. 先建立普通TCP连接 */
+    snprintf(at_cmd, sizeof(at_cmd),
+            "AT+TCPSETUP=%d,%s,%d",
+            sock->socket_id,
+            host,
+            port);
+    
+    ret = ppp_at_send_command(at_cmd, NULL, timeout_ms, &response);
+    
+    if (ret != PPP_SUCCESS || !response.response ||
+        !strstr(response.response, "+TCPSETUP:") ||
+        !strstr(response.response, ",OK")) {
+        sock->state = N706A_TCP_STATE_ERROR;
+        return N706A_TCP_ECONNREF;
+    }
+    
+    /* 2. 如果使用SSL,进行SSL握手 */
+    if (use_ssl) {
+        n706a_tcp_result_t ssl_ret = tcp_mbedtls_init_ssl(sock);
+        if (ssl_ret != N706A_TCP_SUCCESS) {
+            n706a_tcp_close(sock);
+            return ssl_ret;
+        }
+        
+        /* 设置主机名(用于SNI) */
+        mbedtls_ssl_set_hostname(&sock->ssl, host);
+        
+        /* 执行SSL握手 */
+        int handshake_ret;
+        while ((handshake_ret = mbedtls_ssl_handshake(&sock->ssl)) != 0) {
+            if (handshake_ret != MBEDTLS_ERR_SSL_WANT_READ &&
+                handshake_ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+                /* 握手失败 */
+                tcp_mbedtls_cleanup_ssl(sock);
+                n706a_tcp_close(sock);
+                sock->state = N706A_TCP_STATE_ERROR;
+                return N706A_TCP_ERROR;
+            }
+        }
+        
+        /* 验证证书 */
+        uint32_t flags = mbedtls_ssl_get_verify_result(&sock->ssl);
+        if (flags != 0 && sock->auth_mode != MBEDTLS_SSL_VERIFY_NONE) {
+            /* 证书验证失败 */
+            tcp_mbedtls_cleanup_ssl(sock);
+            n706a_tcp_close(sock);
+            sock->state = N706A_TCP_STATE_ERROR;
+            return N706A_TCP_ERROR;
+        }
+    }
+#endif
+    
+    sock->state = N706A_TCP_STATE_CONNECTED;
+    return N706A_TCP_SUCCESS;
+    
+error:
+    sock->state = N706A_TCP_STATE_ERROR;
+    return N706A_TCP_ECONNREF;
+}
+
+/**
+ * @brief 发送TCP数据
+ */
+n706a_tcp_result_t n706a_tcp_send(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;
+    }
+    
+    if (sock->state != N706A_TCP_STATE_CONNECTED) {
+        return N706A_TCP_ENOTCONN;
+    }
+    
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:通过SSL发送 */
+    if (sock->is_ssl && sock->ssl_initialized) {
+        int ret = mbedtls_ssl_write(&sock->ssl, data, len);
+        if (ret > 0) {
+            *sent = ret;
+            return N706A_TCP_SUCCESS;
+        } else {
+            return N706A_TCP_ERROR;
+        }
+    }
+#endif
+    
+    /* AT指令模式或普通TCP */
+    char at_cmd[64];
+    ppp_at_response_t response;
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    if (sock->is_ssl) {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPSEND=%d,%zu",
+                sock->socket_id, len);
+    } else
+#endif
+    {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu",
+                sock->socket_id, len);
+    }
+    
+    /* 发送命令,等待'>'提示符 */
+    ppp_error_t ret = ppp_at_send_command(at_cmd, NULL, 5000, &response);
+    if (ret != PPP_SUCCESS) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 发送实际数据 */
+    ret = ppp_at_send_raw_data(data, len, sock->timeout_ms);
+    if (ret != PPP_SUCCESS) {
+        return N706A_TCP_ERROR;
+    }
+    
+    *sent = len;
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 接收TCP数据
+ */
+n706a_tcp_result_t n706a_tcp_recv(n706a_tcp_socket_t *sock,
+                                  uint8_t *buffer,
+                                  size_t len,
+                                  size_t *received,
+                                  uint32_t timeout_ms)
+{
+    if (!sock || !buffer || len == 0 || !received) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    if (sock->state != N706A_TCP_STATE_CONNECTED) {
+        return N706A_TCP_ENOTCONN;
+    }
+    
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:通过SSL接收 */
+    if (sock->is_ssl && sock->ssl_initialized) {
+        mbedtls_ssl_conf_read_timeout(&sock->conf, timeout_ms);
+        
+        int ret = mbedtls_ssl_read(&sock->ssl, buffer, len);
+        if (ret > 0) {
+            *received = ret;
+            return N706A_TCP_SUCCESS;
+        } else if (ret == 0 || ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
+            *received = 0;
+            return N706A_TCP_SUCCESS;
+        } else {
+            return N706A_TCP_ERROR;
+        }
+    }
+#endif
+    
+    /* AT指令模式或普通TCP */
+    char at_cmd[64];
+    ppp_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);
+    }
+    
+    ppp_error_t ret = ppp_at_send_command(at_cmd, NULL, timeout_ms, &response);
+    if (ret != PPP_SUCCESS) {
+        return N706A_TCP_ETIMEDOUT;
+    }
+    
+    /* 解析响应,提取数据 */
+    if (response.response) {
+        /* 查找数据部分 */
+        const char *data_start = strstr(response.response, "\r\n");
+        if (data_start) {
+            data_start += 2;  // 跳过\r\n
+            size_t data_len = response.response_len - (data_start - response.response);
+            
+            if (data_len > len) {
+                data_len = len;
+            }
+            
+            memcpy(buffer, data_start, data_len);
+            *received = data_len;
+            return N706A_TCP_SUCCESS;
+        }
+    }
+    
+    *received = 0;
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 关闭TCP连接
+ */
+n706a_tcp_result_t n706a_tcp_close(n706a_tcp_socket_t *sock)
+{
+    if (!sock) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    if (sock->state == N706A_TCP_STATE_CLOSED) {
+        return N706A_TCP_SUCCESS;
+    }
+    
+#ifdef N706A_SSL_USE_MBEDTLS
+    /* mbedtls模式:清理SSL上下文 */
+    if (sock->is_ssl && sock->ssl_initialized) {
+        mbedtls_ssl_close_notify(&sock->ssl);
+        tcp_mbedtls_cleanup_ssl(sock);
+    }
+#endif
+    
+    /* 发送关闭命令 */
+    char at_cmd[32];
+    ppp_at_response_t response;
+    
+#ifdef N706A_SSL_USE_AT_COMMAND
+    if (sock->is_ssl) {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCLOSE=%d", sock->socket_id);
+    } else
+#endif
+    {
+        snprintf(at_cmd, sizeof(at_cmd), "AT+TCPCLOSE=%d", sock->socket_id);
+    }
+    
+    ppp_at_send_command(at_cmd, NULL, 5000, &response);
+    
+    sock->state = N706A_TCP_STATE_CLOSED;
+    sock->is_ssl = false;
+    
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 检查socket是否已连接
+ */
+bool n706a_tcp_is_connected(const n706a_tcp_socket_t *sock)
+{
+    if (!sock) {
+        return false;
+    }
+    
+    return (sock->state == N706A_TCP_STATE_CONNECTED);
+}
+
+/**
+ * @brief 获取socket状态
+ */
+n706a_tcp_state_t n706a_tcp_get_state(const n706a_tcp_socket_t *sock)
+{
+    if (!sock) {
+        return N706A_TCP_STATE_ERROR;
+    }
+    
+    return sock->state;
+}
+
+/* ========== mbedtls私有函数实现 ========== */
+
+#ifdef N706A_SSL_USE_MBEDTLS
+
+/**
+ * @brief 初始化mbedtls SSL
+ */
+static n706a_tcp_result_t tcp_mbedtls_init_ssl(n706a_tcp_socket_t *sock)
+{
+    if (!sock) {
+        return N706A_TCP_EINVAL;
+    }
+    
+    /* 初始化SSL上下文 */
+    mbedtls_ssl_init(&sock->ssl);
+    mbedtls_ssl_config_init(&sock->conf);
+    mbedtls_entropy_init(&sock->entropy);
+    mbedtls_ctr_drbg_init(&sock->ctr_drbg);
+    
+    /* 初始化随机数生成器 */
+    const char *pers = "n706a_tcp_client";
+    int ret = mbedtls_ctr_drbg_seed(&sock->ctr_drbg,
+                                   mbedtls_entropy_func,
+                                   &sock->entropy,
+                                   (const unsigned char *)pers,
+                                   strlen(pers));
+    if (ret != 0) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 配置SSL */
+    ret = mbedtls_ssl_config_defaults(&sock->conf,
+                                     MBEDTLS_SSL_IS_CLIENT,
+                                     MBEDTLS_SSL_TRANSPORT_STREAM,
+                                     MBEDTLS_SSL_PRESET_DEFAULT);
+    if (ret != 0) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 设置认证模式 */
+    mbedtls_ssl_conf_authmode(&sock->conf, sock->auth_mode);
+    
+    /* 设置随机数生成器 */
+    mbedtls_ssl_conf_rng(&sock->conf,
+                        mbedtls_ctr_drbg_random,
+                        &sock->ctr_drbg);
+    
+    /* 设置CA证书 */
+    mbedtls_ssl_conf_ca_chain(&sock->conf, &sock->cacert, NULL);
+    
+    /* 设置客户端证书(如果有) */
+    if (sock->clicert.version != 0) {
+        mbedtls_ssl_conf_own_cert(&sock->conf, &sock->clicert, &sock->pkey);
+    }
+    
+    /* 应用配置 */
+    ret = mbedtls_ssl_setup(&sock->ssl, &sock->conf);
+    if (ret != 0) {
+        return N706A_TCP_ERROR;
+    }
+    
+    /* 设置发送/接收回调 */
+    mbedtls_ssl_set_bio(&sock->ssl,
+                       sock,
+                       tcp_mbedtls_send_callback,
+                       tcp_mbedtls_recv_callback,
+                       NULL);
+    
+    sock->ssl_initialized = true;
+    
+    return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 清理mbedtls SSL
+ */
+static void tcp_mbedtls_cleanup_ssl(n706a_tcp_socket_t *sock)
+{
+    if (!sock || !sock->ssl_initialized) {
+        return;
+    }
+    
+    mbedtls_ssl_free(&sock->ssl);
+    mbedtls_ssl_config_free(&sock->conf);
+    mbedtls_ctr_drbg_free(&sock->ctr_drbg);
+    mbedtls_entropy_free(&sock->entropy);
+    
+    if (sock->cacert.version != 0) {
+        mbedtls_x509_crt_free(&sock->cacert);
+    }
+    
+    if (sock->clicert.version != 0) {
+        mbedtls_x509_crt_free(&sock->clicert);
+    }
+    
+    if (sock->pkey.pk_info != NULL) {
+        mbedtls_pk_free(&sock->pkey);
+    }
+    
+    sock->ssl_initialized = false;
+}
+
+/**
+ * @brief mbedtls发送回调(通过AT命令发送)
+ */
+static int tcp_mbedtls_send_callback(void *ctx, const unsigned char *buf, size_t len)
+{
+    n706a_tcp_socket_t *sock = (n706a_tcp_socket_t *)ctx;
+    
+    if (!sock || !buf || len == 0) {
+        return MBEDTLS_ERR_NET_SEND_FAILED;
+    }
+    
+    /* 通过AT+TCPSEND发送加密后的数据 */
+    char at_cmd[64];
+    snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu",
+            sock->socket_id, len);
+    
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command(at_cmd, NULL, 5000, &response);
+    if (ret != PPP_SUCCESS) {
+        return MBEDTLS_ERR_NET_SEND_FAILED;
+    }
+    
+    /* 发送数据 */
+    ret = ppp_at_send_raw_data(buf, len, sock->timeout_ms);
+    if (ret != PPP_SUCCESS) {
+        return MBEDTLS_ERR_NET_SEND_FAILED;
+    }
+    
+    return len;
+}
+
+/**
+ * @brief mbedtls接收回调(通过AT命令接收)
+ */
+static int tcp_mbedtls_recv_callback(void *ctx, unsigned char *buf, size_t len)
+{
+    n706a_tcp_socket_t *sock = (n706a_tcp_socket_t *)ctx;
+    
+    if (!sock || !buf || len == 0) {
+        return MBEDTLS_ERR_NET_RECV_FAILED;
+    }
+    
+    /* 通过AT+TCPREAD接收加密数据 */
+    char at_cmd[64];
+    snprintf(at_cmd, sizeof(at_cmd), "AT+TCPREAD=%d,%zu",
+            sock->socket_id, len);
+    
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command(at_cmd, NULL, sock->timeout_ms, &response);
+    if (ret != PPP_SUCCESS) {
+        return MBEDTLS_ERR_NET_RECV_FAILED;
+    }
+    
+    /* 解析数据 */
+    if (response.response) {
+        const char *data_start = strstr(response.response, "\r\n");
+        if (data_start) {
+            data_start += 2;
+            size_t data_len = response.response_len - (data_start - response.response);
+            
+            if (data_len > len) {
+                data_len = len;
+            }
+            
+            memcpy(buf, data_start, data_len);
+            return data_len;
+        }
+    }
+    
+    return 0;  // 没有数据
+}
+
+#endif /* N706A_SSL_USE_MBEDTLS */
+
diff --git a/code/sdk/zhihui/ppp/src/n706a_websocket.c b/code/sdk/zhihui/ppp/src/n706a_websocket.c
new file mode 100644 (file)
index 0000000..70b86a2
--- /dev/null
@@ -0,0 +1,843 @@
+/**
+ * @file n706a_websocket.c
+ * @brief N706A WebSocket客户端实现(基于TCP)
+ * 
+ * 完整实现RFC 6455 WebSocket协议:
+ * - ws:// (普通TCP)
+ * - wss:// (SSL TCP)
+ * - 握手
+ * - 帧封装/解封装
+ * - Ping/Pong
+ * - 分片传输
+ */
+
+#include "n706a_websocket.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+/* WebSocket GUID(RFC 6455) */
+#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+/* WebSocket帧标志 */
+#define WS_FIN_BIT      0x80
+#define WS_MASK_BIT     0x80
+#define WS_OPCODE_MASK  0x0F
+#define WS_PAYLOAD_MASK 0x7F
+
+/* 全局WebSocket客户端数组 */
+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_send_frame(n706a_websocket_client_t *client,
+                        n706a_ws_opcode_t opcode,
+                        const uint8_t *data,
+                        size_t length,
+                        bool fin);
+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(void *arg);
+static int ws_parse_url(const char *url, char *protocol, char *host,
+                       uint16_t *port, char *path);
+
+/* SHA-1哈希(简化实现,用于WebSocket握手) */
+static void sha1_simple(const unsigned char *input, size_t len, unsigned char output[20]);
+
+/* Base64编码 */
+static void base64_encode(const unsigned char *input, size_t len, char *output);
+
+/* ========== 公共函数实现 ========== */
+
+/**
+ * @brief 创建WebSocket客户端
+ */
+int n706a_websocket_constructor(n706a_ws_on_open_callback_t on_open,
+                                n706a_ws_on_message_callback_t on_message,
+                                n706a_ws_on_error_callback_t on_error,
+                                n706a_ws_on_close_callback_t on_close)
+{
+    /* 查找空闲slot */
+    int client_id = -1;
+    for (int i = 0; i < N706A_WS_MAX_CLIENTS; i++) {
+        if (g_ws_clients[i] == NULL) {
+            client_id = i;
+            break;
+        }
+    }
+    
+    if (client_id < 0) {
+        return -1;  // 没有可用slot
+    }
+    
+    /* 分配客户端结构 */
+    n706a_websocket_client_t *client = 
+        (n706a_websocket_client_t *)malloc(sizeof(n706a_websocket_client_t));
+    
+    if (!client) {
+        return -1;
+    }
+    
+    memset(client, 0, sizeof(n706a_websocket_client_t));
+    
+    client->client_id = client_id;
+    client->state = N706A_WS_STATE_CLOSED;
+    client->is_ssl = false;
+    
+    /* 初始化TCP socket */
+    n706a_tcp_socket_create(&client->tcp_sock, client_id);
+    
+    /* 设置回调 */
+    client->on_open = on_open;
+    client->on_message = on_message;
+    client->on_error = on_error;
+    client->on_close = on_close;
+    
+    /* 分配接收缓冲区 */
+    client->recv_buffer_size = 8192;
+    client->recv_buffer = (uint8_t *)malloc(client->recv_buffer_size);
+    if (!client->recv_buffer) {
+        free(client);
+        return -1;
+    }
+    
+    client->recv_buffer_len = 0;
+    client->running = false;
+    
+    g_ws_clients[client_id] = client;
+    
+    return client_id;
+}
+
+/**
+ * @brief 创建WebSocket客户端(指定socket ID)
+ */
+int n706a_websocket_constructor_with_socket(int socket_id,
+                                            n706a_ws_on_open_callback_t on_open,
+                                            n706a_ws_on_message_callback_t on_message,
+                                            n706a_ws_on_error_callback_t on_error,
+                                            n706a_ws_on_close_callback_t on_close)
+{
+    if (socket_id < 0 || socket_id >= N706A_WS_MAX_CLIENTS) {
+        return -1;
+    }
+    
+    if (g_ws_clients[socket_id] != NULL) {
+        return -1;  // 已被占用
+    }
+    
+    n706a_websocket_client_t *client = 
+        (n706a_websocket_client_t *)malloc(sizeof(n706a_websocket_client_t));
+    
+    if (!client) {
+        return -1;
+    }
+    
+    memset(client, 0, sizeof(n706a_websocket_client_t));
+    
+    client->client_id = socket_id;
+    client->state = N706A_WS_STATE_CLOSED;
+    
+    n706a_tcp_socket_create(&client->tcp_sock, socket_id);
+    
+    client->on_open = on_open;
+    client->on_message = on_message;
+    client->on_error = on_error;
+    client->on_close = on_close;
+    
+    client->recv_buffer_size = 8192;
+    client->recv_buffer = (uint8_t *)malloc(client->recv_buffer_size);
+    if (!client->recv_buffer) {
+        free(client);
+        return -1;
+    }
+    
+    g_ws_clients[socket_id] = client;
+    
+    return socket_id;
+}
+
+/**
+ * @brief 连接到WebSocket服务器
+ */
+int n706a_websocket_connect(int client_id, const char *url, const char *option)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    if (client->state != N706A_WS_STATE_CLOSED) {
+        return N706A_WS_ERROR_ALREADY_OPEN;
+    }
+    
+    /* 解析URL */
+    char protocol[8];
+    
+    if (ws_parse_url(url, protocol, client->host, &client->port, client->path) != 0) {
+        if (client->on_error) {
+            client->on_error(client_id, N706A_WS_ERROR_INVALID_PARAM, "Invalid URL");
+        }
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    strncpy(client->url, url, sizeof(client->url) - 1);
+    
+    /* 判断是否WSS */
+    client->is_ssl = (strcmp(protocol, "wss") == 0);
+    
+    client->state = N706A_WS_STATE_CONNECTING;
+    
+    /* 建立TCP连接 */
+    n706a_tcp_result_t ret = n706a_tcp_connect(&client->tcp_sock,
+                                               client->host,
+                                               client->port,
+                                               client->is_ssl,
+                                               30000);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        client->state = N706A_WS_STATE_CLOSED;
+        if (client->on_error) {
+            client->on_error(client_id, N706A_WS_ERROR_HANDSHAKE, "TCP connection failed");
+        }
+        return N706A_WS_ERROR_HANDSHAKE;
+    }
+    
+    /* WebSocket握手 */
+    if (ws_perform_handshake(client) != 0) {
+        n706a_tcp_close(&client->tcp_sock);
+        client->state = N706A_WS_STATE_CLOSED;
+        if (client->on_error) {
+            client->on_error(client_id, N706A_WS_ERROR_HANDSHAKE, "WebSocket handshake failed");
+        }
+        return N706A_WS_ERROR_HANDSHAKE;
+    }
+    
+    client->state = N706A_WS_STATE_OPEN;
+    
+    /* 启动接收线程 */
+    client->running = true;
+    // TODO: 创建接收线程
+    // pthread_create(&client->recv_thread, NULL, ws_recv_thread_func, client);
+    
+    /* 调用on_open回调 */
+    if (client->on_open) {
+        client->on_open(client_id);
+    }
+    
+    return N706A_WS_SUCCESS;
+}
+
+/**
+ * @brief 发送文本消息
+ */
+int n706a_websocket_send_text(int client_id, const char *data, size_t length)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client || client->state != N706A_WS_STATE_OPEN) {
+        return N706A_WS_ERROR_NOT_CONNECTED;
+    }
+    
+    return ws_send_frame(client, N706A_WS_OPCODE_TEXT, (const uint8_t *)data, length, true);
+}
+
+/**
+ * @brief 发送二进制消息
+ */
+int n706a_websocket_send_binary(int client_id, const uint8_t *data, size_t length)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client || client->state != N706A_WS_STATE_OPEN) {
+        return N706A_WS_ERROR_NOT_CONNECTED;
+    }
+    
+    return ws_send_frame(client, N706A_WS_OPCODE_BINARY, data, length, true);
+}
+
+/**
+ * @brief 发送Ping帧
+ */
+int n706a_websocket_send_ping(int client_id, const char *data, size_t length)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client || client->state != N706A_WS_STATE_OPEN) {
+        return N706A_WS_ERROR_NOT_CONNECTED;
+    }
+    
+    return ws_send_frame(client, N706A_WS_OPCODE_PING, (const uint8_t *)data, length, true);
+}
+
+/**
+ * @brief 发送Pong帧
+ */
+int n706a_websocket_send_pong(int client_id, const char *data, size_t length)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client || client->state != N706A_WS_STATE_OPEN) {
+        return N706A_WS_ERROR_NOT_CONNECTED;
+    }
+    
+    return ws_send_frame(client, N706A_WS_OPCODE_PONG, (const uint8_t *)data, length, true);
+}
+
+/**
+ * @brief 关闭WebSocket连接
+ */
+int n706a_websocket_close(int client_id)
+{
+    return n706a_websocket_close_with_reason(client_id, N706A_WS_CLOSE_NORMAL, NULL);
+}
+
+/**
+ * @brief 关闭WebSocket连接(带原因)
+ */
+int n706a_websocket_close_with_reason(int client_id,
+                                      n706a_ws_close_code_t close_code,
+                                      const char *reason)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    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);
+        
+        client->state = N706A_WS_STATE_CLOSING;
+    }
+    
+    /* 停止接收线程 */
+    client->running = false;
+    // TODO: 等待线程结束
+    // pthread_join(client->recv_thread, NULL);
+    
+    /* 关闭TCP连接 */
+    n706a_tcp_close(&client->tcp_sock);
+    
+    client->state = N706A_WS_STATE_CLOSED;
+    
+    /* 调用on_close回调 */
+    if (client->on_close) {
+        client->on_close(client_id, close_code, reason);
+    }
+    
+    return N706A_WS_SUCCESS;
+}
+
+/**
+ * @brief 获取WebSocket状态
+ */
+n706a_ws_state_t n706a_websocket_get_state(int client_id)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_STATE_CLOSED;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client) {
+        return N706A_WS_STATE_CLOSED;
+    }
+    
+    return client->state;
+}
+
+/**
+ * @brief 检查WebSocket是否已连接
+ */
+bool n706a_websocket_is_connected(int client_id)
+{
+    return (n706a_websocket_get_state(client_id) == N706A_WS_STATE_OPEN);
+}
+
+/**
+ * @brief 销毁WebSocket客户端
+ */
+int n706a_websocket_destructor(int client_id)
+{
+    if (client_id < 0 || client_id >= N706A_WS_MAX_CLIENTS) {
+        return N706A_WS_ERROR_INVALID_PARAM;
+    }
+    
+    n706a_websocket_client_t *client = g_ws_clients[client_id];
+    if (!client) {
+        return N706A_WS_SUCCESS;
+    }
+    
+    /* 确保连接已关闭 */
+    if (client->state != N706A_WS_STATE_CLOSED) {
+        n706a_websocket_close(client_id);
+    }
+    
+    /* 释放资源 */
+    if (client->recv_buffer) {
+        free(client->recv_buffer);
+    }
+    
+    free(client);
+    g_ws_clients[client_id] = NULL;
+    
+    return N706A_WS_SUCCESS;
+}
+
+/* ========== 私有函数实现 ========== */
+
+/**
+ * @brief 执行WebSocket握手
+ */
+static int ws_perform_handshake(n706a_websocket_client_t *client)
+{
+    char handshake_req[1024];
+    char sec_key[25];
+    
+    /* 生成Sec-WebSocket-Key */
+    n706a_websocket_generate_key(sec_key);
+    strncpy(client->sec_key, sec_key, sizeof(client->sec_key) - 1);
+    
+    /* 构造握手请求 */
+    int len = snprintf(handshake_req, sizeof(handshake_req),
+        "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
+    );
+    
+    /* 发送握手请求 */
+    size_t sent;
+    n706a_tcp_result_t ret = n706a_tcp_send(&client->tcp_sock,
+                                            (uint8_t *)handshake_req,
+                                            len,
+                                            &sent);
+    
+    if (ret != N706A_TCP_SUCCESS) {
+        return -1;
+    }
+    
+    /* 接收握手响应 */
+    uint8_t response[2048];
+    size_t received;
+    
+    ret = n706a_tcp_recv(&client->tcp_sock,
+                        response,
+                        sizeof(response) - 1,
+                        &received,
+                        10000);
+    
+    if (ret != N706A_TCP_SUCCESS || received == 0) {
+        return -1;
+    }
+    
+    response[received] = '\0';
+    
+    /* 验证响应 */
+    if (!strstr((char *)response, "HTTP/1.1 101") ||
+        !strstr((char *)response, "Upgrade: websocket") ||
+        !strstr((char *)response, "Connection: Upgrade")) {
+        return -1;
+    }
+    
+    /* 验证Sec-WebSocket-Accept */
+    const char *accept_start = strstr((char *)response, "Sec-WebSocket-Accept:");
+    if (accept_start) {
+        accept_start += 21;  // 跳过"Sec-WebSocket-Accept:"
+        while (*accept_start == ' ') accept_start++;
+        
+        char expected_accept[29];
+        n706a_websocket_calculate_accept(sec_key, expected_accept);
+        
+        if (strncmp(accept_start, expected_accept, strlen(expected_accept)) != 0) {
+            return -1;  // Accept值不匹配
+        }
+    }
+    
+    return 0;
+}
+
+/**
+ * @brief 发送WebSocket帧
+ */
+static int ws_send_frame(n706a_websocket_client_t *client,
+                        n706a_ws_opcode_t opcode,
+                        const uint8_t *data,
+                        size_t length,
+                        bool fin)
+{
+    uint8_t frame[14];  // 最大帧头长度
+    size_t frame_len = 0;
+    
+    /* 第一字节:FIN + RSV + Opcode */
+    frame[frame_len++] = (fin ? WS_FIN_BIT : 0) | (opcode & WS_OPCODE_MASK);
+    
+    /* 第二字节:MASK + Payload length */
+    if (length < 126) {
+        frame[frame_len++] = WS_MASK_BIT | (length & WS_PAYLOAD_MASK);
+    } else if (length < 65536) {
+        frame[frame_len++] = WS_MASK_BIT | 126;
+        frame[frame_len++] = (length >> 8) & 0xFF;
+        frame[frame_len++] = length & 0xFF;
+    } else {
+        frame[frame_len++] = WS_MASK_BIT | 127;
+        for (int i = 7; i >= 0; i--) {
+            frame[frame_len++] = (length >> (i * 8)) & 0xFF;
+        }
+    }
+    
+    /* Masking key */
+    uint8_t mask_key[4];
+    ws_generate_mask_key(mask_key);
+    memcpy(frame + frame_len, mask_key, 4);
+    frame_len += 4;
+    
+    /* 发送帧头 */
+    size_t sent;
+    n706a_tcp_send(&client->tcp_sock, frame, frame_len, &sent);
+    
+    /* 发送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);
+        }
+    }
+    
+    return N706A_WS_SUCCESS;
+}
+
+/**
+ * @brief 接收WebSocket帧
+ */
+static int ws_recv_frame(n706a_websocket_client_t *client)
+{
+    uint8_t header[2];
+    size_t received;
+    
+    /* 接收帧头前2字节 */
+    n706a_tcp_result_t ret = n706a_tcp_recv(&client->tcp_sock,
+                                            header,
+                                            2,
+                                            &received,
+                                            5000);
+    
+    if (ret != N706A_TCP_SUCCESS || received < 2) {
+        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];
+        }
+    }
+    
+    /* 接收payload */
+    if (payload_len > 0) {
+        uint8_t *payload = (uint8_t *)malloc(payload_len + 1);
+        if (!payload) {
+            return -1;
+        }
+        
+        n706a_tcp_recv(&client->tcp_sock, payload, payload_len, &received, 10000);
+        payload[payload_len] = '\0';
+        
+        /* 处理不同类型的帧 */
+        switch (opcode) {
+            case N706A_WS_OPCODE_TEXT:
+            case N706A_WS_OPCODE_BINARY:
+                if (client->on_message) {
+                    client->on_message(client->client_id,
+                                     (char *)payload,
+                                     payload_len,
+                                     fin,
+                                     opcode == N706A_WS_OPCODE_BINARY);
+                }
+                break;
+                
+            case N706A_WS_OPCODE_PING:
+                /* 自动回复Pong */
+                ws_send_frame(client, N706A_WS_OPCODE_PONG, payload, payload_len, true);
+                break;
+                
+            case N706A_WS_OPCODE_PONG:
+                /* 忽略Pong */
+                break;
+                
+            case N706A_WS_OPCODE_CLOSE:
+                /* 服务器关闭连接 */
+                client->state = N706A_WS_STATE_CLOSING;
+                
+                uint16_t close_code = N706A_WS_CLOSE_NO_STATUS;
+                if (payload_len >= 2) {
+                    close_code = (payload[0] << 8) | payload[1];
+                }
+                
+                if (client->on_close) {
+                    client->on_close(client->client_id, close_code,
+                                   payload_len > 2 ? (char *)(payload + 2) : NULL);
+                }
+                break;
+                
+            default:
+                break;
+        }
+        
+        free(payload);
+    }
+    
+    return 0;
+}
+
+/**
+ * @brief 生成mask key
+ */
+static void ws_generate_mask_key(uint8_t *mask_key)
+{
+    for (int i = 0; i < 4; i++) {
+        mask_key[i] = rand() & 0xFF;
+    }
+}
+
+/**
+ * @brief 掩码数据
+ */
+static void ws_mask_data(uint8_t *data, size_t len, const uint8_t *mask_key)
+{
+    for (size_t i = 0; i < len; i++) {
+        data[i] ^= mask_key[i % 4];
+    }
+}
+
+/**
+ * @brief 解析WebSocket URL
+ */
+static int ws_parse_url(const char *url, char *protocol, char *host,
+                       uint16_t *port, char *path)
+{
+    if (!url || !protocol || !host || !port || !path) {
+        return -1;
+    }
+    
+    const char *p = url;
+    
+    /* 解析协议 */
+    if (strncmp(p, "wss://", 6) == 0) {
+        strcpy(protocol, "wss");
+        *port = 443;
+        p += 6;
+    } else if (strncmp(p, "ws://", 5) == 0) {
+        strcpy(protocol, "ws");
+        *port = 80;
+        p += 5;
+    } else {
+        return -1;
+    }
+    
+    /* 解析host */
+    const char *path_start = strchr(p, '/');
+    const char *port_start = strchr(p, ':');
+    
+    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, "/");
+        }
+    }
+    
+    return 0;
+}
+
+/**
+ * @brief 生成WebSocket Key
+ */
+int n706a_websocket_generate_key(char *key)
+{
+    if (!key) {
+        return -1;
+    }
+    
+    /* 生成16字节随机数 */
+    unsigned char random_bytes[16];
+    srand(time(NULL));
+    for (int i = 0; i < 16; i++) {
+        random_bytes[i] = rand() & 0xFF;
+    }
+    
+    /* Base64编码 */
+    base64_encode(random_bytes, 16, key);
+    
+    return 0;
+}
+
+/**
+ * @brief 计算WebSocket Accept
+ */
+int n706a_websocket_calculate_accept(const char *key, char *accept)
+{
+    if (!key || !accept) {
+        return -1;
+    }
+    
+    /* 拼接key和GUID */
+    char concat[128];
+    snprintf(concat, sizeof(concat), "%s%s", key, WS_GUID);
+    
+    /* SHA-1哈希 */
+    unsigned char hash[20];
+    sha1_simple((unsigned char *)concat, strlen(concat), hash);
+    
+    /* Base64编码 */
+    base64_encode(hash, 20, accept);
+    
+    return 0;
+}
+
+/* ========== 辅助函数 ========== */
+
+/**
+ * @brief 简化的SHA-1实现(仅用于WebSocket握手)
+ * 注意:这是简化版,生产环境建议使用成熟的加密库
+ */
+static void sha1_simple(const unsigned char *input, size_t len, unsigned char output[20])
+{
+    /* 这里应该是完整的SHA-1实现 */
+    /* 为了简化,这里仅做占位 */
+    /* 生产环境应使用mbedtls_sha1或OpenSSL的SHA1函数 */
+    
+    /* TODO: 集成真实的SHA-1实现 */
+    memset(output, 0, 20);
+    
+    /* 临时:使用简单哈希作为占位符 */
+    for (size_t i = 0; i < len && i < 20; i++) {
+        output[i % 20] ^= input[i];
+    }
+}
+
+/**
+ * @brief Base64编码
+ */
+static void base64_encode(const unsigned char *input, size_t len, char *output)
+{
+    static const char base64_table[] =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    
+    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;
+        }
+    }
+    
+    if (i) {
+        for (size_t k = i; k < 3; k++) {
+            char_array_3[k] = '\0';
+        }
+        
+        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);
+        
+        for (size_t k = 0; k < i + 1; k++) {
+            output[j++] = base64_table[char_array_4[k]];
+        }
+        
+        while (i++ < 3) {
+            output[j++] = '=';
+        }
+    }
+    
+    output[j] = '\0';
+}
+
index a46ffe6..4c14cce 100644 (file)
@@ -73,7 +73,7 @@ errcode_t ppp_api_power_init(void)
         .reset_pin_en_value = 1, //电路中 控制gpio拉高,reset才拉低
         // .ring_pin = PIN_NONE, //CTS cat1 wakeup mcu modem还没实现,暂时不使用
         .ring_pin = S_MGPIO21, //CTS cat1 wakeup mcu
-        .ring_pin_en_value = 2, //2 双边沿中断 , 1 上升沿中断 , 0 下降沿中断
+        .ring_pin_en_value = 1, //2 双边沿中断 , 1 上升沿中断 , 0 下降沿中断
         .wakemodem_pin = S_MGPIO22, //RTS mcu wakeup cat1
         .wakemodem_pin_en_value = 0 //0 唤醒cat1 , 1 休眠cat1
     };
@@ -245,7 +245,7 @@ errcode_t zhihui_ppp_init(const ppp_config_t *config)
     
     ppp_get_network_signal_info();
 
-    ppp_enable_power_save_mode();
+    // ppp_enable_power_save_mode();
 
     ppp_start_dial();
 #endif
@@ -1935,11 +1935,30 @@ errcode_t ppp_api_get_default_netif(char* netif_name, uint32_t buffer_size)
     return ERRCODE_SUCC;
 }
 
-errcode_t ppp_api_set_baudrate_in_all_mode(uint32_t baudrate)
+/* 静态变量保存暂停状态(用于pause和resume之间的状态传递) */
+static uint8_t s_ppp_need_resume = 0;
+static uint8_t s_ppp_need_deinit_at = 0;
+static uint8_t s_ppp_is_paused = 0;  /* PPP是否处于暂停状态(阻止数据输出) */
+
+/**
+ * @brief 查询PPP是否处于暂停状态
+ * @return true: PPP已暂停,数据输出被阻止; false: PPP正常运行
+ */
+bool ppp_api_is_paused(void)
 {
-    errcode_t ret = ERRCODE_SUCC;
-    uint8_t need_resume = 0;
-    uint8_t need_deinit_at = 0;
+    return (s_ppp_is_paused != 0);
+}
+
+/**
+ * @brief 为执行AT命令暂停PPP模式(高级接口)
+ * @return ERRCODE_SUCC 成功,其他值失败
+ */
+errcode_t ppp_api_pause_for_at_command(void)
+{
+    // 重置内部静态变量
+    s_ppp_need_resume = 0;
+    s_ppp_need_deinit_at = 0;
+    s_ppp_is_paused = 0;
     
     // 1. 如果在 PPP 模式,先暂停
     if(ppp_serial_get_mode() == PPP_SERIAL_MODE_PPP_DATA) {
@@ -1953,7 +1972,7 @@ errcode_t ppp_api_set_baudrate_in_all_mode(uint32_t baudrate)
             PPP_DEBUG_ERROR("Failed to pause PPP mode");
             return ERRCODE_FAIL;
         }
-        need_resume = 1;
+        s_ppp_need_resume = 1;
         
         // 2. 暂停后需要初始化 AT handler 才能执行 AT 命令
         PPP_DEBUG_INFO("Initializing AT handler for command execution...");
@@ -1962,45 +1981,35 @@ errcode_t ppp_api_set_baudrate_in_all_mode(uint32_t baudrate)
             PPP_DEBUG_ERROR("Failed to initialize AT handler: %d", at_ret);
             // 尝试恢复 PPP 模式
             ppp_ppp_mode_resume();
+            s_ppp_need_resume = 0;
             return ERRCODE_FAIL;
         }
-        need_deinit_at = 1;
+        s_ppp_need_deinit_at = 1;
     } else {
         PPP_DEBUG_INFO("Already in AT mode");
-        need_resume = 0;
-        need_deinit_at = 0;
     }
     
-    // 3. 执行 AT 命令设置波特率
-    PPP_DEBUG_CRITICAL("Starting baudrate set... %u", baudrate);
-    ppp_error_t at_ret = ppp_at_set_baudrate(baudrate);
-    if (at_ret != PPP_ERR_OK) {
-        PPP_DEBUG_WARN("First attempt to set baudrate failed, retrying...");
-        // 重试一次
-        at_ret = ppp_at_set_baudrate(baudrate);
-        if (at_ret != PPP_ERR_OK) {
-            PPP_DEBUG_ERROR("Failed to set baudrate %u after retry (error: %d)", baudrate, at_ret);
-            ret = ERRCODE_FAIL;
-            
-            // 如果设置失败,尝试恢复原状态
-            if (need_resume) {
-                if (need_deinit_at) {
-                    ppp_at_handler_deinit();
-                }
-                ppp_ppp_mode_resume();
-            }
-            return ret;
-        } else {
-            PPP_DEBUG_INFO("Baudrate set successfully on retry");
-        }
-    } else {
-        PPP_DEBUG_INFO("Baudrate set successfully: %u", baudrate);
-    }
+    // 设置暂停标志,阻止PPP数据输出
+    s_ppp_is_paused = 1;
+    PPP_DEBUG_INFO("PPP paused for AT command successfully (data output blocked)");
+    return ERRCODE_SUCC;
+}
 
-    // 4. 如果需要,去初始化 AT handler 并恢复 PPP 模式
-    if (need_resume) {
+/**
+ * @brief AT命令执行后恢复PPP模式(高级接口)
+ * @return ERRCODE_SUCC 成功,其他值失败
+ */
+errcode_t ppp_api_resume_after_at_command(void)
+{
+    errcode_t ret = ERRCODE_SUCC;
+    
+    // 使用内部静态变量
+    PPP_DEBUG_INFO("Using internal state: need_resume=%d, need_deinit_at=%d", 
+                   s_ppp_need_resume, s_ppp_need_deinit_at);
+    
+    if (s_ppp_need_resume) {
         // 先去初始化 AT handler
-        if (need_deinit_at) {
+        if (s_ppp_need_deinit_at) {
             PPP_DEBUG_INFO("Deinitializing AT handler...");
             ppp_at_handler_deinit();
         }
@@ -2034,47 +2043,69 @@ errcode_t ppp_api_set_baudrate_in_all_mode(uint32_t baudrate)
             PPP_DEBUG_WARN("PPP mode forcefully recovered, but connection may be unstable");
             ret = ERRCODE_FAIL;
         }
+        
+        // 清除内部状态
+        s_ppp_need_resume = 0;
+        s_ppp_need_deinit_at = 0;
     }
-     
+    
+    // 清除暂停标志,恢复PPP数据输出
+    s_ppp_is_paused = 0;
+    PPP_DEBUG_INFO("PPP resumed successfully (data output enabled)");
     return ret;
 }
 
-errcode_t ppp_api_update_netinfo_in_all_mode(void)
+errcode_t ppp_api_set_baudrate_in_all_mode(uint32_t baudrate)
 {
     errcode_t ret = ERRCODE_SUCC;
-    uint8_t need_resume = 0;
-    uint8_t need_deinit_at = 0;
     
-    // 1. 如果在 PPP 模式,先暂停
-    if(ppp_serial_get_mode() == PPP_SERIAL_MODE_PPP_DATA) {
-        PPP_DEBUG_INFO("In PPP mode, pausing...");
-        if (ppp_interrupt_is_safe_for_command_switch() != true) {
-            PPP_DEBUG_ERROR("Not safe for command switch");
-            return ERRCODE_FAIL;
-        }
-        if (ppp_ppp_mode_pause() != ERRCODE_SUCC) {
-            PPP_DEBUG_ERROR("Failed to pause PPP mode");
-            return ERRCODE_FAIL;
-        }
-        need_resume = 1;
-        
-        // 2. 暂停后需要初始化 AT handler 才能执行 AT 命令
-        PPP_DEBUG_INFO("Initializing AT handler for command execution...");
-        ppp_error_t at_ret = ppp_at_handler_init(SIO_UART_BUS, g_ppp_config.baudrate, true);
+    // 1. 暂停PPP模式(如果需要)
+    if (ppp_api_pause_for_at_command() != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to pause for AT command");
+        return ERRCODE_FAIL;
+    }
+    
+    // 2. 执行 AT 命令设置波特率
+    PPP_DEBUG_CRITICAL("Starting baudrate set... %u", baudrate);
+    ppp_error_t at_ret = ppp_at_set_baudrate(baudrate);
+    if (at_ret != PPP_ERR_OK) {
+        PPP_DEBUG_WARN("First attempt to set baudrate failed, retrying...");
+        // 重试一次
+        at_ret = ppp_at_set_baudrate(baudrate);
         if (at_ret != PPP_ERR_OK) {
-            PPP_DEBUG_ERROR("Failed to initialize AT handler: %d", at_ret);
-            // 尝试恢复 PPP 模式
-            ppp_ppp_mode_resume();
-            return ERRCODE_FAIL;
+            PPP_DEBUG_ERROR("Failed to set baudrate %u after retry (error: %d)", baudrate, at_ret);
+            ret = ERRCODE_FAIL;
+            
+            // 如果设置失败,尝试恢复原状态
+            ppp_api_resume_after_at_command();
+            return ret;
+        } else {
+            PPP_DEBUG_INFO("Baudrate set successfully on retry");
         }
-        need_deinit_at = 1;
     } else {
-        PPP_DEBUG_INFO("Already in AT mode");
-        need_resume = 0;
-        need_deinit_at = 0;
+        PPP_DEBUG_INFO("Baudrate set successfully: %u", baudrate);
+    }
+
+    // 3. 恢复PPP模式(如果需要)
+    if (ppp_api_resume_after_at_command() != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to resume after AT command");
+        ret = ERRCODE_FAIL;
+    }
+     
+    return ret;
+}
+
+errcode_t ppp_api_update_netinfo_in_all_mode(void)
+{
+    errcode_t ret = ERRCODE_SUCC;
+    
+    // 1. 暂停PPP模式(如果需要)
+    if (ppp_api_pause_for_at_command() != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to pause for AT command");
+        return ERRCODE_FAIL;
     }
     
-    // 3. 执行 AT 命令更新信息
+    // 2. 执行 AT 命令更新信息
     PPP_DEBUG_CRITICAL("Starting signal strength update...");
     if (ppp_api_update_signal() != ERRCODE_SUCC) {
         PPP_DEBUG_ERROR("Failed to update signal");
@@ -2094,43 +2125,10 @@ errcode_t ppp_api_update_netinfo_in_all_mode(void)
         ret = ERRCODE_FAIL;
     }
 
-    // 4. 如果需要,去初始化 AT handler 并恢复 PPP 模式
-    if (need_resume) {
-        // 先去初始化 AT handler
-        if (need_deinit_at) {
-            PPP_DEBUG_INFO("Deinitializing AT handler...");
-            ppp_at_handler_deinit();
-        }
-        
-        // 然后恢复 PPP 模式
-        PPP_DEBUG_INFO("Resuming PPP mode...");
-        errcode_t resume_ret = ppp_ppp_mode_resume();
-        if (resume_ret != ERRCODE_SUCC) {
-            PPP_DEBUG_ERROR("Failed to resume PPP mode: %d", resume_ret);
-            PPP_DEBUG_ERROR("Forcing PPP mode recovery...");
-            
-            // 强制恢复到 PPP 模式
-            ppp_serial_switch_to_ppp_mode();
-            
-            #if PPP_USE_INTERRUPT_MODE
-                ppp_error_t int_ret = ppp_interrupt_enable();
-                if (int_ret != PPP_ERR_OK) {
-                    PPP_DEBUG_ERROR("Failed to enable interrupt after forced recovery: %d", int_ret);
-                } else {
-                    PPP_DEBUG_INFO("PPP interrupt mode re-enabled after forced recovery");
-                }
-            #else
-                ppp_error_t thread_ret = ppp_lwip_start_receive_thread();
-                if (thread_ret != PPP_ERR_OK) {
-                    PPP_DEBUG_ERROR("Failed to start receive thread after forced recovery: %d", thread_ret);
-                } else {
-                    PPP_DEBUG_INFO("PPP receive thread restarted after forced recovery");
-                }
-            #endif
-            
-            PPP_DEBUG_WARN("PPP mode forcefully recovered, but connection may be unstable");
-            ret = ERRCODE_FAIL;
-        }
+    // 3. 恢复PPP模式(如果需要)
+    if (ppp_api_resume_after_at_command() != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to resume after AT command");
+        ret = ERRCODE_FAIL;
     }
      
     return ret;
index 7c5fad1..cb663ad 100644 (file)
@@ -528,6 +528,34 @@ ppp_error_t ppp_at_send_command(const char *command, const char *expected_respon
     return ret;
 }
 
+/**
+ * @brief 发送原始数据(不带AT命令格式)
+ * @note 用于 TCP/SSL 数据发送,在 AT+TCPSEND/AT+SSLTCPSEND 命令后使用
+ */
+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 ===");
+    PPP_DEBUG_DEBUG("Length: %zu bytes", len);
+    PPP_DEBUG_DEBUG("Timeout: %u ms", timeout);
+
+    /* 直接通过 UART 发送原始数据,不添加 \r\n */
+    int32_t write_result = uapi_uart_write(g_at_ctx.uart_bus, (const uint8_t *)data, len, 0);
+
+    if (write_result != (int32_t)len) {
+        PPP_DEBUG_ERROR("Failed to send raw data: wrote %d bytes, expected %zu", write_result, len);
+        return PPP_ERR_UART;
+    }
+
+    PPP_DEBUG_DEBUG("Raw data sent successfully (%zu bytes) on bus %d", len, g_at_ctx.uart_bus);
+    return PPP_ERR_OK;
+}
+
 /**
  * @brief 跳过开机日志 (对应chat文件中的 '' \rAT)
  */
index 38916fd..211c10f 100644 (file)
@@ -28,6 +28,7 @@
 
 /* lwIP相关头文件 */
 #include "lwip/err.h"
+#include "lwip/stats.h"
 #include "netif/ppp/ppp.h"
 #include "netif/ppp/pppos.h"
 
@@ -1238,59 +1239,84 @@ void ppp_interrupt_print_stats(void)
     }
     PPP_DEBUG_WARN("");
     
+    // lwIP PPP帧解析层统计
+    PPP_DEBUG_WARN("=== lwIP PPP Frame Parsing Statistics ===");
+#if LINK_STATS
+    PPP_DEBUG_WARN("Valid Frames Received: %u ✓", (uint32_t)lwip_stats.link.recv);
+    PPP_DEBUG_WARN("Frames Transmitted: %u", (uint32_t)lwip_stats.link.xmit);
+    
+    // 解析错误统计
+    uint32_t total_parse_errors = (uint32_t)(lwip_stats.link.drop + 
+                                             lwip_stats.link.chkerr + 
+                                             lwip_stats.link.lenerr + 
+                                             lwip_stats.link.memerr);
+    if (total_parse_errors > 0) {
+        PPP_DEBUG_WARN("Parse Errors: %u ⚠️", total_parse_errors);
+        PPP_DEBUG_WARN("  - FCS Checksum Error: %u", (uint32_t)lwip_stats.link.chkerr);
+        PPP_DEBUG_WARN("  - Length Error (incomplete): %u", (uint32_t)lwip_stats.link.lenerr);
+        PPP_DEBUG_WARN("  - Memory Error: %u", (uint32_t)lwip_stats.link.memerr);
+        PPP_DEBUG_WARN("  - Other Drop: %u", (uint32_t)lwip_stats.link.drop);
+        
+        // 计算错误率
+        uint32_t total_frames = (uint32_t)(lwip_stats.link.recv + total_parse_errors);
+        if (total_frames > 0) {
+            uint32_t error_rate = (total_parse_errors * 10000) / total_frames;
+            PPP_DEBUG_WARN("  Frame Error Rate: %u.%02u%%", error_rate / 100, error_rate % 100);
+            
+            // FCS错误率(如果有)
+            if (lwip_stats.link.chkerr > 0) {
+                uint32_t fcs_rate = ((uint32_t)lwip_stats.link.chkerr * 10000) / total_frames;
+                PPP_DEBUG_WARN("  FCS Error Rate: %u.%02u%% (check UART/cable)", 
+                              fcs_rate / 100, fcs_rate % 100);
+            }
+        }
+    } else {
+        PPP_DEBUG_WARN("Parse Errors: 0 ✓ (no errors)");
+    }
+#else
+    PPP_DEBUG_WARN("LINK_STATS not enabled in lwIP");
+#endif
+    PPP_DEBUG_WARN("");
+    
     // 字节统计(与轮询模式格式一致)
     uint32_t total_discarded = g_ppp_interrupt_stats.dropped_not_initialized + 
                                 g_ppp_interrupt_stats.dropped_lwip_error +
                                 g_ppp_interrupt_stats.dropped_queue_full +
                                 g_pingpong_stats.dropped_both_full;
     
-    PPP_DEBUG_WARN("");
-    PPP_DEBUG_WARN("Byte Statistics:");
-    PPP_DEBUG_WARN("  Total Bytes Received: %u", g_ppp_interrupt_stats.total_bytes_received);
-    PPP_DEBUG_WARN("  Total Bytes Processed: %u", g_ppp_interrupt_stats.total_bytes_sent_to_lwip);
-    PPP_DEBUG_WARN("  Total Bytes Discarded: %u", total_discarded);
-    PPP_DEBUG_WARN("    - Both Buffers Full: %u", g_pingpong_stats.dropped_both_full);
-    PPP_DEBUG_WARN("    - Queue Full: %u", g_ppp_interrupt_stats.dropped_queue_full);
-    PPP_DEBUG_WARN("  lwIP Errors: %u", g_ppp_interrupt_stats.lwip_error_count);
-    PPP_DEBUG_WARN("");
+    PPP_DEBUG_WARN("=== Byte-Level Statistics ===");
+    PPP_DEBUG_WARN("Total Bytes Received (UART): %u", g_ppp_interrupt_stats.total_bytes_received);
+    PPP_DEBUG_WARN("Total Bytes Sent to lwIP: %u", g_ppp_interrupt_stats.total_bytes_sent_to_lwip);
     
-    // 详细丢弃统计(INFO级别)
+    // 丢弃统计
     if (total_discarded > 0) {
-        PPP_DEBUG_WARN("Detailed Dropped Bytes:");
-        PPP_DEBUG_WARN("  - Not Initialized: %u", g_ppp_interrupt_stats.dropped_not_initialized);
-        PPP_DEBUG_WARN("  - lwIP Error: %u", g_ppp_interrupt_stats.dropped_lwip_error);
-        PPP_DEBUG_WARN("  - Queue Full: %u", g_ppp_interrupt_stats.dropped_queue_full);
+        PPP_DEBUG_WARN("Total Bytes Discarded: %u ⚠️", total_discarded);
         PPP_DEBUG_WARN("  - Both Buffers Full: %u", g_pingpong_stats.dropped_both_full);
-        PPP_DEBUG_WARN("");
+        PPP_DEBUG_WARN("  - Queue Full: %u", g_ppp_interrupt_stats.dropped_queue_full);
+        PPP_DEBUG_WARN("  - Not Initialized: %u", g_ppp_interrupt_stats.dropped_not_initialized);
+        PPP_DEBUG_WARN("  - lwIP Input Error: %u", g_ppp_interrupt_stats.dropped_lwip_error);
+    } else {
+        PPP_DEBUG_WARN("Total Bytes Discarded: 0 ✓");
     }
+    PPP_DEBUG_WARN("");
     
-    // 计算丢包率
-    if (g_ppp_interrupt_stats.total_bytes_received > 0) {
-        uint32_t total_dropped = g_ppp_interrupt_stats.dropped_not_initialized + 
-                                 g_ppp_interrupt_stats.dropped_lwip_error +
-                                 g_ppp_interrupt_stats.dropped_queue_full +
-                                 g_pingpong_stats.dropped_both_full;
-        uint32_t drop_rate = (total_dropped * 10000) / g_ppp_interrupt_stats.total_bytes_received;
-        PPP_DEBUG_WARN("Total Drop Rate: %u.%02u%%", drop_rate / 100, drop_rate % 100);
+    // 计算字节级丢包率
+    if (g_ppp_interrupt_stats.total_bytes_received > 0 && total_discarded > 0) {
+        uint32_t drop_rate = (total_discarded * 10000) / g_ppp_interrupt_stats.total_bytes_received;
+        PPP_DEBUG_WARN("Byte-Level Drop Rate: %u.%02u%%", drop_rate / 100, drop_rate % 100);
         
-        // 队列满导致的丢失率
-        if (g_ppp_interrupt_stats.dropped_queue_full > 0) {
-            uint32_t queue_full_rate = (g_ppp_interrupt_stats.dropped_queue_full * 10000) / 
-                                       g_ppp_interrupt_stats.total_bytes_received;
-            PPP_DEBUG_WARN("Queue Full Rate: %u.%02u%%", queue_full_rate / 100, queue_full_rate % 100);
-        }
-        
-        // 两个buffer都满导致的丢失率
-        if (g_pingpong_stats.dropped_both_full > 0) {
-            uint32_t both_full_rate = (g_pingpong_stats.dropped_both_full * 10000) / 
-                                     g_ppp_interrupt_stats.total_bytes_received;
-            PPP_DEBUG_WARN("Both Buffers Full Rate: %u.%02u%% ⚠️ (processing too slow or buffers too small)", 
-                          both_full_rate / 100, both_full_rate % 100);
+        // 如果丢包率超过1%,提示可能的原因
+        if (drop_rate > 100) {
+            PPP_DEBUG_ERROR("⚠️  High byte drop rate detected!");
+            if (g_pingpong_stats.dropped_both_full > 0) {
+                PPP_DEBUG_ERROR("  -> Buffers too small or processing too slow");
+            }
+            if (g_ppp_interrupt_stats.dropped_queue_full > 0) {
+                PPP_DEBUG_ERROR("  -> Message queue too small or thread blocked");
+            }
         }
     }
     
-    // 性能总结(已经在上面各部分显示过了,这里不再重复)
-    
     PPP_DEBUG_WARN("=================================================");
 }
 
index 6b267e9..5996285 100644 (file)
@@ -969,6 +969,12 @@ static void ppp_lwip_netif_status_callback(struct netif *netif)
  */
 static u32_t ppp_lwip_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
 {
+    /* 检查PPP是否处于暂停状态 */
+    if (ppp_api_is_paused()) {
+        PPP_DEBUG_WARN("PPP is paused, blocking data output (%u bytes)", len);
+        return 0;  /* 返回0表示无法发送 */
+    }
+    
     /* 严格的参数检查 - 防止空指针访问 */
     if (!pcb) {
         PPP_DEBUG_ERROR("CRITICAL: ppp_pcb is NULL in output callback!");
index b80ffdd..67169b6 100644 (file)
@@ -437,11 +437,11 @@ void ppp_ring_isr_callback(pin_t pin, uintptr_t param)
     last_level = val;
     same_level_count = 1;
     
-    cat1_api_add_cat1_vote();
     if (val == GPIO_LEVEL_HIGH) {
-        PPP_DEBUG_INFO("PPP: Cat1 ring interrupt on pin %d → HIGH \n", pin);
-    } else {
-        PPP_DEBUG_INFO("PPP: Cat1 ring interrupt on pin %d → LOW\n", pin);
+        PPP_DEBUG_WARN("PPP: Cat1 ring interrupt on pin %d → HIGH \n", pin);
+        cat1_api_add_cat1_vote();
+} else {
+        PPP_DEBUG_WARN("PPP: Cat1 ring interrupt on pin %d → LOW\n", pin);
         //idle后 remove
     }
     uapi_gpio_clear_interrupt(pin);
diff --git a/code/sdk/zhihui/zhihui_net/CMakeLists.txt b/code/sdk/zhihui/zhihui_net/CMakeLists.txt
new file mode 100644 (file)
index 0000000..201be7f
--- /dev/null
@@ -0,0 +1,50 @@
+#===============================================================================
+# @brief    智慧网络统一接口层
+# Copyright (c) @CompanyNameMagicTag 2023-2025. All rights reserved.
+#===============================================================================
+set(COMPONENT_NAME "zhihui_net")
+
+set(SOURCES ${SOURCES}
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/zhihui_net.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/zhihui_net_http.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/zhihui_net_websocket.c
+)
+
+set(PUBLIC_HEADER
+    ${CMAKE_CURRENT_SOURCE_DIR}/include
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/http/include
+    ${CMAKE_CURRENT_SOURCE_DIR}/../websocket/os_ws
+)
+
+set(PUBLIC_DEFINES
+    SUPPORT_ZHIHUI_NET
+)
+
+set(PRIVATE_HEADER
+    ${CMAKE_CURRENT_SOURCE_DIR}/../n706a_net/include
+)
+
+set(PRIVATE_DEFINES
+)
+
+# use this when you want to add ccflags like -include xxx
+set(COMPONENT_PUBLIC_CCFLAGS
+)
+
+set(COMPONENT_CCFLAGS
+    -Wno-incompatible-function-pointer-types
+    -Wno-unused-function
+)
+
+set(WHOLE_LINK
+    true
+)
+
+set(MAIN_COMPONENT
+    false
+)
+
+build_component()
+
+install_sdk(${CMAKE_CURRENT_SOURCE_DIR} "*")
+
diff --git a/code/sdk/zhihui/zhihui_net/adapter/zhihui_net_openharmony_adapter.c b/code/sdk/zhihui/zhihui_net/adapter/zhihui_net_openharmony_adapter.c
new file mode 100644 (file)
index 0000000..3096831
--- /dev/null
@@ -0,0 +1,298 @@
+/**
+ * @file zhihui_net_openharmony_adapter.c
+ * @brief OpenHarmony网络实现适配器实现
+ * 
+ * 将统一接口转换为OpenHarmony的httpclient和net_websocket接口
+ */
+
+#include "zhihui_net_openharmony_adapter.h"
+#include "httpclient.h"
+#include "net_websocket.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* ========== HTTP接口适配 ========== */
+
+/* 将ZhihuiHttpClient转换为OpenHarmony HttpClient */
+static void convert_to_openharmony_http_client(ZhihuiHttpClient *src, HttpClient *dst)
+{
+    memset(dst, 0, sizeof(HttpClient));
+    dst->socket = src->socket;
+    dst->remotePort = src->remotePort;
+    dst->responseCode = src->responseCode;
+    dst->header = src->header;
+    dst->authUser = src->authUser;
+    dst->authPassword = src->authPassword;
+    dst->isHttp = src->isHttp;
+}
+
+/* 将OpenHarmony HttpClient转换回ZhihuiHttpClient */
+static void convert_from_openharmony_http_client(HttpClient *src, ZhihuiHttpClient *dst)
+{
+    dst->socket = src->socket;
+    dst->remotePort = src->remotePort;
+    dst->responseCode = src->responseCode;
+    dst->header = src->header;
+    dst->authUser = src->authUser;
+    dst->authPassword = src->authPassword;
+    dst->isHttp = src->isHttp;
+}
+
+/* 将ZhihuiHttpClientData转换为OpenHarmony HttpClientData */
+static void convert_to_openharmony_http_data(ZhihuiHttpClientData *src, HttpClientData *dst)
+{
+    memset(dst, 0, sizeof(HttpClientData));
+    dst->isMore = src->isMore;
+    dst->isChunked = src->isChunked;
+    dst->method = src->method;
+    dst->retrieveLen = src->retrieveLen;
+    dst->responseContentLen = src->responseContentLen;
+    dst->contentBlockLen = src->contentBlockLen;
+    dst->postBufLen = src->postBufLen;
+    dst->responseBufLen = src->responseBufLen;
+    dst->headerBufLen = src->headerBufLen;
+    dst->postContentType = src->postContentType;
+    dst->postBuf = src->postBuf;
+    dst->responseBuf = src->responseBuf;
+    dst->headerBuf = src->headerBuf;
+    dst->isRedirected = src->isRedirected;
+    dst->redirectUrl = src->redirectUrl;
+}
+
+/* 将OpenHarmony HttpClientData转换回ZhihuiHttpClientData */
+static void convert_from_openharmony_http_data(HttpClientData *src, ZhihuiHttpClientData *dst)
+{
+    dst->isMore = src->isMore;
+    dst->isChunked = src->isChunked;
+    dst->method = src->method;
+    dst->retrieveLen = src->retrieveLen;
+    dst->responseContentLen = src->responseContentLen;
+    dst->contentBlockLen = src->contentBlockLen;
+    dst->postBufLen = src->postBufLen;
+    dst->responseBufLen = src->responseBufLen;
+    dst->headerBufLen = src->headerBufLen;
+    dst->postContentType = src->postContentType;
+    dst->postBuf = src->postBuf;
+    dst->responseBuf = src->responseBuf;
+    dst->headerBuf = src->headerBuf;
+    dst->isRedirected = src->isRedirected;
+    dst->redirectUrl = src->redirectUrl;
+}
+
+int ZhihuiNetOpenHarmonyHttpGet(ZhihuiHttpClient *client, const char *url,
+                                ZhihuiHttpClientData *data)
+{
+    HttpClient oh_client;
+    HttpClientData oh_data;
+    int ret;
+    
+    convert_to_openharmony_http_client(client, &oh_client);
+    convert_to_openharmony_http_data(data, &oh_data);
+    
+    ret = HttpClientGetRequest(&oh_client, url, &oh_data);
+    
+    convert_from_openharmony_http_client(&oh_client, client);
+    convert_from_openharmony_http_data(&oh_data, data);
+    
+    return ret;
+}
+
+int ZhihuiNetOpenHarmonyHttpPost(ZhihuiHttpClient *client, const char *url,
+                                 ZhihuiHttpClientData *data)
+{
+    HttpClient oh_client;
+    HttpClientData oh_data;
+    int ret;
+    
+    convert_to_openharmony_http_client(client, &oh_client);
+    convert_to_openharmony_http_data(data, &oh_data);
+    
+    ret = HttpClientPostRequest(&oh_client, url, &oh_data);
+    
+    convert_from_openharmony_http_client(&oh_client, client);
+    convert_from_openharmony_http_data(&oh_data, data);
+    
+    return ret;
+}
+
+int ZhihuiNetOpenHarmonyHttpHead(ZhihuiHttpClient *client, const char *url,
+                                 ZhihuiHttpClientData *data)
+{
+    HttpClient oh_client;
+    HttpClientData oh_data;
+    int ret;
+    
+    convert_to_openharmony_http_client(client, &oh_client);
+    convert_to_openharmony_http_data(data, &oh_data);
+    
+    ret = HttpClientHeadRequest(&oh_client, url, &oh_data);
+    
+    convert_from_openharmony_http_client(&oh_client, client);
+    convert_from_openharmony_http_data(&oh_data, data);
+    
+    return ret;
+}
+
+int ZhihuiNetOpenHarmonyHttpPut(ZhihuiHttpClient *client, const char *url,
+                                ZhihuiHttpClientData *data)
+{
+    HttpClient oh_client;
+    HttpClientData oh_data;
+    int ret;
+    
+    convert_to_openharmony_http_client(client, &oh_client);
+    convert_to_openharmony_http_data(data, &oh_data);
+    
+    ret = HttpClientPutRequest(&oh_client, url, &oh_data);
+    
+    convert_from_openharmony_http_client(&oh_client, client);
+    convert_from_openharmony_http_data(&oh_data, data);
+    
+    return ret;
+}
+
+int ZhihuiNetOpenHarmonyHttpDelete(ZhihuiHttpClient *client, const char *url,
+                                   ZhihuiHttpClientData *data)
+{
+    HttpClient oh_client;
+    HttpClientData oh_data;
+    int ret;
+    
+    convert_to_openharmony_http_client(client, &oh_client);
+    convert_to_openharmony_http_data(data, &oh_data);
+    
+    ret = HttpClientDeleteRequest(&oh_client, url, &oh_data);
+    
+    convert_from_openharmony_http_client(&oh_client, client);
+    convert_from_openharmony_http_data(&oh_data, data);
+    
+    return ret;
+}
+
+/* ========== WebSocket接口适配 ========== */
+
+/* 回调函数适配器 */
+typedef struct {
+    ZhihuiWebSocketOnOpenCallback zhihui_on_open;
+    ZhihuiWebSocketOnMessageCallback zhihui_on_message;
+    ZhihuiWebSocketOnErrorCallback zhihui_on_error;
+    ZhihuiWebSocketOnCloseCallback zhihui_on_close;
+} ws_callback_adapter_t;
+
+#define MAX_WS_CLIENTS 8
+static ws_callback_adapter_t g_ws_adapters[MAX_WS_CLIENTS];
+
+/* OpenHarmony回调转发到Zhihui回调 */
+static void oh_ws_on_open(int client)
+{
+    if (client >= 0 && client < MAX_WS_CLIENTS) {
+        if (g_ws_adapters[client].zhihui_on_open) {
+            g_ws_adapters[client].zhihui_on_open(client);
+        }
+    }
+}
+
+static void oh_ws_on_message(int client, const char *data, size_t length, bool isFinished)
+{
+    if (client >= 0 && client < MAX_WS_CLIENTS) {
+        if (g_ws_adapters[client].zhihui_on_message) {
+            /* OpenHarmony不区分文本/二进制,默认作为文本 */
+            g_ws_adapters[client].zhihui_on_message(client, data, length, isFinished, false);
+        }
+    }
+}
+
+static void oh_ws_on_error(int client, int errorCode, const char *errorMsg)
+{
+    if (client >= 0 && client < MAX_WS_CLIENTS) {
+        if (g_ws_adapters[client].zhihui_on_error) {
+            g_ws_adapters[client].zhihui_on_error(client, errorCode, errorMsg);
+        }
+    }
+}
+
+static void oh_ws_on_close(int client, int closeCode, const char *reason)
+{
+    if (client >= 0 && client < MAX_WS_CLIENTS) {
+        if (g_ws_adapters[client].zhihui_on_close) {
+            g_ws_adapters[client].zhihui_on_close(client, closeCode, reason);
+        }
+    }
+}
+
+int ZhihuiNetOpenHarmonyWsConstructor(ZhihuiWebSocketOnOpenCallback onOpen,
+                                      ZhihuiWebSocketOnMessageCallback onMessage,
+                                      ZhihuiWebSocketOnErrorCallback onError,
+                                      ZhihuiWebSocketOnCloseCallback onClose)
+{
+    int client = WebSocketClientConstructor(oh_ws_on_open, oh_ws_on_message,
+                                            oh_ws_on_error, oh_ws_on_close);
+    
+    if (client >= 0 && client < MAX_WS_CLIENTS) {
+        g_ws_adapters[client].zhihui_on_open = onOpen;
+        g_ws_adapters[client].zhihui_on_message = onMessage;
+        g_ws_adapters[client].zhihui_on_error = onError;
+        g_ws_adapters[client].zhihui_on_close = onClose;
+    }
+    
+    return client;
+}
+
+int ZhihuiNetOpenHarmonyWsConnect(int client, const char *url, const char *option)
+{
+    return WebSocketClientConnect(client, url, option);
+}
+
+int ZhihuiNetOpenHarmonyWsSendText(int client, const char *text, size_t length)
+{
+    return WebSocketClientSendText(client, text, length);
+}
+
+int ZhihuiNetOpenHarmonyWsSendBinStart(int client, const char *data, size_t length)
+{
+    return WebSocketClientSendBinStart(client, data, length);
+}
+
+int ZhihuiNetOpenHarmonyWsSendBinContinue(int client, const char *data, size_t length)
+{
+    return WebSocketClientSendBinContinue(client, data, length);
+}
+
+int ZhihuiNetOpenHarmonyWsSendBinEnd(int client, const char *data, size_t length)
+{
+    return WebSocketClientSendBinEnd(client, data, length);
+}
+
+int ZhihuiNetOpenHarmonyWsClose(int client)
+{
+    return WebSocketClientClose(client);
+}
+
+int ZhihuiNetOpenHarmonyWsDestructor(int client)
+{
+    int ret = WebSocketClientDestructor(client);
+    
+    /* 清除适配器 */
+    if (client >= 0 && client < MAX_WS_CLIENTS) {
+        memset(&g_ws_adapters[client], 0, sizeof(ws_callback_adapter_t));
+    }
+    
+    return ret;
+}
+
+/* ========== 初始化/清理 ========== */
+
+int ZhihuiNetOpenHarmonyInit(void)
+{
+    printf("[ZhihuiNet][OpenHarmony] Adapter initialized\n");
+    memset(g_ws_adapters, 0, sizeof(g_ws_adapters));
+    return 0;
+}
+
+int ZhihuiNetOpenHarmonyDeinit(void)
+{
+    printf("[ZhihuiNet][OpenHarmony] Adapter deinitialized\n");
+    return 0;
+}
+
diff --git a/code/sdk/zhihui/zhihui_net/adapter/zhihui_net_openharmony_adapter.h b/code/sdk/zhihui/zhihui_net/adapter/zhihui_net_openharmony_adapter.h
new file mode 100644 (file)
index 0000000..29b467c
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * @file zhihui_net_openharmony_adapter.h
+ * @brief OpenHarmony网络实现适配器
+ */
+
+#ifndef ZHIHUI_NET_OPENHARMONY_ADAPTER_H
+#define ZHIHUI_NET_OPENHARMONY_ADAPTER_H
+
+#include "zhihui_net.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* HTTP接口 */
+int ZhihuiNetOpenHarmonyHttpGet(ZhihuiHttpClient *client, const char *url, 
+                                ZhihuiHttpClientData *data);
+int ZhihuiNetOpenHarmonyHttpPost(ZhihuiHttpClient *client, const char *url,
+                                 ZhihuiHttpClientData *data);
+int ZhihuiNetOpenHarmonyHttpHead(ZhihuiHttpClient *client, const char *url,
+                                 ZhihuiHttpClientData *data);
+int ZhihuiNetOpenHarmonyHttpPut(ZhihuiHttpClient *client, const char *url,
+                                ZhihuiHttpClientData *data);
+int ZhihuiNetOpenHarmonyHttpDelete(ZhihuiHttpClient *client, const char *url,
+                                   ZhihuiHttpClientData *data);
+
+/* WebSocket接口 */
+int ZhihuiNetOpenHarmonyWsConstructor(ZhihuiWebSocketOnOpenCallback onOpen,
+                                      ZhihuiWebSocketOnMessageCallback onMessage,
+                                      ZhihuiWebSocketOnErrorCallback onError,
+                                      ZhihuiWebSocketOnCloseCallback onClose);
+int ZhihuiNetOpenHarmonyWsConnect(int client, const char *url, const char *option);
+int ZhihuiNetOpenHarmonyWsSendText(int client, const char *text, size_t length);
+int ZhihuiNetOpenHarmonyWsSendBinStart(int client, const char *data, size_t length);
+int ZhihuiNetOpenHarmonyWsSendBinContinue(int client, const char *data, size_t length);
+int ZhihuiNetOpenHarmonyWsSendBinEnd(int client, const char *data, size_t length);
+int ZhihuiNetOpenHarmonyWsClose(int client);
+int ZhihuiNetOpenHarmonyWsDestructor(int client);
+
+/* 初始化/清理 */
+int ZhihuiNetOpenHarmonyInit(void);
+int ZhihuiNetOpenHarmonyDeinit(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZHIHUI_NET_OPENHARMONY_ADAPTER_H */
+
diff --git a/code/sdk/zhihui/zhihui_net/adapter/zhihui_net_ppp_adapter.c b/code/sdk/zhihui/zhihui_net/adapter/zhihui_net_ppp_adapter.c
new file mode 100644 (file)
index 0000000..bbdc372
--- /dev/null
@@ -0,0 +1,445 @@
+/**
+ * @file zhihui_net_ppp_adapter.c
+ * @brief N706A PPP网络实现适配器实现
+ * 
+ * 将统一接口转换为N706A PPP的接口
+ */
+
+#include "zhihui_net_ppp_adapter.h"
+#include "n706a_http_client.h"
+#include "n706a_websocket.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* ========== HTTP接口适配 ========== */
+
+int ZhihuiNetPppHttpGet(ZhihuiHttpClient *client, const char *url,
+                        ZhihuiHttpClientData *data)
+{
+    n706a_http_client_t *ppp_client = (n706a_http_client_t *)client->impl_data;
+    
+    /* 如果还未初始化PPP客户端,则初始化 */
+    if (ppp_client == NULL) {
+        ppp_client = (n706a_http_client_t *)malloc(sizeof(n706a_http_client_t));
+        if (ppp_client == NULL) {
+            return -1;
+        }
+        
+        n706a_httpc_result_t ret = n706a_http_client_init(ppp_client, 0);
+        if (ret != N706A_HTTP_SUCCESS) {
+            free(ppp_client);
+            return -1;
+        }
+        
+        client->impl_data = ppp_client;
+    }
+    
+    /* 解析URL获取host和path */
+    char host[256] = {0};
+    char path[256] = {0};
+    uint16_t port = 80;
+    bool use_ssl = false;
+    
+    if (strncmp(url, "https://", 8) == 0) {
+        use_ssl = true;
+        port = 443;
+        sscanf(url + 8, "%255[^/]%255s", host, path);
+    } else if (strncmp(url, "http://", 7) == 0) {
+        sscanf(url + 7, "%255[^/]%255s", host, path);
+    } else {
+        return -1;
+    }
+    
+    if (strlen(path) == 0) {
+        strcpy(path, "/");
+    }
+    
+    /* 连接 */
+    n706a_httpc_result_t ret = n706a_http_client_connect(ppp_client, host, port, use_ssl);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return -1;
+    }
+    
+    /* 准备数据结构 */
+    n706a_http_client_data_t ppp_data = {0};
+    ppp_data.responseBuf = data->responseBuf;
+    ppp_data.responseBufLen = data->responseBufLen;
+    ppp_data.headerBuf = data->headerBuf;
+    ppp_data.headerBufLen = data->headerBufLen;
+    
+    /* 执行GET请求 */
+    ret = n706a_http_client_get(ppp_client, path, &ppp_data);
+    
+    /* 设置响应码 */
+    client->responseCode = ppp_client->response_code;
+    data->responseContentLen = ppp_data.responseContentLen;
+    
+    return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+}
+
+int ZhihuiNetPppHttpPost(ZhihuiHttpClient *client, const char *url,
+                         ZhihuiHttpClientData *data)
+{
+    n706a_http_client_t *ppp_client = (n706a_http_client_t *)client->impl_data;
+    
+    if (ppp_client == NULL) {
+        ppp_client = (n706a_http_client_t *)malloc(sizeof(n706a_http_client_t));
+        if (ppp_client == NULL) {
+            return -1;
+        }
+        
+        n706a_httpc_result_t ret = n706a_http_client_init(ppp_client, 0);
+        if (ret != N706A_HTTP_SUCCESS) {
+            free(ppp_client);
+            return -1;
+        }
+        
+        client->impl_data = ppp_client;
+    }
+    
+    /* 解析URL */
+    char host[256] = {0};
+    char path[256] = {0};
+    uint16_t port = 80;
+    bool use_ssl = false;
+    
+    if (strncmp(url, "https://", 8) == 0) {
+        use_ssl = true;
+        port = 443;
+        sscanf(url + 8, "%255[^/]%255s", host, path);
+    } else if (strncmp(url, "http://", 7) == 0) {
+        sscanf(url + 7, "%255[^/]%255s", host, path);
+    } else {
+        return -1;
+    }
+    
+    if (strlen(path) == 0) {
+        strcpy(path, "/");
+    }
+    
+    /* 连接 */
+    n706a_httpc_result_t ret = n706a_http_client_connect(ppp_client, host, port, use_ssl);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return -1;
+    }
+    
+    /* 准备数据结构 */
+    n706a_http_client_data_t ppp_data = {0};
+    ppp_data.responseBuf = data->responseBuf;
+    ppp_data.responseBufLen = data->responseBufLen;
+    ppp_data.headerBuf = data->headerBuf;
+    ppp_data.headerBufLen = data->headerBufLen;
+    
+    /* 执行POST请求 */
+    const char *content_type = data->postContentType ? data->postContentType : "application/json";
+    ret = n706a_http_client_post(ppp_client, path, content_type, 
+                                      data->postBuf, data->postBufLen, &ppp_data);
+    
+    client->responseCode = ppp_client->response_code;
+    data->responseContentLen = ppp_data.responseContentLen;
+    
+    return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+}
+
+int ZhihuiNetPppHttpHead(ZhihuiHttpClient *client, const char *url,
+                         ZhihuiHttpClientData *data)
+{
+    /* N706A暂不直接支持HEAD,使用GET实现 */
+    return ZhihuiNetPppHttpGet(client, url, data);
+}
+
+int ZhihuiNetPppHttpPut(ZhihuiHttpClient *client, const char *url,
+                        ZhihuiHttpClientData *data)
+{
+    n706a_http_client_t *ppp_client = (n706a_http_client_t *)client->impl_data;
+    
+    if (ppp_client == NULL) {
+        ppp_client = (n706a_http_client_t *)malloc(sizeof(n706a_http_client_t));
+        if (ppp_client == NULL) {
+            return -1;
+        }
+        
+        n706a_httpc_result_t ret = n706a_http_client_init(ppp_client, 0);
+        if (ret != N706A_HTTP_SUCCESS) {
+            free(ppp_client);
+            return -1;
+        }
+        
+        client->impl_data = ppp_client;
+    }
+    
+    /* 解析URL */
+    char host[256] = {0};
+    char path[256] = {0};
+    uint16_t port = 80;
+    bool use_ssl = false;
+    
+    if (strncmp(url, "https://", 8) == 0) {
+        use_ssl = true;
+        port = 443;
+        sscanf(url + 8, "%255[^/]%255s", host, path);
+    } else if (strncmp(url, "http://", 7) == 0) {
+        sscanf(url + 7, "%255[^/]%255s", host, path);
+    } else {
+        return -1;
+    }
+    
+    if (strlen(path) == 0) {
+        strcpy(path, "/");
+    }
+    
+    /* 连接 */
+    n706a_httpc_result_t ret = n706a_http_client_connect(ppp_client, host, port, use_ssl);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return -1;
+    }
+    
+    /* 准备数据结构 */
+    n706a_http_client_data_t ppp_data = {0};
+    ppp_data.responseBuf = data->responseBuf;
+    ppp_data.responseBufLen = data->responseBufLen;
+    ppp_data.headerBuf = data->headerBuf;
+    ppp_data.headerBufLen = data->headerBufLen;
+    
+    /* 执行PUT请求 */
+    const char *content_type = data->postContentType ? data->postContentType : "application/json";
+    ret = n706a_http_client_put(ppp_client, path, content_type,
+                                     data->postBuf, data->postBufLen, &ppp_data);
+    
+    client->responseCode = ppp_client->response_code;
+    data->responseContentLen = ppp_data.responseContentLen;
+    
+    return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+}
+
+int ZhihuiNetPppHttpDelete(ZhihuiHttpClient *client, const char *url,
+                           ZhihuiHttpClientData *data)
+{
+    n706a_http_client_t *ppp_client = (n706a_http_client_t *)client->impl_data;
+    
+    if (ppp_client == NULL) {
+        ppp_client = (n706a_http_client_t *)malloc(sizeof(n706a_http_client_t));
+        if (ppp_client == NULL) {
+            return -1;
+        }
+        
+        n706a_httpc_result_t ret = n706a_http_client_init(ppp_client, 0);
+        if (ret != N706A_HTTP_SUCCESS) {
+            free(ppp_client);
+            return -1;
+        }
+        
+        client->impl_data = ppp_client;
+    }
+    
+    /* 解析URL */
+    char host[256] = {0};
+    char path[256] = {0};
+    uint16_t port = 80;
+    bool use_ssl = false;
+    
+    if (strncmp(url, "https://", 8) == 0) {
+        use_ssl = true;
+        port = 443;
+        sscanf(url + 8, "%255[^/]%255s", host, path);
+    } else if (strncmp(url, "http://", 7) == 0) {
+        sscanf(url + 7, "%255[^/]%255s", host, path);
+    } else {
+        return -1;
+    }
+    
+    if (strlen(path) == 0) {
+        strcpy(path, "/");
+    }
+    
+    /* 连接 */
+    n706a_httpc_result_t ret = n706a_http_client_connect(ppp_client, host, port, use_ssl);
+    if (ret != N706A_HTTP_SUCCESS) {
+        return -1;
+    }
+    
+    /* 准备数据结构 */
+    n706a_http_client_data_t ppp_data = {0};
+    ppp_data.responseBuf = data->responseBuf;
+    ppp_data.responseBufLen = data->responseBufLen;
+    ppp_data.headerBuf = data->headerBuf;
+    ppp_data.headerBufLen = data->headerBufLen;
+    
+    /* 执行DELETE请求 */
+    ret = n706a_http_client_delete(ppp_client, path, &ppp_data);
+    
+    client->responseCode = ppp_client->response_code;
+    data->responseContentLen = ppp_data.responseContentLen;
+    
+    return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+}
+
+/* ========== WebSocket接口适配 ========== */
+
+#define MAX_WS_CLIENTS 6  /* N706A支持最多6个并发连接 */
+
+typedef struct {
+    int client_id;
+    bool in_use;
+    ZhihuiWebSocketOnOpenCallback zhihui_on_open;
+    ZhihuiWebSocketOnMessageCallback zhihui_on_message;
+    ZhihuiWebSocketOnErrorCallback zhihui_on_error;
+    ZhihuiWebSocketOnCloseCallback zhihui_on_close;
+} ppp_ws_adapter_t;
+
+static ppp_ws_adapter_t g_ppp_ws_adapters[MAX_WS_CLIENTS];
+
+/* PPP回调转发到Zhihui回调 */
+static void ppp_ws_on_open(int client_id)
+{
+    if (client_id >= 0 && client_id < MAX_WS_CLIENTS) {
+        if (g_ppp_ws_adapters[client_id].in_use && 
+            g_ppp_ws_adapters[client_id].zhihui_on_open) {
+            g_ppp_ws_adapters[client_id].zhihui_on_open(client_id);
+        }
+    }
+}
+
+static void ppp_ws_on_message(int client_id, const char *data, size_t length,
+                               bool isFinished, bool isBinary)
+{
+    if (client_id >= 0 && client_id < MAX_WS_CLIENTS) {
+        if (g_ppp_ws_adapters[client_id].in_use && 
+            g_ppp_ws_adapters[client_id].zhihui_on_message) {
+            g_ppp_ws_adapters[client_id].zhihui_on_message(client_id, data,
+                                                            length, isFinished, isBinary);
+        }
+    }
+}
+
+static void ppp_ws_on_error(int client_id, int errorCode, const char *errorMsg)
+{
+    if (client_id >= 0 && client_id < MAX_WS_CLIENTS) {
+        if (g_ppp_ws_adapters[client_id].in_use && 
+            g_ppp_ws_adapters[client_id].zhihui_on_error) {
+            g_ppp_ws_adapters[client_id].zhihui_on_error(client_id, errorCode, errorMsg);
+        }
+    }
+}
+
+static void ppp_ws_on_close(int client_id, int closeCode, const char *reason)
+{
+    if (client_id >= 0 && client_id < MAX_WS_CLIENTS) {
+        if (g_ppp_ws_adapters[client_id].in_use && 
+            g_ppp_ws_adapters[client_id].zhihui_on_close) {
+            g_ppp_ws_adapters[client_id].zhihui_on_close(client_id, closeCode, reason);
+        }
+    }
+}
+
+int ZhihuiNetPppWsConstructor(ZhihuiWebSocketOnOpenCallback onOpen,
+                              ZhihuiWebSocketOnMessageCallback onMessage,
+                              ZhihuiWebSocketOnErrorCallback onError,
+                              ZhihuiWebSocketOnCloseCallback onClose)
+{
+    int client_id = n706a_websocket_constructor(
+        ppp_ws_on_open,
+        ppp_ws_on_message,
+        ppp_ws_on_error,
+        ppp_ws_on_close
+    );
+    
+    if (client_id >= 0 && client_id < MAX_WS_CLIENTS) {
+        g_ppp_ws_adapters[client_id].client_id = client_id;
+        g_ppp_ws_adapters[client_id].in_use = true;
+        g_ppp_ws_adapters[client_id].zhihui_on_open = onOpen;
+        g_ppp_ws_adapters[client_id].zhihui_on_message = onMessage;
+        g_ppp_ws_adapters[client_id].zhihui_on_error = onError;
+        g_ppp_ws_adapters[client_id].zhihui_on_close = onClose;
+    }
+    
+    return client_id;
+}
+
+int ZhihuiNetPppWsConnect(int client, const char *url, const char *option)
+{
+    if (client < 0 || client >= MAX_WS_CLIENTS || !g_ppp_ws_adapters[client].in_use) {
+        return -1;
+    }
+    
+    return n706a_websocket_connect(client, url, option);
+}
+
+int ZhihuiNetPppWsSendText(int client, const char *text, size_t length)
+{
+    if (client < 0 || client >= MAX_WS_CLIENTS || !g_ppp_ws_adapters[client].in_use) {
+        return -1;
+    }
+    
+    return n706a_websocket_send_text(client, text, length);
+}
+
+int ZhihuiNetPppWsSendBinStart(int client, const char *data, size_t length)
+{
+    if (client < 0 || client >= MAX_WS_CLIENTS || !g_ppp_ws_adapters[client].in_use) {
+        return -1;
+    }
+    
+    return n706a_websocket_send_binary(client, (const uint8_t *)data, length);
+}
+
+int ZhihuiNetPppWsSendBinContinue(int client, const char *data, size_t length)
+{
+    /* N706A一次发送完整帧,不支持分段 */
+    return 0;
+}
+
+int ZhihuiNetPppWsSendBinEnd(int client, const char *data, size_t length)
+{
+    /* N706A一次发送完整帧,不支持分段 */
+    return 0;
+}
+
+int ZhihuiNetPppWsClose(int client)
+{
+    if (client < 0 || client >= MAX_WS_CLIENTS || !g_ppp_ws_adapters[client].in_use) {
+        return -1;
+    }
+    
+    return n706a_websocket_close(client);
+}
+
+int ZhihuiNetPppWsDestructor(int client)
+{
+    if (client < 0 || client >= MAX_WS_CLIENTS || !g_ppp_ws_adapters[client].in_use) {
+        return -1;
+    }
+    
+    int ret = n706a_websocket_destructor(client);
+    
+    if (ret == 0) {
+        memset(&g_ppp_ws_adapters[client], 0, sizeof(ppp_ws_adapter_t));
+        g_ppp_ws_adapters[client].in_use = false;
+    }
+    
+    return ret;
+}
+
+/* ========== 初始化/清理 ========== */
+
+int ZhihuiNetPppInit(void)
+{
+    printf("[ZhihuiNet][PPP] Adapter initialized\n");
+    memset(g_ppp_ws_adapters, 0, sizeof(g_ppp_ws_adapters));
+    return 0;
+}
+
+int ZhihuiNetPppDeinit(void)
+{
+    printf("[ZhihuiNet][PPP] Adapter deinitialized\n");
+    
+    /* 清理所有WebSocket客户端 */
+    for (int i = 0; i < MAX_WS_CLIENTS; i++) {
+        if (g_ppp_ws_adapters[i].in_use) {
+            ZhihuiNetPppWsDestructor(i);
+        }
+    }
+    
+    return 0;
+}
diff --git a/code/sdk/zhihui/zhihui_net/adapter/zhihui_net_ppp_adapter.h b/code/sdk/zhihui/zhihui_net/adapter/zhihui_net_ppp_adapter.h
new file mode 100644 (file)
index 0000000..08bf64f
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * @file zhihui_net_ppp_adapter.h
+ * @brief N706A PPP网络实现适配器
+ */
+
+#ifndef ZHIHUI_NET_PPP_ADAPTER_H
+#define ZHIHUI_NET_PPP_ADAPTER_H
+
+#include "zhihui_net.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* HTTP接口 */
+int ZhihuiNetPppHttpGet(ZhihuiHttpClient *client, const char *url,
+                        ZhihuiHttpClientData *data);
+int ZhihuiNetPppHttpPost(ZhihuiHttpClient *client, const char *url,
+                         ZhihuiHttpClientData *data);
+int ZhihuiNetPppHttpHead(ZhihuiHttpClient *client, const char *url,
+                         ZhihuiHttpClientData *data);
+int ZhihuiNetPppHttpPut(ZhihuiHttpClient *client, const char *url,
+                        ZhihuiHttpClientData *data);
+int ZhihuiNetPppHttpDelete(ZhihuiHttpClient *client, const char *url,
+                           ZhihuiHttpClientData *data);
+
+/* WebSocket接口 */
+int ZhihuiNetPppWsConstructor(ZhihuiWebSocketOnOpenCallback onOpen,
+                              ZhihuiWebSocketOnMessageCallback onMessage,
+                              ZhihuiWebSocketOnErrorCallback onError,
+                              ZhihuiWebSocketOnCloseCallback onClose);
+int ZhihuiNetPppWsConnect(int client, const char *url, const char *option);
+int ZhihuiNetPppWsSendText(int client, const char *text, size_t length);
+int ZhihuiNetPppWsSendBinStart(int client, const char *data, size_t length);
+int ZhihuiNetPppWsSendBinContinue(int client, const char *data, size_t length);
+int ZhihuiNetPppWsSendBinEnd(int client, const char *data, size_t length);
+int ZhihuiNetPppWsClose(int client);
+int ZhihuiNetPppWsDestructor(int client);
+
+/* 初始化/清理 */
+int ZhihuiNetPppInit(void);
+int ZhihuiNetPppDeinit(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZHIHUI_NET_PPP_ADAPTER_H */
+
diff --git a/code/sdk/zhihui/zhihui_net/example/zhihui_net_http_example.c b/code/sdk/zhihui/zhihui_net/example/zhihui_net_http_example.c
new file mode 100644 (file)
index 0000000..e9dda5e
--- /dev/null
@@ -0,0 +1,198 @@
+/**
+ * @file zhihui_net_http_example.c
+ * @brief ZhihuiNet HTTP客户端使用示例
+ */
+
+#include "zhihui_net.h"
+#include <stdio.h>
+#include <string.h>
+
+/* HTTP请求缓冲区 */
+#define REQ_BUF_SIZE 2048
+static char req_buf[REQ_BUF_SIZE];
+
+/* HTTP响应缓冲区 */
+#define RSP_BUF_SIZE 4096
+static char rsp_buf[RSP_BUF_SIZE];
+
+/**
+ * @brief HTTP GET请求示例
+ */
+static int http_get_example(void)
+{
+    ZhihuiHttpClient client = {0};
+    ZhihuiHttpClientData client_data = {0};
+    const char *url = "http://www.baidu.com/";
+    int ret;
+    
+    printf("\n=== HTTP GET Example ===\n");
+    printf("URL: %s\n", url);
+    
+    /* 准备请求头缓冲区 */
+    memset(req_buf, 0, sizeof(req_buf));
+    client_data.headerBuf = req_buf;
+    client_data.headerBufLen = sizeof(req_buf);
+    
+    /* 准备响应缓冲区 */
+    memset(rsp_buf, 0, sizeof(rsp_buf));
+    client_data.responseBuf = rsp_buf;
+    client_data.responseBufLen = sizeof(rsp_buf);
+    
+    /* 发送GET请求 */
+    ret = ZhihuiHttpClientGetRequest(&client, url, &client_data);
+    
+    if (ret >= 0 && client.responseCode == 200) {
+        printf("✓ Request successful\n");
+        printf("Status code: %d\n", client.responseCode);
+        printf("Response length: %d\n", client_data.responseContentLen);
+        printf("Response preview: %.100s...\n", client_data.responseBuf);
+        return 0;
+    } else {
+        printf("✗ Request failed: %d\n", ret);
+        printf("Status code: %d\n", client.responseCode);
+        return -1;
+    }
+}
+
+/**
+ * @brief HTTP POST请求示例
+ */
+static int http_post_example(void)
+{
+    ZhihuiHttpClient client = {0};
+    ZhihuiHttpClientData client_data = {0};
+    const char *url = "http://httpbin.org/post";
+    const char *post_data = "{\"message\": \"Hello from ZhihuiNet!\"}";
+    int ret;
+    
+    printf("\n=== HTTP POST Example ===\n");
+    printf("URL: %s\n", url);
+    printf("POST data: %s\n", post_data);
+    
+    /* 准备请求头缓冲区 */
+    memset(req_buf, 0, sizeof(req_buf));
+    client_data.headerBuf = req_buf;
+    client_data.headerBufLen = sizeof(req_buf);
+    
+    /* 准备POST数据 */
+    client_data.postBuf = (char *)post_data;
+    client_data.postBufLen = strlen(post_data);
+    client_data.postContentType = "application/json";
+    
+    /* 准备响应缓冲区 */
+    memset(rsp_buf, 0, sizeof(rsp_buf));
+    client_data.responseBuf = rsp_buf;
+    client_data.responseBufLen = sizeof(rsp_buf);
+    
+    /* 发送POST请求 */
+    ret = ZhihuiHttpClientPostRequest(&client, url, &client_data);
+    
+    if (ret >= 0 && client.responseCode == 200) {
+        printf("✓ Request successful\n");
+        printf("Status code: %d\n", client.responseCode);
+        printf("Response: %s\n", client_data.responseBuf);
+        return 0;
+    } else {
+        printf("✗ Request failed: %d\n", ret);
+        printf("Status code: %d\n", client.responseCode);
+        return -1;
+    }
+}
+
+/**
+ * @brief HTTPS请求示例
+ */
+static int https_get_example(void)
+{
+    ZhihuiHttpClient client = {0};
+    ZhihuiHttpClientData client_data = {0};
+    const char *url = "https://www.baidu.com/";
+    int ret;
+    
+    printf("\n=== HTTPS GET Example ===\n");
+    printf("URL: %s\n", url);
+    
+    /* 准备请求头缓冲区 */
+    memset(req_buf, 0, sizeof(req_buf));
+    client_data.headerBuf = req_buf;
+    client_data.headerBufLen = sizeof(req_buf);
+    
+    /* 准备响应缓冲区 */
+    memset(rsp_buf, 0, sizeof(rsp_buf));
+    client_data.responseBuf = rsp_buf;
+    client_data.responseBufLen = sizeof(rsp_buf);
+    
+    /* 设置为HTTPS */
+    client.isHttp = false;  /* false表示HTTPS */
+    
+    /* 发送GET请求 */
+    ret = ZhihuiHttpClientGetRequest(&client, url, &client_data);
+    
+    if (ret >= 0 && client.responseCode == 200) {
+        printf("✓ Request successful\n");
+        printf("Status code: %d\n", client.responseCode);
+        printf("Response length: %d\n", client_data.responseContentLen);
+        return 0;
+    } else {
+        printf("✗ Request failed: %d\n", ret);
+        printf("Status code: %d\n", client.responseCode);
+        return -1;
+    }
+}
+
+/**
+ * @brief 测试两种实现
+ */
+static void test_both_implementations(void)
+{
+    printf("\n╔════════════════════════════════════════╗\n");
+    printf("║  Testing OpenHarmony Implementation   ║\n");
+    printf("╚════════════════════════════════════════╝\n");
+    
+    ZhihuiNetSetImplementation(ZHIHUI_NET_IMPL_OPENHARMONY);
+    ZhihuiNetInit();
+    
+    http_get_example();
+    
+    ZhihuiNetDeinit();
+    
+    printf("\n╔════════════════════════════════════════╗\n");
+    printf("║  Testing N706A PPP Implementation     ║\n");
+    printf("╚════════════════════════════════════════╝\n");
+    
+    ZhihuiNetSetImplementation(ZHIHUI_NET_IMPL_N706A_PPP);
+    ZhihuiNetInit();
+    
+    http_get_example();
+    
+    ZhihuiNetDeinit();
+}
+
+/**
+ * @brief 主函数
+ */
+int main(void)
+{
+    printf("╔════════════════════════════════════════╗\n");
+    printf("║  ZhihuiNet HTTP Client Examples       ║\n");
+    printf("╚════════════════════════════════════════╝\n");
+    
+    /* 初始化(使用默认实现:OpenHarmony) */
+    ZhihuiNetInit();
+    
+    /* 运行示例 */
+    http_get_example();
+    http_post_example();
+    https_get_example();
+    
+    /* 清理 */
+    ZhihuiNetDeinit();
+    
+    /* 测试两种实现 */
+    test_both_implementations();
+    
+    printf("\n✓ All examples completed\n");
+    
+    return 0;
+}
+
diff --git a/code/sdk/zhihui/zhihui_net/example/zhihui_net_ws_example.c b/code/sdk/zhihui/zhihui_net/example/zhihui_net_ws_example.c
new file mode 100644 (file)
index 0000000..a7d1b5f
--- /dev/null
@@ -0,0 +1,284 @@
+/**
+ * @file zhihui_net_ws_example.c
+ * @brief ZhihuiNet WebSocket客户端使用示例
+ */
+
+#include "zhihui_net.h"
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/* WebSocket客户端状态 */
+static int g_ws_client_fd = -1;
+static volatile bool g_ws_connected = false;
+static volatile bool g_ws_received = false;
+
+/**
+ * @brief WebSocket连接打开回调
+ */
+static void on_open(int client)
+{
+    printf("[WS] Connection opened (client: %d)\n", client);
+    g_ws_connected = true;
+    
+    /* 连接成功后发送消息 */
+    const char *message = "Hello WebSocket Server!";
+    printf("[WS] Sending: %s\n", message);
+    
+    int ret = ZhihuiWebSocketClientSendText(client, message, strlen(message));
+    if (ret != 0) {
+        printf("[WS] Failed to send message: %d\n", ret);
+    }
+}
+
+/**
+ * @brief WebSocket消息接收回调
+ */
+static void on_message(int client, const char *data, size_t length,
+                       bool isFinished, bool isBinary)
+{
+    printf("[WS] Message received (client: %d, length: %zu, binary: %d)\n",
+           client, length, isBinary);
+    
+    if (isBinary) {
+        printf("[WS] Binary data: ");
+        for (size_t i = 0; i < length && i < 32; i++) {
+            printf("%02X ", (unsigned char)data[i]);
+        }
+        printf("\n");
+    } else {
+        printf("[WS] Text data: %.*s\n", (int)length, data);
+    }
+    
+    g_ws_received = true;
+}
+
+/**
+ * @brief WebSocket错误回调
+ */
+static void on_error(int client, int errorCode, const char *errorMsg)
+{
+    printf("[WS] Error (client: %d, code: %d): %s\n",
+           client, errorCode, errorMsg ? errorMsg : "Unknown error");
+}
+
+/**
+ * @brief WebSocket连接关闭回调
+ */
+static void on_close(int client, int closeCode, const char *reason)
+{
+    printf("[WS] Connection closed (client: %d, code: %d): %s\n",
+           client, closeCode, reason ? reason : "");
+    g_ws_connected = false;
+}
+
+/**
+ * @brief WebSocket基本示例
+ */
+static int websocket_basic_example(void)
+{
+    printf("\n=== WebSocket Basic Example ===\n");
+    
+    /* 创建WebSocket客户端 */
+    int client = ZhihuiWebSocketClientConstructor(
+        on_open,
+        on_message,
+        on_error,
+        on_close
+    );
+    
+    if (client < 0) {
+        printf("✗ Failed to create WebSocket client\n");
+        return -1;
+    }
+    
+    printf("✓ WebSocket client created (id: %d)\n", client);
+    g_ws_client_fd = client;
+    
+    /* 连接到echo服务器 */
+    const char *url = "ws://echo.websocket.org/";
+    printf("Connecting to: %s\n", url);
+    
+    int ret = ZhihuiWebSocketClientConnect(client, url, NULL);
+    if (ret != 0) {
+        printf("✗ Failed to connect: %d\n", ret);
+        ZhihuiWebSocketClientDestructor(client);
+        return -1;
+    }
+    
+    /* 等待连接和消息 */
+    printf("Waiting for connection and messages...\n");
+    int timeout = 30;  /* 30秒超时 */
+    while (timeout > 0 && (!g_ws_connected || !g_ws_received)) {
+        sleep(1);
+        timeout--;
+    }
+    
+    if (g_ws_connected && g_ws_received) {
+        printf("✓ WebSocket communication successful\n");
+    } else {
+        printf("✗ Timeout waiting for WebSocket communication\n");
+    }
+    
+    /* 关闭连接 */
+    printf("Closing WebSocket connection...\n");
+    ZhihuiWebSocketClientClose(client);
+    sleep(1);
+    
+    /* 销毁客户端 */
+    ZhihuiWebSocketClientDestructor(client);
+    g_ws_client_fd = -1;
+    
+    return 0;
+}
+
+/**
+ * @brief WebSocket二进制数据示例
+ */
+static int websocket_binary_example(void)
+{
+    printf("\n=== WebSocket Binary Example ===\n");
+    
+    g_ws_connected = false;
+    g_ws_received = false;
+    
+    /* 创建WebSocket客户端 */
+    int client = ZhihuiWebSocketClientConstructor(
+        on_open,
+        on_message,
+        on_error,
+        on_close
+    );
+    
+    if (client < 0) {
+        printf("✗ Failed to create WebSocket client\n");
+        return -1;
+    }
+    
+    g_ws_client_fd = client;
+    
+    /* 连接 */
+    const char *url = "ws://echo.websocket.org/";
+    int ret = ZhihuiWebSocketClientConnect(client, url, NULL);
+    if (ret != 0) {
+        printf("✗ Failed to connect: %d\n", ret);
+        ZhihuiWebSocketClientDestructor(client);
+        return -1;
+    }
+    
+    /* 等待连接 */
+    int timeout = 10;
+    while (timeout > 0 && !g_ws_connected) {
+        sleep(1);
+        timeout--;
+    }
+    
+    if (!g_ws_connected) {
+        printf("✗ Connection timeout\n");
+        ZhihuiWebSocketClientDestructor(client);
+        return -1;
+    }
+    
+    /* 发送二进制数据 */
+    uint8_t binary_data[] = {0x01, 0x02, 0x03, 0x04, 0x05};
+    printf("Sending binary data...\n");
+    
+    ret = ZhihuiWebSocketClientSendBinStart(client, (const char *)binary_data,
+                                            sizeof(binary_data));
+    if (ret != 0) {
+        printf("✗ Failed to send binary data: %d\n", ret);
+    }
+    
+    /* 等待响应 */
+    timeout = 10;
+    while (timeout > 0 && !g_ws_received) {
+        sleep(1);
+        timeout--;
+    }
+    
+    /* 清理 */
+    ZhihuiWebSocketClientClose(client);
+    ZhihuiWebSocketClientDestructor(client);
+    
+    return 0;
+}
+
+/**
+ * @brief WSS (WebSocket Secure)示例
+ */
+static int websocket_secure_example(void)
+{
+    printf("\n=== WebSocket Secure (WSS) Example ===\n");
+    
+    g_ws_connected = false;
+    g_ws_received = false;
+    
+    /* 创建WebSocket客户端 */
+    int client = ZhihuiWebSocketClientConstructor(
+        on_open,
+        on_message,
+        on_error,
+        on_close
+    );
+    
+    if (client < 0) {
+        printf("✗ Failed to create WebSocket client\n");
+        return -1;
+    }
+    
+    g_ws_client_fd = client;
+    
+    /* 连接到WSS服务器 */
+    const char *url = "wss://echo.websocket.org/";
+    printf("Connecting to: %s\n", url);
+    
+    int ret = ZhihuiWebSocketClientConnect(client, url, NULL);
+    if (ret != 0) {
+        printf("✗ Failed to connect: %d\n", ret);
+        ZhihuiWebSocketClientDestructor(client);
+        return -1;
+    }
+    
+    /* 等待连接和消息 */
+    int timeout = 30;
+    while (timeout > 0 && (!g_ws_connected || !g_ws_received)) {
+        sleep(1);
+        timeout--;
+    }
+    
+    if (g_ws_connected && g_ws_received) {
+        printf("✓ WSS communication successful\n");
+    }
+    
+    /* 清理 */
+    ZhihuiWebSocketClientClose(client);
+    ZhihuiWebSocketClientDestructor(client);
+    
+    return 0;
+}
+
+/**
+ * @brief 主函数
+ */
+int main(void)
+{
+    printf("╔════════════════════════════════════════╗\n");
+    printf("║  ZhihuiNet WebSocket Client Examples  ║\n");
+    printf("╚════════════════════════════════════════╝\n");
+    
+    /* 初始化 */
+    ZhihuiNetInit();
+    
+    /* 运行示例 */
+    websocket_basic_example();
+    websocket_binary_example();
+    websocket_secure_example();
+    
+    /* 清理 */
+    ZhihuiNetDeinit();
+    
+    printf("\n✓ All WebSocket examples completed\n");
+    
+    return 0;
+}
+
diff --git a/code/sdk/zhihui/zhihui_net/include/zhihui_net.h b/code/sdk/zhihui/zhihui_net/include/zhihui_net.h
new file mode 100644 (file)
index 0000000..c31f871
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * @file zhihui_net.h
+ * @brief 智慧网络统一接口层 - 总入口
+ * 
+ * 设计原则:
+ * 1. 拆分 HTTP 和 WebSocket 接口到独立的头文件
+ * 2. HTTP 接口:基于 OpenHarmony 的 httpclient.h
+ * 3. WebSocket 接口:基于 haisi_websocket.h
+ * 4. 支持运行时动态切换实现(OpenHarmony/haisi_websocket vs PPP)
+ * 
+ * @version 4.0.0
+ * @date 2025-10-31
+ */
+
+#ifndef ZHIHUI_NET_H
+#define ZHIHUI_NET_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* 包含 HTTP 和 WebSocket 子模块 */
+#include "zhihui_net_http.h"
+#include "zhihui_net_websocket.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ========== 网络实现类型 ========== */
+
+typedef enum {
+    ZHIHUI_NET_IMPL_OPENHARMONY = 0,  /**< OpenHarmony实现(PPP拨号 + lwIP + haisi_websocket)默认 */
+    ZHIHUI_NET_IMPL_N706A_AT = 1      /**< N706A AT指令实现(不拨号,纯AT指令)*/
+} zhihui_net_impl_type_t;
+
+/* 为了兼容性保留旧名称 */
+#define ZHIHUI_NET_IMPL_N706A_PPP ZHIHUI_NET_IMPL_OPENHARMONY
+
+/* ========== 配置接口 ========== */
+
+/**
+ * @brief 设置网络实现类型
+ * @param type 实现类型
+ * @return 0成功,非0失败
+ */
+int ZhihuiNetSetImplementation(zhihui_net_impl_type_t type);
+
+/**
+ * @brief 获取当前网络实现类型
+ * @return 实现类型
+ */
+zhihui_net_impl_type_t ZhihuiNetGetImplementation(void);
+
+/**
+ * @brief 初始化网络模块
+ * @return 0成功,非0失败
+ */
+int ZhihuiNetInit(void);
+
+/**
+ * @brief 反初始化网络模块
+ * @return 0成功,非0失败
+ */
+int ZhihuiNetDeinit(void);
+
+/*
+ * 注意:
+ * - HTTP 接口定义在 zhihui_net_http.h
+ * - WebSocket 接口定义在 zhihui_net_websocket.h
+ * - 包含 zhihui_net.h 即可使用所有接口
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZHIHUI_NET_H */
diff --git a/code/sdk/zhihui/zhihui_net/include/zhihui_net_http.h b/code/sdk/zhihui/zhihui_net/include/zhihui_net_http.h
new file mode 100644 (file)
index 0000000..3160789
--- /dev/null
@@ -0,0 +1,95 @@
+/**
+ * @file zhihui_net_http.h
+ * @brief 智慧网络统一接口层 - HTTP/HTTPS 接口
+ * 
+ * 提供统一的 HTTP/HTTPS 客户端接口,支持动态切换 OpenHarmony 和 PPP 实现
+ * 
+ * @version 1.0.0
+ * @date 2025-10-31
+ */
+
+#ifndef ZHIHUI_NET_HTTP_H
+#define ZHIHUI_NET_HTTP_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* 直接复用 OpenHarmony 的 HTTP 类型定义 */
+#include "httpclient.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ========== HTTP 客户端接口 ========== */
+
+/**
+ * @brief 准备HTTP客户端数据缓冲区
+ * @param clientData 客户端数据
+ * @param headerBufLen 头部缓冲区大小
+ * @param responseBufLen 响应缓冲区大小
+ * @return HTTP_SUCCESS成功,其他失败
+ */
+int ZhihuiHttpClientPrepare(HttpClientData *clientData, int headerBufLen, int responseBufLen);
+
+/**
+ * @brief 释放HTTP客户端数据缓冲区
+ * @param clientData 客户端数据
+ */
+void ZhihuiHttpClientUnprepare(HttpClientData *clientData);
+
+/**
+ * @brief 连接到HTTP服务器
+ * @param client HTTP客户端
+ * @param url 服务器URL
+ * @return 0成功,非0失败
+ */
+int ZhihuiHttpClientConn(HttpClient *client, const char *url);
+
+/**
+ * @brief 发送HTTP请求
+ * @param client HTTP客户端
+ * @param url 请求URL
+ * @param method HTTP方法 (HTTP_GET, HTTP_POST等)
+ * @param clientData 请求数据
+ * @return 0成功,非0失败
+ */
+int ZhihuiHttpClientSend(HttpClient *client, const char *url, int method, HttpClientData *clientData);
+
+/**
+ * @brief 接收HTTP响应
+ * @param client HTTP客户端
+ * @param clientData 响应数据
+ * @return HTTP_SUCCESS成功,HTTP_EAGAIN需继续接收,其他失败
+ */
+int ZhihuiHttpClientRecvResponse(HttpClient *client, HttpClientData *clientData);
+
+/**
+ * @brief 关闭HTTP连接
+ * @param client HTTP客户端
+ */
+void ZhihuiHttpClientClse(HttpClient *client);
+
+/**
+ * @brief 设置自定义HTTP头部
+ * @param client HTTP客户端
+ * @param header 自定义头部字符串
+ */
+void ZhihuiHttpClientSetCustomHeader(HttpClient *client, char *header);
+
+/**
+ * @brief 执行HTTP GET请求(便捷函数)
+ * @param client HTTP客户端
+ * @param url 请求URL
+ * @param clientData 响应数据
+ * @return HTTP_SUCCESS成功,其他失败
+ */
+int ZhihuiHttpClientGetRequest(HttpClient *client, const char *url, HttpClientData *clientData);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZHIHUI_NET_HTTP_H */
+
diff --git a/code/sdk/zhihui/zhihui_net/include/zhihui_net_websocket.h b/code/sdk/zhihui/zhihui_net/include/zhihui_net_websocket.h
new file mode 100644 (file)
index 0000000..6eed03f
--- /dev/null
@@ -0,0 +1,160 @@
+/**
+ * @file zhihui_net_websocket.h
+ * @brief 智慧网络统一接口层 - WebSocket 接口
+ * 
+ * 提供统一的 WebSocket 客户端接口,支持动态切换 haisi_websocket 和 PPP 实现
+ * 接口设计完全遵循 haisi_websocket.h 的定义
+ * 
+ * @version 1.0.0
+ * @date 2025-10-31
+ */
+
+#ifndef ZHIHUI_NET_WEBSOCKET_H
+#define ZHIHUI_NET_WEBSOCKET_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* 直接复用 haisi_websocket 的类型定义 */
+#include "haisi_websocket.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ========== WebSocket 客户端接口(与 haisi_websocket.h 完全一致)========== */
+
+/**
+ * @brief 启动 WebSocket 服务
+ * @param callbacks 回调函数结构体
+ * @return 0成功,非0失败
+ */
+int ZhihuiWsStart(const ws_call_backs_t *callbacks);
+
+/**
+ * @brief 发送文本消息(null-terminated 字符串)
+ * @param text 文本字符串
+ * @return 0成功,非0失败
+ */
+int ZhihuiWsSendText(const char *text);
+
+/**
+ * @brief 停止 WebSocket 服务
+ * @return 0成功,非0失败
+ */
+int ZhihuiWsStop(void);
+
+/**
+ * @brief 异步停止 WebSocket 服务
+ * @return 0成功,非0失败
+ */
+int ZhihuiWsStopAsync(void);
+
+/**
+ * @brief 强制重连
+ * @return 0成功,非0失败
+ */
+int ZhihuiWsForceReconnect(void);
+
+/**
+ * @brief 进入睡眠模式
+ * @return 0成功,非0失败
+ */
+int ZhihuiWsEnterSleep(void);
+
+/**
+ * @brief 退出睡眠模式
+ * @return 0成功,非0失败
+ */
+int ZhihuiWsExitSleep(void);
+
+/**
+ * @brief 发送二进制消息
+ * @param data 数据
+ * @param len 数据长度
+ * @return 0成功,非0失败
+ */
+int ZhihuiWsSendBIN(uint8_t *data, int len);
+
+/* ========== WebSocket 状态查询接口 ========== */
+
+/**
+ * @brief 获取连接状态
+ * @return 连接状态值
+ */
+int ZhihuiWsGetConnectionState(void);
+
+/**
+ * @brief 获取连接状态字符串
+ * @return 状态字符串
+ */
+const char* ZhihuiWsGetConnectionStateString(void);
+
+/**
+ * @brief 获取关闭状态
+ * @return 关闭状态值
+ */
+int ZhihuiWsGetCloseStatusPublic(void);
+
+/**
+ * @brief 打印状态信息
+ */
+void ZhihuiWsPrintStatusInfo(void);
+
+/**
+ * @brief 获取主线程循环间隔
+ * @return 间隔时长(毫秒)
+ */
+uint32_t ZhihuiWsGetMainThreadInterval(void);
+
+/* ========== 回调函数注册接口 ========== */
+
+/**
+ * @brief 注册文本消息回调
+ * @param callback 回调函数
+ */
+void ZhihuiWsRegisterMessageCallback(ws_message_callback_t callback);
+
+/**
+ * @brief 注册二进制消息回调
+ * @param callback 回调函数
+ */
+void ZhihuiWsRegisterBinaryCallback(ws_binary_message_callback_t callback);
+
+/**
+ * @brief 注册不完整消息回调
+ * @param callback 回调函数
+ */
+void ZhihuiWsRegisterIncompleteCallback(ws_incomplete_message_callback_t callback);
+
+/**
+ * @brief 注册 URL 构建回调
+ * @param callback 回调函数
+ */
+void ZhihuiWsRegisterUrlCallback(ws_url_callback_t callback);
+
+/**
+ * @brief 注册心跳回调
+ * @param callback 回调函数
+ */
+void ZhihuiWsRegisterHeartbeatCallback(ws_heartbeat_callback_t callback);
+
+/**
+ * @brief 注册连接事件回调
+ * @param callback 回调函数
+ */
+void ZhihuiWsRegisterEventCallback(ws_event_callback_t callback);
+
+/**
+ * @brief 注册重连成功回调
+ * @param callback 回调函数
+ */
+void ZhihuiWsRegisterReconnectSuccessCallback(ws_reconnect_success_callback_t callback);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZHIHUI_NET_WEBSOCKET_H */
+
diff --git a/code/sdk/zhihui/zhihui_net/src/zhihui_net.c b/code/sdk/zhihui/zhihui_net/src/zhihui_net.c
new file mode 100644 (file)
index 0000000..4d973a9
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * @file zhihui_net.c
+ * @brief 智慧网络统一接口层 - 配置实现
+ * 
+ * 负责管理网络实现类型的配置和切换
+ */
+
+#include "zhihui_net.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* 当前使用的实现类型 */
+static zhihui_net_impl_type_t g_current_impl = ZHIHUI_NET_IMPL_OPENHARMONY;
+
+/* ========== 配置接口实现 ========== */
+
+int ZhihuiNetSetImplementation(zhihui_net_impl_type_t type)
+{
+    if (type != ZHIHUI_NET_IMPL_OPENHARMONY && type != ZHIHUI_NET_IMPL_N706A_AT) {
+        return -1;
+    }
+    
+    g_current_impl = type;
+    printf("[ZhihuiNet] Switched to %s implementation\n",
+           (type == ZHIHUI_NET_IMPL_OPENHARMONY) ? "OpenHarmony (PPP + lwIP)" : "N706A AT Command");
+    return 0;
+}
+
+zhihui_net_impl_type_t ZhihuiNetGetImplementation(void)
+{
+    return g_current_impl;
+}
+
+int ZhihuiNetInit(void)
+{
+    /* 初始化网络模块 */
+    printf("[ZhihuiNet] Initialized with %s implementation\n",
+           (g_current_impl == ZHIHUI_NET_IMPL_OPENHARMONY) ? "OpenHarmony" : "PPP");
+    return 0;
+}
+
+int ZhihuiNetDeinit(void)
+{
+    /* 反初始化网络模块 */
+    printf("[ZhihuiNet] Deinitialized\n");
+    return 0;
+}
diff --git a/code/sdk/zhihui/zhihui_net/src/zhihui_net_http.c b/code/sdk/zhihui/zhihui_net/src/zhihui_net_http.c
new file mode 100644 (file)
index 0000000..cab9034
--- /dev/null
@@ -0,0 +1,276 @@
+/**
+ * @file zhihui_net_http.c
+ * @brief 智慧网络统一接口层 - HTTP/HTTPS 实现
+ * 
+ * 支持动态切换 OpenHarmony 和 PPP 实现
+ */
+
+#include "zhihui_net_http.h"
+#include "zhihui_net.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* 包含 PPP 网络层的头文件 */
+#include "n706a_http_client.h"
+
+/* 外部函数:获取当前实现类型 */
+extern zhihui_net_impl_type_t ZhihuiNetGetImplementation(void);
+
+/* ========== HTTP 客户端接口实现 ========== */
+
+int ZhihuiHttpClientPrepare(HttpClientData *clientData, int headerBufLen, int responseBufLen)
+{
+    if (!clientData) {
+        return HTTP_ENOBUFS;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 OpenHarmony 的实现 */
+        return HttpClientPrepare(clientData, headerBufLen, responseBufLen);
+    } else {
+        /* PPP 实现:手动分配缓冲区 */
+        clientData->headerBuf = (char *)malloc(headerBufLen);
+        if (!clientData->headerBuf) {
+            return HTTP_ENOBUFS;
+        }
+        clientData->headerBufLen = headerBufLen;
+        memset(clientData->headerBuf, 0, headerBufLen);
+        
+        clientData->responseBuf = (char *)malloc(responseBufLen);
+        if (!clientData->responseBuf) {
+            free(clientData->headerBuf);
+            clientData->headerBuf = NULL;
+            return HTTP_ENOBUFS;
+        }
+        clientData->responseBufLen = responseBufLen;
+        memset(clientData->responseBuf, 0, responseBufLen);
+        
+        /* 初始化其他字段 */
+        clientData->isMore = false;
+        clientData->isChunked = false;
+        clientData->method = 0;
+        clientData->retrieveLen = 0;
+        clientData->responseContentLen = 0;
+        clientData->contentBlockLen = 0;
+        
+        return HTTP_SUCCESS;
+    }
+}
+
+void ZhihuiHttpClientUnprepare(HttpClientData *clientData)
+{
+    if (!clientData) {
+        return;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 OpenHarmony 的实现 */
+        HttpClientUnprepare(clientData);
+    } else {
+        /* PPP 实现:手动释放缓冲区 */
+        if (clientData->headerBuf) {
+            free(clientData->headerBuf);
+            clientData->headerBuf = NULL;
+        }
+        
+        if (clientData->responseBuf) {
+            free(clientData->responseBuf);
+            clientData->responseBuf = NULL;
+        }
+        
+        memset(clientData, 0, sizeof(HttpClientData));
+    }
+}
+
+int ZhihuiHttpClientConn(HttpClient *client, const char *url)
+{
+    if (!client || !url) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 OpenHarmony 的实现 */
+        return HttpClientConn(client, url);
+    } else {
+        /* PPP 实现:简单初始化 */
+        memset(client, 0, sizeof(HttpClient));
+        client->socket = -1;
+        client->isHttp = (strncmp(url, "https://", 8) != 0);
+        return 0;
+    }
+}
+
+int ZhihuiHttpClientSend(HttpClient *client, const char *url, int method, HttpClientData *clientData)
+{
+    if (!client || !url || !clientData) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 OpenHarmony 的实现 */
+        return HttpClientSend(client, url, method, clientData);
+    } else {
+        /* PPP 实现:调用 n706a_http_client */
+        
+        /* 解析 URL */
+        char host[256] = {0};
+        char path[256] = {0};
+        uint16_t port = client->isHttp ? 80 : 443;
+        bool use_ssl = !client->isHttp;
+        
+        const char *start = url;
+        if (strncmp(url, "https://", 8) == 0) {
+            start = url + 8;
+            use_ssl = true;
+            port = 443;
+        } else if (strncmp(url, "http://", 7) == 0) {
+            start = url + 7;
+            use_ssl = false;
+            port = 80;
+        }
+        
+        sscanf(start, "%255[^/]%255s", host, path);
+        if (strlen(path) == 0) {
+            strcpy(path, "/");
+        }
+        
+        /* 创建 PPP HTTP 客户端 */
+        n706a_http_client_t *ppp_client = (n706a_http_client_t *)malloc(sizeof(n706a_http_client_t));
+        if (!ppp_client) {
+            return -1;
+        }
+        
+        n706a_httpc_result_t ret = n706a_http_client_init(ppp_client, 0);
+        if (ret != N706A_HTTP_SUCCESS) {
+            free(ppp_client);
+            return -1;
+        }
+        
+        /* 连接 */
+        ret = n706a_http_client_connect(ppp_client, host, port, use_ssl);
+        if (ret != N706A_HTTP_SUCCESS) {
+            free(ppp_client);
+            return -1;
+        }
+        
+        /* 准备数据结构 */
+        n706a_http_client_data_t ppp_data = {0};
+        ppp_data.responseBuf = clientData->responseBuf;
+        ppp_data.responseBufLen = clientData->responseBufLen;
+        ppp_data.headerBuf = clientData->headerBuf;
+        ppp_data.headerBufLen = clientData->headerBufLen;
+        
+        /* 根据方法执行请求 */
+        switch (method) {
+            case HTTP_GET:
+                ret = n706a_http_client_get(ppp_client, path, &ppp_data);
+                break;
+            case HTTP_POST:
+                if (clientData->postContentType && clientData->postBuf) {
+                    ret = n706a_http_client_post(ppp_client, path, 
+                                                      clientData->postContentType,
+                                                      clientData->postBuf, 
+                                                      clientData->postBufLen, 
+                                                      &ppp_data);
+                } else {
+                    ret = N706A_HTTP_EARG;
+                }
+                break;
+            case HTTP_PUT:
+                if (clientData->postContentType && clientData->postBuf) {
+                    ret = n706a_http_client_put(ppp_client, path,
+                                                     clientData->postContentType,
+                                                     clientData->postBuf,
+                                                     clientData->postBufLen,
+                                                     &ppp_data);
+                } else {
+                    ret = N706A_HTTP_EARG;
+                }
+                break;
+            case HTTP_DELETE:
+                ret = n706a_http_client_delete(ppp_client, path, &ppp_data);
+                break;
+            default:
+                ret = N706A_HTTP_EARG;
+                break;
+        }
+        
+        /* 设置响应码 */
+        client->responseCode = ppp_client->response_code;
+        clientData->responseContentLen = ppp_data.responseContentLen;
+        clientData->contentBlockLen = ppp_data.responseContentLen;
+        clientData->isMore = false;
+        
+        /* 清理 PPP 客户端 */
+        n706a_http_client_close(ppp_client);
+        free(ppp_client);
+        
+        return (ret == N706A_HTTP_SUCCESS) ? 0 : -1;
+    }
+}
+
+int ZhihuiHttpClientRecvResponse(HttpClient *client, HttpClientData *clientData)
+{
+    if (!client || !clientData) {
+        return HTTP_ERECV;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 OpenHarmony 的实现 */
+        return HttpClientRecvResponse(client, clientData);
+    } else {
+        /* PPP 实现:在 Send 中已经接收完毕 */
+        if (clientData->isMore) {
+            return HTTP_EAGAIN;
+        }
+        return HTTP_SUCCESS;
+    }
+}
+
+void ZhihuiHttpClientClse(HttpClient *client)
+{
+    if (!client) {
+        return;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 OpenHarmony 的实现 */
+        HttpClientClse(client);
+    } else {
+        /* PPP 实现:在 Send 中已经清理 */
+        memset(client, 0, sizeof(HttpClient));
+    }
+}
+
+void ZhihuiHttpClientSetCustomHeader(HttpClient *client, char *header)
+{
+    if (!client) {
+        return;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 OpenHarmony 的实现 */
+        HttpClientSetCustomHeader(client, header);
+    } else {
+        /* PPP 实现:暂不支持自定义头部 */
+        printf("[ZhihuiNet][PPP][HTTP] WARNING: Custom headers not yet supported in PPP mode\n");
+        // TODO: 在 N706A HTTP 实现中添加自定义头部支持
+    }
+}
+
+int ZhihuiHttpClientGetRequest(HttpClient *client, const char *url, HttpClientData *clientData)
+{
+    if (!client || !url || !clientData) {
+        return -1;
+    }
+    
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 OpenHarmony 的实现 */
+        return HttpClientGetRequest(client, url, clientData);
+    } else {
+        /* PPP 实现:使用 ZhihuiHttpClientSend */
+        return ZhihuiHttpClientSend(client, url, HTTP_GET, clientData);
+    }
+}
+
diff --git a/code/sdk/zhihui/zhihui_net/src/zhihui_net_websocket.c b/code/sdk/zhihui/zhihui_net/src/zhihui_net_websocket.c
new file mode 100644 (file)
index 0000000..174c11c
--- /dev/null
@@ -0,0 +1,559 @@
+/**
+ * @file zhihui_net_websocket.c
+ * @brief 智慧网络统一接口层 - WebSocket 实现
+ * 
+ * 设计说明:
+ * 1. OpenHarmony 模式:调用 haisi_websocket (基于 net_websocket 的封装层)
+ * 2. N706A 模式:使用 n706a_websocket (实现 net_websocket 接口的 N706A 版本)
+ * 3. 提供适配器层桥接 haisi_websocket 的回调驱动模型
+ * 
+ * @version 5.0.0
+ * @date 2025-10-31
+ */
+
+#include "zhihui_net_websocket.h"
+#include "zhihui_net.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "soc_osal.h"
+
+/* 包含 N706A 网络层的头文件 */
+#include "n706a_websocket.h"
+
+/* 外部函数:获取当前实现类型 */
+extern zhihui_net_impl_type_t ZhihuiNetGetImplementation(void);
+
+/* ========== N706A WebSocket 适配器层 ========== */
+
+/* 适配器上下文 */
+typedef struct {
+    bool initialized;
+    bool running;
+    int client_id;
+    osal_task *worker_thread;
+    
+    /* 保存的回调 */
+    ws_call_backs_t callbacks;
+    
+    /* 线程间通信 */
+    bool stop_requested;
+    bool reconnect_requested;
+    bool sleep_mode;
+    
+} n706a_ws_adapter_t;
+
+static n706a_ws_adapter_t g_adapter = {0};
+
+/* ========== N706A WebSocket 回调适配函数 ========== */
+
+/**
+ * @brief N706A onOpen回调 -> haisi_websocket event回调
+ */
+static void n706a_on_open_callback(int client_id)
+{
+    printf("[ZhihuiNet][N706A][WS] Connection opened (client_id=%d)\n", client_id);
+    
+    if (g_adapter.callbacks.event_callback) {
+        g_adapter.callbacks.event_callback(WS_EVENT_CONNECTED);
+    }
+    
+    if (g_adapter.callbacks.reconnect_success_callback) {
+        g_adapter.callbacks.reconnect_success_callback();
+    }
+}
+
+/**
+ * @brief N706A onMessage回调 -> haisi_websocket message回调
+ */
+static void n706a_on_message_callback(int client_id, const char *data, size_t length, 
+                                      bool is_finished, bool is_binary)
+{
+    if (!data || length == 0) {
+        return;
+    }
+    
+    if (is_binary) {
+        /* 二进制消息 */
+        if (g_adapter.callbacks.binary_callback) {
+            g_adapter.callbacks.binary_callback((const uint8_t *)data, 0, (uint32_t)length);
+        }
+    } else {
+        /* 文本消息 */
+        if (is_finished) {
+            /* 完整消息 */
+            if (g_adapter.callbacks.message_callback) {
+                g_adapter.callbacks.message_callback(data, 0);
+            }
+        } else {
+            /* 不完整消息(分片) */
+            if (g_adapter.callbacks.incomplete_callback) {
+                g_adapter.callbacks.incomplete_callback(data, length, is_binary);
+            }
+        }
+    }
+}
+
+/**
+ * @brief N706A onError回调 -> haisi_websocket event回调
+ */
+static void n706a_on_error_callback(int client_id, int error_code, const char *error_msg)
+{
+    printf("[ZhihuiNet][N706A][WS] Error occurred: code=%d, msg=%s\n", 
+           error_code, error_msg ? error_msg : "unknown");
+    
+    if (g_adapter.callbacks.event_callback) {
+        g_adapter.callbacks.event_callback(WS_EVENT_DISCONNECTED);
+    }
+}
+
+/**
+ * @brief N706A onClose回调 -> haisi_websocket event回调
+ */
+static void n706a_on_close_callback(int client_id, int close_code, const char *reason)
+{
+    printf("[ZhihuiNet][N706A][WS] Connection closed: code=%d, reason=%s\n", 
+           close_code, reason ? reason : "unknown");
+    
+    if (g_adapter.callbacks.event_callback) {
+        g_adapter.callbacks.event_callback(WS_EVENT_DISCONNECTED);
+    }
+}
+
+/**
+ * @brief WebSocket worker线程主函数
+ */
+static void* ws_worker_thread(void *arg)
+{
+    printf("[ZhihuiNet][N706A][WS] Worker thread started\n");
+    
+    /* 获取URL */
+    char url[512] = {0};
+    char header[1024] = {0};
+    
+    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");
+        return NULL;
+    }
+    
+    printf("[ZhihuiNet][N706A][WS] Connecting to: %s\n", url);
+    
+    /* 创建WebSocket客户端 */
+    g_adapter.client_id = n706a_websocket_constructor(
+        n706a_on_open_callback,
+        n706a_on_message_callback,
+        n706a_on_error_callback,
+        n706a_on_close_callback
+    );
+    
+    if (g_adapter.client_id < 0) {
+        printf("[ZhihuiNet][N706A][WS] ERROR: Failed to create WebSocket client\n");
+        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秒
+    }
+    
+    while (g_adapter.running && !g_adapter.stop_requested) {
+        /* 处理心跳 */
+        if (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);
+            }
+        }
+        
+        /* 处理重连请求 */
+        if (g_adapter.reconnect_requested) {
+            printf("[ZhihuiNet][N706A][WS] Reconnecting...\n");
+            n706a_websocket_close(g_adapter.client_id);
+            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;
+        }
+        
+        /* 休眠模式处理 */
+        if (g_adapter.sleep_mode) {
+            // N706A暂不支持suspend/resume,休眠模式下停止发送心跳即可
+        }
+        
+        /* 等待下一次循环 */
+        osal_msleep(interval);
+    }
+    
+    /* 清理 */
+    printf("[ZhihuiNet][N706A][WS] Worker thread exiting\n");
+    n706a_websocket_close(g_adapter.client_id);
+    g_adapter.running = false;
+    
+    return NULL;
+}
+
+/* ========== WebSocket 客户端接口实现 ========== */
+
+int ZhihuiWsStart(const ws_call_backs_t *callbacks)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_start(callbacks);
+    } else {
+        /* N706A 实现 */
+        if (!callbacks || !callbacks->url_callback) {
+            printf("[ZhihuiNet][N706A][WS] ERROR: Invalid callbacks (url_callback required)\n");
+            return -1;
+        }
+        
+        if (g_adapter.initialized) {
+            printf("[ZhihuiNet][N706A][WS] WARNING: Already started\n");
+            return 0;
+        }
+        
+        /* 初始化适配器 */
+        memset(&g_adapter, 0, sizeof(g_adapter));
+        memcpy(&g_adapter.callbacks, callbacks, sizeof(ws_call_backs_t));
+        g_adapter.initialized = true;
+        g_adapter.running = true;
+        g_adapter.stop_requested = false;
+        g_adapter.reconnect_requested = false;
+        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();
+        
+        if (!g_adapter.worker_thread) {
+            printf("[ZhihuiNet][N706A][WS] ERROR: Failed to create worker thread\n");
+            g_adapter.initialized = false;
+            g_adapter.running = false;
+            return -1;
+        }
+        
+        printf("[ZhihuiNet][N706A][WS] Service started successfully\n");
+        return 0;
+    }
+}
+
+int ZhihuiWsStop(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_stop();
+    } else {
+        /* N706A 实现 */
+        if (!g_adapter.initialized) {
+            return 0;
+        }
+        
+        printf("[ZhihuiNet][N706A][WS] Stopping service...\n");
+        
+        g_adapter.stop_requested = true;
+        g_adapter.running = false;
+        
+        /* 等待线程退出 */
+        if (g_adapter.worker_thread) {
+            // 等待最多5秒
+            for (int i = 0; i < 50 && g_adapter.running; i++) {
+                osal_msleep(100);
+            }
+        }
+        
+        g_adapter.initialized = false;
+        printf("[ZhihuiNet][N706A][WS] Service stopped\n");
+        return 0;
+    }
+}
+
+int ZhihuiWsStopAsync(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_stop_async();
+    } else {
+        /* N706A 实现 */
+        if (!g_adapter.initialized) {
+            return 0;
+        }
+        
+        printf("[ZhihuiNet][N706A][WS] Requesting async stop...\n");
+        g_adapter.stop_requested = true;
+        return 0;
+    }
+}
+
+int ZhihuiWsForceReconnect(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_force_reconnect();
+    } else {
+        /* N706A 实现 */
+        if (!g_adapter.initialized || !g_adapter.running) {
+            printf("[ZhihuiNet][N706A][WS] WARNING: Service not running\n");
+            return -1;
+        }
+        
+        printf("[ZhihuiNet][N706A][WS] Force reconnect requested\n");
+        g_adapter.reconnect_requested = true;
+        return 0;
+    }
+}
+
+int ZhihuiWsEnterSleep(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_enter_sleep();
+    } else {
+        /* N706A 实现 */
+        if (!g_adapter.initialized || !g_adapter.running) {
+            return -1;
+        }
+        
+        printf("[ZhihuiNet][N706A][WS] Entering sleep mode\n");
+        g_adapter.sleep_mode = true;
+        return 0;
+    }
+}
+
+int ZhihuiWsExitSleep(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_exit_sleep();
+    } else {
+        /* N706A 实现 */
+        if (!g_adapter.initialized || !g_adapter.running) {
+            return -1;
+        }
+        
+        printf("[ZhihuiNet][N706A][WS] Exiting sleep mode\n");
+        g_adapter.sleep_mode = false;
+        return 0;
+    }
+}
+
+int ZhihuiWsSendText(const char *text)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_send_text(text);
+    } else {
+        /* N706A 实现 */
+        if (!g_adapter.initialized || g_adapter.client_id < 0) {
+            printf("[ZhihuiNet][N706A][WS] ERROR: Not connected\n");
+            return -1;
+        }
+        
+        size_t length = text ? strlen(text) : 0;
+        return n706a_websocket_send_text(g_adapter.client_id, text, length);
+    }
+}
+
+int ZhihuiWsSendBIN(uint8_t *data, int len)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_send_BIN(data, len);
+    } else {
+        /* N706A 实现 */
+        if (!g_adapter.initialized || g_adapter.client_id < 0) {
+            printf("[ZhihuiNet][N706A][WS] ERROR: Not connected\n");
+            return -1;
+        }
+        
+        return n706a_websocket_send_binary(g_adapter.client_id, data, len);
+    }
+}
+
+/* ========== WebSocket 状态查询接口 ========== */
+
+int ZhihuiWsGetConnectionState(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_get_connection_state();
+    } else {
+        /* N706A 实现 */
+        if (!g_adapter.initialized || g_adapter.client_id < 0) {
+            return -1; // 未连接
+        }
+        
+        bool connected = n706a_websocket_is_connected(g_adapter.client_id);
+        return connected ? 1 : 0;
+    }
+}
+
+const char* ZhihuiWsGetConnectionStateString(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_get_connection_state_string();
+    } else {
+        /* N706A 实现 */
+        if (!g_adapter.initialized) {
+            return "NOT_INITIALIZED";
+        }
+        if (g_adapter.client_id < 0) {
+            return "NO_CLIENT";
+        }
+        bool connected = n706a_websocket_is_connected(g_adapter.client_id);
+        return connected ? "CONNECTED" : "DISCONNECTED";
+    }
+}
+
+int ZhihuiWsGetCloseStatusPublic(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_get_close_status_public();
+    } else {
+        /* N706A 实现 */
+        return 0;
+    }
+}
+
+void ZhihuiWsPrintStatusInfo(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        uapi_ws_print_status_info();
+    } else {
+        /* N706A 实现 */
+        printf("[ZhihuiNet][N706A][WS] Status Info:\n");
+        printf("  Initialized: %s\n", g_adapter.initialized ? "YES" : "NO");
+        printf("  Running: %s\n", g_adapter.running ? "YES" : "NO");
+        printf("  Client ID: %d\n", g_adapter.client_id);
+        printf("  Sleep Mode: %s\n", g_adapter.sleep_mode ? "YES" : "NO");
+        if (g_adapter.client_id >= 0) {
+            bool connected = n706a_websocket_is_connected(g_adapter.client_id);
+            printf("  Connected: %s\n", connected ? "YES" : "NO");
+        }
+    }
+}
+
+uint32_t ZhihuiWsGetMainThreadInterval(void)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        return uapi_ws_get_main_thread_interval();
+    } else {
+        /* N706A 实现 */
+        return g_adapter.callbacks.main_thread_interval_ms > 0 ? 
+               g_adapter.callbacks.main_thread_interval_ms : 1000;
+    }
+}
+
+/* ========== 回调函数注册接口 ========== */
+
+void ZhihuiWsRegisterMessageCallback(ws_message_callback_t callback)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        uapi_ws_register_message_callback(callback);
+    } else {
+        /* N706A 实现 */
+        g_adapter.callbacks.message_callback = callback;
+    }
+}
+
+void ZhihuiWsRegisterBinaryCallback(ws_binary_message_callback_t callback)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        uapi_ws_register_binary_callback(callback);
+    } else {
+        /* N706A 实现 */
+        g_adapter.callbacks.binary_callback = callback;
+    }
+}
+
+void ZhihuiWsRegisterIncompleteCallback(ws_incomplete_message_callback_t callback)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        uapi_ws_register_incomplete_callback(callback);
+    } else {
+        /* N706A 实现 */
+        g_adapter.callbacks.incomplete_callback = callback;
+    }
+}
+
+void ZhihuiWsRegisterUrlCallback(ws_url_callback_t callback)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        uapi_ws_register_url_callback(callback);
+    } else {
+        /* N706A 实现 */
+        g_adapter.callbacks.url_callback = callback;
+    }
+}
+
+void ZhihuiWsRegisterHeartbeatCallback(ws_heartbeat_callback_t callback)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        uapi_ws_register_heartbeat_callback(callback);
+    } else {
+        /* N706A 实现 */
+        g_adapter.callbacks.heartbeat_callback = callback;
+    }
+}
+
+void ZhihuiWsRegisterEventCallback(ws_event_callback_t callback)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        uapi_ws_register_event_callback(callback);
+    } else {
+        /* N706A 实现 */
+        g_adapter.callbacks.event_callback = callback;
+    }
+}
+
+void ZhihuiWsRegisterReconnectSuccessCallback(ws_reconnect_success_callback_t callback)
+{
+    if (ZhihuiNetGetImplementation() == ZHIHUI_NET_IMPL_OPENHARMONY) {
+        /* 调用 haisi_websocket 的实现 */
+        uapi_ws_register_reconnect_success_callback(callback);
+    } else {
+        /* N706A 实现 */
+        g_adapter.callbacks.reconnect_success_callback = callback;
+    }
+}