一次组网的完整生命周期
专栏:VNT 2.0 组网源码 · 第 3 / 11 篇:::info 学习目标 完成本篇后你能够:按顺序说出组网生命周期的全部步骤;理解多服务端协调注册的三步握手;找到每一步在源码中的位置。 前置:第 2 篇完成。预计时长:50 分钟。 :::
:::note 本章术语速查(新手建议先读)
- crate:Rust 的编译/发布单元(≈ 一个库或程序)。vnt-core 是一个 crate。
- protobuf:Google 的二进制序列化格式,比 JSON 小且快,VNT 的协议消息全用它。
- 心跳:定期发送的“我还活着”信号,超时未收到即判定掉线。
- 退避(Backoff):重试越来越慢的策略(3s、6s、9s…),避免把故障方打爆。
- TaskGroup:VNT 自制的任务组——组内任何关键任务退出,整个程序停止(防半死状态)。 :::
vnt2_cli -k 123456 -s 101.35.230.139:6660 敲下去之后发生了什么?主线在 vnt-core/src/core/mod.rs 的 NetworkManager,分三段:create_network(装配)→ register(注册拿 IP)→ start_device(起网卡)。
生命周期总览
第一段:create_network——装配车间
create_network(core/mod.rs
// vnt-core/src/core/mod.rs:81-96(节选)
let mtu = config.mtu.unwrap_or(DEFAULT_MTU); // 默认 MTU 1380
let packet_crypto = PacketCrypto::new_from_str(config.password.as_deref())?; // 密钥=SHA256(密码)
let (server_manager_list, tunnel_to_server, server_rpc) =
create_server_tunnel(app_state.clone(), &config, packet_crypto.clone(), default_interface.clone());
let device_io_manager = DeviceIOManager::new(task_group.clone());
let allow_subnet = AllowSubnetExternalRoute::new(config.output.clone());
let (puncher, p2p_socket, p2p_task) = if !config.no_punch {
// 初始化 P2P 隧道:2主UDP+82子UDP+TCP、STUN 检测、打洞器
... init_tunnel(...).await?
装配清单(每项对应后续一篇):包加解密器(第 5 篇)→ 每台服务器一条控制隧道(第 9 篇)→ P2P 隧道与打洞器(第 6 篇)→ 出站流水线 HybridOutbound(压缩+FEC+加密,第 7 篇)→ 按设备模式装 TUN 或内置 NAT(第 4 篇)→ QUIC 增强通道(第 8 篇)。全部组件打包进 RegistrationContext,等待注册。
第二段:register——拿虚拟 IP
// vnt-core/src/core/mod.rs:263-285(节选)
let is_multi_server = ctx.server_managers.len() > 1;
let response = if is_multi_server {
// 多服务端:协调式预注册
coordinated_registration(&mut ctx.server_managers).await?
} else {
ctx.server_managers[0]
.connect_and_reg(RegistrationMode::Normal).await?
};
let reg_response = match response {
ResponseMessage::Reg(reg) => { /* ip, prefix_len */ }
ResponseMessage::Error(e) => return Ok(RegisterResponse::Failed(e)),
...
};
注册协议是 protobuf 的 RegRequestMsg{network_code, device_id, key_sign, ip?…} → RegResponseMsg{ip, prefix_len, gateway}。多服务端容灾的三步协调很精彩:先向第一台 PRE_REGISTER 拿 IP → 把同一个 IP 并发预注册给其余服务器 → 向全部 ConfirmReg。这样任何一台服务器挂了,其余服务器都认识这个 IP,中继与在线列表无缝衔接。
注册成功后客户端启动每服务器的 data_handle_task_connected:5 秒心跳、断线 5 秒退避重连、重连后若网段变了则 60 秒退避(网段变化意味着缓存的路由全部失效)。
第三段:start_device——网卡上线
main_cli.rs:108 之后:start_device() 创建 TUN(Linux 开 offload、Windows 设 metric)、set_device_network_ip 给网卡配 IP、给 input 子网加系统路由。此后数据面常驻(第 7 篇):TUN 读写循环、打洞任务、心跳,全部挂在 TaskGroup 上——任一关键任务退出即整体停止(防半死状态)。
与其他模块的联系
- 第 4 篇:start_device 里的 TUN 细节;
- 第 6 篇:注册完成后启动的打洞任务与 STUN 检测;
- 第 9 篇:RegRequest 各字段(key_sign 口令指纹、ip_variable 等)与服务端视角。
常见踩坑
- 注册失败一直重试——先查网络与服务端地址(协议前缀 quic://tcp://wss:// 决定传输方式);
- 多服务端配置了但 IP 一直变——检查各服务端是否同一组网数据(协调注册假设服务端间数据一致);
- start_device 权限错误——Linux 需 CAP_NET_ADMIN 或 root;Windows 需管理员(wintun)。
随堂练习(带验收标准)
- 用
RUST_LOG=debug跑 vnt2_cli,对照本篇时序图标注每行日志属于哪一步; - 只配两台服务端,观察日志里的 coordinated registration 三步;
- 进阶:给 create_network 的装配顺序画一张依赖图,解释为什么 P2P 隧道要在出站流水线之前初始化。