与peer创建tcp链接后,首先发送handshake消息进行握手python
handshake消息格式以下:多线程
一个字节0x19 + 一个字符串'BitTorrent protocol' + 8 byte 保留字节默认值为0(draft中对保留字节有定义)异步
+ 种子文件中info 部分的sha1字,大小为20个字节 + 20个本身的peer id(从tracker获取到的peer信息大多没有peerid,这个可使用本地的peer id)socket
若是handshake信息协商不上,tcp链接将被关闭。tcp
BT标准BEP-3中定义了8种peer消息:消息格式为msg_len(4个字节) + msg_type(1一个字节) + payload函数
0 - choke --发送该消息表示本段发送阻塞,对端将不能获取到piece数据,payload 为 0性能
1 - unchoke --发送该消息表示解阻塞,对端能够开始发送请求获取piece数据,payload 为 0线程
2 - interested --发送该消息,表示对对端的pieces数据有兴趣,payload 为 0设计
3 - not interested ---发送该消息,表示对对端的pieces数据没有兴趣了,payload 为 0rest
4 - have ---发送该消息,通告对端 本段拥有的pieces,payload为4个字节的piece index
5 - bitfield ---发送该消息,通告对端 本段拥有的pieces,为bit map的方式表示每一个piece index在本端是否拥有。piece index所在bit位为1,表示拥有。
该消息为handshake成功后的第一个消息。
6 - request ---piece请求消息,payload为: index, begin, and length,都是4个字节表示,length通常实现为0x8000, 最大不能超过0x20000。
7 - piece ---piece 数据,payload为: index, begin,data
8 - cancel ---发送该消息,表示本端取消了某个piece请求。payload为:index, begin, and length
使用python的异步socket接口实现,为了减小处理过程被socket阻塞,使用多个线程处理每一个peer。
每一个peer包括3个线程:request timeout timer ,socket send data thread, socket receive data thread,使用select 函数判断socket是否可读、可写。
对socket读写操做时使用RLock进行保护,select阻塞进程时不加锁,避免阻塞其余线程。
发送数据数据时先写一个队列,而后经过set threading.Event 变量出发socket send data thread发送数据,保证发送数据的线程不阻塞
因为 python没有结束线程的接口,socket send data thread, socket receive data thread 须要依赖特殊变量的赋值,使socket处理进程结束。
使用同步调用来触发下载过程运转,尽可能不使用timer轮询的方式,能够下降cpu使用率并加快下载过程。
可是,多线程间的同步调用因为锁的存在,会致使性能降低并容易引入信号量死锁的问题。须要仔细设计好多线程的运行轨迹避免死锁。
draft BEP中定义的功能暂未实现,peer的上传流控未实现,peer质量分级未实现。