l 握手消息是一个长度固定为68字节的消息。消息的格式如下:
<pstrlen><pstr><reserved><info_hash><peer_id>
消息格式中一些参数的含义如表13-9所示。
表13-9 握手消息
参 数 | 含 义 |
pstrlen | pstr的长度,该值固定为19 |
pstr | BitTorrent协议的关键字,即“BitTorrent protocol” |
reserved | 占8字节,用于扩展BT协议,一般这8字节都设置为0。有些BT软件对BT协议进行了某些扩展,因此可能看到有些peer发来的握手消息这8个字节不全为0,不过不必理会,这不会影响正常的通信 |
info_hash | 与发往Tracker的GET请求中的info_hash为同一个值,长度固定为20字节 |
peer_id与发往Tracker的GET请求中的peer_id为同一个值,长度固定为20字节。一般从peer_id可以识别出BT软件的类型,例如,某peer发来的握手消息中peer_id的前8个字节为“-AZ2060-”,则可以断定对方使用的是Azureus;若为“-BCxxxx-”,x为数字,则对方使用的是BitComet。
对于除握手消息之外的其他所有消息,其一般的格式为:
<length prefix><message ID><payload>
length prefix(长度前缀)占4个字节,指明message ID和payload的长度和。message ID(消息编号)占一字节,是一个10进制的整数,指明消息的编号。payload(负载),长度未定,是消息的内容。
2. keep_alive消息:<len=0000>
keep_alive消息的长度固定,为4字节,它没有消息编号和负载。如果一段时间内客户端与peer没有交换任何消息,则与这个peer的连接将被关闭。keep_alive消息用于维持这个连接,通常如果2分钟内没有向peer发送任何消息,则发送一个keep_alive消息。
3.choke消息:<len=0001><id=0>choke消息的长度固定,为5字节,消息长度占4个字节,消息编号占1个字节,没有负载。该消息的功能是,发出该消息的peer将接收该消息的peer阻塞,暂时不允许其下载自己的数据。
4.unchoke消息:<len=0001><id=1>
unchoke消息的长度固定,为5字节,消息长度占4个字节,消息编号占1个字节,没有负载。客户端每隔一定的时间,通常为10秒,计算一次各个peer的下载速度,如果某peer被解除阻塞,则发送unchoke消息。如果某个peer原先是解除阻塞的,而此次被阻塞,则发送choke消息。
5. interested消息:<len=0001><id=2>
interested消息的长度固定,为5字节,消息长度占4个字节,消息编号占1个字节,没有负载。当客户端收到某peer的have消息时,如果发现peer拥有了客户端没有的piece,则发送interested消息告知该peer,客户端对它感兴趣。
6. not interested消息:<len=0001><id=3>
not interested消息的长度固定,为5字节,消息长度占4个字节,消息编号占1个字节,没有负载。当客户端下载了某个piece,如果发现客户端拥有了这个piece后,某个peer拥有的所有piece,客户端都拥有,则发送not interested消息给该peer。
7. have消息:<len=0005><id=4><piece index>
have消息的长度固定,为9字节,消息长度占4个字节,消息编号占1个字节,负载为4个字节。负载为一个整数,指明下标为index的piece,peer已经拥有。每当客户端下载了一个piece,即将该piece的下标作为have消息的负载构造have消息,并把该消息发送给所有与客户端建立连接的peer。
8. have消息:<len=0005><id=4><piece index>
have消息的长度固定,为9字节,消息长度占4个字节,消息编号占1个字节,负载为4个字节。负载为一个整数,指明下标为index的piece,peer已经拥有。每当客户端下载了一个piece,即将该piece的下标作为have消息的负载构造have消息,并把该消息发送给所有与客户端建立连接的peer。
9. bitfield消息:<len=0001+X><id=5><bitfield>
bitfield消息的长度不固定,其中X是bitfield(即位图)的长度。当客户端与peer交换握手消息之后,就交换位图。位图中,每个piece占一位,若该位的值为1,则表明已经拥有该piece;为0则表明该piece尚未下载。具体而言,假定某共享文件共拥有801个piece,则位图为101个字节,位图的第一个字节的最高位指明第一个piece是否拥有,位图的第一个字节的第二高位指明第二个piece是否拥有,依此类推。对于第801个piece,需要单独一个字节,该字节的最高位指明第801个piece是否已被下载,其余的7位放弃不予使用。
10. request消息:<len=0013><id=6><index><begin><length>
request消息的长度固定,为17个字节,index是piece的索引,begin是piece内的偏移,length是请求peer发送的数据的长度。当客户端收到某个peer发来的unchoke消息后,即构造request消息,向该peer发送数据请求。前面提到,peer之间交换数据是以slice(长度为16KB的块)为单位的,因此request消息中length的值一般为16K。对于一个256KB的piece,客户端分16次下载,每次下载一个16K的slice。
11. piece消息:<len=0009+X><id=7><index><begin><block>
piece消息是另外一个长度不固定的消息,长度前缀中的9是id、index、begin的长度总和,index和begin固定为4字节,X为block的长度,一般为16K。因此对于piece消息,长度前缀加上id通常为00 00 40 09 07。当客户端收到某个peer的request消息后,如果判定当前未将该peer阻塞,且peer请求的slice,客户端已经下载,则发送piece消息将文件数据上传给该peer。
12. cancel消息:<len=0013><id<=8><index><begin><length>
cancel消息的长度固定,为17个字节,len、index、begin、length都占4字节。它与request消息对应,作用刚好相反,用于取消对某个slice的数据请求。如果客户端发现,某个piece中的slice,客户端已经下载,而客户端又向其他peer发送了对该slice的请求,则向该peer发送cancel消息,以取消对该slice的请求。事实上,如果算法设计合理,基本不用发送cancel消息,只在某些特殊的情况下才需要发送cancel消息。
13. port消息:<len=0003><id=9><listen-port>
port消息的长度固定,为7字节,其中listen-port占两个字节。该消息只在支持DHT的客户端中才会使用,用于指明DHT监听的端口号,一般不必理会,收到该消息时,直接丢弃即可。