所设计的点对点 (
P2P)
重叠网络传输协议,以达成非集中式的点对点 (
P2P)
电脑网络。它规制了网络的结构及规范了
节点间的通讯和交换资讯的方式。Kademlia 节点间使用
传输通讯协定 UDP (请见
OSI模型) 沟通。Kademlia 节点借以实作
分布式杂凑表 (DHT,distributed hash table) 以储存资料。透过既有的局域网/广域网(
LAN/
WAN) (如同
因特网),一个新的
虚拟网络或是
重叠网络被建立起来。每个
网络节点都是以一组数字(“节点 ID”)来识别。这组数字不但做为识别之用,Kademlia 算法还会用来做其他用途。
一个想要加入网络的节点需要先通过启动。在这个阶段,这个节点需要知道另一个已经在 Kademlia 网络内的节点之 IP 位址 (透过另一个使用者或储存的清单取得)。如果启动中的节点还不是网络的一部分,它便会计算一个尚未指定给其他节点的随机 ID 编号。这个 ID 会一直使用到离开网络为止。
Kademlia 算法是基于两节点间的“距离”来计算。这个距离是以两节点的 ID 进行互斥运算,并将结果四舍五入至整数。
这个“距离”跟实际的地理环境无关,而是标明 ID 范围内的距离。因此一个德国的节点和一个澳洲的节点就有可能被称为“邻居”或“芳邻”。
Kademlia 内的资讯都储存在称为“数值”的东西内,每个数值都连接著一个“金钥”。
当搜寻某个金钥时,算法会透果几个步骤探整个网络一圈,每个步骤都会更接近要搜寻的金钥,直到被连线的节点传回数值,或找不到更近的节点。网络的大小仅会稍微影响到进行搜寻时接触到的节点数目:假如目前网络的使用者突然增为两倍,那使用者节点大概只需要在搜寻时多查询一个节点,而不是两倍的节点量。
非集中式的结构提供了更大的优势,并很明显地增加了对拒绝服务阻断攻击的抵抗。即使一整系列的节点被壅塞,也不会对网络可用度造成太多影响,最后网络会透过绕过这些“洞”而自我修复。
Kademlia 被用来进行档案分享。透过进行 Kademlia 关键字搜寻,任何人可以在档案分享网络中寻找资料以下载东西。由于没有任何中央服务器储存盘案列表的索引,因此这项工作是平均的由所有的客户端担当:拥有要分享的档案枝节点,会先处理档案的内容,并从内容计算出一组数字(杂凑),这组数字将会在档案分享网络中辨识这个档案。杂凑与节点 ID 的长度必须相同。接著会搜寻几个 ID 与杂凑相近、且节点内有储存著自己 IP 位址的节点。搜寻的客户端会使用 Kademlia 来搜寻网络上节点ID离自己最近距离的节点来取得档案杂凑,然后会取得在该节点上的联络清单。当节点联入和联出时,这份存储在网络上的联络清单也将保持不变。因为内嵌的冗余存储算法,联系清单将复制在多个点上。
档案杂凑通常都是由其它地方的特制因特网键结来取得,或者被包含在来自其它来源中的索引档中。
对档案名称的搜索是基于关键字来实现的。档案名称被分成几个组成档案名称的单字。 每个关键字都会被杂凑,并和相对的档案名称与档案杂凑利用和档案杂凑一样的方式储存到网络上。一个搜索者会选择其中的一个关键字,联系上和关键字杂凑最相近的节点ID,然后取得含有关键字的档案名称列。既然在档案名称列中的每个档案名称都附有自己的凑杂,那麽被选的档案就可以由一般的方式取得。
Kad是Kademlia的简称,eMule的官方网站在2004年2月27日正式发布的 eMule v0.42b中,Kad开始正式内嵌成为eMule的一个功
能模块,可以说从这个版本开始eMule便开始支持Kad网络了。
Kad的出现,结束了之前edonkey时代,在ed圈里只存在着ED2K一种网络的模式,它通过新的协议开创并形成了自己的kad网络,使之
和ED2K网络并驾齐驱,而且它还完全支持两种网络,可以在两种网络之间通用。Kad同样也属于开源的自由软件。它的程序和源代码
可以在官方网站http://www.emule-project.net上下载。
Kad网络拓扑的最大特点在于它完全不需要服务器,我们都知道传统的ed2k网络需要服务器支持作为中转和存储hash列表信息,kad
可以不通过服务器同样完成ed2k网络的一切功能,你唯一要做的就是连线上网,然后打开kad。Kad需要UDP端口的支持,之后Emule
会自动按照客户端的要求,来判断它能否自由连线,然后同样也会分配给你一个id,这个过程和我们ed2k的高id和低id检查很像,不
过这个id所代表的意义不同于ed2k网络,它代表一个是否“freely”的状态。
Kad和ed2k网络有着完全不同的观念但是相同的目的: 都是搜索和寻找文件的源。 Kad网络的主要的目标是做到不需要服务器和改善
可量测性。相对于传统的ed2k服务器只能处理一定数量的使用者(我们在服务器列表也都看到了,每个服务器都有最大人数限制),而
且如果服务器比较大连接人数过多,还会严重的的拖垮网络。而Kad能够自我组织,并且自我调节最佳的使用者数量以及他们的连接
效果。因此, 它更能使网络的损失达到最小。由于具备了以上所叙述的功能,Kad也被称之为Serverless network(无服务器
网络)。虽然目前一直处于开发阶段(alpha stage) 。但毫无疑问,它无可比拟的优势,将会使它成为p2p的明天。
可能很多朋友会关注, kad网络没有高低id的计算原则,是否对于低id来言就畅通无阻了呢?
我们大家知道在ed2k网络里面,我们的id是通过ip进行如下的算法计算得出的
设我们的IP = A.B.C.D
那么我们的ID number= A + 256*B + 256*256*C + 256*256*256*D
low ID的产生是由于我们的ID计算结果小于16777216.
即 ID number= A + 256*B + 256*256*C + 256*256*256*D < 16777216
Kad的 id计算原则并不是象上面那样,他更关注我们是否open和freely。
但是kad里面是如何计算我们的id呢?
事实上它的计算方法是这样
ID number=256*256*256*A+256*256*B+256*C+D
所以kad其实也有高低id的分别。所以内网用户在使用的时候依旧无法达到内网用户完全穿透网络的效果,而且目前来看,还存在着kad
模块引入,导致占用系统资源会变大以及会突然产生Memory Leak的问题,对于内存的控制,目前emule做的效果还是不好。
其实kad本身有一个nodes.dat文件,也叫做节点文件,这里面存放了我们在Kad网络中的邻居节点,我们都是通过这些节点来进入Kad
网络的。其实kad的网络倒更像是overnet和Kazaa网络,有兴趣的朋友大家可以对比看看。Kad网络提供了帮助寻找节点以及记录节点的机制。
下面我们来说说这个机制的原理:
Kad拥有一个160bit的ID,每一个节点送出的讯息都必须包含此ID。每一个节点都必须记录一个资料来保存已经存在的节点,资料的格式是
(IP address, UDP port, Node ID),节点所必须负责的范围是2的i次方及2的i+1次方,i的范围是0 < i <160,这个结构叫做
k-bucket,该结构会形成一个tree的形状,每一次接收到新的信息时,各个节点都必须更新k-bucket內的资料,透过k-bucket结构我
们可以保证所有的节点状态都是新的,而且一定会知道这个节点在哪里。
Kademlia网络提供四种Potocol(RPC)
(1)PING 测试是否节点存在
(2)STORE存储通知的资料
(3)FIND_NODE 通知其他节点帮助寻找node
(4)FIND_VALUE 通知其他节点帮助寻找Value
而当每一个指令被接受到后,每一个节点都会到k-bucket上搜寻,通过这样的结构,kad提供一个方便快速且可以被保证在logN次数下找到所需的节点。
通俗的来讲就是在kad网络中,我们每个emule用户端只负责处理一小部分搜索和查找源的工作。分配这些工作的时候,通过我们每个用户端的唯一
的ID和搜索文件的hash值之间的匹配来决定。比如像我猜我猜我猜猜.rm这个文件由用户小王来负责(通过该文件的hash值来决定),那么任何其他用
户在下载这个文件的時候都会告诉其他用户,小王有这个文件,其他用户去下载这个文件的時候也会询问小王,小王也会告诉他们谁正在共享这个文件,这
样kad找源的工作就完成了。搜索时候的方法也差不多,只不过是每个人负责一个关键字。
整个过程有点像在照线索循序问路而找到正确方向,而不是路上随便到处抓人在问路。而每个地方里的网络相关信息,则会随着电脑及文件的加入而持续
更新。好处在于让你可以搜索整个网络,而不只是在某一地区。目前来讲,这个机制和算法是绝对领先而且非常优秀的。
如何找到用户小王则是通过将用户id异或的方式,两个id的二进位异或值决定他们之间的逻辑距离,如1100距离1101要比距离1001近。那么当一个用户
加入kad后,首先通过一个已知的用户找到一批用户的id和ip地址和端口。当该用户要寻找一个特定用户A的时候,该用户先询问几个已知的逻辑距离较A较
近的用户,如B用户,C用户,D用户,B,C,D会告诉该用户他们知道的更加近的用户的id和ip地址和端口,同理类推,这个用户最终就能找到A。所以寻
找的次数会在logN数量级,这里N代表询问的人数。
其实也就是一种分散式杂凑的方法,基本上是对网络上某一特定时刻的文件进行快照(snapshot),然后将这些信息分散到整个网络里。 为了找到特定
的文件,搜索的要求先到达网络上的任何一台电脑上,然后这台电脑就会再将它转到另一台有更多文件信息的电脑。第三台电脑可能就拥有文件本
身──或者也可能再继续转到其他有正确信息的电脑。采用这种方法,通常只需要跳转两到三次,便可以轻松查找到所需文件。
以上几个部分,便是对于kad作用原理以及算法的分析,可能好多人看了之后头大,那么我们普通用户到底该注意些什么呢?
很简单,你要作的就是再使用emule的时候打开kad,你会发现有两个明显的特点
(1)你的下载速度会加快
(2)你的下载文件的源会增加
以上两条对于lowid和经常下载源在国外的文件用户,效果就更为突出,特别对于在ed2k网络中只有几个源或者没有源的文件,在kad网络中,一般都
能找到源,所以说你使用了emule下载文件,基本上不会出现没有源的请况,无论多长时间,差别只是源的多少个数问题,由于kad网络都是自动配置的
,所以你丝毫不用分心,那么索性我们就打开它,何乐而不为呢?
另外对于我们搜索的时候,如果采用kad网络搜索,多数情况下找到的文件源会远远多于ed2k的全局搜索,对于大家都是一个明智的选择。
1. ID and Key
Node ID:160 bit (每一个Node拥有一个ID,随机产生)
Key:160 bit (Key也许是某个很大的数据的SHA-1 hash值)
(Key,Value)这一对数据保存在ID最“接近”Key的Node上。
“接近”的意思是Key和ID之间的“距离”很短。
Kad网络中“距离”的定义是:d(x,y) = x XOR y,也就是x和y的异或值。
* 选择XOR作为衡量距离的尺度,是因为XOR有xxx,yyy,zzz,...等特性,具体请看Kademlia的paper [1] section 2.1。
2. k-bucket
每一个Node保持160个 list。对于第 i (0 <= i < 160) 个 list,此 list 中保存着和该 Node 距离为 2*i ~ 2*(i+1)
【2的 i 次方到 2的 i+1 次方】的
一些 Node 的信息(Node ID,IP地址,UDP端口)。这些 list 叫做 k-bucket 。k是每一个 listk 中保存的 Node 的最大
个数(比如,20)。
+------------------------+
list 0: | | 距离为[1,2)的node
+------------------------+
(ID前159 bit相同,第160 bit一定不同的node: 1个)
+------------------------+
list 1: | | 距离为[2,4)的node
+------------------------+
(ID前158 bit相同,第159 bit一定不同的node: 2个)
+------------------------+
list 2: | | 距离为[4,8)的node
+------------------------+
(ID前157 bit相同,第158 bit一定不同的node: 4个)
+------------------------+
list 3: | | 距离为[8,16)的node
+------------------------+
(ID前156 bit相同,第157 bit一定不同的node: 8个)
+------------------------+
list 4: | | 距离为[16,32)的node
+------------------------+
(ID前155 bit相同,第156 bit一定不同的node: 16个)
。
。
。
+------------------------+
list 159: | | 距离为[2*159,2*160)的node
+------------------------+
(ID第1 bit不同的node: 2*159个。此list中最多保存最近访问的k个)
每一个list (k-bucket)中的node按照访问的时间顺序排列,最先访问的在list头部,最近访问的在list的尾部:
Head Tail
| |
V V
+--+ +--+ +--+ +--+ +--+ +--+
| |-->| |-->| |-->| |-->| |--> ... --> | |
+--+ +--+ +--+ +--+ +--+ +--+
Node0 Node1 ... Node[N]
A A
| |
least-recently most-recently
3.k-bucket的刷新
当一个Node收到另一个Node发来的消息的时候,它根据以下规则(least-recently seen eviction policy)来刷新相应的k-bucket:
1) 如果发送者已经在某一个k-bucket中,将它在该 k-bucket 中移动到尾部
2) 如果发送者不在其对应的k-bucket中,并且该 k-bucket 还不满 k 个Node,将这个发送者加入到 k-bucket 的尾部
如果该 k-bucket 已经满了(有k个Node),那么接受消息的 Node 向该 k-bucket 中最老的(也就是头部的)
Node发送一个 ping 消息
如果这个最老的 Node 没有响应,则将它从 k-bucket 中删除,将新的发送消息的 Node 加入到 k-bucket 的尾部
如果这个最老的 Node 响应了,则将它移动到 k-bucket 的尾部,并抛弃新的 Node 的信息
* 为什么要采取这样的规则,请看Kademlia的paper [1] section 2.2
4. 基本协议(protocol)
四种基本的RPC:PING, STORE, FIND_NODE, FIND_VALUE
1) PING
PING 探测一个Node是否在线
2) STORE
STORE 以(Key,Value)为参数。指示另一个Node(消息接收者)保存一个(Key, Value)对,以供以后取用。
3) FIND_NODE
FIND_NODE 以一个160 bit的ID作为参数。接收者返回它所知道的最接近该ID的 k 个 Node 的 Contact 信息
(ID,UPD Port,IP)。这些 Node 不一定要从同一个 k-bucket 中产生。除非它所有的 k-bucket 中所有的
Node 加在一起都不到 k 个,否则它必需凑足 k 个。
4) FIND_VALUE
FIND_VALUE 以一个160 bit的ID/Key作为参数。如果接收者先前已经收到一个 STORE RPC,而且 STORE 的
参数之一 Key 就是当前 FIND_VALUE 的参数,那么,接收者将 STORE 的另一个参数 Value 返回给发送者。
否则,FIND_VALUE 和 FIND_NODE 一样,返回它所知道的最接近该ID的 k 个 Node 的 Contact 信息
(ID,UPD Port,IP)。
FIND_VALUE 的意思就是:如果接收者有发送着所要的 Value,就将该 Value 返回。否则,接收者就帮忙找
更接近该 ID/Key 的 Node。
所有的 RPC 消息都带有一个 160 bit的随机 RPC ID,由发送者产生,接收者在响应每一个消息的时候,返回
的消息里面都必需拷贝此 RPC ID。目的是防止地址伪造。PING 消息可以在 RPC 响应里使用捎带确认机制来获
得发送者网络地址(原文:pings can also be piggy-backed on RPC replies for the RPC recipient to
obtain additional assurance of the sender's network address.)。
* piggyback
捎带确认(法)
A technique used to return acknowledgement information across a full-duplex (two-way
simultaneous) data link without the use of special (acknowledgement) message. The
acknowledgement information relating to the flow of message in one direction is embedded
(piggybacked) into normal data-carrying message flowing in the reverse direction.
经全双工(双向同时)数据链路,不用专门(确认)报文返回确认信息所用的技术。与一个方向的报文流有关的确
认信息钳在反方向正常携带数据的报文流中。
[1] Kadmelia的paper:http://www.cs.rice.edu/Conferences/IPTPS02/109.pdf
5. 节点查找(node lookup)
node lookup:找到距离给定的 ID 最近的 k 个 node
定义 a:系统范围内的并发参数,比如3。
步骤:
1) 从最近的 k-bucket 里面取出 a 个最近的 node,然后向这 a 个 node 发送并行的、异步的 FIND_NODE RPC。
2) 再次发送 FIND_NODE RPC 给从前一步返回的 node(这一步可以不必等到前一步中所有 a 个 PRC 都返回之后才开始):
从发送者已知的 k 个最接近目标的 node 当中,取出 a 个还没有查询的 node,向这 a 个 node 发送 FIND_NODE RPC。
没有迅速响应的 node 将被排除出考虑之列,直到其响应。
如果一轮 FIND_NODE RPC 没有返回一个比已知的所有 node 更接近目标的 node,发送者将再向 k 个最接近目
标的、还没有查询的 node 发送 FIND_NODE RPC。
3) 查找结束的条件:发送者已经向 k 个最近的 node 发送了查询,并且也得到了响应。
6. 存储<Key, Value>(store a <Key,Value> pair)
步骤:
1) 使用node lookup算法,找到距离 Key 最近的 k 个 node
2) 向这 k 个 node 发送 STORE RPC
3) 每一个 node 必要的时候重新发布(re-publish)所有的<Key,Value>
(对于当前的 Kademlia 应用(文件共享),每一个<Key,Value>的原始发布者被要求每隔24小时重新发布一次,
否则<Key,Value>将在发布之后的24小时之后过期。对于其他一些应用,比如digital certificates,
cryptographic hash to value mapping,过期时间可以更长一些)
7. 搜索<Key,Value> (find a <Key,Value> pair)
步骤:
1) 使用 FIND_VALUE 代替 FIND_NODE 进行"node lookup"过程。一旦任何其他 node 返回了所要的 Value,搜索的过程就结束。
2) cache: 如果搜索成功,搜索的发起者将这个<Key,Value>对存储到已知的、最近的、但是在第一步中没有返回该 Value 的 node 上。
显然,有了cache之后,以后对于该 <Key,Value> 的搜索很可能首先找到 cache,而不是直到找到最接近 Key 的那个 node。
如果一个 <Key,Value> 被频繁的搜索,那么它很可能被缓存到很多 ID 不太接近 Key 的 node 中。
为了避免过度缓存(over-caching),每一个 <Key,Value> 都有一个过期时间,这个过期时间和 当前node 和“最接近Key
之node”之间的 node 的个数(这个数目可以从当前node的bucket接口推断出)的指数倒数成正比。(To avoid "over-caching", we make
the expiration time of a <key,value> pair in any node's database exponentially inversely proportional
to the number of nodes between the current node and the node whose ID is closest to the key ID. This number can be inferred from the bucket structure of the current node)
8. 刷新bucket (refresh bucket)
所有的 bucket 通过node之间的请求来刷新。
如果某一个bucket在过去一个小时之内没有任何的node lookup操作,那么这个node就随机搜索一个在这个bucket覆盖范围内的ID。
9. node加入网络
步骤:
1) 一个新加入的node(假设为u)必需首先获得一个已经在网络中的node(比如为w)的contact信息。
2) u 首先将 w 加入到其对应的 k-bucket 中
3) u 执行一个对自己ID的 node lookup 操作
4) u 刷新所有的 k-bucket
与BT的比较
简单的说,emule与bt 协议两者各有千秋
1.传统连接方式
bt使用统一的torrent文件先作一个原下载文件的信息记录,然后客户下载后通过torrent的信息与服务器连接并下载,
emule仅有一个文件ID,客户自行与服务器连接再下载
2.底层传输协议比较
bt只使用TCP协议进行下载,协议简单有效,但是功能比较单一,有的功能不完整
emule使用TCP和UDP两种协议进行通信,更加有效的利用了网络资源,功能完整强大,但这也同时使主机的负荷加大,程序编写难度提高
3.文件组织方式和数据验证方式
bt会在开始前对文件进行一次完全的HASH,就是将文件首尾相联然后按固定块取SHA值,这些值最终被放入torrent文件编码中,客户从网上一次下载完全,高效简单,一般情况下bt软件会在每小块下载完成后就对其进行HASH测试,检查其正确性
emule 在连接字符串中只存放了整体文件的HASH值,通过将这个HASH到服务器上取出文件的相关信息,在实际的操作中,会将文件分解成9.28M大小的块并进行HASH用于对块的完整性测试.新版的emul会用一种叫AICH的技术,就是说将文件分成120K大小的块然后HASH再将HASH值进行二进迭代式(具体的看emule协议)的HASH最终组成一个HASH二叉树这种方式的好处是可以在连接时只加入根结节的HASH值而不用加入叶子节点,减小了连接时的字符串大小,如果在最终文件下载完毕后,测试出的根节点HASH与得到的根节点的HASH值不同,则可以通过协议与网络上的其它主机的树进行比较快速得出错误的块.
4.流量控制方式
bt采用有名的针锋相对的方式处理上传下载平衡的控制,这种方式会记录短期内与客户连接的所有节点的上传下载流量,通过在固定时间内对下载流量的比较,得出允许上传的客户;为防止新客户长时间得不到其它客户的认同,bt会在一断时间停止他的上传作为对他的警告;对于已经下载完毕的客户,bt会简单的使上传流量最的客户得到更多的时间完成上传;为了防止在文件的最后阶段下载速度下降,bt会在最后时向所有连接的客户发送请求迅速完成下载;在整个下载过程中, bt会对文件块在整个网络中的存在复本的多少进行跟踪,那些存在的比较少的复本总是会得到优先的下载权,以使整个网络的文件冗余度提高.简单的说bt使用的是针对文件的流量控制方式.
Emule采用的是客户积分的方式,就是对所有用户的上传和下载量进行一个运算,从而得出一个客户的积分值,那些积分比较高的用户总是可以得到优先的下载权,甚至可以不进行排队直接下载,这样就在一个比较长的时间内对用户对其它用户的整体贡献有了一个评估.简单的说emule采用的是针对用户的流量控制方式.
5.kad与dht
kad和dht两者都是基于kademlia技术的分布式HASH表查找技术,可惜的是由于协议上的区别,两者不能互通.简单介绍下kad,它首先给每个客户分配一个唯一的ID值,然后对不同的ID值进行异或来得到两个客户之间的"距离",kad会维护一个桶,"距离"越近的用户桶里的数量会越多,kad 定其的对桶里的用户进行清理,以保持其有效性.对于文件和用户emule会有两个这个东西,所以我们可以通过kad来查找文件和文件相关的用户信息;同样为了考虑冗余的问题,kad会将其自身的信息复制一份给"距离"它最近的一定数量的用户,这样就算在它下线后,这些信息也不会丢失.bt的 dht不太了解,呵呵,不过估计差不多.
6.功能比较
emule具有查找功能,而这在bt 只能通过网站来实现
新版的emule在对防火墙的支持上采用的代理的方式,就是如果一个用户处在内网,那么它会找到一个在公网的用户作为它的朋友.bt在这方面没有明显的变化,但是不同的bt客户端实现方式有些不同的支持.
7.总体性能比较
个人感觉bt的方式更注重于简单高效的快速传输,而emule更注重于整个网络状态的变化及用户体验.单从下载效率上说bt占优,而从网络状态及完整强大的协议支持上说,emule作了更多的事情.从性能上考虑,在相同网络状态下,bt下载单文件的能力比较强, emule比较适合于长时间的多文件下载,这源于两者对网络均衡及p2p模式的不同理解.