fix:更新客户端功能

This commit is contained in:
nnbcccscdscdsc
2026-03-16 22:28:05 +08:00
parent 7f2f79e672
commit 6c975d9ae3
18 changed files with 8067 additions and 33092 deletions

View File

@@ -1,17 +1,25 @@
/*
* common.h
. * 全局公共定义:消息头、错误码、通用宏
* 全局公共定义:消息头、错误码、通用宏
*
* 这个头文件承担两类职责:
* 1) 定义跨模块共享的线协议结构,保证 client / server / relay 的编码口径一致。
* 2) 提供与协议无关的基础工具,例如时间戳和 64 位字节序转换。
*/
#ifndef OMNISOCKET_COMMON_H
#define OMNISOCKET_COMMON_H
#include <arpa/inet.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
/* 统一的 16 字节消息头(应用层消息头) */
/*
* 统一的 16 字节应用层消息头。
* 注意:这不是 TCP/KCP/UDP 的传输层头,而是项目自己定义的“业务帧头”。
*/
typedef struct MsgHeader {
uint32_t type; /* 消息类型:文件块/控制指令等 */
uint32_t len; /* 后续负载长度(字节数) */
@@ -20,15 +28,183 @@ typedef struct MsgHeader {
#define MSG_HEADER_SIZE (sizeof(MsgHeader)) /* 16 字节 */
/* 应用层消息类型约定。 */
enum {
MSG_TYPE_FILE_CHUNK = 1,
MSG_TYPE_FILE_END = 2,
MSG_TYPE_COMMAND = 3,
MSG_TYPE_RAW = 100
MSG_TYPE_FILE_CHUNK = 1, //文件数据分片TransferChunkMeta + 实际文件字节)
MSG_TYPE_FILE_END = 2, //文件传输结束通知
MSG_TYPE_COMMAND = 3, //控制指令(服务端发起+客户端响应)
MSG_TYPE_TRANSFER_ACK = 4, //传输确认
MSG_TYPE_TIME_SYNC_REQ = 5, //时钟同步探测请求
MSG_TYPE_TIME_SYNC_RESP = 6, //时钟同步探测响应
MSG_TYPE_TIME_SYNC_REPORT = 7, //客户端确认后的最终 offset
MSG_TYPE_PEER_REGISTER = 8, //客户端向 hub 注册自己的逻辑 ID
MSG_TYPE_PEER_BIND = 9, //客户端请求把默认目标绑定到某个 peer_id
MSG_TYPE_PEER_STATUS = 10, //hub 返回给客户端的状态/错误/通知
MSG_TYPE_PEER_TUNNEL = 11, //hub 按 src_id/dst_id 转发的通用隧道消息
MSG_TYPE_RAW = 100 //原始数据(不带业务头,直接透传 payload
};
/* 默认分片大小(包)上限 */
#define OMNI_DEFAULT_MTU 1400u
/*
* 文件分片元数据:
* - 用于进度统计、端到端延迟估算、UDP 丢包区间分析
* - offset_bytes 让接收端可以按原始偏移写盘,避免 UDP 乱序时直接追加写坏文件
*/
typedef struct TransferChunkMeta {
uint32_t transfer_id; /* 一次文件传输的逻辑 ID用于区分多次任务。 */
uint32_t seq; /* 当前分片序号,从 1 开始。 */
uint32_t total_chunks; /* 本次传输总分片数,便于接收端判断是否丢片。 */
uint32_t window_id; /* 发送时间窗口编号,用于按秒聚合吞吐/丢包分布。 */
uint64_t total_bytes; /* 原始文件总大小。 */
uint64_t offset_bytes; /* 当前分片在原文件中的字节偏移。 */
uint32_t chunk_bytes; /* 当前分片实际承载的数据字节数。 */
uint32_t reserved; /* 预留字段,当前未使用,发送时写 0。 */
uint64_t origin_ts_ms; /* 分片在发送端最初产生的时间戳,用于端到端延迟估算。 */
} TransferChunkMeta;
#define TRANSFER_CHUNK_META_SIZE (sizeof(TransferChunkMeta))
/*
* 文件发送结束消息。
* 这类消息不携带实际文件内容,而是告诉接收端“数据面已经发完”,
* 便于输出汇总统计并回传 ACK。
*/
typedef struct TransferEndMeta {
uint32_t transfer_id;
uint32_t total_chunks;
uint64_t total_bytes;
uint32_t total_windows;
uint32_t reserved;
} TransferEndMeta;
#define TRANSFER_END_META_SIZE (sizeof(TransferEndMeta))
/*
* 传输结束确认消息。
* 服务端在刷盘和统计完成后回传该结构,客户端据此得知:
* - 哪个 transfer_id 完成了
* - 服务端实际写入了多少字节
* - 这次 ACK 对应的是哪次 FILE_END
*/
typedef struct TransferAckMeta {
uint32_t transfer_id;
uint32_t total_chunks;
uint64_t total_bytes;
uint64_t bytes_written;
uint64_t echoed_end_ts_ms;
} TransferAckMeta;
#define TRANSFER_ACK_META_SIZE (sizeof(TransferAckMeta))
/*
* 时钟同步探测请求。
* 客户端发出探测时带上自己的发送时刻 t0服务端收到后会立即回包。
*/
typedef struct TimeSyncProbeMeta {
uint32_t probe_id;
uint32_t reserved;
uint64_t client_send_ts_ms;
} TimeSyncProbeMeta;
#define TIME_SYNC_PROBE_META_SIZE (sizeof(TimeSyncProbeMeta))
/*
* 时钟同步响应。
* 服务端把客户端的 t0 原样回显,并补上:
* - t1: 服务端收到请求的本地时间
* - t2: 服务端发出响应的本地时间
*/
typedef struct TimeSyncReplyMeta {
uint32_t probe_id;
uint32_t reserved;
uint64_t client_send_ts_ms;
uint64_t server_recv_ts_ms;
uint64_t server_send_ts_ms;
} TimeSyncReplyMeta;
#define TIME_SYNC_REPLY_META_SIZE (sizeof(TimeSyncReplyMeta))
/*
* 客户端选定最优样本后,把最终 offset 上报给服务端。
* offset 定义为server_time - client_time。
*/
typedef struct TimeSyncReportMeta {
int64_t server_minus_client_offset_ms;
uint64_t best_rtt_ms;
uint32_t sample_count;
uint32_t reserved;
} TimeSyncReportMeta;
#define TIME_SYNC_REPORT_META_SIZE (sizeof(TimeSyncReportMeta))
#define OMNI_PEER_ID_SIZE 32u
#define OMNI_PEER_STATUS_DETAIL_SIZE 96u
enum {
PEER_STATUS_OK = 0,
PEER_STATUS_ERROR = 1,
PEER_STATUS_REGISTERED = 2,
PEER_STATUS_BOUND = 3,
PEER_STATUS_UNBOUND = 4
};
/*
* peer 注册消息:
* - client_id 是该客户端在 hub 中的逻辑身份
* - 后续 bind / route 都依赖这个 ID而不是私网 IP
*/
typedef struct PeerRegisterMeta {
char client_id[OMNI_PEER_ID_SIZE];
uint32_t reserved;
} PeerRegisterMeta;
#define PEER_REGISTER_META_SIZE (sizeof(PeerRegisterMeta))
/*
* peer 绑定消息:
* - peer_id 是当前会话的默认目标
* - 绑定成功后,客户端可以直接 send 文本而无需每次重复写目标 ID
*/
typedef struct PeerBindMeta {
char peer_id[OMNI_PEER_ID_SIZE];
uint32_t reserved;
} PeerBindMeta;
#define PEER_BIND_META_SIZE (sizeof(PeerBindMeta))
/*
* hub -> peer 的状态消息:
* - code 区分成功、错误、解绑通知
* - self_id / peer_id 便于客户端更新本地状态
* - detail 留给人读日志和交互终端输出
*/
typedef struct PeerStatusMeta {
uint32_t code;
uint32_t reserved;
char self_id[OMNI_PEER_ID_SIZE];
char peer_id[OMNI_PEER_ID_SIZE];
char detail[OMNI_PEER_STATUS_DETAIL_SIZE];
} PeerStatusMeta;
#define PEER_STATUS_META_SIZE (sizeof(PeerStatusMeta))
/*
* 通用隧道头:
* - src_id 由 hub 在转发时写入真实发送方
* - dst_id 标识目标 peer
* - inner_type 复用现有业务消息类型,后续文件/视频消息也可直接套进来
*/
typedef struct PeerTunnelMeta {
char src_id[OMNI_PEER_ID_SIZE];
char dst_id[OMNI_PEER_ID_SIZE];
uint32_t inner_type;
uint32_t reserved;
} PeerTunnelMeta;
#define PEER_TUNNEL_META_SIZE (sizeof(PeerTunnelMeta))
/* 通用错误码(负数返回表示出错) */
enum {
OMNI_OK = 0,
@@ -38,14 +214,18 @@ enum {
OMNI_ERR_TIMEOUT = -4
};
/* 获取当前单调时间(毫秒),用于延迟统计 */
/* 获取当前单调时间(毫秒),避免系统时间回拨影响延迟统计 */
static inline uint64_t omni_now_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)(ts.tv_nsec / 1000000u);
}
// 64 位整数主机序与网络序转换工具
/*
* 手写 64 位字节翻转工具。
* 之所以单独实现,是因为标准库广泛提供 htonl/ntohl但 64 位版本并非所有平台都统一。
*/
static inline uint64_t omni_bswap64(uint64_t x)
{
return ((x & 0x00000000000000FFull) << 56) |
@@ -58,6 +238,7 @@ static inline uint64_t omni_bswap64(uint64_t x)
((x & 0xFF00000000000000ull) >> 56);
}
/* 将主机字节序的 64 位整数编码成网络字节序。 */
static inline uint64_t omni_htonll(uint64_t x)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@@ -67,11 +248,56 @@ static inline uint64_t omni_htonll(uint64_t x)
#endif
}
/* 64 位网络字节序转回主机字节序;实现上与 htonll 对称。 */
static inline uint64_t omni_ntohll(uint64_t x)
{
return omni_htonll(x);
}
/*
* 保留 int64_t 的原始位模式,便于把 signed offset 按网络字节序放到线协议里。
* 这里用 memcpy避免依赖实现相关的强制类型转换。
*/
static inline uint64_t omni_i64_bits(int64_t x)
{
uint64_t bits = 0;
memcpy(&bits, &x, sizeof(bits));
return bits;
}
static inline int64_t omni_i64_from_bits(uint64_t bits)
{
int64_t x = 0;
memcpy(&x, &bits, sizeof(x));
return x;
}
static inline void omni_i64_net_encode(int64_t *out_value, int64_t host_value)
{
uint64_t bits = omni_htonll(omni_i64_bits(host_value));
memcpy(out_value, &bits, sizeof(bits));
}
static inline int64_t omni_i64_net_decode(const int64_t *net_value)
{
uint64_t bits = 0;
memcpy(&bits, net_value, sizeof(bits));
return omni_i64_from_bits(omni_ntohll(bits));
}
static inline void omni_copy_fixed_ascii(char *dst, size_t dst_sz, const char *src)
{
if (!dst || dst_sz == 0) {
return;
}
memset(dst, 0, dst_sz);
if (!src) {
return;
}
snprintf(dst, dst_sz, "%s", src);
}
/* 将业务侧的消息头字段编码成网络字节序,便于跨主机传输。 */
static inline void omni_msg_header_encode(MsgHeader *out_hdr,
uint32_t type,
uint32_t len,
@@ -82,6 +308,7 @@ static inline void omni_msg_header_encode(MsgHeader *out_hdr,
out_hdr->timestamp = omni_htonll(timestamp_ms);
}
/* 将网络帧头解码回本机可直接使用的主机字节序结构。 */
static inline void omni_msg_header_decode(const MsgHeader *net_hdr,
MsgHeader *host_hdr)
{
@@ -90,5 +317,247 @@ static inline void omni_msg_header_decode(const MsgHeader *net_hdr,
host_hdr->timestamp = omni_ntohll(net_hdr->timestamp);
}
#endif /* OMNISOCKET_COMMON_H */
/* 编码单个文件分片的元数据,发送端在每个 chunk 前都要附上它。 */
static inline void omni_transfer_chunk_meta_encode(TransferChunkMeta *out_meta,
uint32_t transfer_id,
uint32_t seq,
uint32_t total_chunks,
uint32_t window_id,
uint64_t total_bytes,
uint64_t offset_bytes,
uint32_t chunk_bytes,
uint64_t origin_ts_ms)
{
out_meta->transfer_id = htonl(transfer_id);
out_meta->seq = htonl(seq);
out_meta->total_chunks = htonl(total_chunks);
out_meta->window_id = htonl(window_id);
out_meta->total_bytes = omni_htonll(total_bytes);
out_meta->offset_bytes = omni_htonll(offset_bytes);
out_meta->chunk_bytes = htonl(chunk_bytes);
out_meta->reserved = 0;
out_meta->origin_ts_ms = omni_htonll(origin_ts_ms);
}
/* 将网络中的分片元数据解码回主机字节序,供接收端统计与写盘使用。 */
static inline void omni_transfer_chunk_meta_decode(const TransferChunkMeta *net_meta,
TransferChunkMeta *host_meta)
{
host_meta->transfer_id = ntohl(net_meta->transfer_id);
host_meta->seq = ntohl(net_meta->seq);
host_meta->total_chunks = ntohl(net_meta->total_chunks);
host_meta->window_id = ntohl(net_meta->window_id);
host_meta->total_bytes = omni_ntohll(net_meta->total_bytes);
host_meta->offset_bytes = omni_ntohll(net_meta->offset_bytes);
host_meta->chunk_bytes = ntohl(net_meta->chunk_bytes);
host_meta->reserved = ntohl(net_meta->reserved);
host_meta->origin_ts_ms = omni_ntohll(net_meta->origin_ts_ms);
}
/* 编码“文件发送完成”消息的元数据。 */
static inline void omni_transfer_end_meta_encode(TransferEndMeta *out_meta,
uint32_t transfer_id,
uint32_t total_chunks,
uint64_t total_bytes,
uint32_t total_windows)
{
out_meta->transfer_id = htonl(transfer_id);
out_meta->total_chunks = htonl(total_chunks);
out_meta->total_bytes = omni_htonll(total_bytes);
out_meta->total_windows = htonl(total_windows);
out_meta->reserved = 0;
}
/* 解码 FILE_END 元数据,接收端据此补全汇总统计。 */
static inline void omni_transfer_end_meta_decode(const TransferEndMeta *net_meta,
TransferEndMeta *host_meta)
{
host_meta->transfer_id = ntohl(net_meta->transfer_id);
host_meta->total_chunks = ntohl(net_meta->total_chunks);
host_meta->total_bytes = omni_ntohll(net_meta->total_bytes);
host_meta->total_windows = ntohl(net_meta->total_windows);
host_meta->reserved = ntohl(net_meta->reserved);
}
/* 编码服务端回给客户端的传输确认信息。 */
static inline void omni_transfer_ack_meta_encode(TransferAckMeta *out_meta,
uint32_t transfer_id,
uint32_t total_chunks,
uint64_t total_bytes,
uint64_t bytes_written,
uint64_t echoed_end_ts_ms)
{
out_meta->transfer_id = htonl(transfer_id);
out_meta->total_chunks = htonl(total_chunks);
out_meta->total_bytes = omni_htonll(total_bytes);
out_meta->bytes_written = omni_htonll(bytes_written);
out_meta->echoed_end_ts_ms = omni_htonll(echoed_end_ts_ms);
}
/* 解码 ACK 元数据,客户端用它计算确认 RTT 和最终落盘结果。 */
static inline void omni_transfer_ack_meta_decode(const TransferAckMeta *net_meta,
TransferAckMeta *host_meta)
{
host_meta->transfer_id = ntohl(net_meta->transfer_id);
host_meta->total_chunks = ntohl(net_meta->total_chunks);
host_meta->total_bytes = omni_ntohll(net_meta->total_bytes);
host_meta->bytes_written = omni_ntohll(net_meta->bytes_written);
host_meta->echoed_end_ts_ms = omni_ntohll(net_meta->echoed_end_ts_ms);
}
/* 编码时钟同步探测请求。 */
static inline void omni_time_sync_probe_meta_encode(TimeSyncProbeMeta *out_meta,
uint32_t probe_id,
uint64_t client_send_ts_ms)
{
out_meta->probe_id = htonl(probe_id);
out_meta->reserved = 0;
out_meta->client_send_ts_ms = omni_htonll(client_send_ts_ms);
}
/* 解码时钟同步探测请求。 */
static inline void omni_time_sync_probe_meta_decode(const TimeSyncProbeMeta *net_meta,
TimeSyncProbeMeta *host_meta)
{
host_meta->probe_id = ntohl(net_meta->probe_id);
host_meta->reserved = ntohl(net_meta->reserved);
host_meta->client_send_ts_ms = omni_ntohll(net_meta->client_send_ts_ms);
}
/* 编码时钟同步响应。 */
static inline void omni_time_sync_reply_meta_encode(TimeSyncReplyMeta *out_meta,
uint32_t probe_id,
uint64_t client_send_ts_ms,
uint64_t server_recv_ts_ms,
uint64_t server_send_ts_ms)
{
out_meta->probe_id = htonl(probe_id);
out_meta->reserved = 0;
out_meta->client_send_ts_ms = omni_htonll(client_send_ts_ms);
out_meta->server_recv_ts_ms = omni_htonll(server_recv_ts_ms);
out_meta->server_send_ts_ms = omni_htonll(server_send_ts_ms);
}
/* 解码时钟同步响应。 */
static inline void omni_time_sync_reply_meta_decode(const TimeSyncReplyMeta *net_meta,
TimeSyncReplyMeta *host_meta)
{
host_meta->probe_id = ntohl(net_meta->probe_id);
host_meta->reserved = ntohl(net_meta->reserved);
host_meta->client_send_ts_ms = omni_ntohll(net_meta->client_send_ts_ms);
host_meta->server_recv_ts_ms = omni_ntohll(net_meta->server_recv_ts_ms);
host_meta->server_send_ts_ms = omni_ntohll(net_meta->server_send_ts_ms);
}
/* 编码客户端最终确认的 offset。 */
static inline void omni_time_sync_report_meta_encode(TimeSyncReportMeta *out_meta,
int64_t server_minus_client_offset_ms,
uint64_t best_rtt_ms,
uint32_t sample_count)
{
omni_i64_net_encode(&out_meta->server_minus_client_offset_ms,
server_minus_client_offset_ms);
out_meta->best_rtt_ms = omni_htonll(best_rtt_ms);
out_meta->sample_count = htonl(sample_count);
out_meta->reserved = 0;
}
/* 解码客户端最终确认的 offset。 */
static inline void omni_time_sync_report_meta_decode(const TimeSyncReportMeta *net_meta,
TimeSyncReportMeta *host_meta)
{
host_meta->server_minus_client_offset_ms =
omni_i64_net_decode(&net_meta->server_minus_client_offset_ms);
host_meta->best_rtt_ms = omni_ntohll(net_meta->best_rtt_ms);
host_meta->sample_count = ntohl(net_meta->sample_count);
host_meta->reserved = ntohl(net_meta->reserved);
}
/* 编码 peer 注册消息。 */
static inline void omni_peer_register_meta_encode(PeerRegisterMeta *out_meta,
const char *client_id)
{
memset(out_meta, 0, sizeof(*out_meta));
omni_copy_fixed_ascii(out_meta->client_id, sizeof(out_meta->client_id), client_id);
out_meta->reserved = 0;
}
/* 解码 peer 注册消息。 */
static inline void omni_peer_register_meta_decode(const PeerRegisterMeta *net_meta,
PeerRegisterMeta *host_meta)
{
memset(host_meta, 0, sizeof(*host_meta));
omni_copy_fixed_ascii(host_meta->client_id, sizeof(host_meta->client_id), net_meta->client_id);
host_meta->reserved = ntohl(net_meta->reserved);
}
/* 编码 peer bind 消息。 */
static inline void omni_peer_bind_meta_encode(PeerBindMeta *out_meta,
const char *peer_id)
{
memset(out_meta, 0, sizeof(*out_meta));
omni_copy_fixed_ascii(out_meta->peer_id, sizeof(out_meta->peer_id), peer_id);
out_meta->reserved = 0;
}
/* 解码 peer bind 消息。 */
static inline void omni_peer_bind_meta_decode(const PeerBindMeta *net_meta,
PeerBindMeta *host_meta)
{
memset(host_meta, 0, sizeof(*host_meta));
omni_copy_fixed_ascii(host_meta->peer_id, sizeof(host_meta->peer_id), net_meta->peer_id);
host_meta->reserved = ntohl(net_meta->reserved);
}
/* 编码 hub 状态消息。 */
static inline void omni_peer_status_meta_encode(PeerStatusMeta *out_meta,
uint32_t code,
const char *self_id,
const char *peer_id,
const char *detail)
{
memset(out_meta, 0, sizeof(*out_meta));
out_meta->code = htonl(code);
out_meta->reserved = 0;
omni_copy_fixed_ascii(out_meta->self_id, sizeof(out_meta->self_id), self_id);
omni_copy_fixed_ascii(out_meta->peer_id, sizeof(out_meta->peer_id), peer_id);
omni_copy_fixed_ascii(out_meta->detail, sizeof(out_meta->detail), detail);
}
/* 解码 hub 状态消息。 */
static inline void omni_peer_status_meta_decode(const PeerStatusMeta *net_meta,
PeerStatusMeta *host_meta)
{
memset(host_meta, 0, sizeof(*host_meta));
host_meta->code = ntohl(net_meta->code);
host_meta->reserved = ntohl(net_meta->reserved);
omni_copy_fixed_ascii(host_meta->self_id, sizeof(host_meta->self_id), net_meta->self_id);
omni_copy_fixed_ascii(host_meta->peer_id, sizeof(host_meta->peer_id), net_meta->peer_id);
omni_copy_fixed_ascii(host_meta->detail, sizeof(host_meta->detail), net_meta->detail);
}
/* 编码 peer tunnel 头。 */
static inline void omni_peer_tunnel_meta_encode(PeerTunnelMeta *out_meta,
const char *src_id,
const char *dst_id,
uint32_t inner_type)
{
memset(out_meta, 0, sizeof(*out_meta));
omni_copy_fixed_ascii(out_meta->src_id, sizeof(out_meta->src_id), src_id);
omni_copy_fixed_ascii(out_meta->dst_id, sizeof(out_meta->dst_id), dst_id);
out_meta->inner_type = htonl(inner_type);
out_meta->reserved = 0;
}
/* 解码 peer tunnel 头。 */
static inline void omni_peer_tunnel_meta_decode(const PeerTunnelMeta *net_meta,
PeerTunnelMeta *host_meta)
{
memset(host_meta, 0, sizeof(*host_meta));
omni_copy_fixed_ascii(host_meta->src_id, sizeof(host_meta->src_id), net_meta->src_id);
omni_copy_fixed_ascii(host_meta->dst_id, sizeof(host_meta->dst_id), net_meta->dst_id);
host_meta->inner_type = ntohl(net_meta->inner_type);
host_meta->reserved = ntohl(net_meta->reserved);
}
#endif /* OMNISOCKET_COMMON_H */