Files
OmniSocket/include/common.h
2026-03-16 22:28:05 +08:00

564 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 字节应用层消息头。
* 注意:这不是 TCP/KCP/UDP 的传输层头,而是项目自己定义的“业务帧头”。
*/
typedef struct MsgHeader {
uint32_t type; /* 消息类型:文件块/控制指令等 */
uint32_t len; /* 后续负载长度(字节数) */
uint64_t timestamp; /* 发送时间戳(毫秒) */
} MsgHeader;
#define MSG_HEADER_SIZE (sizeof(MsgHeader)) /* 16 字节 */
/* 应用层消息类型约定。 */
enum {
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,
OMNI_ERR_GENERIC = -1,
OMNI_ERR_PARAM = -2,
OMNI_ERR_IO = -3,
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 位字节翻转工具。
* 之所以单独实现,是因为标准库广泛提供 htonl/ntohl但 64 位版本并非所有平台都统一。
*/
static inline uint64_t omni_bswap64(uint64_t x)
{
return ((x & 0x00000000000000FFull) << 56) |
((x & 0x000000000000FF00ull) << 40) |
((x & 0x0000000000FF0000ull) << 24) |
((x & 0x00000000FF000000ull) << 8) |
((x & 0x000000FF00000000ull) >> 8) |
((x & 0x0000FF0000000000ull) >> 24) |
((x & 0x00FF000000000000ull) >> 40) |
((x & 0xFF00000000000000ull) >> 56);
}
/* 将主机字节序的 64 位整数编码成网络字节序。 */
static inline uint64_t omni_htonll(uint64_t x)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return omni_bswap64(x);
#else
return 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,
uint64_t timestamp_ms)
{
out_hdr->type = htonl(type);
out_hdr->len = htonl(len);
out_hdr->timestamp = omni_htonll(timestamp_ms);
}
/* 将网络帧头解码回本机可直接使用的主机字节序结构。 */
static inline void omni_msg_header_decode(const MsgHeader *net_hdr,
MsgHeader *host_hdr)
{
host_hdr->type = ntohl(net_hdr->type);
host_hdr->len = ntohl(net_hdr->len);
host_hdr->timestamp = omni_ntohll(net_hdr->timestamp);
}
/* 编码单个文件分片的元数据,发送端在每个 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 */