p2p是peer-to-peer的简称,以前对它的了解主要来自BT下载工具,通过p2p技术我们能够更加方便地分享与下载文件资源。这些天看了一些资料,才知道在分布式计算,即时通信领域p2p技术也应用广泛,skype,QQ,pplive等常用的软件也是用了p2p技术。p2sp核心大体也是p2p的内容,只是多了一个中心用来调度或者提供服务的服务器,我们来看看简单设计一个P2SP的文件分享系统需要考虑的问题。
当然要再实现一个P2P私有协议复杂度很高,涉及算法、加密、安全性、可用性等很多问题,估计我是弄不出来的,这里只是从业务上来看看设计实现一个类似Napster的软件需要考虑的几点问题(当然Napster也不是P2P,它仍需要一个中心协调机制,属于典型的P2SP吧)。file1 => file_info (table) Server: 192.168.0.1 Client A: 192.168.0.10:10000 (Uploaded by Client A) Client B: 192.168.0.11:10001 (Insert)b,如果服务器S上不存在此文件,则在服务器端创建一条新记录
file1 => file_info (table) Server: 192.168.0.1 (Create) Client B: 192.168.0.11:10001 (Create)3,下载时,客户端E需要下载文件file2,首先与服务器S交互,获取到file2的存放地址Server,Client C,Client D(如上所示)
file2 => file_info (table) Server: 192.168.0.1 Client C: 192.168.0.12:10002 (Shall delete or not) Client D: 192.168.0.13:10001 (Already exised) Client E: 192.168.0.14:10002 (Insert)
(客户端需要搜集各个位置的传输分片大小,失败次数等反馈给服务器S,以便做速度可用性的优先级调整,并发下载当然需要保证最终文件是完整正确的,可以通过计算下载副本md5值与服务器比对得知)
4,为了防止客户端出现异常或者客户端网络环境断掉,客户端应定期发送心跳包给服务器,未获取到心跳一定次数后认为客户端断开。
5,客户端登出时,通知服务器关闭它的服务功能。
B,如果是在跨局域网,存放的应该是用户的外网IP地址以及端口信息,此时需要利用到UDP穿透技术(或者TCP穿透技术,不过后者实现起来麻烦很多,毕竟多了三次握手的过程)UDP传输则需要在应用程序中控制数据的完整性。
C,P2P传输需要一个可靠的网络拓扑结构,尽量找距离下载客户端邻近的网络节点提供服务,比如针对A客户端按照距离它网络远近顺序分别为B,C,D提供下载服务。
D,其他问题:客户端升级后要保证能和以前版本客户端兼容。这个需要考虑很多方面的设计,这是题外话了。
3,上面的p2sp设计并没有充分利用到p2p技术的优点,因为比如客户端B,C同时下载一个文件,它们只会在下载完毕后才能在服务器上记录它们的信息,原本按照p2p的设计,应该是B和C分别下载一部分分片,然后彼此分享。不过这种设计就要麻烦太多了,还没有头绪。
4,p2sp相比p2p有个明显的缺点,就是如果中心服务器S被攻击,整个系统都不能正常运转,所以要特别注意。
关于UDP穿透
我原本想着为每个客户端都安装一个Web服务,互相之间仅仅通过Http协议传输数据,后来想想还是不可行,为了保证P2P传输的可靠性,安全性,客户端之间通信的不仅仅有数据,还应该包含分片的MD5值,加密信息等。尤其采用UDP打洞后,传输的是UDP包,需要在应用层保证数据的顺序和完整(毕竟UDP通信是一种不可靠的连接)。
而且在UDP打洞过程需要服务器端与客户端频繁的通信交互,而这种Web架构不适合完成这样的功能。简单的UDP打洞过程如下:
Client A——Nat A——Server S——Nat B——Client B
1,客户端A连接服务器S,服务器S返回客户端B,C,D...的信息给A(它们的外网IP以及端口);
2,客户端A想和服务器端B通信,发送一个UDP包到B的外网地址(Nat B),并通知服务器S说想和客户端B通信
(此时Nat A已被穿透,任意来自Nat B对应端口的数据都能传给Client A)
3,服务器S联系客户端B,并告之A想和它通信,同时告之客户端A的信息(外网IP与端口)。
4,客户端B在得到服务器S的通知后,发送UDP包给A的外网地址(Nat A)。
(此时Nat B也被穿透,任意来自Nat A对应端口的数据都能传给Client B)
5,然后Client A与Client B之间就能任意收发UDP数据包了。
(第2步与第4步之间会有个时间差,不知道Nat 设备在没有收到回复数据多久后会关闭穿透的通道,所以还需要考虑维持这个通道,保证两个客户端之间传输的同步)
倘若A,B都是在一个局域网内,通过上面的方式可能会有问题,它们彼此之间其实能直接连通,反而通过UDP穿透后不能通信(百度上的人说的),所以应该在第一步先比较A分别与B,C,D是否在一个网段(比较方法比如掩码和内网IP),如果是则尝试直接通信,如果不是则再进行UDP穿透。