feat: 实现并完成核心功能测试套件

- 编译系统:支持通过 `make clean all` 进行全量编译,生成可执行文件 `omni_client`、`omni_server`、`omni_relay` 和 `omni_test`。
- 客户端-服务端文件传输:支持 TCP/UDP/KCP 协议,已验证文件收发功能(使用 `/tmp/input.bin` 作为测试文件)。
- 服务端指令驱动:服务端可通过控制台发送 ASCII 指令(如 `hello-client`)实时驱动客户端。
- 动态转发功能 (Relay):实现 UDP 协议下的动态目标切换,支持 `show` 查询和 `set` 命令实时修改转发目标(如从 9102 端口切换到 9103 端口)。
- 所有功能已在本地环境(127.0.0.1)通过完整流程验证。
This commit is contained in:
nnbcccscdscdsc
2026-03-13 22:39:41 +08:00
parent 4d475f8c92
commit 7ecd8a4ef4
9 changed files with 34061 additions and 152 deletions

View File

@@ -1,7 +1,13 @@
/*
* tcp_impl.c
* TCP 协议实现,带 16 字节包头解决粘包
*/
/*
* tcp_impl.c
* TCP 协议实现,带 16 字节包头解决粘包
*
* 设计说明:
* 1) TCP 是字节流,天然没有消息边界,因此这里通过“固定 16 字节头 + payload 长度”
* 显式划分消息边界,避免粘包/拆包带来的上层读取混乱。
* 2) 本层的头只用于“流边界管理”,上层业务仍可在 payload 中定义自己的消息头。
* 3) send/recv 均采用阻塞全量读写语义:要么完整收发一帧,要么返回错误/关闭状态。
*/
#include "common.h"
#include "network.h"
@@ -20,9 +26,10 @@
/* Linux 下 TCP_INFO 定义通常已在 <netinet/tcp.h> 提供,避免引入 <linux/tcp.h> 重定义 */
struct TcpContext {
int fd;
};
struct TcpContext {
/* 已建立连接的 socket fd服务端 accept 后或客户端 connect 后)。 */
int fd;
};
#ifdef __linux__
static void tcp_log_info(int fd, const char *tag)
@@ -64,29 +71,33 @@ static void tcp_log_info(int fd, const char *tag)
}
#endif
static int tcp_set_nodelay(int fd)
{
int flag = 1;
return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
}
static int tcp_set_nodelay(int fd)
{
/* 关闭 Nagle降低小包时延更利于交互指令场景。 */
int flag = 1;
return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
}
static int tcp_set_reuseaddr(int fd)
{
/* 允许端口快速复用,减少开发/测试时 TIME_WAIT 影响。 */
int flag = 1;
return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
}
static int tcp_set_reuseaddr(int fd)
{
int flag = 1;
return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
}
static int tcp_bind_and_listen(struct TcpContext *ctx,
const char *bind_ip,
uint16_t bind_port)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
static int tcp_bind_and_listen(struct TcpContext *ctx,
const char *bind_ip,
uint16_t bind_port)
{
/* 创建监听 socket。 */
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
logger_log("ERROR", "tcp", "socket_failed errno=%d", errno);
return -1;
}
tcp_set_reuseaddr(fd);
/* 监听 socket 打开地址复用。 */
tcp_set_reuseaddr(fd);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
@@ -100,7 +111,11 @@ static int tcp_bind_and_listen(struct TcpContext *ctx,
return -1;
}
if (listen(fd, 1) < 0) {
/*
* 这里 backlog 取 1符合当前“单连接演示/测试”场景。
* 若后续要支持多客户端,可提升 backlog 并改为事件循环/线程池模型。
*/
if (listen(fd, 1) < 0) {
logger_log("ERROR", "tcp", "listen_failed errno=%d", errno);
close(fd);
return -1;
@@ -108,26 +123,28 @@ static int tcp_bind_and_listen(struct TcpContext *ctx,
logger_log("INFO", "tcp", "listening port=%u", (unsigned)bind_port);
/* 简化:阻塞接受一个客户端,之后用于长连接 */
int cfd = accept(fd, NULL, NULL);
/* 简化:阻塞接受一个客户端,连接建立后作为长连接使用。 */
int cfd = accept(fd, NULL, NULL);
if (cfd < 0) {
logger_log("ERROR", "tcp", "accept_failed errno=%d", errno);
close(fd);
return -1;
}
close(fd);
tcp_set_nodelay(cfd);
/* 监听 fd 仅用于 accept一旦接入成功即可关闭监听 fd。 */
close(fd);
tcp_set_nodelay(cfd);
ctx->fd = cfd;
return 0;
}
static int tcp_connect_peer(struct TcpContext *ctx,
const char *peer_ip,
uint16_t peer_port)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
static int tcp_connect_peer(struct TcpContext *ctx,
const char *peer_ip,
uint16_t peer_port)
{
/* 创建主动连接 socket。 */
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
logger_log("ERROR", "tcp", "socket_failed errno=%d", errno);
return -1;
@@ -139,7 +156,8 @@ static int tcp_connect_peer(struct TcpContext *ctx,
addr.sin_port = htons(peer_port);
addr.sin_addr.s_addr = inet_addr(peer_ip);
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
/* 阻塞 connect 到对端。 */
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
logger_log("ERROR", "tcp", "connect_failed errno=%d", errno);
close(fd);
return -1;
@@ -152,10 +170,16 @@ static int tcp_connect_peer(struct TcpContext *ctx,
return 0;
}
static ssize_t tcp_read_n(int fd, void *buf, size_t n)
{
size_t off = 0;
char *p = (char *)buf;
static ssize_t tcp_read_n(int fd, void *buf, size_t n)
{
/*
* 从 TCP 流中“恰好读取 n 字节”:
* - 正常返回 n
* - 返回 0 表示对端关闭(如果发生在中途,返回已读字节数)
* - 返回 -1 表示系统调用错误
*/
size_t off = 0;
char *p = (char *)buf;
while (off < n) {
ssize_t r = read(fd, p + off, n - off);
if (r < 0) {
@@ -170,10 +194,15 @@ static ssize_t tcp_read_n(int fd, void *buf, size_t n)
return (ssize_t)off;
}
static ssize_t tcp_write_n(int fd, const void *buf, size_t n)
{
size_t off = 0;
const char *p = (const char *)buf;
static ssize_t tcp_write_n(int fd, const void *buf, size_t n)
{
/*
* 向 TCP 流中“恰好写入 n 字节”:
* - EINTR 自动重试
* - 其余错误返回 -1
*/
size_t off = 0;
const char *p = (const char *)buf;
while (off < n) {
ssize_t r = write(fd, p + off, n - off);
if (r < 0) {
@@ -185,19 +214,21 @@ static ssize_t tcp_write_n(int fd, const void *buf, size_t n)
return (ssize_t)off;
}
static OmniContext *tcp_init(OmniRole role,
const char *bind_ip,
uint16_t bind_port,
const char *peer_ip,
uint16_t peer_port)
{
struct TcpContext *ctx = (struct TcpContext *)calloc(1, sizeof(*ctx));
static OmniContext *tcp_init(OmniRole role,
const char *bind_ip,
uint16_t bind_port,
const char *peer_ip,
uint16_t peer_port)
{
/* 协议私有上下文(通过 OmniContext* 向上层做不透明传递)。 */
struct TcpContext *ctx = (struct TcpContext *)calloc(1, sizeof(*ctx));
if (!ctx) {
return NULL;
}
int rc;
if (role == OMNI_ROLE_SERVER) {
/* 按角色决定是被动监听还是主动连接。 */
if (role == OMNI_ROLE_SERVER) {
rc = tcp_bind_and_listen(ctx, bind_ip, bind_port);
} else {
if (!peer_ip || peer_port == 0) {
@@ -216,21 +247,24 @@ static OmniContext *tcp_init(OmniRole role,
return (OmniContext *)ctx;
}
static ssize_t tcp_send(OmniContext *c, const void *buf, size_t len)
{
struct TcpContext *ctx = (struct TcpContext *)c;
if (!ctx || ctx->fd < 0) return OMNI_ERR_PARAM;
static ssize_t tcp_send(OmniContext *c, const void *buf, size_t len)
{
struct TcpContext *ctx = (struct TcpContext *)c;
if (!ctx || ctx->fd < 0) return OMNI_ERR_PARAM;
/*
* 外层 TCP 帧头16B仅用于切分消息边界。
* 当前 type 统一标记为 MSG_TYPE_RAW表示“payload 是上层透传内容”。
*/
uint64_t t0 = omni_now_ms();
MsgHeader hdr;
omni_msg_header_encode(&hdr, MSG_TYPE_RAW, (uint32_t)len, t0);
uint8_t header_buf[MSG_HEADER_SIZE];
memcpy(header_buf, &hdr, MSG_HEADER_SIZE);
uint64_t t0 = omni_now_ms();
MsgHeader hdr;
hdr.magic = htonl(MSG_MAGIC);
hdr.length = htonl((uint32_t)len);
hdr.seq = 0; /* 如有需要,上层可扩展维护序列号 */
uint8_t header_buf[MSG_HEADER_SIZE];
memcpy(header_buf, &hdr, MSG_HEADER_SIZE);
ssize_t n1 = tcp_write_n(ctx->fd, header_buf, MSG_HEADER_SIZE);
/* 先写固定头,再写 payload接收侧可据此恢复完整帧。 */
ssize_t n1 = tcp_write_n(ctx->fd, header_buf, MSG_HEADER_SIZE);
if (n1 != (ssize_t)MSG_HEADER_SIZE) {
return OMNI_ERR_IO;
}
@@ -240,8 +274,9 @@ static ssize_t tcp_send(OmniContext *c, const void *buf, size_t len)
return OMNI_ERR_IO;
}
uint64_t t1 = omni_now_ms();
logger_on_proto_send_latency(t1 - t0);
uint64_t t1 = omni_now_ms();
/* 记录协议层发送耗时,便于后续性能分析。 */
logger_on_proto_send_latency(t1 - t0);
logger_log("DEBUG", "tcp", "send payload_bytes=%zu header_bytes=%zu proto_ms=%llu",
len, (size_t)MSG_HEADER_SIZE, (unsigned long long)(t1 - t0));
#ifdef __linux__
@@ -250,13 +285,19 @@ static ssize_t tcp_send(OmniContext *c, const void *buf, size_t len)
return (ssize_t)len;
}
static ssize_t tcp_recv(OmniContext *c, void *buf, size_t len)
{
struct TcpContext *ctx = (struct TcpContext *)c;
if (!ctx || ctx->fd < 0) return OMNI_ERR_PARAM;
uint64_t t0 = omni_now_ms();
uint8_t header_buf[MSG_HEADER_SIZE];
static ssize_t tcp_recv(OmniContext *c, void *buf, size_t len)
{
struct TcpContext *ctx = (struct TcpContext *)c;
if (!ctx || ctx->fd < 0) return OMNI_ERR_PARAM;
/*
* 收包流程:
* 1) 固定先读 16 字节头
* 2) 解析 payload_len
* 3) 再读 payload_len 字节
*/
uint64_t t0 = omni_now_ms();
uint8_t header_buf[MSG_HEADER_SIZE];
ssize_t n1 = tcp_read_n(ctx->fd, header_buf, MSG_HEADER_SIZE);
if (n1 <= 0) {
return n1; /* 0 表示对端关闭,负数为错误 */
@@ -265,18 +306,21 @@ static ssize_t tcp_recv(OmniContext *c, void *buf, size_t len)
return OMNI_ERR_IO;
}
MsgHeader hdr;
memcpy(&hdr, header_buf, MSG_HEADER_SIZE);
if (ntohl(hdr.magic) != MSG_MAGIC) {
logger_log("ERROR", "tcp", "invalid_magic");
return OMNI_ERR_IO;
}
uint32_t payload_len = ntohl(hdr.length);
if (payload_len > len) {
logger_log("ERROR", "tcp", "buffer_too_small payload=%u buf_len=%zu",
payload_len, len);
/* 简化:这里返回错误,实际可考虑丢弃或扩展缓冲区 */
/* 解码网络字节序头字段。 */
MsgHeader hdr;
MsgHeader host_hdr;
memcpy(&hdr, header_buf, MSG_HEADER_SIZE);
omni_msg_header_decode(&hdr, &host_hdr);
uint32_t payload_len = host_hdr.len;
/*
* 调用方缓冲区不足时直接报错。
* 当前实现不做“读取并丢弃剩余字节”,因此调用方应保证 recv 缓冲足够大。
*/
if (payload_len > len) {
logger_log("ERROR", "tcp", "buffer_too_small payload=%u buf_len=%zu",
payload_len, len);
/* 简化:这里返回错误,实际可考虑丢弃或扩展缓冲区 */
return OMNI_ERR_PARAM;
}
@@ -285,23 +329,29 @@ static ssize_t tcp_recv(OmniContext *c, void *buf, size_t len)
return OMNI_ERR_IO;
}
uint64_t t1 = omni_now_ms();
logger_on_proto_recv_latency(t1 - t0);
logger_log("DEBUG", "tcp", "recv payload_bytes=%u header_bytes=%zu proto_ms=%llu",
payload_len, (size_t)MSG_HEADER_SIZE, (unsigned long long)(t1 - t0));
uint64_t t1 = omni_now_ms();
/* 记录协议层接收耗时。 */
logger_on_proto_recv_latency(t1 - t0);
logger_log("DEBUG", "tcp",
"recv payload_bytes=%u header_bytes=%zu msg_type=%u ts_ms=%llu proto_ms=%llu",
payload_len, (size_t)MSG_HEADER_SIZE,
(unsigned)host_hdr.type,
(unsigned long long)host_hdr.timestamp,
(unsigned long long)(t1 - t0));
#ifdef __linux__
tcp_log_info(ctx->fd, "after_recv");
#endif
return (ssize_t)payload_len;
}
static void tcp_close(OmniContext *c)
{
struct TcpContext *ctx = (struct TcpContext *)c;
if (!ctx) return;
if (ctx->fd >= 0) {
close(ctx->fd);
}
static void tcp_close(OmniContext *c)
{
struct TcpContext *ctx = (struct TcpContext *)c;
if (!ctx) return;
/* 关闭连接并释放私有上下文。 */
if (ctx->fd >= 0) {
close(ctx->fd);
}
free(ctx);
}