P2P在线互传文件小工具

5,835 阅读4分钟

简介

这两天把之前写的一个P2P文件传输demo优化了一下界面和体验,只需要其中一方输入对方的6位UUID就可以建立连接,然后就可以选择文件发送了,下面是体验链接:

peer.codeasily.net

使用步骤

  1. 张三想找李四发个小视频给他,但是怕经过互联网传文件被查水表,而且张三和李四用的是不同平台操作系统的设备,此时他们想到了peer.codeasily.net 这个小工具,于是他们一起打开了这个小工具;
  2. 张三把自己的UUID告诉李四,李四输入张三的UUID后与张三建立了点对点连接;
  3. 李四把小视频拖进红色区域,点击"Send"按钮发送; image-20210827160324872
  4. 张三这边的蓝色区域收到了李四发来的视频,不一会儿就传完了 image-20210827160554739
  5. 最后张三点击"Download"就可以安心的将小视频放在放在了自己的电脑硬盘里了

这里解释一下为什么张三李四会用这个工具的原因:

  • 上述场景如果张三李四是处在同一个局域网内的话,理想情况下可以只经过局域网传输文件的,文件内容不经过互联网
  • 文件传输过程是被分片并以加密的二进制数据进行传输的
  • 其他在线互传工具使用繁琐(扫码、等待广告、关注公众号、外网传输不稳定等因素)
  • 小视频指的是文件大小不能太大的文件,太大了传输速度很慢,建议小于100M大小

实现逻辑

这里我大概讲一下实现流程就好了哈,这工具实现起来很容易,但深究有太多内容需要掌握(webRTC、STUN、TURN、P2P加密等),感兴趣的朋友可以跳到文章末尾的参考资料学习哈。

下面是流程图:

peer-transport-flow

大概讲讲图中的要点吧:

  • 图中正中间被很多箭头指着的绿色云朵是我服务器里的websocket信令服务器,负责两端互传各自的UUID、offer和ICEcandidate,就是用于两端建立P2P连接的“公证人”,有了它才能使两端知道彼此身份
  • 图中PeerA和PeerB都需要先生成自己的UUID,在其中一方已知对方UUID的情况下告诉信令服务器我要连接的目标UUID,信令服务器就将连接方的UUID也告诉被连接方,这样两端就知道接下来该发送信令给谁了
  • 接下来的RTCPeerConnectioncreateDataChannelcreateOffersetLocalDescriptiononicdecandidatesetRemoteDescriptionaddiceCandidate这些全都是WebRTC的API,他们会各自产生offer(用于SDP媒体协商)和icecandidate(用于网络协商)两种信令,经过WebRTC协商后会确立点对点连接
  • 建立了P2P连接后就可以通过DataChannel数据通道互传文件数据,我的步骤如下:
    • 先对整个文件的arraybuffer进行md5加密得到文件hash用于在传输后校验文件
    • 然后将文件分块,与文件名、hash、总大小、块序号等信息组合成一个个数据包
    • 将一个个数据包转成arraybuffer通过DataChannel传给对端,数据包以队列形式同步发送
    • 对端接收到发送端的数据包再按上面步骤反向解包,最后对同hash的数据包合并成一个文件,并校验合并后的文件hash是否一致
    • 对端收到数据包同时会发信令告诉发送方已收到包的消息,这里的已收消息内容只有包序号,没有其他内容。
    • 发送端收到对方已收消息就能知道对端的接收情况进行界面提示

以上就是实现P2P文件传输的完整流程了

这里有一个点要提一下,上面有一个步骤是数据包是以队列形式一个一个同步发送明显是很低效的方式,其实一开始我是以异步形式一次性全部发出的,但由于P2P的DataChannel通道是走UDP协议的,UDP的特点之一就是不可靠性,所以当我以异步形式一次性发出或者分批一次同时发送若干个数据包时都有几率产生丢包的现象,这对于文件传输的场景来说是不能满足的

所以我才决定以队列形式同步一个一个发送,这么一来传输速度就会比较慢,不过也不是没有别的方案,也许可以尝试开多好几个通道来实现并行传输,就像开多线程一样,理论上可以复用协商过的sdpicecandidate,但不知道还会不会丢包了,等有时间会尝试一下。

参考资料

WebRTC实现P2P文件传输

P2P通信原理与实现 - evilpan

P2P通信标准协议之STUN - evilpan

P2P通信标准协议(二)之TURN - evilpan