\r
unsigned int ret = LOS_SemPend((unsigned int)(UINTPTR)sem->sem, ticks);\r
if (ret == LOS_ERRNO_SEM_TIMEOUT) {\r
- osal_log("LOS_ERRNO_SEM_TIMEOUT!\n");\r
+ // 超时是正常行为(例如mbedtls等待数据时),不打印日志以避免日志刷屏\r
+ // osal_log("LOS_ERRNO_SEM_TIMEOUT!\n");\r
return OSAL_ETIME;\r
} else if (ret != OSAL_SUCCESS) {\r
osal_log("LOS_SemPend failed! ret = %#x.\n", ret);\r
SUPPORT_N706A_NET # 定义此宏启用 N706A AT 模式(独立于PPP)
SUPPORT_AT_N706A_NET # 开机自动注册 N706A AT 命令
# SSL实现方式(二选一,仅影响n706a_net内部):
- N706A_SSL_USE_AT_COMMAND # 使用AT指令实现SSL(推荐,利用N706A内置SSL)
- # N706A_SSL_USE_MBEDTLS # 使用mbedtls库实现SSL(不推荐:与AT模式不兼容)
+ # N706A_SSL_USE_AT_COMMAND # 使用AT指令实现SSL(推荐,利用N706A内置SSL)
+ N706A_SSL_USE_MBEDTLS # 使用mbedtls库实现SSL(不推荐:与AT模式不兼容)
)
set(PRIVATE_HEADER
*/
typedef void (*n706a_tcp_urc_callback_t)(int socket_id, const uint8_t *data, size_t length);
+/**
+ * @brief +TCPSETUP URC结果回调函数类型
+ * @param socket_id Socket ID (0-5)
+ * @param success true表示+TCPSETUP: n,OK,false表示+TCPSETUP: n,FAIL
+ */
+typedef void (*n706a_tcpsetup_urc_callback_t)(int socket_id, bool success);
+
+/**
+ * @brief +TCPCLOSE URC回调函数类型
+ * @param socket_id Socket ID (0-5)
+ */
+typedef void (*n706a_tcpclose_urc_callback_t)(int socket_id);
+
+/**
+ * @brief +TCPSEND URC回调函数类型(发送完成通知)
+ * @param socket_id Socket ID (0-5)
+ * @param sent_bytes 实际发送的字节数
+ */
+typedef void (*n706a_tcpsend_urc_callback_t)(int socket_id, size_t sent_bytes);
+
+/**
+ * @brief 网络状态URC回调函数类型
+ * @param urc_type URC类型字符串(如"+CREG", "+CGREG", "+CGATT")
+ * @param data URC数据内容(不含前缀)
+ * @param length 数据长度
+ */
+typedef void (*n706a_network_status_urc_callback_t)(const char *urc_type, const char *data, size_t length);
+
/**
* @brief 注册TCP URC接收回调
* @param callback 回调函数指针,NULL表示取消注册
*/
void n706a_at_register_tcp_urc_callback(n706a_tcp_urc_callback_t callback);
+/**
+ * @brief 注册+TCPSETUP URC回调
+ * @param callback 回调函数指针,NULL表示取消注册
+ */
+void n706a_at_register_tcpsetup_urc_callback(n706a_tcpsetup_urc_callback_t callback);
+
+/**
+ * @brief 注册+TCPCLOSE URC回调
+ * @param callback 回调函数指针,NULL表示取消注册
+ */
+void n706a_at_register_tcpclose_urc_callback(n706a_tcpclose_urc_callback_t callback);
+
+/**
+ * @brief 注册+TCPSEND URC回调
+ * @param callback 回调函数指针,NULL表示取消注册
+ */
+void n706a_at_register_tcpsend_urc_callback(n706a_tcpsend_urc_callback_t callback);
+
+/**
+ * @brief 注册网络状态URC回调
+ * @param callback 回调函数指针,NULL表示取消注册
+ */
+void n706a_at_register_network_status_urc_callback(n706a_network_status_urc_callback_t callback);
+
#ifdef __cplusplus
}
#endif
} n706a_http_client_data_t;
/**
- * @brief 初始化N706A HTTP客户端
- * @param client HTTP客户端结构指针
- * @param socket_id TCP socket编号(0-5)
- * @return N706A_HTTP_SUCCESS成功,其他失败
+ * @brief 初始化HTTP客户端
+ * @param client HTTP客户端结构体指针
+ * @param socket_id socket ID (0-5),如果传入-1则自动分配空闲的socket_id(推荐,避免冲突)
+ * @return N706A_HTTP_SUCCESS 成功,其他值失败
*/
n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client, int socket_id);
extern "C" {
#endif
+/* ========== Buffer大小定义 ========== */
+/**
+ * @brief URC接收缓冲区大小分级定义
+ *
+ * 根据使用场景选择合适的大小:
+ * - SMALL (2KB): 普通TCP连接,无SSL
+ * - MEDIUM (4KB): WebSocket长连接,无SSL
+ * - LARGE (6KB): SSL/TLS连接,推荐默认值,覆盖99%场景
+ * - XLARGE (8KB): 超大证书链场景(银行/政府网站)
+ */
+#define N706A_TCP_URC_BUFFER_SIZE_SMALL 2048 /**< 2KB:普通TCP */
+#define N706A_TCP_URC_BUFFER_SIZE_MEDIUM 4096 /**< 4KB:WebSocket */
+#define N706A_TCP_URC_BUFFER_SIZE_LARGE 6144 /**< 6KB:SSL/TLS(推荐默认) */
+#define N706A_TCP_URC_BUFFER_SIZE_XLARGE 8192 /**< 8KB:超大证书链 */
+
+#define N706A_TCP_URC_BUFFER_SIZE_DEFAULT N706A_TCP_URC_BUFFER_SIZE_LARGE /**< 默认6KB */
+
/* N706A TCP错误码 */
typedef enum {
N706A_TCP_SUCCESS = 0, /**< 操作成功 */
size_t urc_recv_capacity; /**< 缓冲区容量 */
size_t urc_recv_length; /**< 当前数据长度 */
void *urc_recv_mutex; /**< 缓冲区互斥锁 */
+ void *urc_data_available_sem; /**< 数据可用信号量(用于mbedtls recv等待) */
/* SSL相关字段(根据编译宏不同而不同) */
#ifdef N706A_SSL_USE_AT_COMMAND
} n706a_tcp_socket_t;
/**
- * @brief 创建TCP socket
+ * @brief 创建TCP socket(带自定义URC buffer大小)
* @param sock TCP socket指针
* @param socket_id socket编号(0-5)
+ * @param urc_buffer_size URC缓冲区大小(0=使用默认值N706A_TCP_URC_BUFFER_SIZE_DEFAULT)
* @return N706A_TCP_SUCCESS成功,其他值表示失败
+ *
+ * @note 根据使用场景选择buffer大小:
+ * - 普通TCP: N706A_TCP_URC_BUFFER_SIZE_SMALL (2KB)
+ * - WebSocket: N706A_TCP_URC_BUFFER_SIZE_MEDIUM (4KB)
+ * - SSL/TLS: N706A_TCP_URC_BUFFER_SIZE_LARGE (6KB, 默认)
+ * - 超大证书: N706A_TCP_URC_BUFFER_SIZE_XLARGE (8KB)
+ */
+n706a_tcp_result_t n706a_tcp_socket_create_ex(n706a_tcp_socket_t *sock, int socket_id, size_t urc_buffer_size);
+
+/**
+ * @brief 创建TCP socket(使用默认buffer大小)
+ * @param sock TCP socket指针
+ * @param socket_id socket编号(0-5)
+ * @return N706A_TCP_SUCCESS成功,其他值表示失败
+ *
+ * @note 内部调用 n706a_tcp_socket_create_ex(),使用默认6KB buffer
*/
n706a_tcp_result_t n706a_tcp_socket_create(n706a_tcp_socket_t *sock, int socket_id);
/**
* @brief 初始化TCP socket的URC接收缓存
* @param sock TCP socket指针
- * @param buffer_size 缓冲区大小(建议2048字节)
+ * @param buffer_size 缓冲区大小(建议使用N706A_TCP_URC_BUFFER_SIZE_*宏)
* @return N706A_TCP_SUCCESS成功,其他值表示失败
* @note 由 n706a_tcp_socket_create 自动调用,通常不需要手动调用
+ *
+ * 功能:
+ * - 分配URC接收缓冲区
+ * - 创建互斥锁(保护buffer)
+ * - 创建信号量(通知数据可用,用于mbedtls recv等待)
*/
n706a_tcp_result_t n706a_tcp_init_urc_buffer(n706a_tcp_socket_t *sock, size_t buffer_size);
*/
void n706a_tcp_on_urc_received(int socket_id, const uint8_t *data, size_t length);
+/**
+ * @brief +TCPSETUP URC回调(由n706a_at_handler调用)
+ * @param socket_id Socket ID
+ * @param success true=OK, false=FAIL
+ * @note 此函数在中断/线程上下文中被调用,需要注意线程安全
+ */
+void n706a_tcp_on_tcpsetup_urc(int socket_id, bool success);
+
#ifdef __cplusplus
}
#endif
/* 调试配置 */
#define PPP_DEBUG_ENABLE 1 // 启用调试输出
-// #define PPP_DEBUG_LEVEL PPP_DEBUG_LEVEL_DEBUG // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
+#define PPP_DEBUG_LEVEL PPP_DEBUG_LEVEL_DEBUG // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
// #define PPP_DEBUG_LEVEL PPP_DEBUG_LEVEL_INFO // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
-#define PPP_DEBUG_LEVEL PPP_DEBUG_LEVEL_WARN // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
+// #define PPP_DEBUG_LEVEL PPP_DEBUG_LEVEL_WARN // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
/*
* 调试级别说明:
/* TCP URC回调 */
static n706a_tcp_urc_callback_t g_tcp_urc_callback = NULL;
+/* +TCPSETUP URC回调 */
+static n706a_tcpsetup_urc_callback_t g_tcpsetup_urc_callback = NULL;
+
+/* +TCPCLOSE URC回调 */
+static n706a_tcpclose_urc_callback_t g_tcpclose_urc_callback = NULL;
+
+/* +TCPSEND URC回调 */
+static n706a_tcpsend_urc_callback_t g_tcpsend_urc_callback = NULL;
+
+/* 网络状态URC回调 */
+static n706a_network_status_urc_callback_t g_network_status_urc_callback = NULL;
+
/* AT命令响应解析 */
static bool ppp_at_parse_response(const char *response, const char *expected);
static ppp_error_t ppp_at_wait_response(ppp_at_response_t *response, uint32_t timeout);
/* URC解析函数 */
static bool n706a_parse_tcprecv_urc(const char *urc_data, size_t urc_len,
int *socket_id, size_t *data_len, const uint8_t **data);
+static bool n706a_parse_tcpsetup_urc(const char *urc_data, size_t urc_len,
+ int *socket_id, bool *success);
+static bool n706a_parse_tcpclose_urc(const char *urc_data, size_t urc_len, int *socket_id);
+static bool n706a_parse_tcpsend_urc(const char *urc_data, size_t urc_len,
+ int *socket_id, size_t *sent_bytes);
+static bool n706a_hex_char_to_value(char c, uint8_t *value);
+static size_t n706a_binary_to_hex_string(const uint8_t *binary_data, size_t binary_len,
+ char *hex_str, size_t hex_buf_size);
+static size_t n706a_hex_string_to_binary(const char *hex_str, size_t hex_len,
+ uint8_t *binary_buf, size_t binary_buf_size);
/**
- * @brief 写入AT响应数据到环形缓冲区(由ppp_interrupt.c调用)
+ * @brief AT响应分类处理和分发
* @param data 数据指针
* @param length 数据长度
+ * @return true=已处理(URC),false=需要写入环形缓冲区(普通AT响应)
+ *
+ * 按照优先级顺序检测各类URC,一旦匹配就立即触发callback
*/
-void n706a_at_response_buffer_write(const uint8_t *data, uint32_t length)
+static bool n706a_classify_and_dispatch_urc(const uint8_t *data, uint32_t length);
+
+/**
+ * @brief AT响应分类处理和分发
+ * @param data 数据指针
+ * @param length 数据长度
+ * @return true=已处理(URC),false=需要写入环形缓冲区(普通AT响应)
+ *
+ * 按照优先级顺序检测各类URC,一旦匹配就立即触发callback
+ */
+static bool n706a_classify_and_dispatch_urc(const uint8_t *data, uint32_t length)
{
if (!data || length == 0) {
- return;
+ return false;
+ }
+
+ const char *data_str = (const char *)data;
+
+ PPP_DEBUG_DEBUG("[URC分类] Checking data block: length=%u, first 64 bytes: [%.*s]",
+ length, (length < 64) ? (int)length : 64, data_str);
+
+ /* ========== 类别1:TCP数据接收URC(最高优先级,因为包含数据) ========== */
+ if (length >= 10 && g_tcp_urc_callback) {
+ if (memcmp(data, "+TCPRECV: ", 10) == 0 || memcmp(data, "+SSLTCPRECV: ", 13) == 0) {
+ int socket_id;
+ size_t data_len;
+ const uint8_t *tcp_data;
+
+ if (n706a_parse_tcprecv_urc((const char *)data, length, &socket_id, &data_len, &tcp_data)) {
+ // 注意:如果使用HEX模式,tcp_data指向的是HEX字符串,需要转换为二进制
+ // 这里假设当前使用的是HEX模式(AT+RECVMODE=1,1)
+ // HEX字符串长度是实际数据长度的2倍
+
+ // 使用双buffer ping-pong机制避免并发冲突
+ // 注意:解码后的数据会立即拷贝到URC buffer,所以hex buffer可以立即重用
+ static uint8_t hex_decode_buffer_0[2048]; // Ping buffer
+ static uint8_t hex_decode_buffer_1[2048]; // Pong buffer
+ static volatile int hex_decode_buffer_active = 0; // 当前使用的buffer索引(0或1)
+
+ size_t binary_data_len = data_len;
+ const uint8_t *binary_data = tcp_data;
+
+ // 检查是否是HEX格式(HEX字符串长度应该是数据长度的2倍)
+ // 或者可以通过检查是否所有字符都是0-9A-F来判断
+ bool is_hex_format = false;
+ if (data_len > 0) {
+ // 简单检查:如果数据长度是偶数,且所有字符都是十六进制字符,可能是HEX格式
+ size_t hex_str_len = 0;
+ const char *p = (const char *)tcp_data;
+ while (hex_str_len < data_len &&
+ ((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'F') || (*p >= 'a' && *p <= 'f'))) {
+ p++;
+ hex_str_len++;
+ }
+
+ // 如果所有字符都是HEX字符,且长度是2的倍数,认为是HEX格式
+ if (hex_str_len == data_len && data_len % 2 == 0) {
+ is_hex_format = true;
+
+ // 选择非活跃的buffer进行解码(ping-pong机制)
+ // 由于解码后立即拷贝到URC buffer,active buffer可以立即切换
+ int buffer_idx = hex_decode_buffer_active;
+ uint8_t *decode_buffer = (buffer_idx == 0) ? hex_decode_buffer_0 : hex_decode_buffer_1;
+
+ // 将HEX字符串转换为二进制
+ binary_data_len = n706a_hex_string_to_binary((const char *)tcp_data, data_len,
+ decode_buffer, sizeof(hex_decode_buffer_0));
+ if (binary_data_len > 0) {
+ binary_data = decode_buffer;
+
+ // 切换active buffer(虽然不必须,但保持ping-pong逻辑)
+ // 注意:由于数据会立即拷贝到URC buffer,这里切换是安全的
+ hex_decode_buffer_active = 1 - hex_decode_buffer_active;
+
+ PPP_DEBUG_DEBUG("HEX decoded: hex_len=%u -> binary_len=%u (buffer=%d)",
+ (unsigned int)data_len, (unsigned int)binary_data_len, buffer_idx);
+ } else {
+ PPP_DEBUG_ERROR("Failed to decode HEX string");
+ is_hex_format = false;
+ }
+ }
+ }
+
+ if (binary_data_len > 0) {
+ PPP_DEBUG_INFO("[URC分类] TCP URC detected: socket=%d, len=%u (format=%s)",
+ socket_id, (unsigned int)binary_data_len,
+ is_hex_format ? "HEX" : "ASCII");
+
+ // 立即回调应用层(在中断上下文或处理线程中)
+ g_tcp_urc_callback(socket_id, binary_data, binary_data_len);
+
+ // URC已处理,不写入AT响应缓冲区
+ return true;
+ } else {
+ PPP_DEBUG_WARN("Invalid TCP URC data length");
+ }
+ } else {
+ PPP_DEBUG_WARN("Failed to parse +TCPRECV URC");
+ }
+ }
+ }
+
+ /* ========== 类别2:TCP连接状态URC ========== */
+ // 检查数据开头或数据中间是否包含+TCPSETUP或+SSLTCPSETUP
+ if (length >= 11) {
+ const char *tcpsetup_pos = strstr(data_str, "+TCPSETUP: ");
+ const char *ssltcpsetup_pos = strstr(data_str, "+SSLTCPSETUP: ");
+
+ if (tcpsetup_pos || ssltcpsetup_pos) {
+ const char *urc_start = tcpsetup_pos ? tcpsetup_pos : ssltcpsetup_pos;
+ const char *urc_end = strstr(urc_start, "\r\n");
+ if (!urc_end) {
+ urc_end = strstr(urc_start, "\n");
+ }
+ if (!urc_end) {
+ urc_end = data_str + length;
+ }
+
+ size_t urc_len = urc_end - urc_start;
+ if (urc_len >= 15) {
+ int socket_id;
+ bool success;
+ if (n706a_parse_tcpsetup_urc(urc_start, urc_len, &socket_id, &success)) {
+ PPP_DEBUG_INFO("[URC分类] +TCPSETUP URC detected: socket=%d, success=%d", socket_id, success);
+ if (g_tcpsetup_urc_callback) {
+ g_tcpsetup_urc_callback(socket_id, success);
+ }
+ // URC已处理,如果是独立URC则不写入缓冲区,否则继续(混合响应)
+ if (urc_start == data_str && urc_end >= data_str + length - 2) {
+ return true; // 独立URC,不写入缓冲区
+ }
+ } else {
+ PPP_DEBUG_WARN("[URC分类] +TCPSETUP URC found but parse failed, urc_len=%u", (unsigned int)urc_len);
+ // 输出原始URC内容用于调试
+ char debug_urc[128] = {0};
+ size_t debug_len = (urc_len < sizeof(debug_urc) - 1) ? urc_len : (sizeof(debug_urc) - 1);
+ memcpy(debug_urc, urc_start, debug_len);
+ PPP_DEBUG_WARN("[URC分类] Raw URC content: [%.*s]", (int)debug_len, debug_urc);
+ }
+ }
+ }
}
- // 检测是否是 +TCPRECV URC(TCP数据接收通知)
- // 格式:+TCPRECV: <socket_id>,<length>,<data>\r\n
- if (length >= 10 && memcmp(data, "+TCPRECV: ", 10) == 0 && g_tcp_urc_callback) {
- int socket_id;
- size_t data_len;
- const uint8_t *tcp_data;
+ // 检查+TCPCLOSE或+SSLTCPCLOSE
+ if (length >= 11) {
+ const char *tcpclose_pos = strstr(data_str, "+TCPCLOSE: ");
+ const char *ssltcpclose_pos = strstr(data_str, "+SSLTCPCLOSE: ");
- if (n706a_parse_tcprecv_urc((const char *)data, length, &socket_id, &data_len, &tcp_data)) {
- PPP_DEBUG_INFO("TCP URC detected: socket=%d, len=%u", socket_id, (unsigned int)data_len);
+ if (tcpclose_pos || ssltcpclose_pos) {
+ const char *urc_start = tcpclose_pos ? tcpclose_pos : ssltcpclose_pos;
+ const char *urc_end = strstr(urc_start, "\r\n");
+ if (!urc_end) {
+ urc_end = strstr(urc_start, "\n");
+ }
+ if (!urc_end) {
+ urc_end = data_str + length;
+ }
- // 立即回调应用层(在中断上下文或处理线程中)
- g_tcp_urc_callback(socket_id, tcp_data, data_len);
+ size_t urc_len = urc_end - urc_start;
+ int socket_id;
+ if (n706a_parse_tcpclose_urc(urc_start, urc_len, &socket_id)) {
+ PPP_DEBUG_INFO("[URC分类] +TCPCLOSE URC detected: socket=%d", socket_id);
+ if (g_tcpclose_urc_callback) {
+ g_tcpclose_urc_callback(socket_id);
+ }
+ // URC已处理,如果是独立URC则不写入缓冲区
+ if (urc_start == data_str && urc_end >= data_str + length - 2) {
+ return true;
+ }
+ } else {
+ PPP_DEBUG_WARN("[URC分类] +TCPCLOSE URC found but parse failed");
+ }
+ }
+ }
+
+ /* ========== 类别3:TCP发送结果URC ========== */
+ if (length >= 10 && g_tcpsend_urc_callback) {
+ const char *tcpsend_pos = strstr(data_str, "+TCPSEND: ");
+ const char *ssltcpend_pos = strstr(data_str, "+SSLTCPEND: ");
+
+ if (tcpsend_pos || ssltcpend_pos) {
+ const char *urc_start = tcpsend_pos ? tcpsend_pos : ssltcpend_pos;
+ const char *urc_end = strstr(urc_start, "\r\n");
+ if (!urc_end) {
+ urc_end = strstr(urc_start, "\n");
+ }
+ if (!urc_end) {
+ urc_end = data_str + length;
+ }
- // URC已处理,不需要写入AT响应缓冲区
- return;
- } else {
- PPP_DEBUG_WARN("Failed to parse +TCPRECV URC");
+ size_t urc_len = urc_end - urc_start;
+ int socket_id;
+ size_t sent_bytes;
+ if (n706a_parse_tcpsend_urc(urc_start, urc_len, &socket_id, &sent_bytes)) {
+ PPP_DEBUG_INFO("[URC分类] +TCPSEND URC detected: socket=%d, sent=%u", socket_id, (unsigned int)sent_bytes);
+ g_tcpsend_urc_callback(socket_id, sent_bytes);
+ // URC已处理,如果是独立URC则不写入缓冲区
+ if (urc_start == data_str && urc_end >= data_str + length - 2) {
+ return true;
+ }
+ }
}
}
- // 普通AT响应或无法识别的数据,写入环形缓冲区
+ /* ========== 类别4:网络状态URC ========== */
+ if (length >= 6 && g_network_status_urc_callback) {
+ const char *creg_pos = strstr(data_str, "+CREG: ");
+ const char *cgreg_pos = strstr(data_str, "+CGREG: ");
+ const char *cgatt_pos = strstr(data_str, "+CGATT: ");
+
+ const char *urc_start = NULL;
+ const char *urc_type = NULL;
+
+ if (creg_pos) {
+ urc_start = creg_pos;
+ urc_type = "+CREG";
+ } else if (cgreg_pos) {
+ urc_start = cgreg_pos;
+ urc_type = "+CGREG";
+ } else if (cgatt_pos) {
+ urc_start = cgatt_pos;
+ urc_type = "+CGATT";
+ }
+
+ if (urc_start) {
+ const char *colon = strchr(urc_start, ':');
+ if (colon) {
+ const char *urc_data_start = colon + 2; // 跳过 ": "
+ const char *urc_end = strstr(urc_data_start, "\r\n");
+ if (!urc_end) {
+ urc_end = strstr(urc_data_start, "\n");
+ }
+ if (!urc_end) {
+ urc_end = data_str + length;
+ }
+
+ size_t urc_data_len = urc_end - urc_data_start;
+ PPP_DEBUG_INFO("[URC分类] Network status URC detected: %s, data_len=%u", urc_type, (unsigned int)urc_data_len);
+ g_network_status_urc_callback(urc_type, urc_data_start, urc_data_len);
+ // URC已处理,如果是独立URC则不写入缓冲区
+ if (urc_start == data_str && urc_end >= data_str + length - 2) {
+ return true;
+ }
+ }
+ }
+ }
+
+ /* ========== 不是URC,是普通AT响应 ========== */
+ return false;
+}
+
+/**
+ * @brief 写入AT响应数据到环形缓冲区(由ppp_interrupt.c调用)
+ * @param data 数据指针
+ * @param length 数据长度
+ */
+void n706a_at_response_buffer_write(const uint8_t *data, uint32_t length)
+{
+ if (!data || length == 0) {
+ return;
+ }
+
+ /* ========== 第一步:分类处理和分发URC ========== */
+ /* 如果返回true,说明是URC已处理,不需要写入缓冲区 */
+ bool is_urc = n706a_classify_and_dispatch_urc(data, length);
+ if (is_urc) {
+ // URC已通过callback处理,不写入AT响应缓冲区
+ return;
+ }
+
+ /* ========== 第二步:普通AT响应写入环形缓冲区 ========== */
osal_mutex_lock(&g_at_response_mutex);
for (uint32_t i = 0; i < length; i++) {
osal_mutex_unlock(&g_at_response_mutex);
- // 通知有新数据到达
+ // 通知等待线程有新数据
osal_sem_up(&g_at_response_sem);
PPP_DEBUG_DEBUG("AT response buffer: wrote %u bytes", length);
return ret;
}
+/**
+ * @brief 等待 '>' 提示符(用于 AT+TCPSEND/AT+SSLTCPSEND)
+ * @param timeout 超时时间(毫秒)
+ * @return PPP_ERR_OK 成功,其他值失败
+ *
+ * 根据N706A AT命令手册,AT+TCPSEND发送后会收到 '>' 提示符,
+ * 此时需要延迟50-100ms后再发送数据。
+ */
+static ppp_error_t ppp_at_wait_for_prompt(uint32_t timeout)
+{
+ ppp_at_response_t response;
+ ppp_error_t ret = ppp_at_wait_response(&response, timeout);
+
+ if (ret != PPP_ERR_OK) {
+ PPP_DEBUG_ERROR("Failed to wait for '>' prompt: error %d", ret);
+ return ret;
+ }
+
+ /* 检查响应中是否包含 '>' */
+ if (strstr((const char *)response.response, ">") == NULL) {
+ PPP_DEBUG_ERROR("Response does not contain '>' prompt: %s", response.response);
+ return PPP_ERR_AT_FAILED;
+ }
+
+ PPP_DEBUG_DEBUG("Received '>' prompt, delaying 75ms before sending data...");
+
+ /* 延迟75ms(在50-100ms范围内,取中间值) */
+ osal_msleep(75);
+
+ return PPP_ERR_OK;
+}
+
/**
* @brief 发送原始数据(不带AT命令格式)
* @note 用于 TCP/SSL 数据发送,在 AT+TCPSEND/AT+SSLTCPSEND 命令后使用
+ * @note 调用此函数前,必须先调用 ppp_at_send_command 发送 AT+TCPSEND 命令并等待 '>' 提示符
*/
ppp_error_t ppp_at_send_raw_data(const void *data, size_t len, uint32_t timeout)
{
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);
+ PPP_DEBUG_DEBUG("=== Sending Raw Data (HEX Mode) ===");
+ PPP_DEBUG_DEBUG("Binary length: %zu bytes", len);
+
+ /* 将二进制数据编码为HEX字符串 */
+ size_t hex_str_len = len * 2; // 每个字节转换为2个HEX字符
+ char *hex_str = (char *)malloc(hex_str_len + 1); // +1 for null terminator
+ if (!hex_str) {
+ PPP_DEBUG_ERROR("Failed to allocate memory for HEX string");
+ return PPP_ERR_MEMORY;
+ }
+
+ size_t encoded_len = n706a_binary_to_hex_string((const uint8_t *)data, len, hex_str, hex_str_len + 1);
+ if (encoded_len == 0) {
+ free(hex_str);
+ PPP_DEBUG_ERROR("Failed to encode binary data to HEX string");
+ return PPP_ERR_INVALID_PARAM;
+ }
+
+ PPP_DEBUG_DEBUG("HEX encoded: binary_len=%zu -> hex_len=%zu", len, encoded_len);
+
+ /* 通过 UART 发送HEX字符串(ASCII字符),不添加 \r\n */
+ int32_t write_result = uapi_uart_write(g_at_ctx.uart_bus, (const uint8_t *)hex_str, encoded_len, 0);
- /* 直接通过 UART 发送原始数据,不添加 \r\n */
- int32_t write_result = uapi_uart_write(g_at_ctx.uart_bus, (const uint8_t *)data, len, 0);
+ free(hex_str);
- if (write_result != (int32_t)len) {
- PPP_DEBUG_ERROR("Failed to send raw data: wrote %d bytes, expected %zu", write_result, len);
+ if (write_result != (int32_t)encoded_len) {
+ PPP_DEBUG_ERROR("Failed to send HEX data: wrote %d bytes, expected %zu", write_result, encoded_len);
return PPP_ERR_UART;
}
- PPP_DEBUG_DEBUG("Raw data sent successfully (%zu bytes) on bus %d", len, g_at_ctx.uart_bus);
+ PPP_DEBUG_DEBUG("HEX data sent successfully (%zu hex chars, %zu binary bytes) on bus %d",
+ encoded_len, len, g_at_ctx.uart_bus);
return PPP_ERR_OK;
}
* @param urc_len URC数据长度
* @param socket_id 输出:socket ID
* @param data_len 输出:数据长度
+ * - ASCII模式:二进制数据长度(字节数)
+ * - HEX模式:HEX字符串长度(字节数),需要除以2得到二进制数据长度
* @param data 输出:数据指针(指向 urc_data 内部)
+ * - ASCII模式:指向ASCII数据
+ * - HEX模式:指向HEX字符串
* @return true=解析成功,false=失败
*
- * URC格式:+TCPRECV: <socket_id>,<length>,<data>\r\n
- * 例如:+TCPRECV: 0,10,1234567890\r\n
+ * URC格式(ASCII模式):+TCPRECV: <socket_id>,<binary_len>,<ascii_data>\r\n
+ * 例如:+TCPRECV: 0,10,1234567890\r\n (10字节ASCII数据)
+ * 注意:<length>是二进制数据长度(10)
+ *
+ * URC格式(HEX模式):+TCPRECV: <socket_id>,<hex_str_len>,<hex_data>\r\n
+ * 例如:+TCPRECV: 0,6,313233\r\n (表示3字节二进制数据:0x31, 0x32, 0x33)
+ * 注意:<length>是HEX字符串长度(6),不是二进制数据长度(3)
+ *
+ * 与AT+TCPSEND的对应关系:
+ * - AT+TCPSEND的length参数:二进制数据长度(字节数)
+ * - +TCPRECV的length参数:HEX模式下是HEX字符串长度(字节数)= 二进制数据长度 × 2
*/
static bool n706a_parse_tcprecv_urc(const char *urc_data, size_t urc_len,
int *socket_id, size_t *data_len, const uint8_t **data)
}
p++; // 跳过逗号
- // 解析 length
+ // 解析 length(HEX模式下这是HEX字符串的长度)
*data_len = 0;
while (p < end && *p >= '0' && *p <= '9') {
*data_len = *data_len * 10 + (*p - '0');
}
}
- PPP_DEBUG_DEBUG("Parsed URC: socket=%d, len=%u", *socket_id, (unsigned int)*data_len);
+ PPP_DEBUG_DEBUG("Parsed URC: socket=%d, data_len=%u", *socket_id, (unsigned int)*data_len);
return true;
}
+/**
+ * @brief 解析 +TCPCLOSE URC
+ * @param urc_data URC数据(以 "+TCPCLOSE: " 或 "+SSLTCPCLOSE: " 开头)
+ * @param urc_len URC数据长度
+ * @param socket_id 输出:socket ID(如果格式是ERROR,socket_id设为-1)
+ * @return true=解析成功,false=失败
+ *
+ * URC格式:
+ * +TCPCLOSE: <socket_id>
+ * +TCPCLOSE: <socket_id>,Link Closed
+ * +TCPCLOSE: ERROR(错误格式,socket_id设为-1)
+ * 或:+SSLTCPCLOSE: <socket_id>
+ */
+static bool n706a_parse_tcpclose_urc(const char *urc_data, size_t urc_len, int *socket_id)
+{
+ if (!urc_data || !socket_id || urc_len < 12) {
+ return false;
+ }
+
+ // 查找 ": " 位置
+ const char *colon_space = strstr(urc_data, ": ");
+ if (!colon_space) {
+ return false;
+ }
+
+ const char *p = colon_space + 2; // 跳过 ": "
+ const char *end = urc_data + urc_len;
+
+ // 检查是否是ERROR格式
+ if (p + 5 <= end && memcmp(p, "ERROR", 5) == 0 &&
+ (p[5] == '\r' || p[5] == '\n' || p[5] == '\0' || p[5] == ' ')) {
+ *socket_id = -1; // ERROR格式,socket_id设为-1表示错误
+ return true;
+ }
+
+ // 解析 socket_id
+ *socket_id = 0;
+ while (p < end && *p >= '0' && *p <= '9') {
+ *socket_id = *socket_id * 10 + (*p - '0');
+ p++;
+ }
+
+ // 可能后面有 ",Link Closed" 或其他信息,但socket_id已经解析完成
+ return (*socket_id >= 0); // 确保至少解析到一个有效的socket_id或ERROR
+}
+
+/**
+ * @brief 解析 +TCPSEND URC
+ * @param urc_data URC数据(以 "+TCPSEND: " 或 "+SSLTCPEND: " 开头)
+ * @param urc_len URC数据长度
+ * @param socket_id 输出:socket ID
+ * @param sent_bytes 输出:实际发送的字节数
+ * @return true=解析成功,false=失败
+ *
+ * URC格式:+TCPSEND: <socket_id>,<sent_bytes>
+ * 或:+SSLTCPEND: <socket_id>,<sent_bytes>
+ */
+static bool n706a_parse_tcpsend_urc(const char *urc_data, size_t urc_len,
+ int *socket_id, size_t *sent_bytes)
+{
+ if (!urc_data || !socket_id || !sent_bytes || urc_len < 15) {
+ return false;
+ }
+
+ // 查找 ": " 位置
+ const char *colon_space = strstr(urc_data, ": ");
+ if (!colon_space) {
+ return false;
+ }
+
+ const char *p = colon_space + 2; // 跳过 ": "
+ const char *end = urc_data + urc_len;
+
+ // 解析 socket_id
+ *socket_id = 0;
+ while (p < end && *p >= '0' && *p <= '9') {
+ *socket_id = *socket_id * 10 + (*p - '0');
+ p++;
+ }
+
+ if (p >= end || *p != ',') {
+ PPP_DEBUG_ERROR("TCPSEND URC parse error: no comma after socket_id");
+ return false;
+ }
+ p++; // 跳过逗号
+
+ // 解析 sent_bytes
+ *sent_bytes = 0;
+ while (p < end && *p >= '0' && *p <= '9') {
+ *sent_bytes = *sent_bytes * 10 + (*p - '0');
+ p++;
+ }
+
+ return true;
+}
+
+/**
+ * @brief 解析 +TCPSETUP URC
+ * @param urc_data URC数据(以 "+TCPSETUP: " 或 "+SSLTCPSETUP: " 开头)
+ * @param urc_len URC数据长度
+ * @param socket_id 输出:socket ID
+ * @param success 输出:true表示OK,false表示FAIL
+ * @return true=解析成功,false=失败
+ *
+ * URC格式:+TCPSETUP: <socket_id>,OK 或 +TCPSETUP: <socket_id>,FAIL
+ * 或:+SSLTCPSETUP: <socket_id>,OK 或 +SSLTCPSETUP: <socket_id>,FAIL
+ * 例如:+TCPSETUP: 0,OK 或 +TCPSETUP: 0,FAIL
+ */
+static bool n706a_parse_tcpsetup_urc(const char *urc_data, size_t urc_len,
+ int *socket_id, bool *success)
+{
+ if (!urc_data || !socket_id || !success || urc_len < 15) {
+ return false;
+ }
+
+ // 查找 ": " 位置(跳过 "+TCPSETUP" 或 "+SSLTCPSETUP")
+ const char *colon_space = strstr(urc_data, ": ");
+ if (!colon_space) {
+ return false;
+ }
+
+ const char *p = colon_space + 2; // 跳过 ": "
+ const char *end = urc_data + urc_len;
+
+ // 跳过可能的空格
+ while (p < end && (*p == ' ' || *p == '\t')) {
+ p++;
+ }
+
+ // 解析 socket_id
+ *socket_id = 0;
+ while (p < end && *p >= '0' && *p <= '9') {
+ *socket_id = *socket_id * 10 + (*p - '0');
+ p++;
+ }
+
+ if (p >= end || *p != ',') {
+ PPP_DEBUG_ERROR("TCPSETUP URC parse error: no comma after socket_id (found: 0x%02x)",
+ p < end ? (unsigned char)*p : 0);
+ return false;
+ }
+ p++; // 跳过逗号
+
+ // 跳过可能的空格
+ while (p < end && (*p == ' ' || *p == '\t')) {
+ p++;
+ }
+
+ // 解析结果(OK或FAIL),忽略前后空白字符
+ if (p < end) {
+ // 检查OK
+ if (p + 2 <= end && (p[0] == 'O' || p[0] == 'o') && (p[1] == 'K' || p[1] == 'k')) {
+ // 检查OK后面是否是结束符或空白字符
+ if (p + 2 >= end || p[2] == '\r' || p[2] == '\n' || p[2] == '\0' ||
+ p[2] == ' ' || p[2] == '\t') {
+ *success = true;
+ return true;
+ }
+ }
+
+ // 检查FAIL
+ if (p + 4 <= end &&
+ (p[0] == 'F' || p[0] == 'f') &&
+ (p[1] == 'A' || p[1] == 'a') &&
+ (p[2] == 'I' || p[2] == 'i') &&
+ (p[3] == 'L' || p[3] == 'l')) {
+ // 检查FAIL后面是否是结束符或空白字符
+ if (p + 4 >= end || p[4] == '\r' || p[4] == '\n' || p[4] == '\0' ||
+ p[4] == ' ' || p[4] == '\t') {
+ *success = false;
+ return true;
+ }
+ }
+ }
+
+ // 如果都不匹配,输出详细的调试信息
+ char debug_buf[64] = {0};
+ size_t debug_len = (end - p < sizeof(debug_buf) - 1) ? (end - p) : (sizeof(debug_buf) - 1);
+ memcpy(debug_buf, p, debug_len);
+ PPP_DEBUG_ERROR("TCPSETUP URC parse error: invalid result (expected OK/FAIL), found: [%.*s] (len=%u)",
+ (int)debug_len, debug_buf, (unsigned int)(end - p));
+ return false;
+}
+
+/**
+ * @brief 将单个HEX字符转换为数值
+ * @param c HEX字符('0'-'9', 'A'-'F', 'a'-'f')
+ * @param value 输出的数值
+ * @return true=成功,false=失败
+ */
+static bool n706a_hex_char_to_value(char c, uint8_t *value)
+{
+ if (c >= '0' && c <= '9') {
+ *value = c - '0';
+ return true;
+ } else if (c >= 'A' && c <= 'F') {
+ *value = c - 'A' + 10;
+ return true;
+ } else if (c >= 'a' && c <= 'f') {
+ *value = c - 'a' + 10;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief 将二进制数据转换为HEX字符串
+ * @param binary_data 二进制数据
+ * @param binary_len 二进制数据长度
+ * @param hex_str 输出的HEX字符串缓冲区
+ * @param hex_buf_size HEX缓冲区大小(至少需要binary_len*2+1)
+ * @return 转换后的HEX字符串长度(不含结束符),0表示失败
+ */
+static size_t n706a_binary_to_hex_string(const uint8_t *binary_data, size_t binary_len,
+ char *hex_str, size_t hex_buf_size)
+{
+ if (!binary_data || !hex_str || binary_len == 0) {
+ return 0;
+ }
+
+ // 需要binary_len * 2字节(每个字节转换为2个HEX字符),再加1个结束符
+ if (hex_buf_size < binary_len * 2 + 1) {
+ PPP_DEBUG_ERROR("HEX encode: buffer too small (need %u, got %u)",
+ (unsigned int)(binary_len * 2 + 1), (unsigned int)hex_buf_size);
+ return 0;
+ }
+
+ static const char hex_chars[] = "0123456789ABCDEF";
+
+ for (size_t i = 0; i < binary_len; i++) {
+ hex_str[i * 2] = hex_chars[(binary_data[i] >> 4) & 0x0F];
+ hex_str[i * 2 + 1] = hex_chars[binary_data[i] & 0x0F];
+ }
+
+ hex_str[binary_len * 2] = '\0';
+ return binary_len * 2;
+}
+
+/**
+ * @brief 将HEX字符串转换为二进制数据
+ * @param hex_str HEX字符串(如 "313233")
+ * @param hex_len HEX字符串长度(字节数,必须是偶数)
+ * @param binary_buf 输出的二进制缓冲区
+ * @param binary_buf_size 二进制缓冲区大小
+ * @return 转换后的二进制数据长度(字节数),0表示失败
+ */
+static size_t n706a_hex_string_to_binary(const char *hex_str, size_t hex_len,
+ uint8_t *binary_buf, size_t binary_buf_size)
+{
+ if (!hex_str || !binary_buf || hex_len == 0 || hex_len % 2 != 0) {
+ return 0;
+ }
+
+ size_t binary_len = hex_len / 2;
+ if (binary_len > binary_buf_size) {
+ PPP_DEBUG_ERROR("HEX decode: binary_len=%u exceeds buffer size=%u",
+ (unsigned int)binary_len, (unsigned int)binary_buf_size);
+ return 0;
+ }
+
+ for (size_t i = 0; i < binary_len; i++) {
+ uint8_t high_nibble, low_nibble;
+
+ if (!n706a_hex_char_to_value(hex_str[i * 2], &high_nibble) ||
+ !n706a_hex_char_to_value(hex_str[i * 2 + 1], &low_nibble)) {
+ PPP_DEBUG_ERROR("Invalid HEX character at position %u", (unsigned int)(i * 2));
+ return 0;
+ }
+
+ binary_buf[i] = (high_nibble << 4) | low_nibble;
+ }
+
+ return binary_len;
+}
+
/**
* @brief 注册TCP URC接收回调
* @param callback 回调函数指针,NULL表示取消注册
{
g_tcp_urc_callback = callback;
PPP_DEBUG_INFO("TCP URC callback %s", callback ? "registered" : "unregistered");
+}
+
+/**
+ * @brief 注册+TCPSETUP URC回调
+ * @param callback 回调函数指针,NULL表示取消注册
+ */
+void n706a_at_register_tcpsetup_urc_callback(n706a_tcpsetup_urc_callback_t callback)
+{
+ g_tcpsetup_urc_callback = callback;
+ PPP_DEBUG_INFO("+TCPSETUP URC callback %s", callback ? "registered" : "unregistered");
+}
+
+/**
+ * @brief 注册+TCPCLOSE URC回调
+ */
+void n706a_at_register_tcpclose_urc_callback(n706a_tcpclose_urc_callback_t callback)
+{
+ g_tcpclose_urc_callback = callback;
+ PPP_DEBUG_INFO("+TCPCLOSE URC callback %s", callback ? "registered" : "unregistered");
+}
+
+/**
+ * @brief 注册+TCPSEND URC回调
+ */
+void n706a_at_register_tcpsend_urc_callback(n706a_tcpsend_urc_callback_t callback)
+{
+ g_tcpsend_urc_callback = callback;
+ PPP_DEBUG_INFO("+TCPSEND URC callback %s", callback ? "registered" : "unregistered");
+}
+
+/**
+ * @brief 注册网络状态URC回调
+ */
+void n706a_at_register_network_status_urc_callback(n706a_network_status_urc_callback_t callback)
+{
+ g_network_status_urc_callback = callback;
+ PPP_DEBUG_INFO("Network status URC callback %s", callback ? "registered" : "unregistered");
}
\ No newline at end of file
*/
#include "n706a_http_client.h"
+#include "n706a_tcp_client.h"
+#include "ppp_debug.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/**
* @brief 初始化HTTP客户端
+ * @param client HTTP客户端结构体指针
+ * @param socket_id socket ID (0-5),如果传入-1则自动分配空闲的socket_id
+ * @return N706A_HTTP_SUCCESS 成功,其他值失败
*/
n706a_httpc_result_t n706a_http_client_init(n706a_http_client_t *client, int socket_id)
{
- if (!client || socket_id < 0 || socket_id > 5) {
+ if (!client) {
return N706A_HTTP_EARG;
}
memset(client, 0, sizeof(n706a_http_client_t));
+ /* 如果socket_id为-1,自动分配空闲的socket_id(避免与WebSocket冲突) */
+ if (socket_id == -1) {
+ socket_id = n706a_tcp_find_free_socket_id();
+ if (socket_id < 0 || socket_id > 5) {
+ PPP_DEBUG_ERROR("[HTTP] No free socket ID available for HTTP client");
+ return N706A_HTTP_ENOBUFS;
+ }
+ PPP_DEBUG_INFO("[HTTP] Auto-allocated socket ID: %d", socket_id);
+ } else if (socket_id < 0 || socket_id > 5) {
+ return N706A_HTTP_EARG;
+ }
+
client->socket_id = socket_id;
client->is_https = false;
client->remote_port = 0;
return ERRCODE_FAIL;
}
- // 7. 配置TCP接收模式为直接输出(URC模式)
- PPP_DEBUG_PRINT("Step 7: Configuring TCP URC mode\n");
+ // 7. é\85\8dç½®TCPæ\8e¥æ\94¶æ¨¡å¼\8f为ç\9b´æ\8e¥è¾\93å\87ºï¼\88URC模å¼\8fï¼\8cHEXä¸\8aæ\8a¥ï¼\89
+ PPP_DEBUG_PRINT("Step 7: Configuring TCP URC mode (HEX format)\n");
n706a_at_response_t at_resp;
- n706a_at_error_t at_ret = n706a_at_send_command("AT+RECVMODE=1", "OK", 3000, &at_resp);
+ n706a_at_error_t at_ret = n706a_at_send_command("AT+RECVMODE=1,1", "OK", 3000, &at_resp);
if (at_ret != N706A_AT_OK) {
- PPP_DEBUG_WARN("Failed to set RECVMODE=1, using default\n");
+ PPP_DEBUG_WARN("Failed to set RECVMODE=1,1 (HEX mode), trying ASCII mode\n");
+ // 如果HEX模式失败,尝试ASCII模式作为备用
+ at_ret = n706a_at_send_command("AT+RECVMODE=1,0", "OK", 3000, &at_resp);
+ if (at_ret != N706A_AT_OK) {
+ PPP_DEBUG_WARN("Failed to set RECVMODE=1,0, using default\n");
+ } else {
+ PPP_DEBUG_WARN("Using ASCII mode as fallback\n");
+ }
+ } else {
+ PPP_DEBUG_INFO("TCP receive mode set to HEX format\n");
}
PPP_DEBUG_INFO("response: %d , %s\n",at_resp.length,at_resp.response);
// 8. 注册TCP URC接收回调
PPP_DEBUG_PRINT("Step 8: Registering TCP URC callback\n");
n706a_at_register_tcp_urc_callback(n706a_tcp_on_urc_received);
+
+ // 8.1. 注册+TCPSETUP URC回调(用于TCP连接状态验证)
+ PPP_DEBUG_PRINT("Step 8.1: Registering +TCPSETUP URC callback\n");
+ // n706a_tcp_on_tcpsetup_urc在n706a_tcp_client.c中实现,直接调用
+ n706a_at_register_tcpsetup_urc_callback(n706a_tcp_on_tcpsetup_urc);
// 9. 检查GPRS附着状态(AT+CGATT?)
PPP_DEBUG_PRINT("Step 9: Checking GPRS attachment status (AT+CGATT?)\n");
#include "ppp_serial.h"
#include "ppp_debug.h"
#include "lock/osal_mutex.h"
+#include "semaphore/osal_semaphore.h"
+#include "soc_osal.h" // 定义 OSAL_SUCCESS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static osal_mutex g_socket_table_mutex;
static bool g_socket_table_initialized = false;
+/* URC buffer的mutex和semaphore池(每个socket一个) */
+static osal_mutex g_socket_urc_mutexes[N706A_MAX_SOCKETS];
+static osal_semaphore g_socket_urc_sems[N706A_MAX_SOCKETS];
+static bool g_urc_resources_initialized = false;
+
+/* +TCPSETUP URC等待机制(每个socket一个信号量) */
+static osal_semaphore g_tcpsetup_urc_sems[N706A_MAX_SOCKETS];
+static bool g_tcpsetup_urc_results[N706A_MAX_SOCKETS]; // true=OK, false=FAIL
+static osal_mutex g_tcpsetup_urc_mutex;
+
/* ========== 私有函数声明 ========== */
#ifdef N706A_SSL_USE_MBEDTLS
static void tcp_mbedtls_cleanup_ssl(n706a_tcp_socket_t *sock);
#endif
+/* Socket管理辅助函数 */
+static n706a_tcp_result_t n706a_tcp_force_close_socket_by_id(int socket_id);
+int n706a_tcp_find_free_socket_id(void); /* 公共API,供HTTP和WebSocket使用 */
+static bool n706a_tcp_wait_for_tcpsetup_urc(int socket_id, uint32_t timeout_ms, bool *success);
+
/* ========== 公共函数实现 ========== */
/**
- * @brief 创建TCP socket
+ * @brief 创建TCP socket(带自定义URC buffer大小)
*/
-n706a_tcp_result_t n706a_tcp_socket_create(n706a_tcp_socket_t *sock, int socket_id)
+n706a_tcp_result_t n706a_tcp_socket_create_ex(n706a_tcp_socket_t *sock, int socket_id, size_t urc_buffer_size)
{
if (!sock || socket_id < 0 || socket_id > 5) {
return N706A_TCP_EINVAL;
sock->is_ssl = false;
sock->timeout_ms = 15000; // 默认15秒超时
- // 初始化URC接收缓存(2KB)
- n706a_tcp_result_t ret = n706a_tcp_init_urc_buffer(sock, 2048);
+ // 确定buffer大小(0表示使用默认值)
+ if (urc_buffer_size == 0) {
+ urc_buffer_size = N706A_TCP_URC_BUFFER_SIZE_DEFAULT; // 默认6KB
+ }
+
+ // 初始化URC接收缓存
+ n706a_tcp_result_t ret = n706a_tcp_init_urc_buffer(sock, urc_buffer_size);
if (ret != N706A_TCP_SUCCESS) {
- PPP_DEBUG_ERROR("Failed to init URC buffer for socket %d", socket_id);
+ PPP_DEBUG_ERROR("Failed to init URC buffer (%zu bytes) for socket %d", urc_buffer_size, socket_id);
return ret;
}
g_socket_table_initialized = true;
}
+ // 初始化URC资源池(mutex和semaphore)
+ if (!g_urc_resources_initialized) {
+ for (int i = 0; i < N706A_MAX_SOCKETS; i++) {
+ if (osal_mutex_init(&g_socket_urc_mutexes[i]) != OSAL_SUCCESS) {
+ PPP_DEBUG_ERROR("Failed to init URC mutex for socket %d", i);
+ return N706A_TCP_ERROR;
+ }
+ if (osal_sem_init(&g_socket_urc_sems[i], 0) != OSAL_SUCCESS) {
+ PPP_DEBUG_ERROR("Failed to init URC semaphore for socket %d", i);
+ osal_mutex_destroy(&g_socket_urc_mutexes[i]);
+ return N706A_TCP_ERROR;
+ }
+ }
+ g_urc_resources_initialized = true;
+ PPP_DEBUG_DEBUG("URC resource pool initialized for %d sockets", N706A_MAX_SOCKETS);
+ }
+
osal_mutex_lock(&g_socket_table_mutex);
g_tcp_sockets[socket_id] = sock;
osal_mutex_unlock(&g_socket_table_mutex);
- PPP_DEBUG_INFO("Socket %d created with URC buffer", socket_id);
+ PPP_DEBUG_INFO("Socket %d created with %zu bytes URC buffer", socket_id, urc_buffer_size);
+
+ return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 创建TCP socket(使用默认buffer大小)
+ */
+n706a_tcp_result_t n706a_tcp_socket_create(n706a_tcp_socket_t *sock, int socket_id)
+{
+ n706a_tcp_result_t ret = n706a_tcp_socket_create_ex(sock, socket_id, 0); // 使用默认大小
+ if (ret != N706A_TCP_SUCCESS) {
+ return ret;
+ }
#ifdef N706A_SSL_USE_AT_COMMAND
sock->ssl_version = 3; // 默认TLS 1.2
}
#endif
+/**
+ * @brief 强制关闭指定socket ID的socket(方案1:连接前强制关闭旧socket)
+ * @param socket_id socket ID (0-5)
+ * @return N706A_TCP_SUCCESS 成功,其他值失败
+ */
+static n706a_tcp_result_t n706a_tcp_force_close_socket_by_id(int socket_id)
+{
+ if (socket_id < 0 || socket_id >= N706A_MAX_SOCKETS) {
+ return N706A_TCP_EINVAL;
+ }
+
+ /* 尝试通过AT命令强制关闭socket */
+ char at_cmd[32];
+ n706a_at_response_t response;
+
+ /* 先尝试关闭普通TCP */
+ snprintf(at_cmd, sizeof(at_cmd), "AT+TCPCLOSE=%d", socket_id);
+ n706a_at_error_t ret = n706a_at_send_command(at_cmd, "OK", 3000, &response);
+
+ if (ret == N706A_AT_OK && response.length > 0 &&
+ strstr(response.response, "OK") && !strstr(response.response, "ERROR")) {
+ PPP_DEBUG_DEBUG("Socket %d force closed successfully (TCP)", socket_id);
+ return N706A_TCP_SUCCESS;
+ }
+
+ /* 如果普通TCP关闭失败,尝试关闭SSL TCP */
+ snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPCLOSE=%d", socket_id);
+ ret = n706a_at_send_command(at_cmd, "OK", 3000, &response);
+
+ if (ret == N706A_AT_OK && response.length > 0 &&
+ strstr(response.response, "OK") && !strstr(response.response, "ERROR")) {
+ PPP_DEBUG_DEBUG("Socket %d force closed successfully (SSL TCP)", socket_id);
+ return N706A_TCP_SUCCESS;
+ }
+
+ /* 即使关闭命令失败,也返回成功(模块端可能已经关闭) */
+ PPP_DEBUG_WARN("Socket %d force close command failed, but continuing (module may have closed it)", socket_id);
+
+ /* 等待一段时间让模块端完全清理socket状态(避免状态混乱) */
+ osal_msleep(100);
+
+ return N706A_TCP_SUCCESS;
+}
+
+/**
+ * @brief 查找空闲的socket ID(公共API,供HTTP和WebSocket使用)
+ * @return 空闲的socket ID (0-5),如果没有空闲则返回-1
+ */
+int n706a_tcp_find_free_socket_id(void)
+{
+ if (!g_socket_table_initialized) {
+ return 0; // 如果表未初始化,返回0(默认)
+ }
+
+ osal_mutex_lock(&g_socket_table_mutex);
+
+ /* 查找空闲的socket ID(表中没有注册的socket) */
+ for (int i = 0; i < N706A_MAX_SOCKETS; i++) {
+ if (g_tcp_sockets[i] == NULL) {
+ osal_mutex_unlock(&g_socket_table_mutex);
+ PPP_DEBUG_DEBUG("Found free socket ID: %d", i);
+ return i;
+ }
+ }
+
+ osal_mutex_unlock(&g_socket_table_mutex);
+
+ /* 如果所有socket都被占用,尝试查找状态为CLOSED的socket */
+ osal_mutex_lock(&g_socket_table_mutex);
+ for (int i = 0; i < N706A_MAX_SOCKETS; i++) {
+ if (g_tcp_sockets[i] != NULL &&
+ g_tcp_sockets[i]->state == N706A_TCP_STATE_CLOSED) {
+ osal_mutex_unlock(&g_socket_table_mutex);
+ PPP_DEBUG_DEBUG("Found closed socket ID: %d", i);
+ return i;
+ }
+ }
+ osal_mutex_unlock(&g_socket_table_mutex);
+
+ PPP_DEBUG_WARN("No free socket ID found, will use socket 0");
+ return -1; // 没有空闲socket,返回-1表示失败
+}
+
+/**
+ * @brief 初始化+TCPSETUP URC等待机制
+ */
+static void n706a_tcp_init_tcpsetup_urc_wait(void)
+{
+ static bool initialized = false;
+ if (initialized) {
+ return;
+ }
+
+ osal_mutex_init(&g_tcpsetup_urc_mutex);
+ for (int i = 0; i < N706A_MAX_SOCKETS; i++) {
+ osal_sem_init(&g_tcpsetup_urc_sems[i], 0);
+ g_tcpsetup_urc_results[i] = false;
+ }
+ initialized = true;
+ PPP_DEBUG_DEBUG("+TCPSETUP URC wait mechanism initialized");
+}
+
+/**
+ * @brief +TCPSETUP URC回调(由n706a_at_handler调用)
+ * @param socket_id Socket ID
+ * @param success true=OK, false=FAIL
+ *
+ * 注意:此函数由n706a_at_handler.c通过URC回调调用,不是应用层API
+ */
+void n706a_tcp_on_tcpsetup_urc(int socket_id, bool success)
+{
+ if (socket_id < 0 || socket_id >= N706A_MAX_SOCKETS) {
+ return;
+ }
+
+ osal_mutex_lock(&g_tcpsetup_urc_mutex);
+ g_tcpsetup_urc_results[socket_id] = success;
+ osal_mutex_unlock(&g_tcpsetup_urc_mutex);
+
+ /* 通知等待的线程 */
+ osal_sem_up(&g_tcpsetup_urc_sems[socket_id]);
+
+ PPP_DEBUG_INFO("+TCPSETUP URC received: socket=%d, success=%d", socket_id, success);
+}
+
+/**
+ * @brief 等待+TCPSETUP URC(方案3:验证+TCPSETUP URC)
+ * @param socket_id 等待的socket ID
+ * @param timeout_ms 超时时间(毫秒)
+ * @param success 输出:true表示收到+TCPSETUP: n,OK,false表示收到FAIL或超时
+ * @return true=收到URC,false=超时
+ *
+ * 使用信号量机制等待URC回调,实现真正的异步等待
+ */
+static bool n706a_tcp_wait_for_tcpsetup_urc(int socket_id, uint32_t timeout_ms, bool *success)
+{
+ if (!success || socket_id < 0 || socket_id >= N706A_MAX_SOCKETS) {
+ return false;
+ }
+
+ /* 初始化等待机制(首次调用时) */
+ n706a_tcp_init_tcpsetup_urc_wait();
+
+ /* 清除之前的结果 */
+ osal_mutex_lock(&g_tcpsetup_urc_mutex);
+ g_tcpsetup_urc_results[socket_id] = false;
+ osal_mutex_unlock(&g_tcpsetup_urc_mutex);
+
+ /* 等待URC信号量(超时时间) */
+ /* 注意:增加等待时间到2000ms,因为URC可能异步延迟到达 */
+ uint32_t actual_timeout = (timeout_ms < 2000) ? 2000 : timeout_ms;
+ int ret = osal_sem_down_timeout(&g_tcpsetup_urc_sems[socket_id], actual_timeout);
+
+ if (ret == OSAL_SUCCESS) {
+ /* 收到URC,读取结果 */
+ osal_mutex_lock(&g_tcpsetup_urc_mutex);
+ *success = g_tcpsetup_urc_results[socket_id];
+ osal_mutex_unlock(&g_tcpsetup_urc_mutex);
+ PPP_DEBUG_INFO("+TCPSETUP URC received via semaphore: socket=%d, success=%d", socket_id, *success);
+ return true;
+ } else {
+ /* 超时 */
+ PPP_DEBUG_WARN("+TCPSETUP URC wait timeout for socket %d (timeout=%u ms, actual waited=%u ms)",
+ socket_id, timeout_ms, actual_timeout);
+
+ /* 即使超时,也检查一下是否有之前已经设置的结果(可能URC在等待之前就到了) */
+ osal_mutex_lock(&g_tcpsetup_urc_mutex);
+ if (g_tcpsetup_urc_results[socket_id] != false) { // 如果结果不是默认的false,说明URC已经到达过
+ *success = g_tcpsetup_urc_results[socket_id];
+ osal_mutex_unlock(&g_tcpsetup_urc_mutex);
+ PPP_DEBUG_INFO("+TCPSETUP URC result found (URC arrived before wait): socket=%d, success=%d", socket_id, *success);
+ return true;
+ }
+ osal_mutex_unlock(&g_tcpsetup_urc_mutex);
+
+ *success = false;
+ return false;
+ }
+}
+
/**
* @brief 连接到TCP服务器
+ *
+ * 实现了三个方案:
+ * 1. 方案1:连接前强制关闭旧socket
+ * 2. 方案2:如果当前socket有问题,尝试使用其他socket ID
+ * 3. 方案3:验证+TCPSETUP URC响应
*/
n706a_tcp_result_t n706a_tcp_connect(n706a_tcp_socket_t *sock,
const char *host,
return N706A_TCP_SUCCESS; // 已连接
}
+ /* ========== 方案1:连接前强制关闭旧socket ========== */
+ if (sock->state != N706A_TCP_STATE_CLOSED) {
+ PPP_DEBUG_INFO("Socket %d not in CLOSED state (current: %d), forcing close before reconnect",
+ sock->socket_id, sock->state);
+ n706a_tcp_result_t close_ret = n706a_tcp_close(sock);
+ if (close_ret != N706A_TCP_SUCCESS) {
+ PPP_DEBUG_WARN("Failed to close socket %d before reconnect, trying force close", sock->socket_id);
+ n706a_tcp_force_close_socket_by_id(sock->socket_id);
+ }
+ }
+
+ /* 额外检查:即使状态是CLOSED,也强制关闭模块端的socket(防止模块端状态不一致) */
+ PPP_DEBUG_DEBUG("Force closing socket %d at module side before new connection", sock->socket_id);
+ n706a_tcp_force_close_socket_by_id(sock->socket_id);
+
+ /* 等待模块端完全清理socket状态(避免状态混乱导致连接失败) */
+ osal_msleep(200);
+
/* 保存连接信息 */
strncpy(sock->remote_ip, host, sizeof(sock->remote_ip) - 1);
sock->remote_port = port;
PPP_DEBUG_CRITICAL("[N706A_TCP] Response: %s", response.response);
}
+ /* ========== 方案3:验证+TCPSETUP URC响应 ========== */
/* 检查响应是否包含OK且不包含ERROR */
if (ret == N706A_AT_OK && response.length > 0 &&
strstr(response.response, "OK") &&
!strstr(response.response, "ERROR")) {
- PPP_DEBUG_CRITICAL("[N706A_TCP] SSL TCP connection established successfully!");
- sock->state = N706A_TCP_STATE_CONNECTED;
- return N706A_TCP_SUCCESS;
+
+ /* 进一步验证:检查是否有+SSLTCPSETUP或+TCPSETUP URC */
+ bool urc_ok = false;
+ char urc_pattern[32];
+
+ /* SSL模式可能返回+SSLTCPSETUP或+TCPSETUP URC */
+ snprintf(urc_pattern, sizeof(urc_pattern), "+SSLTCPSETUP: %d,OK", sock->socket_id);
+ if (strstr(response.response, urc_pattern)) {
+ urc_ok = true;
+ } else {
+ snprintf(urc_pattern, sizeof(urc_pattern), "+TCPSETUP: %d,OK", sock->socket_id);
+ if (strstr(response.response, urc_pattern)) {
+ urc_ok = true;
+ } else {
+ /* 检查是否有FAIL */
+ snprintf(urc_pattern, sizeof(urc_pattern), "+SSLTCPSETUP: %d,FAIL", sock->socket_id);
+ if (strstr(response.response, urc_pattern)) {
+ PPP_DEBUG_ERROR("[N706A_TCP] Found +SSLTCPSETUP: %d,FAIL in response", sock->socket_id);
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
+
+ /* 等待异步URC */
+ bool urc_success = false;
+ if (n706a_tcp_wait_for_tcpsetup_urc(sock->socket_id, 500, &urc_success)) {
+ urc_ok = urc_success;
+ } else {
+ PPP_DEBUG_WARN("[N706A_TCP] +SSLTCPSETUP URC wait timeout, but command returned OK, assuming success");
+ urc_ok = true;
+ }
+ }
+ }
+
+ if (urc_ok) {
+ PPP_DEBUG_CRITICAL("[N706A_TCP] SSL TCP connection established successfully!");
+ sock->state = N706A_TCP_STATE_CONNECTED;
+ return N706A_TCP_SUCCESS;
+ } else {
+ PPP_DEBUG_ERROR("[N706A_TCP] SSL TCP connection failed: URC indicates FAIL");
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
} else {
PPP_DEBUG_ERROR("[N706A_TCP] SSL TCP connection failed! Response: %s",
response.length > 0 ? response.response : "(empty)");
+
+ /* ========== 方案2:如果当前socket连接失败,尝试使用其他socket ID ========== */
+ int free_socket_id = n706a_tcp_find_free_socket_id();
+ if (free_socket_id >= 0 && free_socket_id != sock->socket_id) {
+ PPP_DEBUG_WARN("[N706A_TCP] SSL connection failed on socket %d, trying free socket %d",
+ sock->socket_id, free_socket_id);
+
+ /* 更新socket ID并重试 */
+ int old_socket_id = sock->socket_id;
+ sock->socket_id = free_socket_id;
+
+ /* 重新注册到全局表 */
+ if (g_socket_table_initialized) {
+ osal_mutex_lock(&g_socket_table_mutex);
+ if (g_tcp_sockets[old_socket_id] == sock) {
+ g_tcp_sockets[old_socket_id] = NULL;
+ }
+ g_tcp_sockets[free_socket_id] = sock;
+ osal_mutex_unlock(&g_socket_table_mutex);
+ }
+
+ /* 重新初始化URC buffer */
+ n706a_tcp_free_urc_buffer(sock);
+ n706a_tcp_result_t buffer_ret = n706a_tcp_init_urc_buffer(sock, 0);
+ if (buffer_ret != N706A_TCP_SUCCESS) {
+ PPP_DEBUG_ERROR("[N706A_TCP] Failed to reinit URC buffer for new socket %d", free_socket_id);
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
+
+ /* 强制关闭新socket */
+ n706a_tcp_force_close_socket_by_id(free_socket_id);
+
+ /* 递归重试连接 */
+ return n706a_tcp_connect(sock, host, port, use_ssl, timeout_ms);
+ }
+
sock->state = N706A_TCP_STATE_ERROR;
return N706A_TCP_ECONNREF;
}
PPP_DEBUG_CRITICAL("[N706A_TCP] Response: %s", response.response);
}
+ /* ========== 方案3:验证+TCPSETUP URC响应 ========== */
/* 检查响应是否包含OK且不包含ERROR */
if (ret == N706A_AT_OK && response.length > 0 &&
strstr(response.response, "OK") &&
!strstr(response.response, "ERROR")) {
- PPP_DEBUG_CRITICAL("[N706A_TCP] TCP connection established successfully!");
- sock->state = N706A_TCP_STATE_CONNECTED;
- return N706A_TCP_SUCCESS;
+
+ /* 进一步验证:检查是否有+TCPSETUP URC(可能在响应中,也可能是异步URC) */
+ bool urc_ok = false;
+
+ /* 检查响应中是否包含+TCPSETUP: n,OK */
+ char urc_pattern[32];
+ snprintf(urc_pattern, sizeof(urc_pattern), "+TCPSETUP: %d,OK", sock->socket_id);
+ if (strstr(response.response, urc_pattern)) {
+ urc_ok = true;
+ PPP_DEBUG_CRITICAL("[N706A_TCP] Found +TCPSETUP: %d,OK in response", sock->socket_id);
+ } else {
+ /* 检查是否有+TCPSETUP: n,FAIL */
+ snprintf(urc_pattern, sizeof(urc_pattern), "+TCPSETUP: %d,FAIL", sock->socket_id);
+ if (strstr(response.response, urc_pattern)) {
+ PPP_DEBUG_ERROR("[N706A_TCP] Found +TCPSETUP: %d,FAIL in response", sock->socket_id);
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
+
+ /* 如果没有找到明确的URC,等待一下看是否有异步URC到达 */
+ bool urc_success = false;
+ if (n706a_tcp_wait_for_tcpsetup_urc(sock->socket_id, 500, &urc_success)) {
+ urc_ok = urc_success;
+ } else {
+ /* 超时,但命令返回OK,认为连接成功(可能是URC还没到达) */
+ PPP_DEBUG_WARN("[N706A_TCP] +TCPSETUP URC wait timeout, but command returned OK, assuming success");
+ urc_ok = true;
+ }
+ }
+
+ if (urc_ok) {
+ PPP_DEBUG_CRITICAL("[N706A_TCP] TCP connection established successfully!");
+ sock->state = N706A_TCP_STATE_CONNECTED;
+ return N706A_TCP_SUCCESS;
+ } else {
+ PPP_DEBUG_ERROR("[N706A_TCP] TCP connection failed: URC indicates FAIL");
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
} else {
PPP_DEBUG_ERROR("[N706A_TCP] TCP connection failed! Response: %s",
response.length > 0 ? response.response : "(empty)");
+
+ /* ========== 方案2:如果当前socket连接失败,尝试使用其他socket ID ========== */
+ /* 检查是否有空闲的socket ID可以使用 */
+ int free_socket_id = n706a_tcp_find_free_socket_id();
+ if (free_socket_id >= 0 && free_socket_id != sock->socket_id) {
+ PPP_DEBUG_WARN("[N706A_TCP] Connection failed on socket %d, trying free socket %d",
+ sock->socket_id, free_socket_id);
+
+ /* 更新socket ID并重试连接 */
+ int old_socket_id = sock->socket_id;
+ sock->socket_id = free_socket_id;
+
+ /* 重新注册到全局表 */
+ if (g_socket_table_initialized) {
+ osal_mutex_lock(&g_socket_table_mutex);
+ if (g_tcp_sockets[old_socket_id] == sock) {
+ g_tcp_sockets[old_socket_id] = NULL;
+ }
+ g_tcp_sockets[free_socket_id] = sock;
+ osal_mutex_unlock(&g_socket_table_mutex);
+ }
+
+ /* 重新初始化URC buffer(因为socket_id改变了) */
+ n706a_tcp_free_urc_buffer(sock);
+ n706a_tcp_result_t buffer_ret = n706a_tcp_init_urc_buffer(sock, 0);
+ if (buffer_ret != N706A_TCP_SUCCESS) {
+ PPP_DEBUG_ERROR("[N706A_TCP] Failed to reinit URC buffer for new socket %d", free_socket_id);
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
+
+ /* 强制关闭新socket(确保干净状态) */
+ n706a_tcp_force_close_socket_by_id(free_socket_id);
+
+ /* 递归重试连接(使用新的socket_id) */
+ return n706a_tcp_connect(sock, host, port, use_ssl, timeout_ms);
+ }
+
sock->state = N706A_TCP_STATE_ERROR;
return N706A_TCP_ECONNREF;
}
ret = n706a_at_send_command(at_cmd, NULL, timeout_ms, &response);
- PPP_DEBUG_CRITICAL("[N706A_TCP] AT+TCPSETUP (mbedtls mode) response: ret=%d, len=%u", ret, response.length);
+ PPP_DEBUG_CRITICAL("[N706A_TCP] AT+TCPSETUP response: ret=%d, len=%u", ret, response.length);
if (response.length > 0) {
PPP_DEBUG_CRITICAL("[N706A_TCP] Response: %s", response.response);
}
+ /* ========== 方案3:验证+TCPSETUP URC响应 ========== */
/* N706A的AT+TCPSETUP成功响应格式:
* AT+TCPSETUP=0,host,port
* OK
+ * +TCPSETUP: 0,OK (异步URC,可能在响应中或稍后到达)
*
- * 只需检查是否包含"OK"且不包含"ERROR"
+ * 需要检查是否包含"OK"且不包含"ERROR",同时验证URC
*/
if (ret != N706A_AT_OK || response.length == 0 ||
!strstr(response.response, "OK") ||
strstr(response.response, "ERROR")) {
PPP_DEBUG_ERROR("[N706A_TCP] TCP connection failed! Response: %s",
response.length > 0 ? response.response : "(empty)");
+
+ /* ========== 方案2:如果当前socket连接失败,尝试使用其他socket ID ========== */
+ int free_socket_id = n706a_tcp_find_free_socket_id();
+ if (free_socket_id >= 0 && free_socket_id != sock->socket_id) {
+ PPP_DEBUG_WARN("[N706A_TCP] Connection failed on socket %d (mbedtls mode), trying free socket %d",
+ sock->socket_id, free_socket_id);
+
+ /* 更新socket ID并重试 */
+ int old_socket_id = sock->socket_id;
+ sock->socket_id = free_socket_id;
+
+ /* 重新注册到全局表 */
+ if (g_socket_table_initialized) {
+ osal_mutex_lock(&g_socket_table_mutex);
+ if (g_tcp_sockets[old_socket_id] == sock) {
+ g_tcp_sockets[old_socket_id] = NULL;
+ }
+ g_tcp_sockets[free_socket_id] = sock;
+ osal_mutex_unlock(&g_socket_table_mutex);
+ }
+
+ /* 重新初始化URC buffer */
+ n706a_tcp_free_urc_buffer(sock);
+ n706a_tcp_result_t buffer_ret = n706a_tcp_init_urc_buffer(sock, 0);
+ if (buffer_ret != N706A_TCP_SUCCESS) {
+ PPP_DEBUG_ERROR("[N706A_TCP] Failed to reinit URC buffer for new socket %d", free_socket_id);
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
+
+ /* 强制关闭新socket */
+ n706a_tcp_force_close_socket_by_id(free_socket_id);
+
+ /* 递归重试连接 */
+ return n706a_tcp_connect(sock, host, port, use_ssl, timeout_ms);
+ }
+
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
+
+ /* 进一步验证+TCPSETUP URC */
+ bool urc_ok = false;
+ char urc_pattern[32];
+ snprintf(urc_pattern, sizeof(urc_pattern), "+TCPSETUP: %d,OK", sock->socket_id);
+ if (strstr(response.response, urc_pattern)) {
+ urc_ok = true;
+ PPP_DEBUG_CRITICAL("[N706A_TCP] Found +TCPSETUP: %d,OK in response", sock->socket_id);
+ } else {
+ /* 检查是否有FAIL */
+ snprintf(urc_pattern, sizeof(urc_pattern), "+TCPSETUP: %d,FAIL", sock->socket_id);
+ if (strstr(response.response, urc_pattern)) {
+ PPP_DEBUG_ERROR("[N706A_TCP] Found +TCPSETUP: %d,FAIL in response", sock->socket_id);
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
+
+ /* 检查响应中是否有SSLTCPSETUP URC(SSL模式) */
+ snprintf(urc_pattern, sizeof(urc_pattern), "+SSLTCPSETUP: %d,OK", sock->socket_id);
+ if (strstr(response.response, urc_pattern)) {
+ urc_ok = true;
+ PPP_DEBUG_CRITICAL("[N706A_TCP] Found +SSLTCPSETUP: %d,OK in response", sock->socket_id);
+ } else {
+ snprintf(urc_pattern, sizeof(urc_pattern), "+SSLTCPSETUP: %d,FAIL", sock->socket_id);
+ if (strstr(response.response, urc_pattern)) {
+ PPP_DEBUG_ERROR("[N706A_TCP] Found +SSLTCPSETUP: %d,FAIL in response", sock->socket_id);
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
+ }
+
+ /* 如果响应中没有找到URC,等待异步URC到达(最多等待2秒) */
+ if (!urc_ok) {
+ PPP_DEBUG_WARN("[N706A_TCP] No URC found in response, waiting for async URC...");
+ bool urc_success = false;
+ if (n706a_tcp_wait_for_tcpsetup_urc(sock->socket_id, 2000, &urc_success)) {
+ urc_ok = urc_success;
+ if (urc_ok) {
+ PPP_DEBUG_CRITICAL("[N706A_TCP] +TCPSETUP URC received: OK");
+ } else {
+ PPP_DEBUG_ERROR("[N706A_TCP] +TCPSETUP URC received: FAIL");
+ }
+ } else {
+ /* URC超时,但命令返回OK,假设连接成功 */
+ PPP_DEBUG_WARN("[N706A_TCP] +TCPSETUP URC timeout, but command returned OK, assuming success");
+ urc_ok = true;
+ }
+ }
+ }
+
+ if (!urc_ok) {
+ PPP_DEBUG_ERROR("[N706A_TCP] TCP connection failed: URC indicates FAIL");
+
+ /* ========== 方案2:如果当前socket连接失败,尝试使用其他socket ID ========== */
+ int free_socket_id = n706a_tcp_find_free_socket_id();
+ if (free_socket_id >= 0 && free_socket_id != sock->socket_id) {
+ PPP_DEBUG_WARN("[N706A_TCP] Connection failed on socket %d, trying free socket %d",
+ sock->socket_id, free_socket_id);
+
+ /* 更新socket ID并重试 */
+ int old_socket_id = sock->socket_id;
+ sock->socket_id = free_socket_id;
+
+ /* 重新注册到全局表 */
+ if (g_socket_table_initialized) {
+ osal_mutex_lock(&g_socket_table_mutex);
+ if (g_tcp_sockets[old_socket_id] == sock) {
+ g_tcp_sockets[old_socket_id] = NULL;
+ }
+ g_tcp_sockets[free_socket_id] = sock;
+ osal_mutex_unlock(&g_socket_table_mutex);
+ }
+
+ /* 重新初始化URC buffer */
+ n706a_tcp_free_urc_buffer(sock);
+ n706a_tcp_result_t buffer_ret = n706a_tcp_init_urc_buffer(sock, 0);
+ if (buffer_ret != N706A_TCP_SUCCESS) {
+ PPP_DEBUG_ERROR("[N706A_TCP] Failed to reinit URC buffer for new socket %d", free_socket_id);
+ sock->state = N706A_TCP_STATE_ERROR;
+ return N706A_TCP_ECONNREF;
+ }
+
+ /* 强制关闭新socket */
+ n706a_tcp_force_close_socket_by_id(free_socket_id);
+
+ /* 递归重试连接 */
+ return n706a_tcp_connect(sock, host, port, use_ssl, timeout_ms);
+ }
+
sock->state = N706A_TCP_STATE_ERROR;
return N706A_TCP_ECONNREF;
}
int handshake_attempts = 0;
while ((handshake_ret = mbedtls_ssl_handshake(&sock->ssl)) != 0) {
handshake_attempts++;
- if (handshake_ret != MBEDTLS_ERR_SSL_WANT_READ &&
- handshake_ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+
+ if (handshake_ret == MBEDTLS_ERR_SSL_WANT_READ) {
+ if (handshake_attempts == 1 || handshake_attempts % 50 == 0) {
+ PPP_DEBUG_DEBUG("[N706A_TCP] SSL handshake waiting for data... (attempt %d)",
+ handshake_attempts);
+ }
+ } else if (handshake_ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ PPP_DEBUG_DEBUG("[N706A_TCP] SSL handshake needs to send data... (attempt %d)", handshake_attempts);
+ } else {
/* 握手失败 */
PPP_DEBUG_ERROR("[N706A_TCP] SSL handshake failed after %d attempts! Error: -0x%04x",
handshake_attempts, -handshake_ret);
sock->state = N706A_TCP_STATE_ERROR;
return N706A_TCP_ERROR;
}
-
- if (handshake_attempts % 10 == 0) {
- PPP_DEBUG_DEBUG("[N706A_TCP] SSL handshake in progress... (attempt %d)", handshake_attempts);
- }
}
PPP_DEBUG_CRITICAL("[N706A_TCP] SSL handshake completed successfully after %d attempts", handshake_attempts);
#endif
/* AT指令模式或普通TCP */
+ /* 注意:使用HEX模式发送(mode=1),数据需要编码为HEX字符串 */
char at_cmd[64];
n706a_at_response_t response;
#ifdef N706A_SSL_USE_AT_COMMAND
if (sock->is_ssl) {
- snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPSEND=%d,%zu",
+ /* HEX模式:AT+SSLTCPSEND=<n>,<binary_len>,,1 */
+ snprintf(at_cmd, sizeof(at_cmd), "AT+SSLTCPSEND=%d,%zu,,1",
sock->socket_id, len);
} else
#endif
{
- snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu",
+ /* HEX模式:AT+TCPSEND=<n>,<binary_len>,,1 */
+ snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu,,1",
sock->socket_id, len);
}
- /* 发送命令,等待'>'提示符 */
+ /* 发送 AT+TCPSEND 命令 */
n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 5000, &response);
if (ret != N706A_AT_OK) {
+ PPP_DEBUG_ERROR("Failed to send %s command", at_cmd);
+ return N706A_TCP_ERROR;
+ }
+
+ /* 检查响应中是否包含 '>' 提示符(根据AT命令手册要求) */
+ if (strstr((const char *)response.response, ">") == NULL) {
+ PPP_DEBUG_ERROR("Response does not contain '>' prompt: %s", response.response);
return N706A_TCP_ERROR;
}
+ /* 延迟50-100ms(根据AT命令手册要求,这里取75ms) */
+ PPP_DEBUG_DEBUG("Received '>' prompt, delaying 75ms before sending data...");
+ osal_msleep(75);
+
/* 发送实际数据 */
ret = ppp_at_send_raw_data(data, len, sock->timeout_ms);
if (ret != N706A_AT_OK) {
}
#endif
- /* 发送关闭命令 */
+ /* 发送关闭命令,必须确认关闭成功 */
char at_cmd[32];
n706a_at_response_t response;
snprintf(at_cmd, sizeof(at_cmd), "AT+TCPCLOSE=%d", sock->socket_id);
}
- n706a_at_send_command(at_cmd, NULL, 5000, &response);
+ /* 尝试关闭socket,最多重试3次 */
+ n706a_at_error_t close_ret;
+ int retry_count = 0;
+ const int max_retries = 3;
+ bool close_success = false;
+ while (retry_count < max_retries) {
+ close_ret = n706a_at_send_command(at_cmd, "OK", 5000, &response);
+
+ if (close_ret == N706A_AT_OK && response.length > 0 &&
+ strstr(response.response, "OK") &&
+ !strstr(response.response, "ERROR")) {
+ /* 关闭成功 */
+ PPP_DEBUG_INFO("Socket %d closed successfully (attempt %d/%d)",
+ sock->socket_id, retry_count + 1, max_retries);
+ close_success = true;
+ break;
+ }
+
+ retry_count++;
+ if (retry_count < max_retries) {
+ PPP_DEBUG_WARN("Socket %d close attempt %d/%d failed, retrying...",
+ sock->socket_id, retry_count, max_retries);
+ /* 短暂延迟后重试(使用简单的忙等待,避免引入额外依赖) */
+ /* 注意:这里使用简单的循环延迟,在实际应用中可以考虑使用定时器 */
+ volatile int delay_count = 0;
+ for (volatile int i = 0; i < 10000; i++) { // 约100ms延迟(取决于CPU频率)
+ delay_count++;
+ }
+ } else {
+ PPP_DEBUG_ERROR("Socket %d close failed after %d attempts! Response: %s",
+ sock->socket_id, max_retries,
+ response.length > 0 ? response.response : "(empty)");
+ /* 即使失败也标记为关闭,避免资源泄露,但记录错误 */
+ }
+ }
+
+ /* 无论成功与否,都标记为关闭(避免资源泄露) */
+ /* 但如果关闭失败,socket在模块端可能仍在打开状态,需要等待或强制关闭 */
sock->state = N706A_TCP_STATE_CLOSED;
sock->is_ssl = false;
mbedtls_ctr_drbg_random,
&sock->ctr_drbg);
+ /* 设置握手超时时间(30秒) */
+ mbedtls_ssl_conf_handshake_timeout(&sock->conf, 30000, 60000);
+
/* 设置CA证书 */
mbedtls_ssl_conf_ca_chain(&sock->conf, &sock->cacert, NULL);
}
/**
- * @brief mbedtls发送回调(通过AT命令发送)
+ * @brief mbedtls发送回调(BIO接口)
+ *
+ * mbedtls会自动调用此回调发送加密后的数据。
+ *
+ * 数据流:
+ * 1. 应用调用 mbedtls_ssl_write(plaintext_data)
+ * 2. mbedtls内部加密 → 调用此回调,buf=加密后的二进制数据
+ * 3. 此函数将加密数据编码为HEX字符串发送到模块
+ *
+ * @param ctx socket上下文
+ * @param buf 加密后的二进制数据(由mbedtls提供,已加密)
+ * @param len 加密数据长度(字节数)
+ * @return 成功返回发送的字节数,失败返回负数错误码
*/
static int tcp_mbedtls_send_callback(void *ctx, const unsigned char *buf, size_t len)
{
return MBEDTLS_ERR_NET_SEND_FAILED;
}
- /* 通过AT+TCPSEND发送加密后的数据 */
+ /* buf已经是mbedtls加密后的二进制数据,直接通过AT+TCPSEND发送(使用HEX模式) */
char at_cmd[64];
- snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu",
+ snprintf(at_cmd, sizeof(at_cmd), "AT+TCPSEND=%d,%zu,,1",
sock->socket_id, len);
n706a_at_response_t response;
n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, 5000, &response);
if (ret != N706A_AT_OK) {
+ PPP_DEBUG_ERROR("Failed to send %s command (mbedtls)", at_cmd);
+ return MBEDTLS_ERR_NET_SEND_FAILED;
+ }
+
+ /* 检查响应中是否包含 '>' 提示符(根据AT命令手册要求) */
+ if (strstr((const char *)response.response, ">") == NULL) {
+ PPP_DEBUG_ERROR("Response does not contain '>' prompt (mbedtls): %s", response.response);
return MBEDTLS_ERR_NET_SEND_FAILED;
}
- /* 发送数据 */
+ /* 延迟50-100ms(根据AT命令手册要求,这里取75ms) */
+ PPP_DEBUG_DEBUG("Received '>' prompt (mbedtls), delaying 75ms before sending data...");
+ osal_msleep(75);
+
+ /* 发送加密后的二进制数据(ppp_at_send_raw_data内部会将二进制编码为HEX字符串发送) */
ret = ppp_at_send_raw_data(buf, len, sock->timeout_ms);
if (ret != N706A_AT_OK) {
return MBEDTLS_ERR_NET_SEND_FAILED;
}
+ /* 返回实际发送的字节数(len是加密后的数据长度) */
return len;
}
/**
- * @brief mbedtls接收回调(通过AT命令接收)
+ * @brief mbedtls接收回调(基于URC buffer,无AT命令,避免死锁)
+ *
+ * 设计思路:
+ * 1. 从URC buffer读取数据(如果有)
+ * 2. 如果buffer空,等待信号量(有超时)
+ * 3. 超时返回WANT_READ,让mbedtls重试
+ * 4. ✓ 全程不发送AT命令,避免与SSL握手的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) {
+ PPP_DEBUG_ERROR("[mbedtls_recv] Invalid parameters");
+ return MBEDTLS_ERR_NET_INVALID_CONTEXT;
+ }
+
+ if (!sock->urc_recv_buffer || !sock->urc_recv_mutex || !sock->urc_data_available_sem) {
+ PPP_DEBUG_ERROR("[mbedtls_recv] URC buffer not initialized for socket %d", sock->socket_id);
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);
+ osal_mutex *mutex = (osal_mutex *)sock->urc_recv_mutex;
+ osal_semaphore *sem = (osal_semaphore *)sock->urc_data_available_sem;
- n706a_at_response_t response;
- n706a_at_error_t ret = n706a_at_send_command(at_cmd, NULL, sock->timeout_ms, &response);
- if (ret != N706A_AT_OK) {
- return MBEDTLS_ERR_NET_RECV_FAILED;
+ // 第一次尝试:检查buffer是否有数据
+ osal_mutex_lock(mutex);
+
+ if (sock->urc_recv_length > 0) {
+ // ✓ 有数据,直接读取
+ size_t copy_len = (sock->urc_recv_length < len) ? sock->urc_recv_length : len;
+ memcpy(buf, sock->urc_recv_buffer, copy_len);
+
+ // 移动剩余数据到缓冲区开头
+ if (copy_len < sock->urc_recv_length) {
+ memmove(sock->urc_recv_buffer, sock->urc_recv_buffer + copy_len,
+ sock->urc_recv_length - copy_len);
+ }
+ sock->urc_recv_length -= copy_len;
+
+ osal_mutex_unlock(mutex);
+
+ PPP_DEBUG_DEBUG("[mbedtls_recv] Read %zu bytes from URC buffer (socket=%d, remaining=%u)",
+ copy_len, sock->socket_id, (unsigned int)sock->urc_recv_length);
+ return (int)copy_len;
}
- /* 解析数据 */
- 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);
+ osal_mutex_unlock(mutex);
+
+ // Buffer空,等待URC数据到达
+ // 使用静态计数器减少日志打印频率(只在第一次或每100次超时时打印)
+ static uint32_t s_wait_counter = 0;
+ s_wait_counter++;
+
+ if (s_wait_counter == 1 || (s_wait_counter % 100) == 0) {
+ PPP_DEBUG_DEBUG("[mbedtls_recv] Buffer empty, waiting for URC data (socket=%d, wait_count=%u)...",
+ sock->socket_id, s_wait_counter);
+ }
+
+ // 等待信号量(超时设置为100ms,避免长时间阻塞)
+ // mbedtls会在WANT_READ后重试,所以短超时是合理的
+ int ret = osal_sem_down_timeout(sem, 100); // 100ms超时
+
+ if (ret == 0) { // 0表示成功
+ // 有新数据到达,重置计数器
+ s_wait_counter = 0;
+
+ // 有新数据到达,重新尝试读取
+ osal_mutex_lock(mutex);
+
+ if (sock->urc_recv_length > 0) {
+ size_t copy_len = (sock->urc_recv_length < len) ? sock->urc_recv_length : len;
+ memcpy(buf, sock->urc_recv_buffer, copy_len);
- if (data_len > len) {
- data_len = len;
+ if (copy_len < sock->urc_recv_length) {
+ memmove(sock->urc_recv_buffer, sock->urc_recv_buffer + copy_len,
+ sock->urc_recv_length - copy_len);
}
+ sock->urc_recv_length -= copy_len;
+
+ osal_mutex_unlock(mutex);
- memcpy(buf, data_start, data_len);
- return data_len;
+ PPP_DEBUG_DEBUG("[mbedtls_recv] Read %zu bytes after wait (socket=%d)",
+ copy_len, sock->socket_id);
+ return (int)copy_len;
}
+
+ osal_mutex_unlock(mutex);
}
- return 0; // 没有数据
+ // 超时或无数据,返回WANT_READ让mbedtls重试
+ // 这是正常的,mbedtls会不断重试直到收到数据或真正超时
+ // 减少超时日志打印频率(只在每100次或特定次数时打印)
+ if (s_wait_counter == 1 || (s_wait_counter % 100) == 0) {
+ PPP_DEBUG_DEBUG("[mbedtls_recv] Timeout, returning WANT_READ (socket=%d, wait_count=%u)",
+ sock->socket_id, s_wait_counter);
+ }
+ return MBEDTLS_ERR_SSL_WANT_READ;
}
#endif /* N706A_SSL_USE_MBEDTLS */
sock->urc_recv_capacity = buffer_size;
sock->urc_recv_length = 0;
- // 初始化互斥锁
- osal_mutex *mutex = (osal_mutex *)malloc(sizeof(osal_mutex));
- if (!mutex) {
+ // 使用全局资源池中的mutex和semaphore(已初始化)
+ if (sock->socket_id < 0 || sock->socket_id >= N706A_MAX_SOCKETS) {
free(sock->urc_recv_buffer);
sock->urc_recv_buffer = NULL;
- return N706A_TCP_ENOBUFS;
+ return N706A_TCP_EINVAL;
}
- osal_mutex_init(mutex);
- sock->urc_recv_mutex = mutex;
+ sock->urc_recv_mutex = &g_socket_urc_mutexes[sock->socket_id];
+ sock->urc_data_available_sem = &g_socket_urc_sems[sock->socket_id];
- PPP_DEBUG_DEBUG("URC buffer initialized: socket=%d, size=%u",
+ PPP_DEBUG_DEBUG("URC buffer initialized: socket=%d, size=%u (mutex+sem created)",
sock->socket_id, (unsigned int)buffer_size);
return N706A_TCP_SUCCESS;
return;
}
- if (sock->urc_recv_mutex) {
- osal_mutex *mutex = (osal_mutex *)sock->urc_recv_mutex;
- osal_mutex_destroy(mutex);
- free(mutex);
- sock->urc_recv_mutex = NULL;
- }
+ // 注意:mutex和semaphore是全局资源池中的,不需要销毁
+ // 只需要清除指针引用即可
+ sock->urc_recv_mutex = NULL;
+ sock->urc_data_available_sem = NULL;
+ // 释放缓冲区
if (sock->urc_recv_buffer) {
free(sock->urc_recv_buffer);
sock->urc_recv_buffer = NULL;
sock->urc_recv_capacity = 0;
sock->urc_recv_length = 0;
- PPP_DEBUG_DEBUG("URC buffer freed: socket=%d", sock->socket_id);
+ PPP_DEBUG_DEBUG("URC buffer freed: socket=%d (mutex+sem destroyed)", sock->socket_id);
}
/**
size_t available_space = sock->urc_recv_capacity - sock->urc_recv_length;
size_t copy_len = (length <= available_space) ? length : available_space;
+ bool data_added = false;
+
if (copy_len < length) {
PPP_DEBUG_WARN("URC buffer overflow: socket=%d, need=%u, available=%u",
socket_id, (unsigned int)length, (unsigned int)available_space);
if (copy_len > 0) {
memcpy(sock->urc_recv_buffer + sock->urc_recv_length, data, copy_len);
sock->urc_recv_length += copy_len;
+ data_added = true;
- PPP_DEBUG_INFO("URC data buffered: socket=%d, received=%u, total=%u/%u",
+ PPP_DEBUG_DEBUG("URC data buffered: socket=%d, received=%u, total=%u/%u",
socket_id, (unsigned int)copy_len,
(unsigned int)sock->urc_recv_length,
(unsigned int)sock->urc_recv_capacity);
}
+ // 通知等待的recv函数(mbedtls recv回调)
+ if (data_added && sock->urc_data_available_sem) {
+ osal_semaphore *sem = (osal_semaphore *)sock->urc_data_available_sem;
+ osal_sem_up(sem); // 增加信号量计数,唤醒等待者
+ }
+
osal_mutex_unlock(mutex);
+
+ if (copy_len < length) {
+ PPP_DEBUG_WARN("URC buffer overflow: socket=%d, dropped %u bytes",
+ socket_id, (unsigned int)(length - copy_len));
+ }
}
*/
#include "n706a_websocket.h"
+#include "n706a_tcp_client.h"
+#include "ppp_debug.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
+#include <stdint.h>
+
+#ifdef __unix__
+#include <pthread.h>
+#include <unistd.h>
+#elif defined(__rtthread__)
+#include <rtthread.h>
+#endif
/* WebSocket GUID(RFC 6455) */
#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
n706a_ws_on_error_callback_t on_error,
n706a_ws_on_close_callback_t on_close)
{
- /* 查找空闲slot */
- int client_id = -1;
+ /* 查找空闲的WebSocket客户端slot */
+ int client_slot = -1;
for (int i = 0; i < N706A_WS_MAX_CLIENTS; i++) {
if (g_ws_clients[i] == NULL) {
- client_id = i;
+ client_slot = i;
break;
}
}
- if (client_id < 0) {
+ if (client_slot < 0) {
return -1; // 没有可用slot
}
memset(client, 0, sizeof(n706a_websocket_client_t));
- client->client_id = client_id;
+ /* 使用TCP的socket ID分配机制,避免与HTTP冲突 */
+ int socket_id = n706a_tcp_find_free_socket_id();
+ if (socket_id < 0 || socket_id > 5) {
+ PPP_DEBUG_ERROR("[WebSocket] No free socket ID available for WebSocket client");
+ free(client);
+ return -1;
+ }
+
+ client->client_id = client_slot; /* WebSocket客户端slot */
client->state = N706A_WS_STATE_CLOSED;
client->is_ssl = false;
- /* 初始化TCP socket */
- n706a_tcp_socket_create(&client->tcp_sock, client_id);
+ /* 初始化TCP socket(使用自动分配的socket_id,避免与HTTP冲突) */
+ n706a_tcp_socket_create(&client->tcp_sock, socket_id);
+ PPP_DEBUG_INFO("[WebSocket] Auto-allocated socket ID: %d (client_slot: %d)", socket_id, client_slot);
/* 设置回调 */
client->on_open = on_open;
client->recv_buffer_len = 0;
client->running = false;
- g_ws_clients[client_id] = client;
+ g_ws_clients[client_slot] = client;
- return client_id;
+ return client_slot;
}
/**
/* 启动接收线程 */
client->running = true;
- // TODO: 创建接收线程
- // pthread_create(&client->recv_thread, NULL, ws_recv_thread_func, client);
+#ifdef __unix__
+ if (pthread_create((pthread_t *)&client->recv_thread, NULL, ws_recv_thread_func, client) != 0) {
+ PPP_DEBUG_ERROR("Failed to create WebSocket receive thread for client %d", client_id);
+ client->running = false;
+ return N706A_WS_ERROR_SEND_FAIL;
+ }
+#elif defined(__rtthread__)
+ /* RT-Thread使用rt_thread_create */
+ rt_thread_t thread = rt_thread_create("ws_recv",
+ (void (*)(void *))ws_recv_thread_func,
+ client,
+ 4096,
+ 20,
+ 5);
+ if (thread) {
+ client->recv_thread = thread;
+ rt_thread_startup(thread);
+ } else {
+ PPP_DEBUG_ERROR("Failed to create WebSocket receive thread for client %d", client_id);
+ client->running = false;
+ return N706A_WS_ERROR_SEND_FAIL;
+ }
+#else
+ /* 其他平台暂不支持线程 */
+ PPP_DEBUG_WARN("WebSocket receive thread not supported on this platform");
+#endif
/* 调用on_open回调 */
if (client->on_open) {
/* 停止接收线程 */
client->running = false;
- // TODO: 等待线程结束
- // pthread_join(client->recv_thread, NULL);
+#ifdef __unix__
+ if (client->recv_thread) {
+ pthread_join((pthread_t)client->recv_thread, NULL);
+ client->recv_thread = NULL;
+ }
+#elif defined(__rtthread__)
+ if (client->recv_thread) {
+ rt_thread_delete((rt_thread_t)client->recv_thread);
+ client->recv_thread = NULL;
+ }
+#endif
/* 关闭TCP连接 */
n706a_tcp_close(&client->tcp_sock);
}
}
+ /* 接收masking key(如果有) */
+ uint8_t mask_key[4] = {0};
+ if (masked) {
+ n706a_tcp_recv(&client->tcp_sock, mask_key, 4, &received, 5000);
+ if (received < 4) {
+ return -1;
+ }
+ }
+
/* 接收payload */
if (payload_len > 0) {
uint8_t *payload = (uint8_t *)malloc(payload_len + 1);
}
n706a_tcp_recv(&client->tcp_sock, payload, payload_len, &received, 10000);
+ if (received < payload_len) {
+ free(payload);
+ return -1;
+ }
payload[payload_len] = '\0';
+ /* 如果数据被掩码,需要解掩码 */
+ if (masked) {
+ ws_mask_data(payload, payload_len, mask_key);
+ }
+
/* 处理不同类型的帧 */
switch (opcode) {
case N706A_WS_OPCODE_TEXT:
/* ========== 辅助函数 ========== */
/**
- * @brief 简化的SHA-1实现(仅用于WebSocket握手)
- * 注意:这是简化版,生产环境建议使用成熟的加密库
+ * @brief SHA-1哈希实现(用于WebSocket握手)
+ *
+ * 使用完整的SHA-1算法实现,符合RFC 3174标准
+ * 这个实现是独立的,不依赖外部加密库
*/
static void sha1_simple(const unsigned char *input, size_t len, unsigned char output[20])
{
- /* 这里应该是完整的SHA-1实现 */
- /* 为了简化,这里仅做占位 */
- /* 生产环境应使用mbedtls_sha1或OpenSSL的SHA1函数 */
+ uint32_t h[5] = {
+ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
+ };
+
+ uint32_t w[80];
+ uint32_t a, b, c, d, e, f, k, temp;
+ size_t i;
+
+ /* 创建填充后的消息缓冲区 */
+ size_t padded_len = ((len + 9 + 63) / 64) * 64;
+ unsigned char *padded = (unsigned char *)malloc(padded_len);
+ if (!padded) {
+ /* 内存分配失败,返回全0(不应该发生) */
+ memset(output, 0, 20);
+ return;
+ }
+
+ /* 复制原始数据 */
+ memcpy(padded, input, len);
+
+ /* 添加填充:首先是0x80字节 */
+ padded[len] = 0x80;
+
+ /* 填充0字节 */
+ memset(padded + len + 1, 0, padded_len - len - 9);
+
+ /* 添加长度(64位,大端序) */
+ uint64_t bit_len = (uint64_t)len * 8;
+ padded[padded_len - 8] = (unsigned char)((bit_len >> 56) & 0xFF);
+ padded[padded_len - 7] = (unsigned char)((bit_len >> 48) & 0xFF);
+ padded[padded_len - 6] = (unsigned char)((bit_len >> 40) & 0xFF);
+ padded[padded_len - 5] = (unsigned char)((bit_len >> 32) & 0xFF);
+ padded[padded_len - 4] = (unsigned char)((bit_len >> 24) & 0xFF);
+ padded[padded_len - 3] = (unsigned char)((bit_len >> 16) & 0xFF);
+ padded[padded_len - 2] = (unsigned char)((bit_len >> 8) & 0xFF);
+ padded[padded_len - 1] = (unsigned char)(bit_len & 0xFF);
+
+ /* 处理每个512位(64字节)块 */
+ for (size_t chunk = 0; chunk < padded_len; chunk += 64) {
+ /* 准备消息调度数组(w[0..15]) */
+ for (i = 0; i < 16; i++) {
+ w[i] = ((uint32_t)padded[chunk + i * 4] << 24) |
+ ((uint32_t)padded[chunk + i * 4 + 1] << 16) |
+ ((uint32_t)padded[chunk + i * 4 + 2] << 8) |
+ ((uint32_t)padded[chunk + i * 4 + 3]);
+ }
+
+ /* 扩展消息调度数组(w[16..79]) */
+ for (i = 16; i < 80; i++) {
+ temp = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
+ w[i] = (temp << 1) | (temp >> 31); /* 左循环移位1位 */
+ }
+
+ /* 初始化工作变量 */
+ a = h[0];
+ b = h[1];
+ c = h[2];
+ d = h[3];
+ e = h[4];
+
+ /* 主循环(80轮) */
+ for (i = 0; i < 80; i++) {
+ if (i < 20) {
+ f = (b & c) | ((~b) & d);
+ k = 0x5A827999;
+ } else if (i < 40) {
+ f = b ^ c ^ d;
+ k = 0x6ED9EBA1;
+ } else if (i < 60) {
+ f = (b & c) | (b & d) | (c & d);
+ k = 0x8F1BBCDC;
+ } else {
+ f = b ^ c ^ d;
+ k = 0xCA62C1D6;
+ }
+
+ temp = ((a << 5) | (a >> 27)) + f + e + k + w[i];
+ e = d;
+ d = c;
+ c = (b << 30) | (b >> 2);
+ b = a;
+ a = temp;
+ }
+
+ /* 添加到哈希值 */
+ h[0] += a;
+ h[1] += b;
+ h[2] += c;
+ h[3] += d;
+ h[4] += e;
+ }
+
+ /* 释放临时缓冲区 */
+ free(padded);
+
+ /* 输出哈希值(大端序) */
+ output[0] = (unsigned char)(h[0] >> 24);
+ output[1] = (unsigned char)(h[0] >> 16);
+ output[2] = (unsigned char)(h[0] >> 8);
+ output[3] = (unsigned char)h[0];
+ output[4] = (unsigned char)(h[1] >> 24);
+ output[5] = (unsigned char)(h[1] >> 16);
+ output[6] = (unsigned char)(h[1] >> 8);
+ output[7] = (unsigned char)h[1];
+ output[8] = (unsigned char)(h[2] >> 24);
+ output[9] = (unsigned char)(h[2] >> 16);
+ output[10] = (unsigned char)(h[2] >> 8);
+ output[11] = (unsigned char)h[2];
+ output[12] = (unsigned char)(h[3] >> 24);
+ output[13] = (unsigned char)(h[3] >> 16);
+ output[14] = (unsigned char)(h[3] >> 8);
+ output[15] = (unsigned char)h[3];
+ output[16] = (unsigned char)(h[4] >> 24);
+ output[17] = (unsigned char)(h[4] >> 16);
+ output[18] = (unsigned char)(h[4] >> 8);
+ output[19] = (unsigned char)h[4];
+}
+
+/**
+ * @brief WebSocket接收线程函数
+ *
+ * 持续接收WebSocket帧并调用回调函数通知应用层
+ */
+static void *ws_recv_thread_func(void *arg)
+{
+ n706a_websocket_client_t *client = (n706a_websocket_client_t *)arg;
+ if (!client) {
+ return NULL;
+ }
+
+ PPP_DEBUG_INFO("WebSocket receive thread started for client %d", client->client_id);
+
+ while (client->running && client->state == N706A_WS_STATE_OPEN) {
+ /* 尝试接收一帧数据 */
+ int ret = ws_recv_frame(client);
+
+ if (ret != 0) {
+ /* 接收失败,可能是连接断开或超时 */
+ if (client->running) {
+ PPP_DEBUG_WARN("WebSocket receive frame failed for client %d (ret=%d)",
+ client->client_id, ret);
+
+ /* 调用错误回调 */
+ if (client->on_error) {
+ client->on_error(client->client_id,
+ N706A_WS_ERROR_RECV_FAIL,
+ "Receive frame failed");
+ }
+
+ /* 自动关闭连接 */
+ client->state = N706A_WS_STATE_CLOSING;
+ break;
+ }
+ }
+
+ /* 如果状态变为CLOSING,退出循环 */
+ if (client->state == N706A_WS_STATE_CLOSING ||
+ client->state == N706A_WS_STATE_CLOSED) {
+ break;
+ }
+
+ /* 短暂延迟,避免CPU占用过高 */
+#ifdef __unix__
+ usleep(1000); // 1ms
+#elif defined(__rtthread__)
+ rt_thread_mdelay(1);
+#else
+ /* 其他平台:简单延迟循环 */
+ volatile int i;
+ for (i = 0; i < 1000; i++);
+#endif
+ }
- /* TODO: 集成真实的SHA-1实现 */
- memset(output, 0, 20);
+ PPP_DEBUG_INFO("WebSocket receive thread stopped for client %d", client->client_id);
- /* 临时:使用简单哈希作为占位符 */
- for (size_t i = 0; i < len && i < 20; i++) {
- output[i % 20] ^= input[i];
+ /* 如果是因为接收失败而退出,关闭连接 */
+ if (client->state == N706A_WS_STATE_OPEN || client->state == N706A_WS_STATE_CLOSING) {
+ client->state = N706A_WS_STATE_CLOSED;
+
+ /* 调用关闭回调 */
+ if (client->on_close) {
+ client->on_close(client->client_id,
+ N706A_WS_CLOSE_ABNORMAL,
+ "Receive thread exited");
+ }
}
+
+ return NULL;
}
/**
return -1;
}
- n706a_httpc_result_t ret = n706a_http_client_init(ppp_client, 0);
+ n706a_httpc_result_t ret = n706a_http_client_init(ppp_client, -1); /* 自动分配socket_id,避免与WebSocket冲突 */
if (ret != N706A_HTTP_SUCCESS) {
free(ppp_client);
return -1;