服务端协议与多服务端容灾
专栏:VNT 2.0 组网源码 · 第 9 / 11 篇:::info 学习目标 完成本篇后你能够:列出服务端的四大职责与对应协议消息;解释多服务端协调注册的三步握手;说出三种传输协议与 dynamic:// 的发现机制。 前置:第 3、5 篇完成。预计时长:55 分钟。 :::
:::note 本章术语速查(新手建议先读)
- 容灾:一台服务挂了,备用的无缝顶上。
- 增量推送:只推“变化的部分”(带版本号对比),省流量。
- TXT 记录:DNS 的一种记录,可存任意文本——VNT 用它动态发现服务端地址。
- WSS:走 HTTPS 通道的 WebSocket,容易穿过只放行网页流量的网络。 :::
服务端 vnts 是独立项目(本仓库只有客户端侧的协议实现),但从 tunnel_core/server/ 与 proto/ 的协议定义可以精确还原它的四大职责。
服务端的四大职责
| 职责 | 协议 | 说明 |
|---|---|---|
| ① 注册与 IP 分配 | RegRequest/RegResponse/ConfirmReg | 同组网编号分配互不冲突的虚拟 IP |
| ② 在线列表 | ClientSimpleInfoList(推送)+ client_list RPC | 谁在线、叫什么、什么版本 |
| ③ 中继转发 | Turn 密文透传 | 无直连时的兜底通道(服务端只见密文) |
| ④ 打洞撮合 | PunchStart1/2 转发 | 交换双方 NAT 信息(第 6 篇) |
注册协议全字段
// vnt-core/proto/control_message.proto:9-33(节选)
message RegRequestMsg {
string network_code = 1; // 组网编号
string device_id = 2; // 设备唯一标识(默认 machine uid)
optional fixed32 ip = 3; // 请求的固定 IP(可选)
string name = 4; // 设备名
string version = 5;
optional string key_sign = 6; // 口令指纹(第 5 篇)
bool ip_variable = 7; // 是否接受服务端改 IP
fixed32 server_id = 8;
RegistrationMode registration_mode = 9; // NORMAL / PRE_REGISTER
}
message RegResponseMsg {
fixed32 ip = 1; // 分配的虚拟 IP
uint32 prefix_len = 2; // 网段前缀
fixed32 gateway = 3; // 虚拟网关
string server_version = 4;
}
key_sign 让服务端校验”同编号必同密码”(零知识);ip_variable 决定 IP 冲突时是拒绝还是接受新分配。
多服务端协调注册:三步握手
配多台服务端时(容灾/负载均衡),所有服务器必须对”谁用哪个 IP”达成一致:
// vnt-core/src/core/mod.rs:263-272(节选)
let response = if is_multi_server {
// Multi-server: coordinated pre-registration
coordinated_registration(&mut ctx.server_managers).await?
} else {
ctx.server_managers[0].connect_and_reg(RegistrationMode::Normal).await?
};
注册完成后每台服务器一条数据通道:5 秒心跳(顺带携带在线列表版本号)、断线 5 秒退避重连、重连后网段变化则 60 秒退避(全部路由失效需重建)。
三种传输协议与 dynamic://
服务端地址支持前缀选择传输方式(server/transport/config.rs:73-91):
| 前缀 | 传输 | 特点 |
|---|---|---|
(无)或 tcp:// | TLS over TCP | 兼容性最好(默认) |
quic:// | QUIC | 握手快、多路复用 |
wss:// | WebSocket over TLS | 可过只放行 WebSocket 的网络 |
dynamic://域名 | DNS TXT 记录发现 | TXT 里写真实协议与地址——服务端搬家不用改客户端 |
三种传输统一帧化(LengthDelimitedCodec)后跑同一套 protobuf 协议。在线列表双通道:服务端增量推送(ClientSimpleInfoList{data_version, list[]},data_version 对不上才全量)+ 客户端主动 RPC client_list(返回 name/version/ip/key_sign/online/last_connected_time)。
与其他模块的联系
- 第 5 篇:key_sign 是服务端唯一的密码知识;中继的包全是密文;
- 第 6 篇:PunchStart1/2 经服务端转发——服务端是打洞的信使;
- 第 7 篇:ServerOutbound 按 RTT 选最低延迟的已连服务器发中继包。
常见踩坑
- v1 客户端连 v2 服务端——协议不兼容(v2 明示整体重构);
- 多服务端只配了一台在配置里却期望容灾——
-s参数可重复,确认全部写上; - dynamic:// 解析失败——检查域名的 TXT 记录格式(支持 udp://quic://tcp://ws://wss:// 前缀)。
随堂练习(带验收标准)
- 搭两台服务端(或读 vnts 项目源码),完成协调注册三步。验收:断掉一台后中继照常;
- 抓握手包确认传输协议与证书校验模式(skip 时能看到明文证书);
- 进阶:配置 dynamic:// 并自建 DNS TXT 记录,验证发现流程。