From de907e95a242bd9752a2aebf2e72b49f09de8fdb Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E5=AD=99=E6=98=A5=E9=98=B3?= Date: Mon, 11 Aug 2025 05:32:47 +0800 Subject: [PATCH] =?utf8?q?tcp=5Fsimple=5Fmanager=E6=B7=BB=E5=8A=A0dnscache?= =?utf8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../slip/tcpmanager/tcp_manager_at_cmd.c | 55 ++ .../slip/tcpmanager/tcp_manager_test.c | 55 +- .../slip/tcpmanager/tcp_simple_manager.c | 487 +++++++++++++++++- .../slip/tcpmanager/tcp_simple_manager.h | 20 + ws_lwip_test/tcp_test_server.py | 17 +- 5 files changed, 601 insertions(+), 33 deletions(-) diff --git a/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_manager_at_cmd.c b/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_manager_at_cmd.c index 975213421..def7e9688 100644 --- a/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_manager_at_cmd.c +++ b/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_manager_at_cmd.c @@ -391,6 +391,60 @@ static uint32_t tcp_stress_test_cmd(uint8_t *para, uint32_t paraLen, char *strCo } } + + +/** + * TCP管理命令 + * 格式: AT^TCPCMD + * 示例: AT^TCPCMD DNSCACHE + */ +static uint32_t tcp_manager_cmd(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + (void)strCommonCmd; + + printf("TCP管理命令: AT^TCPCMD\n"); + + if (paraLen == 0 || para == NULL) { + printf("用法: AT^TCPCMD <命令>\n"); + printf("支持的命令:\n"); + printf(" STATUS - 显示状态信息\n"); + printf(" DNSCACHE - 显示DNS缓存状态\n"); + return ERRCODE_FAIL; + } + + char cmd[64]; + snprintf(cmd, sizeof(cmd), "%.*s", (int)paraLen, (char*)para); + + if (strcmp(cmd, "STATUS") == 0) { + if (!g_tcp_manager_inited) { + printf("错误: TCP管理器未初始化\n"); + return ERRCODE_FAIL; + } + + tcp_manager_stats_t stats; + tcp_get_manager_stats(&stats); + printf("=== TCP管理器状态 ===\n"); + printf("总连接数: %u\n", stats.total_connections); + printf("活跃连接数: %u\n", stats.active_connections); + printf("发送字节数: %u\n", stats.bytes_sent); + printf("接收字节数: %u\n", stats.bytes_received); + printf("错误计数: %u\n", stats.errors); + printf("=====================\n"); + } else if (strcmp(cmd, "DNSCACHE") == 0) { + if (!g_tcp_manager_inited) { + printf("错误: TCP管理器未初始化\n"); + return ERRCODE_FAIL; + } + + tcp_show_dns_cache(); + } else { + printf("未知命令: %s\n", cmd); + return ERRCODE_FAIL; + } + + return ERRCODE_SUCC; +} + /* AT命令表 */ static const at_cmd_table_t g_tcp_manager_cmd[] = { { (char *)"AT^TCPINIT", tcp_manager_init_cmd, (char *)"1" }, @@ -400,6 +454,7 @@ static const at_cmd_table_t g_tcp_manager_cmd[] = { { (char *)"AT^TCPSEND", tcp_send_cmd, (char *)"1" }, { (char *)"AT^TCPSTATUS", tcp_status_cmd, (char *)"1" }, { (char *)"AT^TCPTEST", tcp_stress_test_cmd, (char *)"1" }, + { (char *)"AT^TCPCMD", tcp_manager_cmd, (char *)"1" }, }; /** diff --git a/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_manager_test.c b/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_manager_test.c index 4ad0ad7e0..4f55932f8 100644 --- a/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_manager_test.c +++ b/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_manager_test.c @@ -66,16 +66,22 @@ static tcp_stress_test_t g_stress_test = {0}; /* 压力测试回调函数 */ static void stress_test_on_connect(tcp_handle_t handle, bool success) { + printf("DEBUG: stress_test_on_connect 被调用: handle=%p, success=%d\n", handle, success); + /* 查找对应的连接 */ tcp_stress_connection_t *conn = NULL; for (int i = 0; i < TCP_MANAGER_MAX_CONNECTIONS; i++) { if (g_stress_test.connections[i].handle == handle) { conn = &g_stress_test.connections[i]; + printf("DEBUG: 找到连接 %d: conn_id=%d\n", i, conn->conn_id); break; } } - if (!conn) return; + if (!conn) { + printf("DEBUG: 警告: 找不到对应的连接 handle=%p\n", handle); + return; + } if (success) { conn->connected = true; @@ -539,7 +545,34 @@ int tcp_manager_stress_test(const char* host, uint16_t port, uint32_t test_durat /* 等待TCP管理器完成清理 */ printf("等待TCP管理器清理完成...\n"); - sys_msleep(2000); + /* 分多次检查,确保TCP连接正常关闭 */ + for (int wait_round = 0; wait_round < 10; wait_round++) { + sys_msleep(1000); /* 每次等待1秒 */ + + /* 检查连接状态 */ + tcp_manager_stats_t current_stats; + if (tcp_get_manager_stats(¤t_stats) == TCP_MANAGER_OK) { + printf("第%d次检查: 活跃连接=%u\n", wait_round + 1, current_stats.active_connections); + if (current_stats.active_connections == 0) { + printf("所有连接已成功关闭!\n"); + break; + } + } + + if (wait_round == 9) { + printf("等待超时,仍有连接未关闭\n"); + } + } + + /* 再次检查连接状态 */ + printf("检查连接清理状态...\n"); + for (uint32_t i = 0; i < g_stress_test.connection_count; i++) { + if (g_stress_test.connections[i].handle) { + printf("警告: 连接 %u 仍然存在,强制清理\n", i); + tcp_manager_close(g_stress_test.connections[i].handle); + g_stress_test.connections[i].handle = NULL; + } + } /* 重置连接状态 */ for (uint32_t i = 0; i < g_stress_test.connection_count; i++) { @@ -549,6 +582,16 @@ int tcp_manager_stress_test(const char* host, uint16_t port, uint32_t test_durat printf("所有连接已关闭\n"); + /* 显示最终统计信息 */ + tcp_manager_stats_t final_stats; + if (tcp_get_manager_stats(&final_stats) == TCP_MANAGER_OK) { + printf("最终统计: 总连接=%u, 活跃连接=%u\n", + final_stats.total_connections, final_stats.active_connections); + if (final_stats.active_connections > 0) { + printf("警告: 仍有 %u 个连接未清理!\n", final_stats.active_connections); + } + } + /* 强制清理TCP管理器 */ printf("清理TCP管理器...\n"); tcp_simple_manager_deinit(); @@ -636,8 +679,12 @@ int tcp_manager_basic_test(void) */ int tcp_manager_test_init(void) { - printf("TCP管理器测试模块初始化\n"); - /* 这里可以添加一些基本的初始化测试 */ + tcp_manager_result_t result = tcp_simple_manager_init(); + if (result == TCP_MANAGER_OK) { + printf("TCP管理器初始化成功\n"); + } else { + printf("TCP管理器初始化失败: %d\n", result); + } return 0; } diff --git a/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_simple_manager.c b/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_simple_manager.c index 98431f9e5..8f44e0dd5 100644 --- a/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_simple_manager.c +++ b/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_simple_manager.c @@ -1,18 +1,19 @@ /** - * TCP单线程管理器 v1.3 - 最终修复版本 + * TCP单线程管理器 v1.5 - lwIP API版本 * 修复内容: * 1. 增大线程栈空间:2KB -> 8KB * 2. 简化DNS解析逻辑,避免递归调用 * 3. 增强资源清理,防止内存泄漏 * 4. 优化错误处理,避免死循环 * 5. 使用正确的枚举值 - * TCP单线程管理器 v1.4 - 压力测试优化版本 - * 修复内容: - * 1. 改进DNS解析,支持并发解析 - * 2. 添加发送队列,避免发送阻塞 - * 3. 优化并发连接处理,增加连接间隔 - * 4. 改进错误处理,支持重试机制 - * 5. 增强连接状态管理 + * 6. 改进DNS解析,支持并发解析 + * 7. 添加发送队列,避免发送阻塞 + * 8. 优化并发连接处理,增加连接间隔 + * 9. 改进错误处理,支持重试机制 + * 10. 增强连接状态管理 + * 11. 完全使用lwIP Socket API,移除标准BSD socket依赖 + * 12. 添加DNS缓存机制,提高连接性能 + * 13. 强化连接清理逻辑,避免连接泄漏 */ #include "tcp_simple_manager.h" @@ -21,18 +22,16 @@ #include #include #include -#include -#include -#include -#include -#include "lwip/sockets.h" +/* 使用lwIP网络头文件替代标准网络头文件 */ +#include "lwip/sockets.h" /* 包含lwIP socket API和fd_set等定义 */ #include "lwip/netdb.h" #include "lwip/sys.h" #include "lwip/dns.h" #include "lwip/ip.h" #include "lwip/err.h" +#include "lwip/inet.h" -/* 重定义lwIP函数,确保使用标准接口 */ +/* 重定义lwIP函数,确保使用lwIP API */ #ifndef socket #define socket lwip_socket #define connect lwip_connect @@ -43,12 +42,70 @@ #define fcntl lwip_fcntl #define getsockopt lwip_getsockopt #define setsockopt lwip_setsockopt +#define shutdown lwip_shutdown +#define bind lwip_bind +#define listen lwip_listen +#define accept lwip_accept +// #define inet_aton lwip_inet_aton +// #define inet_ntoa lwip_inet_ntoa #endif +/* 使用lwIP的常量定义 */ #ifndef MSG_DONTWAIT #define MSG_DONTWAIT 0x40 #endif +#ifndef SHUT_RDWR +#define SHUT_RDWR 2 +#endif + +#ifndef SOL_SOCKET +#define SOL_SOCKET 0xfff +#endif + +#ifndef SO_LINGER +#define SO_LINGER 13 +#endif + +#ifndef SO_KEEPALIVE +#define SO_KEEPALIVE 9 +#endif + +#ifndef IPPROTO_TCP +#define IPPROTO_TCP 6 +#endif + +#ifndef TCP_KEEPIDLE +#define TCP_KEEPIDLE 1 +#endif + +#ifndef TCP_KEEPINTVL +#define TCP_KEEPINTVL 2 +#endif + +#ifndef TCP_KEEPCNT +#define TCP_KEEPCNT 3 +#endif + +/* struct linger已经在系统头文件中定义,无需重复定义 */ + +/* 确保使用lwIP错误代码 */ +#ifdef LWIP_ERRNO_STDINCLUDE +/* 如果lwIP使用标准errno,则使用lwIP定义 */ +#include "lwip/errno.h" +#else +/* 使用lwIP内部错误代码 */ +#ifndef EAGAIN +#define EAGAIN 11 +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif +#ifndef EINPROGRESS +#define EINPROGRESS 115 +#endif +#endif + /* 优化后的常量定义 */ #define TCP_RECV_BUFFER_SIZE 1024 #define TCP_SEND_QUEUE_SIZE 16 /* 每个连接的发送队列大小 */ @@ -85,6 +142,11 @@ static sys_mutex_t g_connections_mutex; static tcp_manager_stats_t g_stats; static dns_resolve_state_t g_dns_states[TCP_MANAGER_MAX_CONNECTIONS * 2]; /* 增加DNS状态槽位 */ +/* DNS缓存全局变量 */ +static dns_cache_entry_t g_dns_cache[DNS_CACHE_MAX_ENTRIES]; +static sys_mutex_t g_dns_cache_mutex; +static uint32_t g_dns_cache_access_counter = 0; + /* IO线程相关 */ static bool g_io_thread_running = false; static sys_thread_t g_io_thread; @@ -98,6 +160,14 @@ static int set_socket_nonblocking(int sockfd); static int setup_keepalive(int sockfd); static void tcp_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg); static int sync_dns_resolve(const char* hostname, struct in_addr* addr); + +/* DNS缓存函数声明 */ +static void dns_cache_init(void); +static void dns_cache_deinit(void); +static bool dns_cache_lookup(const char* hostname, struct in_addr* addr); +static void dns_cache_add(const char* hostname, const struct in_addr* addr); +static void dns_cache_cleanup_expired(void); +static int dns_resolve_with_cache(const char* hostname, struct in_addr* addr); static bool add_to_send_queue(tcp_connection_info_t* conn, const uint8_t* data, uint16_t length); static bool process_send_queue(tcp_connection_info_t* conn); static void reset_connection_retry(tcp_connection_info_t* conn); @@ -117,7 +187,7 @@ static void tcp_dns_found_callback(const char *name, const ip_addr_t *ipaddr, vo dns_state->failed = false; dns_state->resolved_ip = *ipaddr; printf("DNS解析成功: %s -> %s\n", dns_state->hostname, - inet_ntoa(*(struct in_addr*)&dns_state->resolved_ip.u_addr.ip4)); + inet_ntoa(*(struct in_addr*)&dns_state->resolved_ip.u_addr.ip4)); } else { dns_state->resolved = false; dns_state->failed = true; @@ -186,15 +256,25 @@ static int sync_dns_resolve(const char* hostname, struct in_addr* addr) printf("DNS缓存命中: %s\n", hostname); } else if (err == ERR_INPROGRESS) { /* DNS查询进行中,等待完成 */ + printf("DNS查询进行中,等待完成: %s\n", hostname); if (sys_arch_sem_wait(&dns_state->dns_sem, TCP_DNS_TIMEOUT_MS) != SYS_ARCH_TIMEOUT) { if (dns_state->resolved && !dns_state->failed) { /* DNS解析成功 */ memcpy(&addr->s_addr, &dns_state->resolved_ip.u_addr.ip4, 4); result = 0; + printf("DNS解析成功完成: %s -> %s\n", hostname, + inet_ntoa(*(struct in_addr*)&dns_state->resolved_ip.u_addr.ip4)); + } else if (dns_state->failed) { + printf("DNS解析失败完成: %s\n", hostname); + result = -1; } } else { printf("DNS解析超时: %s\n", hostname); + result = -1; } + } else { + printf("DNS查询启动失败: %s, 错误: %d\n", hostname, err); + result = -1; } /* 清理DNS状态 */ @@ -204,6 +284,200 @@ static int sync_dns_resolve(const char* hostname, struct in_addr* addr) return result; } +/** + * @brief 初始化DNS缓存 + */ +static void dns_cache_init(void) +{ + memset(g_dns_cache, 0, sizeof(g_dns_cache)); + g_dns_cache_access_counter = 0; + + if (sys_mutex_new(&g_dns_cache_mutex) != ERR_OK) { + printf("DNS缓存: 创建互斥量失败\n"); + } + + printf("DNS缓存: 初始化完成, 最大条目数=%d, TTL=%ds\n", + DNS_CACHE_MAX_ENTRIES, DNS_CACHE_TTL_MS / 1000); +} + +/** + * @brief 清理DNS缓存 + */ +static void dns_cache_deinit(void) +{ + sys_mutex_free(&g_dns_cache_mutex); + memset(g_dns_cache, 0, sizeof(g_dns_cache)); + printf("DNS缓存: 清理完成\n"); +} + +/** + * @brief 清理过期的DNS缓存条目 + */ +static void dns_cache_cleanup_expired(void) +{ + uint32_t current_time = sys_now(); + int cleaned_count = 0; + + for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) { + dns_cache_entry_t* entry = &g_dns_cache[i]; + if (entry->in_use) { + if ((current_time - entry->timestamp) > DNS_CACHE_TTL_MS) { + printf("DNS缓存: 清理过期条目 %s\n", entry->hostname); + memset(entry, 0, sizeof(dns_cache_entry_t)); + cleaned_count++; + } + } + } + + if (cleaned_count > 0) { + printf("DNS缓存: 清理了 %d 个过期条目\n", cleaned_count); + } +} + +/** + * @brief 查找DNS缓存 + * @param hostname 主机名 + * @param addr 输出IP地址 + * @return true表示缓存命中 + */ +static bool dns_cache_lookup(const char* hostname, struct in_addr* addr) +{ + if (!hostname || !addr) return false; + + sys_mutex_lock(&g_dns_cache_mutex); + + uint32_t current_time = sys_now(); + + for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) { + dns_cache_entry_t* entry = &g_dns_cache[i]; + if (entry->in_use && strcmp(entry->hostname, hostname) == 0) { + /* 检查是否过期 */ + if ((current_time - entry->timestamp) > DNS_CACHE_TTL_MS) { + printf("DNS缓存: 条目已过期 %s\n", hostname); + memset(entry, 0, sizeof(dns_cache_entry_t)); + sys_mutex_unlock(&g_dns_cache_mutex); + return false; + } + + /* 缓存命中 */ + *addr = entry->ip_addr; + entry->access_count = ++g_dns_cache_access_counter; /* 更新LRU */ + + printf("DNS缓存: 命中 %s -> %s (访问次数: %u)\n", + hostname, inet_ntoa(*addr), entry->access_count); + + sys_mutex_unlock(&g_dns_cache_mutex); + return true; + } + } + + sys_mutex_unlock(&g_dns_cache_mutex); + return false; +} + +/** + * @brief 添加DNS缓存条目 + * @param hostname 主机名 + * @param addr IP地址 + */ +static void dns_cache_add(const char* hostname, const struct in_addr* addr) +{ + if (!hostname || !addr) return; + + /* 检查主机名长度 */ + if (strlen(hostname) >= DNS_HOSTNAME_MAX_LEN) { + printf("DNS缓存: 主机名过长, 不缓存 %s\n", hostname); + return; + } + + sys_mutex_lock(&g_dns_cache_mutex); + + /* 先查找是否已存在相同的主机名 */ + for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) { + dns_cache_entry_t* entry = &g_dns_cache[i]; + if (entry->in_use && strcmp(entry->hostname, hostname) == 0) { + /* 更新现有条目 */ + entry->ip_addr = *addr; + entry->timestamp = sys_now(); + entry->access_count = ++g_dns_cache_access_counter; + printf("DNS缓存: 更新条目 %s -> %s\n", hostname, inet_ntoa(*addr)); + sys_mutex_unlock(&g_dns_cache_mutex); + return; + } + } + + /* 查找空闲槽位 */ + dns_cache_entry_t* free_entry = NULL; + for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) { + if (!g_dns_cache[i].in_use) { + free_entry = &g_dns_cache[i]; + break; + } + } + + /* 如果没有空闲槽位,使用LRU算法替换最旧的条目 */ + if (!free_entry) { + uint32_t oldest_access = UINT32_MAX; + int oldest_index = 0; + + for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) { + if (g_dns_cache[i].access_count < oldest_access) { + oldest_access = g_dns_cache[i].access_count; + oldest_index = i; + } + } + + free_entry = &g_dns_cache[oldest_index]; + printf("DNS缓存: LRU替换 %s -> %s\n", free_entry->hostname, hostname); + } + + /* 添加新条目 */ + memset(free_entry, 0, sizeof(dns_cache_entry_t)); + free_entry->in_use = true; + strncpy(free_entry->hostname, hostname, DNS_HOSTNAME_MAX_LEN - 1); + free_entry->ip_addr = *addr; + free_entry->timestamp = sys_now(); + free_entry->access_count = ++g_dns_cache_access_counter; + + printf("DNS缓存: 添加条目 %s -> %s\n", hostname, inet_ntoa(*addr)); + + sys_mutex_unlock(&g_dns_cache_mutex); +} + +/** + * @brief 带缓存的DNS解析函数 + * @param hostname 主机名 + * @param addr 输出IP地址 + * @return 0表示成功,-1表示失败 + */ +static int dns_resolve_with_cache(const char* hostname, struct in_addr* addr) +{ + if (!hostname || !addr) return -1; + + /* 首先检查是否为IP地址 */ + if (inet_aton(hostname, addr) != 0) { + printf("DNS解析: 直接使用IP地址 %s\n", hostname); + return 0; + } + + /* 查找缓存 */ + if (dns_cache_lookup(hostname, addr)) { + return 0; + } + + printf("DNS解析: 缓存未命中,进行网络解析 %s\n", hostname); + + /* 执行实际的DNS解析 */ + int result = sync_dns_resolve(hostname, addr); + + if (result == 0) { + /* 解析成功,添加到缓存 */ + dns_cache_add(hostname, addr); + } + + return result; +} + /** * @brief 设置socket为非阻塞模式 */ @@ -273,11 +547,43 @@ static void cleanup_connection_unsafe(tcp_connection_info_t* conn) printf("清理连接资源: handle=%p, sockfd=%d\n", conn->handle, conn->sockfd); - /* 关闭socket */ + /* 优雅关闭socket */ if (conn->sockfd >= 0) { - shutdown(conn->sockfd, SHUT_RDWR); /* 优雅关闭 */ + printf("关闭Socket: %d (状态: %d)\n", conn->sockfd, conn->state); + + /* 首先发送FIN,启动正常的TCP关闭流程 */ + if (conn->state == TCP_STATE_CONNECTED) { + printf("发送FIN,启动TCP关闭流程...\n"); + shutdown(conn->sockfd, SHUT_WR); /* 只关闭写端,允许接收FIN-ACK */ + conn->state = TCP_STATE_CLOSING; + + /* 给一些时间让TCP完成四次挥手 */ + for (int i = 0; i < 50; i++) { /* 最多等待500ms */ + sys_msleep(10); + + /* 检查连接是否已经关闭 */ + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(conn->sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) { + if (error != 0) { + printf("TCP连接已关闭,错误码: %d\n", error); + break; + } + } + + /* 尝试读取,检查是否收到FIN */ + char dummy; + int ret = recv(conn->sockfd, &dummy, 1, MSG_DONTWAIT); + if (ret == 0) { + printf("收到FIN,TCP连接正常关闭\n"); + break; + } + } + } + + /* 最后关闭socket文件描述符 */ close(conn->sockfd); - printf("Socket已关闭: %d\n", conn->sockfd); + printf("Socket文件描述符已关闭: %d\n", conn->sockfd); } /* 清理发送队列 */ @@ -540,6 +846,45 @@ static void tcp_manager_io_thread(void* arg) switch (conn->state) { case TCP_STATE_CONNECTING: + /* 检查连接是否完成 */ + if (conn->sockfd >= 0) { + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(conn->sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) { + if (error == 0) { + /* 连接成功完成 */ + printf("连接完成: handle=%p, sockfd=%d\n", conn->handle, conn->sockfd); + conn->state = TCP_STATE_CONNECTED; + conn->last_activity = sys_now(); + g_stats.active_connections++; + + /* 设置Keep-Alive */ + setup_keepalive(conn->sockfd); + + /* 调用连接成功回调 */ + if (conn->connect_cb) { + printf("调用连接成功回调: handle=%p\n", conn->handle); + conn->connect_cb(conn->handle, true); + } else { + printf("警告: 连接 %p 没有连接回调函数\n", conn->handle); + } + continue; + } else { + printf("连接失败: handle=%p, error=%d\n", conn->handle, error); + if (conn->connect_cb) { + printf("调用连接失败回调: handle=%p\n", conn->handle); + conn->connect_cb(conn->handle, false); + } + cleanup_connection_unsafe(conn); + continue; + } + } else { + printf("调试: 连接 %p 的getsockopt失败: %d\n", conn->handle, errno); + } + } else { + printf("调试: 连接 %p 的sockfd为%d\n", conn->handle, conn->sockfd); + } + /* 检查连接超时 */ if ((sys_now() - conn->last_activity) > TCP_CONNECT_TIMEOUT_MS) { printf("连接超时: handle=%p\n", conn->handle); @@ -571,9 +916,42 @@ static void tcp_manager_io_thread(void* arg) break; case TCP_STATE_CLOSING: - /* 等待关闭完成 */ - if (conn->sockfd < 0) { - cleanup_connection_unsafe(conn); + /* 正在关闭的连接,监控关闭进度 */ + if (conn->sockfd >= 0) { + /* 检查连接是否已经关闭 */ + char dummy; + int ret = recv(conn->sockfd, &dummy, 1, MSG_DONTWAIT); + if (ret == 0) { + /* 收到FIN,连接已正常关闭 */ + printf("TCP连接已正常关闭: handle=%p\n", conn->handle); + close(conn->sockfd); + conn->sockfd = -1; + conn->state = TCP_STATE_DISCONNECTED; + if (conn->disconnect_cb) { + conn->disconnect_cb(conn->handle); + } + /* 清理连接资源,但不再尝试关闭socket */ + memset(conn, 0, sizeof(tcp_connection_info_t)); + conn->sockfd = -1; + conn->in_use = false; + printf("连接资源清理完成\n"); + } else if (ret < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + /* 连接出错,强制关闭 */ + printf("TCP连接关闭异常: handle=%p, errno=%d\n", conn->handle, errno); + close(conn->sockfd); + conn->sockfd = -1; + conn->state = TCP_STATE_ERROR; + } + } else { + /* socket文件描述符已关闭,清理连接记录 */ + conn->state = TCP_STATE_DISCONNECTED; + if (conn->disconnect_cb) { + conn->disconnect_cb(conn->handle); + } + memset(conn, 0, sizeof(tcp_connection_info_t)); + conn->sockfd = -1; + conn->in_use = false; + printf("连接记录已清理: handle=%p\n", conn->handle); } break; @@ -635,6 +1013,13 @@ static void tcp_manager_io_thread(void* arg) } } + /* 定期清理过期的DNS缓存条目 */ + static uint32_t last_cache_cleanup = 0; + if ((sys_now() - last_cache_cleanup) > 60000) { /* 每60秒清理一次 */ + dns_cache_cleanup_expired(); + last_cache_cleanup = sys_now(); + } + /* 防止CPU占用过高 */ if (total_connections == 0) { sys_msleep(50); @@ -669,12 +1054,16 @@ tcp_manager_result_t tcp_simple_manager_init(void) /* 初始化DNS状态数组 */ memset(g_dns_states, 0, sizeof(g_dns_states)); + /* 初始化DNS缓存 */ + dns_cache_init(); + /* 初始化统计信息 */ memset(&g_stats, 0, sizeof(g_stats)); /* 创建互斥锁 */ if (sys_mutex_new(&g_connections_mutex) != ERR_OK) { printf("TCP管理器初始化失败: 无法创建互斥锁\n"); + dns_cache_deinit(); return TCP_MANAGER_THREAD_ERROR; } @@ -733,12 +1122,50 @@ tcp_manager_result_t tcp_simple_manager_deinit(void) /* 释放互斥锁 */ sys_mutex_free(&g_connections_mutex); + /* 清理DNS缓存 */ + dns_cache_deinit(); + g_manager_initialized = false; printf("TCP管理器反初始化完成\n"); return TCP_MANAGER_OK; } +/** + * @brief 显示DNS缓存状态 + */ +void tcp_show_dns_cache(void) +{ + sys_mutex_lock(&g_dns_cache_mutex); + + printf("\n=== DNS缓存状态 ===\n"); + printf("最大条目数: %d\n", DNS_CACHE_MAX_ENTRIES); + printf("缓存TTL: %d秒\n", DNS_CACHE_TTL_MS / 1000); + printf("当前时间: %u\n", sys_now()); + + int used_entries = 0; + uint32_t current_time = sys_now(); + + for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) { + dns_cache_entry_t* entry = &g_dns_cache[i]; + if (entry->in_use) { + used_entries++; + uint32_t age = current_time - entry->timestamp; + bool expired = (age > DNS_CACHE_TTL_MS); + + printf("[%02d] %s -> %s (年龄: %us, 访问: %u%s)\n", + i, entry->hostname, inet_ntoa(entry->ip_addr), + age / 1000, entry->access_count, + expired ? " [已过期]" : ""); + } + } + + printf("使用中的条目: %d/%d\n", used_entries, DNS_CACHE_MAX_ENTRIES); + printf("==================\n\n"); + + sys_mutex_unlock(&g_dns_cache_mutex); +} + /** * @brief 连接到指定地址 */ @@ -785,9 +1212,9 @@ tcp_handle_t tcp_open_url(const char* host, uint16_t port, sys_mutex_unlock(&g_connections_mutex); - /* DNS解析 */ + /* DNS解析(带缓存) */ struct in_addr addr; - if (sync_dns_resolve(host, &addr) < 0) { + if (dns_resolve_with_cache(host, &addr) < 0) { printf("DNS解析失败: %s\n", host); sys_mutex_lock(&g_connections_mutex); cleanup_connection_unsafe(conn); @@ -825,16 +1252,23 @@ tcp_handle_t tcp_open_url(const char* host, uint16_t port, } /* 发起连接 */ + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr = addr; + server_addr.sin_port = htons(port); + int result = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (result == 0) { - /* 连接立即成功 */ + /* 立即连接成功 */ + conn->sockfd = sockfd; /* 关键修复:设置socket文件描述符 */ conn->state = TCP_STATE_CONNECTED; conn->last_activity = sys_now(); g_stats.total_connections++; g_stats.active_connections++; - printf("连接立即成功: %s:%d, handle=%p\n", host, port, conn->handle); + printf("连接立即成功: %s:%d, handle=%p, sockfd=%d\n", host, port, conn->handle, sockfd); /* 设置Keep-Alive */ setup_keepalive(sockfd); @@ -849,12 +1283,13 @@ tcp_handle_t tcp_open_url(const char* host, uint16_t port, } else if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) { /* 连接正在进行中 */ + conn->sockfd = sockfd; /* 关键修复:设置socket文件描述符 */ conn->state = TCP_STATE_CONNECTING; conn->last_activity = sys_now(); g_stats.total_connections++; g_stats.active_connections++; - printf("连接进行中: %s:%d, handle=%p\n", host, port, conn->handle); + printf("连接进行中: %s:%d, handle=%p, sockfd=%d\n", host, port, conn->handle, sockfd); sys_mutex_unlock(&g_connections_mutex); return conn->handle; diff --git a/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_simple_manager.h b/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_simple_manager.h index ffd8f88ce..9c239694c 100644 --- a/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_simple_manager.h +++ b/code/sdk/drivers/peripheral/slip/tcpmanager/tcp_simple_manager.h @@ -15,6 +15,7 @@ #include #include +#include "lwip/inet.h" /* for struct in_addr - 使用lwIP版本 */ #ifdef __cplusplus extern "C" { @@ -153,6 +154,20 @@ typedef struct { uint32_t errors; } tcp_manager_stats_t; +/* DNS缓存配置 */ +#define DNS_CACHE_MAX_ENTRIES 16 /* 最大缓存条目数 */ +#define DNS_CACHE_TTL_MS 300000 /* 缓存有效期: 5分钟 */ +#define DNS_HOSTNAME_MAX_LEN 64 /* 主机名最大长度 */ + +/* DNS缓存条目结构 */ +typedef struct { + bool in_use; /* 是否使用中 */ + char hostname[DNS_HOSTNAME_MAX_LEN]; /* 主机名 */ + struct in_addr ip_addr; /* 解析的IP地址 */ + uint32_t timestamp; /* 缓存时间戳 */ + uint32_t access_count; /* 访问次数(用于LRU) */ +} dns_cache_entry_t; + /** * @brief 初始化TCP管理器 * 创建IO线程、初始化队列、设置回调等 @@ -223,6 +238,11 @@ tcp_manager_result_t tcp_manager_close(tcp_handle_t handle); */ tcp_connection_state_t tcp_get_connection_state(tcp_handle_t handle); +/** + * @brief 显示DNS缓存状态 + */ +void tcp_show_dns_cache(void); + /** * @brief 获取管理器统计信息 * diff --git a/ws_lwip_test/tcp_test_server.py b/ws_lwip_test/tcp_test_server.py index f1cb0e9e7..f68f3f4fb 100644 --- a/ws_lwip_test/tcp_test_server.py +++ b/ws_lwip_test/tcp_test_server.py @@ -220,6 +220,9 @@ class TCPTestServer: print(f" 平均包大小: {conn_info['bytes_received'] / conn_info['packets_count']:.1f} 字节" if conn_info['packets_count'] > 0 else " 平均包大小: 0 字节") print("-" * 60) + # 显示当前连接状态 + print(f"当前活跃连接数: {self.global_stats['active_connections']}") + del self.connections[conn_id] def stats_monitor(self): @@ -234,8 +237,8 @@ class TCPTestServer: avg_throughput = self.global_stats['total_bytes'] / uptime if uptime > 0 else 0 print(f"\n=== 服务器总体统计 (运行时间: {uptime:.0f}s) ===") - print(f"活跃连接: {self.global_stats['active_connections']}") - print(f"总连接数: {self.global_stats['total_connections']}") + print(f"当前连接数: {self.global_stats['active_connections']} (活跃)") + print(f"历史连接数: {self.global_stats['total_connections']} (累计)") print(f"总包数: {self.global_stats['total_packets']:,}") print(f"总数据量: {self.format_bytes(self.global_stats['total_bytes'])}") print(f"平均吞吐量: {self.format_bytes(avg_throughput)}/s") @@ -279,11 +282,19 @@ class TCPTestServer: avg_throughput = self.global_stats['total_bytes'] / uptime if uptime > 0 else 0 print(f"运行时间: {uptime:.2f} 秒") - print(f"总连接数: {self.global_stats['total_connections']}") + print(f"剩余连接数: {self.global_stats['active_connections']} (应该为0)") + print(f"历史连接数: {self.global_stats['total_connections']} (累计)") print(f"总包数: {self.global_stats['total_packets']:,}") print(f"总数据量: {self.format_bytes(self.global_stats['total_bytes'])}") print(f"平均吞吐量: {self.format_bytes(avg_throughput)}/s") print(f"峰值吞吐量: {self.format_bytes(self.global_stats['max_throughput_bps'])}/s") + + # 检查是否有连接泄漏 + if self.global_stats['active_connections'] > 0: + print(f"⚠️ 警告: 仍有 {self.global_stats['active_connections']} 个连接未正常关闭!") + else: + print("✅ 所有连接已正常关闭") + print("服务器已关闭。") def main(): -- 2.17.1