您现在的位置: 首页 > 网站导航收录 > 百科知识百科知识
如何从头开始构建类似BitTorrent的P2P网络?
节点,文件,过程如何从头开始构建类似BitTorrent的P2P网络?
发布时间:2020-12-06加入收藏来源:互联网点击:
Kademlia 将节点之间的距离定义为节点 ID 的“按位异或”(XOR)。XOR 运算比较两个输入值:如果这些输入相同,则结果为 false(0);如果输入不同,则结果为 true(1)。两个数字的异或是通过在这两个数字的二进制表示中找到每一位的 XOR 来计算的。
例如,下图假设 4 位密钥空间中的节点 ID 为 11(只有 0~15 的的 ID 是可能的)。为证明这个概念,我们使用一些其他 ID 来计算 11 的 XOR。
在第一个例子中,我们计算节点 ID 11 和节点 ID 10 的 XOR 结果。两个 ID 的前三位是相同的,只有最后一位不同。ID 11 和 ID 10 进行 XOR 运算的结果是二进制的 0001,或者十进制的 1。
接下来我们计算 ID 11 和 ID 12 的 XOR 结果,只有第一位是相同的,而其他部分都是不同的。ID 11 和 ID 12 的 XOR 运算的结果是二进制的 0111,或十进制的 7。
我们最后的一个例子是计算 ID 11 和 ID 4 的 XOR 结果。这里所有的位都不相同,结果是二进制的 1111,十进制为 15。
从这些结果中,你会注意到 Kademlia 的 XOR 度量的一个重要特征:如果节点 ID 和当前节点的 ID 的二进制表示所共有的位相同个数越多,那么计算得到的 XOR 结果就越小。
Kademlia 网络和路由表
Kademlia 中的每个节点都可以看做是二叉树中的叶子。下面我们在 4 位密钥空间中画出所有可能的 ID:0~15,从根(root)出发,每一步往左则在该位上新增一个“0”,往右则在该位上新增一个“1”。
与 Kademlia 网络相关的二叉树最重要的特性是 O(log n) 查找时间。在 Kademlia 网络中查找节点或文件是非常有效率的过程。
如前所述,路由表将联系节点进行分组并存储到桶中,每个桶中包含一定距离的节点。这个距离就是“共享位长度”,它是通过节点 ID 与当前 ID 进行 XOR 运算得到结果来得到的。
从下图我们将看到这些桶是如何在一个 4 位密钥空间中以 ID 11 的节点中组织的。其 ID 与当前节点共享前 3 位前缀的节点存储在一个桶中,而 ID 与当前节点共享前 2 位前缀的节点存储在另一个桶中,以此类推。
文件切分和检索
文件切分是将文件切分成更小的片段,命名为切片,并将 files/names 记录在一个清单文件(即种子)中,这样它们就可以按照适当的顺序检索和重新合并。
文件切分提高了 P2P 网络的分布性和可靠性,因为多个节点可以存储共享文件的部分或全部切片。如果包含切片下载信息的节点离线了,此时可以从不同的源检索该切片信息。
文件切分还可以节省网络带宽,共享潜在大文件的负载分布在许多节点中。
文件切分过程中会生成多个切片和一个清单文件(种子)。
在我们的实现中,文件添加和切分的过程是这样的:
通过拖放将要共享的文件上传到网络中。
通过计算文件内容的 SHA1 哈希值为文件创建 ID。
这个 ID 被重新用作种子的文件名,扩展名为“.xro”。这个过程对用户来说是透明的,用户只需知道 ID 即可。
原始文件切分成 1 MB 块,如果小于 1 MB,则切分为原始大小的 50%。
每个切片都被写入磁盘,切片的名称就是其内容的 SHA1 哈希值。
切片名称是按照顺序写入种子文件的,以及原始文件名和文件大小(以字节为单位)。
种子和切片位置信息通过 STORE 远程过程调用广播给对等节点。对于每个 shard/manifest,宿主节点在自己的路由表中查找与文件 ID 最接近的节点。这些对等节点都接收包含文件的 ID 和位置的 STORE 远程过程调用。
下面是 JSON 格式的种子内容:
复制代码
{
"file_name":"banana.mp4",
"length":9137395,
"pieces":[
"272610812651008498817059664145444816819140431736",
"255845820650928817902394043384061703021184974492",
"709124865584808529999187320247131501825035282844",
"463972141944555281071361859762050722622562309482",
"665233928362169136349271011451642022996948352498",
"460767108478119568061824765684889409150273585314",
"242856439500459087965632547950882773486858003109",
"1113118586291233368092664992853829437069513635744",
"55094692080869844054492088107211106202780121432"
]
}
文件检索的处理方式也是类似的:
用户输入他们想要从网络中检索的文件的 ID。这个 ID 是文件内容的哈希值,也恰好是他们首先检索的清单文件的名称。
通过向最接近清单文件 ID 的对等节点发出 FIND VALUE 远程过程调用,从网络中检索清单文件。
下载清单文件后,节点为清单文件中记录的每个切片重复查找和下载过程(FIND VALUR 远程过程调用)。
下载完所有切片后,将它们重新合并到原始文件中。
然后,节点向最接近文件各自 ID 的对等节点广播其获取的切片和清单的位置信息。
从 Xorro P2P 网络中检索文件的例子。
开发策略:从本地环境模拟并扩展网络应用到真实网络。
第一阶段:测试模式
我们是如何着手构建和测试的?
在项目的早期阶段,我们需要测试节点间的通信,但我们只有类和测试套件,没有网络,没有远程过程调用传输,甚至也没有几台计算机。
我们经常启动 Ruby 的交互式 Shell(IRB),将几个节点进行实例化,并让它们手动通信。当然,这可以通过脚本来实现。但一旦引入真正的网络环境,并且节点对象不能在相同的 Ruby 进程中直接使用,否则它就会很快崩溃。
我们需要某种代理对象,它在测试和本地开发期间以一种方式运行,在实际网络环境中部署时又以另一种方式运行。
我们的解决方案是让每个节点将所有网络通信都委托给先前存在的网卡(Network Adapter)对象。
在测试时,在这个网卡对象实际上是一个“Fake Network Adapter”(伪网卡),本质上是一个由其他节点组成的数组,带有一些用于查找和远程过程调用代理的方法。这允许我们在没有远程过程调用传输协议的情况下在本地沙箱中测试节点的交互过程。
典型的工作流程如下图所示:
节点 A 要求网络向节点 B 发送远程过程调用,节点 A 提供联系节点的 ID、IP 和端口。
网络查找节点 B 是否存在于 Fake Network (伪网络)环境中,这是通过联系节点的 ID 来完成的。
网络直接调用节点 B 上的方法,改变状态和 / 或返回一些数据作为响应。
网络将该响应传递回节点 A,节点 A 反过来改变状态或对这些信息做出响应。
第二阶段:在本地沙箱中通过 HTTP 进行远程过程调用
我们的下一步是引入 HTTP 协议作为构建远程过程调用方法的基础协议。
为此,我们实现了一个 Real Network Adapter (真网卡)对象。它与我们的 Fake Network Adapter 具有相同的接口,但不是通过 ID 查找接收节点并直接调用该方法的,Real Network Adapter 从所提供的联系节点信息和对应于远程过程调用的路由中列出的 IP / 端口处理一个 HTTP Post 请求,可能包括请求主体中的相关数据——请求节点的联系信息、查询信息等。
典型工作流程与上图类似:
节点 A 要求网络向节点 B 提供远程过程调用,节点 A 提供联系节点的 ID、IP 和端口。
网络为 IP、端口和远程过程调用路径生成 HTTP POST 请求,并将其与任何有效负载数据一起发送。
上一篇:两个女孩同时爱上了一个男孩,而且谁也不肯放手,他该怎么办?
下一篇:返回列表
相关链接 |
||
网友回复(共有 0 条回复) |