上一篇中,我们已经实现了对tracker的访问,从而获取到了peer对等体的ip地址以及端口号。我们这一篇要实现的是对等体之间的通讯。
在bt种子下载中,对等体就是正在下载你需要文件的另一台主机或提供下载你需要文件的主机。每一个种子一开始都是有一个做种者做种,将种子,或磁力链发布到网上,提供给其他用户下载,上传的种子如果被其他人下载了。这时做种者即使退出,种子同样可以在网络上传播,因为已下载的用户又会自动充当种子的提供者,提供后来用户下载,就和种子一样,开花,结果,又产生新的种子。
在bt协议中,每个用户,既是下载者又是种子的提供者。所以bt协议种资源不在一个网站上,而是在对等体中。我们只需要与对等体在bt协议下进行通讯,即可获得资源。
part1对等体协议包
参见这篇论文 https://www.docin.com/p-1038423389.html 基本上是这篇论文里提到的11种格式,但是我抓包还发现了两种格式
4bytes 1byte Xbytes
port 1+X 9 端口号
4bytes 1byte Xbytes
extended 1+X 20 extended
可能还有其他的包格式,但是基本的结构都是
4bytes 1byte Xbytes
1+X 包种类编号 内容
为了安全起见,代码中还是要预留一个未知包的处理情况
判断包种类的流程是
1.从输入流中读取4个字节(Head)
2.判断前4个字节是否为0,为0表示心跳包(主要为了维持tcp连接,防止tcp连接因长时间为响应断开,可以选择不做处理,后面另外开一个线程专门处理心跳包),否则再读取一个字节
3.通过这个字节判断包种类,1-9,default,对不通包设置不同的处理函数
4.读取Head-1长的字节。
part2对等体通讯协议基本流程
1.建立tcp连接
2.发送握手请求包
3.接收对方的握手请求包
4.检查握手请求包,主要检查hash检验值(磁力链特征码)是否和你需要下载的文件一致
5.如果一致,发送己方bitfield包
6.接收对方的bitfield包
7.检验bitfield包长度是否正确
8.比较己方和对方的bitfield包,如果对方含有己方没有的piece片段,发送interested包,反之,发送uninterested包
9.等待对方的unchoke包。若收到unchoke包即可进行请求交互。
10.发送request请求,请求需要资源片段
11.如果request请求合法,对方将发送给你piece包,piece包中包含了资源片段。非法的话对方会choke你的。
12.检验资源片段是否正确,正确者存入磁盘,反之,重新发送请求
13.重复10,11,12,即可获取资源
上面是站在请求资源角度情况下一对一的主要流程。实际情况下,
1.对方也会向你发送请求(每个用户既是下载者又是提供者),倒过来对方也会通过以上流程向你发送请求。
2.一般而言bt下载是一对多的,这样下载快。不太可能只从一个用户获取资源。
3.还有许多可能的错误情况需要处理,上面只是理想情况下的流程
所以实际上还是有些复杂的。主要需要用到的知识是 一些并发编程的知识。
基本思路,
主程序开两种线程,检验下载是否完成的线程,socket连接线程,socket线程里包含三条线程,心跳包线程,发送信息线程,接收信息线程。
目前初步实现了socket线程,代码放最后,以后继续完善
贴上实验结果
由于只和一个peer进行交互,下了半天,才下了10片段。等全部下载完成后,将所有文件按照种子文件里各文件的排序,以及各文件长度,将资源切分合并成对应格式即可(比如原来种子中显示资源中有100字节的文本,200M的视频,我们自需要将piece文件对照切分,前100各字节打包成txt文本,后200M打包成视频文件)。
ps:在编写代码中我碰到了几个问题,这里记录下来
1.tcp连接除非是对方主动shutdown,否则在inputstream中是不会读到结尾标识-1的,所以在tcp交互时,不要以-1作为一次读取的结尾标志。我一开始就以为每次发送数据都会有结尾标识符,然后一直read timeout。现在理解起来tcp交互的模型和管道通讯有点像。
2.在第二篇我们通过tracker获取的ip地址,一般情况下80%以上的我们是访问不了的。为了快捷找到可用对等体进行测试,可以利用比特彗星(比特彗星下载后360会提示木马,我没理它,现在用着也没问题,大概没问题的吧2333)查看。
3.根据实验发现,内网可访问的对等体数目比较少(不知道是网络原因还是需要tcp打洞?),我连接室内移动的wifi时一些无法访问的ip,在换成外部电信网就好多了。
4.包格式一定要对