/**
- * 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"
#include <string.h>
#include <unistd.h>
#include <errno.h>
-#include <sys/select.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#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
#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 /* 每个连接的发送队列大小 */
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;
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);
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;
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状态 */
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为非阻塞模式
*/
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);
}
/* 清理发送队列 */
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);
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;
}
}
+ /* 定期清理过期的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);
/* 初始化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;
}
/* 释放互斥锁 */
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 连接到指定地址
*/
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);
}
/* 发起连接 */
+ 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);
} 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;