在上一章 虚拟网络 (Network) 中,我们把虚拟网络比作一个安全的"线上办公室"。我们知道,只有被邀请的成员才能进入这个办公室并相互交流。
那么,究竟是谁在加入这些"线上办公室"呢?我们用来上网的设备------无论是笔记本电脑、服务器还是手机------在 ZeroTier 的世界里是如何被表示的呢?
这就是 节点 (Node)
概念的作用所在。如果说 Network
是办公室,那么 Node
就是进入办公室工作的"你"的数字分身。
什么是节点 (Node)?
Node
是 ZeroTier 客户端实例的核心,是整个系统的大脑和总指挥。当你在设备上运行 ZeroTier 时,一个 Node
对象就会被创建出来,代表你这台独一无二的设备。
让我们继续使用"线上办公室"的比喻来理解 Node
:
把一个 Node
想象成你在 ZeroTier 世界里的 "数字身份和个人助理"。
- 独一无二的身份 :每个
Node
都有一个全球唯一的地址(例如a1b2c3d4e5
),这是通过一个加密的身份 (Identity)生成的。这就像是你的数字护照,无论你加入哪个"线上办公室",这个身份都跟你走,证明"你就是你"。 - 办公室通行证管理员 :你的
Node
持有一串"钥匙",可以进入多个不同的虚拟网络 (Network)(线上办公室)。它负责管理你加入了哪些网络,并处理加入(join
)和离开(leave
)这些网络的所有事务。 - 通信总调度员 :当你想和办公室里的同事(另一个节点)说话时,你的个人助理(
Node
)会负责打包你的信息(数据包),贴上正确的地址标签,然后通过最高效的路径发送出去。反之,当有信息发给你时,也是由它签收、拆包,并交给你。 - 日程和维护管家 :你的助理会默默地处理所有后台杂务,比如:定期和同事们打个招呼(
ping
)以确保联系畅通,检查办公室的规章制度(网络配置)有没有更新,保持整个通信系统的健康和稳定。
简而言之,你的设备上所有与 ZeroTier 相关的活动,都是围绕着这个 Node
实例展开的。它是所有其他核心组件的"老板",负责创建和管理它们。
Node
对象的核心职责
Node
对象是 ZeroTier 中最顶层的管理类,它的主要职责包括:
- 初始化与生命周期管理 :当 ZeroTier 启动时,
Node
对象被创建;当 ZeroTier 关闭时,它被销毁。它负责初始化所有必要的子系统,比如身份 (Identity)管理器、交换机 (Switch)和拓扑 (Topology)管理器。 - 网络成员关系管理 :通过
join()
和leave()
方法,Node
负责将自身加入或脱离一个虚拟网络 (Network)。它内部维护着一个列表,记录了所有已加入的Network
对象。 - 数据包流转中心 :
Node
是数据进出的主要入口。它有两个核心的数据处理函数:processVirtualNetworkFrame()
:处理来自你本地电脑(虚拟网卡)的数据包。processWirePacket()
:处理来自物理网络(互联网)的数据包。
- 后台任务调度 :通过
processBackgroundTasks()
方法,Node
定期执行维护任务,例如保持与对等节点 (Peer)的连接、向网络控制器请求最新配置等。
代码中的 Node
Node
类的定义可以在 node/Node.hpp
文件中找到。下面是它简化后的结构:
cpp
// 文件: node/Node.hpp
class Node
{
private:
// ... (省略部分成员)
// 运行环境,包含身份、拓扑等所有核心子组件
RuntimeEnvironment _RR;
// 用于与外部(如操作系统服务)交互的回调函数
ZT_Node_Callbacks _cb;
// 已加入的网络列表 (Key: 网络ID, Value: Network对象)
Hashtable< uint64_t,SharedPtr<Network> > _networks;
Mutex _networks_m; // 用于保护网络列表的锁
volatile int64_t _now; // 缓存的当前时间戳
bool _online; // 节点的在线状态
public:
// 构造函数:当 ZeroTier 客户端启动时调用
Node(void *uptr, void *tptr, const struct ZT_Node_Callbacks *callbacks, int64_t now);
// 加入一个虚拟网络
ZT_ResultCode join(uint64_t nwid, void *uptr, void *tptr);
// 离开一个虚拟网络
ZT_ResultCode leave(uint64_t nwid, void **uptr, void *tptr);
// 处理来自物理网络(互联网)的数据包
ZT_ResultCode processWirePacket(...);
// 处理来自虚拟网络(你的电脑应用)的数据包
ZT_ResultCode processVirtualNetworkFrame(...);
// 执行后台维护任务
ZT_ResultCode processBackgroundTasks(...);
};
_RR (RuntimeEnvironment)
: 这是一个非常重要的辅助结构体,可以看作是Node
的"工具箱"。它捆绑了几乎所有Node
需要的子组件,包括节点的身份 (Identity)、网络拓扑 (Topology)(记录了所有已知的对等节点 (Peer))和核心的交换机 (Switch)等。_networks
: 这是一个哈希表,存储了所有当前Node
已加入的虚拟网络 (Network) 对象。join()
/leave()
: 这两个函数让你能控制Node
加入或离开一个网络。调用join(nwid)
就会在_networks
哈希表中创建一个新的Network
对象。process...()
函数:这些是Node
的主要工作入口,负责接收和调度所有的数据包和任务。
Node
的工作流程:数据包的旅程
想象一下,你的电脑(IP为 10.0.0.1
)想要 ping
同一个虚拟网络里的另一台服务器(IP为 10.0.0.2
)。这个数据包是如何通过 Node
发送出去的呢?
这是一个简化的流程图:
- 你的应用程序(如
ping
)创建了一个标准网络数据包,希望发送到10.0.0.2
。 - 操作系统看到这个目标地址属于 ZeroTier 的虚拟网卡,于是将数据包交给了 ZeroTier 服务。
- ZeroTier 服务调用
Node
对象的processVirtualNetworkFrame()
方法,将数据包传入。 Node
并不亲自处理这个数据包的路由逻辑。它扮演的是一个"总指挥"的角色,将这个任务委派给了更专业的下属------交换机 (Switch) 对象。Switch
就像一个物理交换机,它会查看数据包的目标 MAC 地址(由 IP 地址10.0.0.2
解析而来),然后在自己的"地址簿"里查找这个 MAC 地址属于哪个对等节点 (Peer)。- 找到对应的
Peer
后,Switch
对数据包进行加密,然后命令Peer
对象将其发送出去。 Peer
对象负责处理所有与那个特定远程节点的通信细节,比如通过哪条物理路径 (Path) 发送数据包最快。
深入代码:任务的委派
让我们看看 processVirtualNetworkFrame
的代码实现,它完美地体现了 Node
的"委派"角色。
cpp
// 文件: node/Node.cpp
ZT_ResultCode Node::processVirtualNetworkFrame(
void *tptr,
int64_t now,
uint64_t nwid, // 网络ID
// ... 其他参数
const void *frameData,
unsigned int frameLength)
{
_now = now; // 更新内部时钟
// 1. 根据网络ID,从 _networks 列表中找到对应的 Network 对象
SharedPtr<Network> nw(this->network(nwid));
if (nw) {
// 2. 如果找到了,就把数据包和网络信息一起交给 Switch 处理
RR->sw->onLocalEthernet(tptr,nw,MAC(sourceMac),MAC(destMac),etherType,vlanId,frameData,frameLength);
return ZT_RESULT_OK;
} else {
// 如果节点没有加入这个网络,则返回错误
return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
}
这段代码非常清晰。Node
做的第一件事是确认这个数据包属于哪个它已经加入的虚拟网络 (Network)。如果确认无误,它就直接把所有信息(包括找到的 Network
对象和数据包本身)一股脑地扔给它的"下属" RR->sw
(即 交换机 (Switch) 对象),让它去处理后续所有复杂的路由、加密和发送工作。
这种"高层指挥,底层执行"的设计模式是 ZeroTier 代码结构的核心,它使得 Node
类可以保持相对简洁,专注于宏观管理,而将具体实现细节封装在各自的组件中。
总结
在本章中,我们认识了 ZeroTier 系统的核心------节点 (Node)
。
Node
是你设备在 ZeroTier 网络中的数字体现,是所有操作的中心。它就像一个拥有唯一身份 的个人助理。- 它负责管理你的身份 (Identity),维护你加入的所有虚拟网络 (Network) 的列表。
Node
是一个总指挥 和调度员 ,它接收来自本地应用和互联网的数据包,并将其委派给专门的子组件(如Switch
)进行处理。- 它还负责执行必要的后台任务,确保整个系统的健康和稳定。
现在我们知道了 Node
是代表我们设备的核心实体,而每个 Node
都必须有一个独一无二的、可验证的身份。这个身份是如何产生的?它又是如何保证我们的通信安全的呢?
在下一章中,我们将深入探讨 Node
的"数字护照"------身份 (Identity)。