删除多连接内容,只支持单连接

This commit is contained in:
2026-08-25 12:41:33 +08:00
parent d6bc3f6253
commit 0a707034cc
10 changed files with 109 additions and 507 deletions

View File

@@ -27,7 +27,6 @@
|------|------|
| TCP Client | 主动连接远程服务器 |
| TCP Server单连接 | 支持 1 个客户端连接 |
| TCP Server多连接 | 支持最多 7 个并发连接 |
| UDP Client | 发送 UDP 数据报 |
| UDP Server | 接收 UDP 数据报 |
| select/poll | I/O 多路复用 |
@@ -38,7 +37,6 @@
| 特性 | Linux Socket | 本实现 |
|------|--------------|--------|
| Socket 数量 | 无限制 | 最多 8 个 |
| TCP Server 多连接 | 无限制 | 最多 7 个并发连接 |
| 非阻塞模式 | 支持 | 通过 `net_poll()` 实现 |
| 错误码 | errno | `net_get_errno()` |
| 头文件 | `<sys/socket.h>` | `"net_socket.h"` |
@@ -82,19 +80,6 @@
| `net_select.h` | select/poll API 头文件 |
| `net_select.c` | select/poll 实现 |
### 2.3 底层驱动改进v2.1
ch395f.c/h 已完成以下优化:
| 改进项 | 说明 |
|--------|------|
| 命令执行等待 | `open/close/listen/connect/disconnect` 所有长命令内部调用 `poll_cmd_status` 等待完成20ms × 200 次 = 4s 超时) |
| 全局中断 | 使用 `GET_GLOB_INT_STATUS_ALL`2字节版命令码 0x19支持全部 8 个 Socket |
| 错误码 | 与芯片手册完全对齐的 17 个状态码定义 |
| FUN_PARA 标志 | `CH395F_FUN_PARA_TCP_SERVER` / `SOCKET_CLOSE` / `DISABLE_SEND_OK` 宏 |
| 新增函数 | `ch395f_set_arp()` / `ch395f_set_ttl()` / `ch395f_get_unreach_info()` / `ch395f_poll_cmd_status()` |
| KeepAlive | 参数单位为毫秒(非秒),必须为 500 的倍数 |
### 2.4 Socket 状态机
```
@@ -116,12 +101,11 @@ ch395f.c/h 已完成以下优化:
│ net_accept() │ (连接成功) │
│ │ │ │
│ ▼ ▼ │
TCP_ACCEPT ESTABLISHED ◄──
│ │
(分配连接)│ net_send/recv()
│ │
▼ ▼
│ ESTABLISHED │
│ ESTABLISHED ◄──────────────┘
│ │
net_accept() 返回监听 Socket 自身
│ │
net_send/recv()
│ │ │
│ net_close() │
│ │ │
@@ -317,8 +301,8 @@ void tcp_server_single(void)
return;
}
/* 3. 开始监听(单连接模式 backlog=0 */
if (net_listen(listen_sock, 0) != 0) {
/* 3. 开始监听 */
if (net_listen(listen_sock) != 0) {
printf("监听失败\r\n");
net_close(listen_sock);
return;
@@ -365,97 +349,7 @@ void tcp_server_single(void)
}
```
### 5.2 TCP Server多连接
多连接模式使用 1 个监听 Socket + N 个数据 Socket支持并发连接
```c
#include "net_socket.h"
#include "net_select.h"
#define MAX_CLIENTS 4
void tcp_server_multi(void)
{
int listen_sock;
struct net_sockaddr_in addr;
int i;
/* 1. 创建监听 Socket */
listen_sock = net_socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0) {
printf("创建 Socket 失败\r\n");
return;
}
/* 2. 绑定 */
addr.sin_family = AF_INET;
addr.sin_port = net_htons(8080);
addr.sin_addr.s_addr = net_inet_addr("0.0.0.0");
if (net_bind(listen_sock, (struct net_sockaddr *)&addr, sizeof(addr)) != 0) {
printf("绑定失败\r\n");
return;
}
/* 3. 启动监听backlog 设为并发连接数) */
if (net_listen(listen_sock, MAX_CLIENTS) != 0) {
printf("监听失败\r\n");
return;
}
printf("TCP Server 启动,支持 %d 个并发连接\r\n", MAX_CLIENTS);
/* 4. 使用 select 等待事件 */
while (1)
{
net_fd_set readfds;
struct net_timeval timeout;
NET_FD_ZERO(&readfds);
/* 监听 Socket 加入读集合 */
NET_FD_SET(listen_sock, &readfds);
/* 数据 Socket1~4加入读集合 */
for (i = 1; i <= MAX_CLIENTS; i++) {
NET_FD_SET(i, &readfds);
}
timeout.tv_sec = 0;
timeout.tv_usec = 100000; /* 100ms 超时 */
int ret = net_select(listen_sock + MAX_CLIENTS + 1,
&readfds, NULL, NULL, &timeout);
if (ret > 0)
{
/* 检查数据 Socket */
for (i = 1; i <= MAX_CLIENTS; i++)
{
if (NET_FD_ISSET(i, &readfds))
{
net_sock_t *sock = net_get_sock(i);
if (sock != NULL && sock->state == NET_SOCK_STATE_ESTABLISHED)
{
char buf[256];
int len = net_recv(i, buf, sizeof(buf) - 1, 0);
if (len > 0) {
buf[len] = '\0';
printf("Socket %d: %s\r\n", i, buf);
net_send(i, buf, len, 0); /* 回显 */
} else {
printf("Socket %d 断开\r\n", i);
}
}
}
}
}
}
}
```
### 5.3 TCP Client
### 5.2 TCP Client
```c
#include "net_socket.h"
@@ -922,13 +816,12 @@ int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen);
#### net_listen - 开始监听
```c
int net_listen(int sockfd, int backlog);
int net_listen(int sockfd);
```
| 参数 | 说明 |
|------|------|
| `sockfd` | socket 描述符 |
| `backlog` | 最大并发连接数0=单连接) |
| 返回值 | 0 成功,-1 失败 |
#### net_accept - 接受连接
@@ -1092,103 +985,7 @@ NET_FD_ISSET(fd, fdset) /* 检查 fd 是否在集合中 */
## 11. 完整示例
### 11.1 TCP Echo Server多连接
```c
#include "net_socket.h"
#include "net_select.h"
#include <stdio.h>
#include <string.h>
#define SERVER_PORT 8080
#define MAX_CLIENTS 4
void tcp_echo_server(void)
{
int listen_sock;
struct net_sockaddr_in addr;
int i;
/* 初始化网络 */
net_init("192.168.1.100", "255.255.255.0", "192.168.1.1");
/* 创建监听 Socket */
listen_sock = net_socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0) {
printf("创建 Socket 失败\r\n");
while(1);
}
/* 绑定 */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = net_htons(SERVER_PORT);
addr.sin_addr.s_addr = net_inet_addr("0.0.0.0");
if (net_bind(listen_sock, (struct net_sockaddr *)&addr, sizeof(addr)) != 0) {
printf("绑定失败\r\n");
while(1);
}
/* 监听 */
if (net_listen(listen_sock, MAX_CLIENTS) != 0) {
printf("监听失败\r\n");
while(1);
}
printf("TCP Echo Server 启动,端口 %d\r\n", SERVER_PORT);
/* 主循环 */
while (1)
{
net_fd_set readfds;
struct net_timeval timeout;
NET_FD_ZERO(&readfds);
NET_FD_SET(listen_sock, &readfds);
for (i = 1; i <= MAX_CLIENTS; i++) {
NET_FD_SET(i, &readfds);
}
timeout.tv_sec = 0;
timeout.tv_usec = 100000;
int ret = net_select(listen_sock + MAX_CLIENTS + 1,
&readfds, NULL, NULL, &timeout);
if (ret > 0)
{
for (i = 1; i <= MAX_CLIENTS; i++)
{
if (NET_FD_ISSET(i, &readfds))
{
net_sock_t *sock = net_get_sock(i);
if (sock == NULL) continue;
if (sock->state == NET_SOCK_STATE_ESTABLISHED)
{
char buf[256];
int len = net_recv(i, buf, sizeof(buf) - 1, 0);
if (len > 0) {
buf[len] = '\0';
printf("客户端 %d: %s\r\n", i, buf);
net_send(i, buf, len, 0);
} else {
printf("客户端 %d 断开\r\n", i);
net_close(i);
}
}
}
}
}
}
}
```
### 11.2 TCP Client 自动重连
### 11.1 TCP Client 自动重连
```c
#include "net_socket.h"
@@ -1261,8 +1058,7 @@ void tcp_client_reconnect(void)
}
}
else if (sock_info != NULL &&
(sock_info->state == NET_SOCK_STATE_TCP_ACCEPT ||
sock_info->state == NET_SOCK_STATE_CLOSED))
(sock_info->state == NET_SOCK_STATE_CLOSED))
{
/* 连接失败或断开 */
printf("连接失败,准备重连...\r\n");
@@ -1287,27 +1083,17 @@ void tcp_client_reconnect(void)
### 12.2 Socket 数量有限制吗?
是的CH395F 最多支持 8 个 Socket索引 0~7。其中
- TCP Server 多连接模式1 个监听 Socket + 最多 7 个数据 Socket
- TCP Server(单连接)1 个监听 Socket 即数据通道
- TCP Client最多 8 个
- UDP最多 8 个
### 12.3 TCP Server 多连接模式有什么要求
**模式工作原理:**
- CH395F 多连接模式下Socket 0 专职监听Socket 1~7 由芯片自动分配
- 客户端连接时CH395F 自动查找协议类型为 TCP、源端口与监听端口相同、处于关闭状态的 Socket
- 找到后自动打开该 Socket 并分配连接,触发 CONNECT 中断
**硬件要求:**
- CH395F 固件版本 >= 0x44支持 Socket 4~7
### 12.4 发送数据有什么限制?
### 12.3 发送数据有什么限制
- 每次发送的数据长度不能超过 `NET_SEND_BUF_SIZE`(默认 4096 字节)
- TCP 需要等待发送缓冲区空闲(`send_ready` 标志)
- UDP 单次发送最大 1460 字节
- UDP 单包载荷最大 1472 字节写入超过此值CH395F 会自动分片为多个 UDP 包发送)
### 12.5 如何处理连接断开?
### 12.4 如何处理连接断开?
**方式一:阻塞接收**
```c
@@ -1338,7 +1124,7 @@ void callback(int sockfd, net_event_t event, void *arg)
}
```
### 12.6 select 和 poll 的区别?
### 12.5 select 和 poll 的区别?
| 特性 | select | poll |
|------|--------|------|
@@ -1348,7 +1134,7 @@ void callback(int sockfd, net_event_t event, void *arg)
两者功能相同,推荐使用 `net_select()`,更接近 Linux 编程习惯。
### 12.7 错误码说明
### 12.6 错误码说明
| 错误码 | 宏定义 | 说明 |
|--------|--------|------|
@@ -1365,7 +1151,7 @@ void callback(int sockfd, net_event_t event, void *arg)
| -10 | NET_ERR_NOSPACE | 发送缓冲区无空间 |
| -11 | NET_ERR_BUSY | 操作未完成BUSY |
### 12.8 CH395F 命令执行状态码
### 12.7 CH395F 命令执行状态码
CH395F 底层驱动返回的状态码(`ch395f_get_cmd_status()``ch395f_poll_cmd_status()`
@@ -1390,15 +1176,7 @@ CH395F 底层驱动返回的状态码(`ch395f_get_cmd_status()` 或 `ch395f_po
| 0x5F | CH395F_CMD_RET_ABORT | 命令中止 |
| 0xFA | CH395F_ERR_UNKNOW | 未知错误 |
### 12.9 故障排查
**问题TCP Client 连接超时,无 CONNECT 中断**
- 检查多连接模式下数据 Socket1~7是否设置了 `SET_PROTO_TCP` + `SET_SOUR_PORT`(与监听端口相同)
- 如果不设置CH395F 不知道哪些 Socket 可用于自动分配TCP SYN 被静默丢弃
**问题:数据 Socket OPEN 返回 0x1B已被使用**
- CH395F 多连接模式下,数据 Socket 由芯片自动管理,软件不应手动 `OPEN_SOCKET`
- 只需 `SET_PROTO_TCP` + `SET_SOUR_PORT`CH395F 在有连接时自动打开
### 12.8 故障排查
**问题Socket 4~7 的 CONNECT/RECV 中断不触发**
- 需使用 `GET_GLOB_INT_STATUS_ALL`(命令码 0x19返回 2 字节)才能检测 Socket 4~7 的中断
@@ -1408,7 +1186,7 @@ CH395F 底层驱动返回的状态码(`ch395f_get_cmd_status()` 或 `ch395f_po
- 芯片 INIT 需要约 200ms轮询间隔应为 20ms过短的查询可能干扰内部处理
- 超时值建议设为 4 秒以上
### 12.10 线程安全问题
### 12.9 线程安全问题
**问题:`net_send()` / `net_recv()` / `net_close()` 可以在任意任务中调用吗?**