P2P协议
一般想下一个电影,通过什么方式呢?最简单的就是通过HTTP协议,但是只要文件大点,最后都会慢的要死。
还有一种下载文件的方式,就是通过FTP,也即文件传输协议。FTP采用两个TCP连接来传输一个文件。
- 控制连接:服务器以被动的方式,打开众所周知用于FTP的端口21,客户端则主动发起连接。该连接将命令从客户端传给服务器,并传回服务器的应答。常用的命令有:list-获取文件目录;reter-获取一个文件;store-存储一个文件。
- 数据连接。每个一个文件在客户端和服务器之间传输时,就创建一个数据连接。
FTP工作的两种模式
每传输一个文件,都需要建立一个新的数据连接。FTP有两种工作模式,分别是主动模式(PORT)和被动模式(PASV),这些都是站在FTP服务器的角度来说的。
主动模式下,客户端随机打开一个大于1024的端口N,向服务器的命令端口21发起连接,同时开放N+1端口监听,并向服务器发出PORT N+1 的命令,由服务器从自己的数据端口20,主动连接到客户端制定的数据端口N+1。
被动模式下,当开启一个FTP连接时,客户端打开两个任意本地端口N(大于1024)和N+1。第一个端口连接服务器的21端口,提交PASV命令。然后,服务器会开启一个任意的端口P(大于1024),返回227 entering passive mode消息,里面有FTP服务器开放的用来进行数据传输的端口。客户端收到消息取得端口号之后,会通过N+1端口连接服务器的端口P,然后在两个端口之间进行数据传输。
P2P是什么?
无论是HTTP,还是FTP,都有一个比较大的缺点,就是难以解决单一服务器带宽的压力,因为它们使用的都是传统的客户端服务器的方式。
一种创新的方式流行起来,P2P,是peer-to-peer,资源开始并不集中地存储在某些设备上,而是分散的存储在多台设备上,这些设备我们成为peer。
想要下载一个文件,你只要得到那些已经存在了文件的peer,并和这些peer之间,建立点对点的连接,而不需要到中心服务器上,就可以就近下载文件。一旦下载了文件,你就成为了peer中的一员,你旁边的机器,也可能选择从你这里下载文件,当你使用P2P软件的时候,例如BitTorrent,既有下载流量,也有上传的流量,也即你自己也加入了这个P2P网络,自己从别人的地方下载,同时也提供给别人下载,这样,下载的人越多,下载速度越快。
种子(.torrent) 文件
当你想下载一个文件的时候,怎么知道那些peer有这个文件呢?
这就用到种子了,也即咱们比较熟悉的torrent文件。文件由两部分组成,分别是announce(tracker URL)和文件信息。
文件信息里面有这些内容
- info区:这里指定的是该种子有几个文件、文件有多长、目录结构,以及目录和文件的名字。
- Name字段:指定顶层目录名字。
- 每个段的大小:BitTorrent 协议把一个文件分成很多个小段,然后分段下载。
- 段哈希值:将整个种子中,每个段的SHA-1哈希值拼在一起。
下载时,BT客户端首先解析.torrent文件,得到tracker地址,然后连接tracker服务器。tracker服务器回应下载着的请求,将其他下载者(包括发布者)的IP提供给下载者。下载者再连接其他下载者,根据.torrent文件,两者分别对方告知自己已经有的块,然后交换对方没有的数据。此时不需要其他服务器参与,并且分散了单个线路上的数据流量,因此减轻了服务器的负担。
下载者每得到一块,需要算出下载块的Hash验证码,并于.torrent文件中的对比。如果一样,则说明块正确,不一样则需要重新下载整个块。这种规定是为了解决下载内容准确性的问题。
从这个过程知道,这种方式很依赖tracker。tracker 需要收集下载者信息的服务器,并将此信息提供给其他下载者,使下载者们互相连接起来,传输数据。虽然下载过程是非中心化的,但是加入P2P网络的时候,都需要借助tracker中心服务器,这个服务器是用来登记有那些用户在请求哪些资源。
所以这种方式有一种弊端,一旦tracker服务器出现故障或者线路遭到屏蔽,BT工具就无法正常工作了。
去中心化网络(DHT)
有了一种叫做DHT的去中心化网络。每个加入这个网络的人,都要负责存储这个网络里的资源信息和其他成员的联系信息,相当于所有人一起构成了一个庞大的分布式数据库。
有一种著名的DHT协议,叫Kademlia协议。
任何一个BitTorrent启动之后,它都有两个角色。一个是peer,监听一个TCP端口,用来上传和下载文件,这个角色表明,我这里有某个文件。另一个角色DHT node,监听一个UDP的端口,通过这个角色,这个节点加入了一个DHT的网络。
在DHT网络里面,每一个DHT node都有一个ID。这个ID是一个很长的串。每个DHT node都有责任责任掌握一些知识,也就是文件索引,也即它应该知道某些文件是保存在哪些节点上。它只需要有这些知识就可以了。而它自己本身不一定就是保存这个文件的节点。
哈希值
当然,每个DHT node不会有全局的知识,也即不知道所有的文件保存在哪里,它只需要知道一部分,应该知道哪一部分呢,这就需要哈希算法计算出来。
每个文件可以计算出来一个哈希值,而DHT node的ID是和哈希值相同长度的串。
DHT算法是这样规定的:如果一个文件计算出一个哈希值,则和这个哈希值一样的那个DHT node,就有责任知道从哪里下载这个文件,即便它自己没有保存这个文件。
当然不一定这么巧,总能找到和哈希值一摸一样的,有可能一摸一样的DHT node也下线了,所以DHT算法还规定;除了一摸一样的DHT node应该知道,ID和这个哈希值非常接近的N个DHT node也应该知道。
什么叫和哈希值很接近呢?例如只修改了最后一位,就很接近,修改了倒数第二位,第三位,都可以接受,总之,凑齐规定的N这个数就行。
DHT 网络里中,朋友圈是怎么维护的
DHT网络的朋友圈里也是一样的,远近都有,并且按距离分层。
如果一个节点的ID,前面所有位数相同,从倒数第i位开始不同,这样节点只有2的i-1次方个,与基础节点的距离范围为[2^(i-1), 2^i),这样的节点归为k-bucket i;
DHT网络是如何查找朋友的
Kademlia通过折办查找的方式来收缩范围,对于总的节点数据为N,最多只需要查询log2N 次。
DHT网络中,朋友之间怎么沟通呢?
Kademlia算法中,每个节点只有四个指令。
- PING;测试一个节点是否在线,还活着没,相当于打个电话
- STORE:要求每一个节点存储一份数据,既然加入了组织,有义务保存一份数据。
- FIND_NODE:根据节点ID查询一个节点,就是给一个160位的ID,通过上面朋友圈的方式,找到那个节点。
- FIND_VALUE:根据KEY查找一个数据,实则跟FIND_NODE相似,KEY就是对应文件的160位ID,就是要找到保存了文件的节点。
DHT网络里,朋友圈如何更新呢
- 每个bucket里的节点,都按最后一次接触的时间倒序排列,这就相当于,朋友圈里面最近联系过的人往往是最熟的。
- 每次执行四个指令中的任意一个,都会触发更新
- 当一个节点与自己接触时,检查它是否已经在K-bucket中,也就是说是否已经在朋友圈。如果在,那么将它挪到K-bucket列表的最低,也就是最新的位置,刚联系过,就置顶一下,方便以后多联系;如果不在,新的联系人要不要加到通讯录里面呢?假设通讯录已满的情况,PING一下列表最上面,也即最老的一个节点,如果PING通了,将旧节点挪到列表最底,并丢弃新节点,老朋友还是留一下,如果PING不通,删除旧节点,并将新节点加入列表中,这人联系不上了,删了吧。
这个机制保证了任意节点加入和离开都不影响整体网络。
小结: - 下载一个文件可以使用HTTP或者FTP ,这两种都是集中下载的方式,而P2P则换了一种思路,采用非中心化的下载的方式;
- P2P 也是有两种,一种是依赖于tracker的,也即元数据集中,文件数据分散;另一种是基于分布式的哈希算法,元数据和文件数据全部分散。