对端管理

指的是远端peer集合的管理(尽管自身client也能够视为一个peer。但对端管理不包括自身peer)

一个client(client)必须维持与每一个远程peer连接的状态信息,即1V1关系(本端对某个远端peer)

在本代码中PcPeer指这样的1V1关系,而不是仅指远程peer

对于每一个连接连接来说。每一端的peer应该是4种状态之中的一个:一端是interested或者not interested。则还有一端是choking或者unchoking。反之亦然。Choking表示直到unchoking发生之前,都不会有不论什么数据传送给连接还有一端的peer

代码例如以下:

#define CPE_STAT_NONE                ((char)0x00)
#define CPE_STAT_LC_I ((char)0x01) //B 是否拥有 A 感兴趣的分片
#define CPE_STAT_RM_I ((char)0x02) //A 是否拥有 B 感兴趣的分片
#define CPE_STAT_LC_C ((char)0x04) //A 是否能向 B 请求分片数据
#define CPE_STAT_RM_C ((char)0x08) //B 是否能向 A 请求分片数据

这些信息由例如以下关键字来描写叙述:

  1. choke(拒绝/阻塞)、unchoke(接受)两种状态

    假设远程peer对本client是choke,远程peer将不会接收来自本client的请求

    而本client在接收到来自远程peer的choke消息后,就不会再试图向远程peer发送数据请求,由于本client会觉得全部发给远程peer的请求都已被丢弃

  2. interest(兴趣)

    远程peer是否对本client提供的数据感兴趣。这由远程peer通知本client,当本client不choke它时,远程peer就開始请求块(block),这也意味着

    本代码中PcPeer须要记录本client是否对远程peer感兴趣。以及它是否choke/unchoke远程peer

client连接開始时的初始状态为choke和not interstered(不感兴趣)

当本client对远程peer感兴趣,而且该远程peer没有choke时。那么本client就能够从远程peer下载块(block)

当本client没有choke远程peer,而且该远程peer对本client感兴趣时,本client就会上传块(block)

这也就意味着,在建立连接后,本client必须不断的通知远程peer。告诉远程peer。是否对它还感兴趣

本和远程peer的状态信息必须保持最新。即使本client被choke,这样才干保证,当远程peer不再choke本client时,本client能够開始下载(反之亦然)

对端通信协议

对端之间要进行通信。须要使用P2P通信协议

本代码位于

bool CPcPeer::pack(char *pack, int &len, HOSTHANDLE& addr)
bool CPcPeer::unpack(char *pack, const int len)

P2P协议的消息类型例如以下:

// 下述为0x0f下的
#define P2P_CONTROL_INV ((char)0x01) //连接请求
#define P2P_CONTROL_PAV ((char)0x02) //连接响应
#define P2P_CONTROL_CMD ((char)0x03) //命令
#define P2P_CONTROL_DAT ((char)0x04) //数据
#define P2P_CONTROL_LIV ((char)0x05) //心跳
// 下述为0xf0下的
#define P2P_MASK_CHK ((char)0x80) //CHOKE标记掩码
#define P2P_MASK_REQ ((char)0x40) //REQUEST标记掩码
#define P2P_MASK_HAV ((char)0x20) //HAVE标记掩码

PcPeer的step状态为

enum {ST_NONE, ST_PEND, ST_LINK, ST_TRAN};

初始本client和远程peer均为ST_NONE状态

step状态的转换图例如以下:



1.本client发送连接请求报文后。本client状态更新为ST_PEND

远程peer收到连接请求协议后,远程peer的状态更新为ST_LINK

2.远程peer发送连接请求回复协议,远程peer的状态更新为ST_TRAN

3.本client收到远端peer发回的连接回复协议,本client状态由ST_PEND更新为ST_TRAN

4.此后本client和远端peer状态均为ST_TRAN

协议流程图例如以下:





任务文件

  1. http:serve的地址列表
  2. info_hash:任务文件中info部分的sha校验码。即公布的种子数据
  3. peer_id:自身的标识
  4. port:提供上传的port号
  5. ip:你的ip地址,没有的话serve会自己找到
  6. uploaded、dowloaded:你上传和下载了多少,server能够用它做流量分析
  7. left:还要下载多少字节
  8. event:状态,告诉服务端你是准备開始下载,还是停止,或下载完毕了

    BT在打开一个任务文件后,先选择文件保存在哪里,然后推断文件不存在的话就建立新文件,存在的话就用sha-1去校验文件,假设校验错误。说明是未下载的文件,就能够实现续传

server

  1. 服务端会使用info_hash查找列表。同一时候。依据ip和port进行反向连接。以測试用户是内网用户还是公网用户(假设是内网用户。是无法连通的)
  2. 服务端的返回信息中,有一个时间參数。用来告诉client用户隔多少时间一次服务端。由于BT的动态性非常高。1秒内,可能有成千的peer添加或退出,所以须要一个參数设置查询频率

分片选择算法

严格的优先级

第一个策略是。一旦请求了某个分片的子片段,然后该分片余下的子片段会优先被请求,这样,能够尽可能获得一个完整的分片

最少优先

对一个下载者来说。选择下一个被下载分片时,通常选择它的peer们所拥有的最少的那个分片,即最少优先。这样的技术。确保了每一个peer都拥有其它peer们最希望得到的那些片段,从而一旦有须要,上传就能够開始

随机的第一个片段

最少优先的一个例外是下载開始时。此时,下载者没有不论什么分片可供上传。所以需尽快获得一个完整的分片。所以,第一个分片是随机选择的,直到第一个分片下载完毕。才切换回最少优先的策略

最后阶段模式

有时候,peer可能从一个速率非常慢的peer那里请求一个分片,为了防止这样的情况,在最后阶段,peer向全部的peer都发送某分片的子片段请求,一旦某些子片段到了,就向其它的peers发送取消消息,取消对这些子片段的请求。以避免带宽浪费

请求的并发发送

在BT中。非常重要的一点是同一时候发送多个请求,以避免单个请求的两个片段发送之间的延迟

peer发送到serve的请求

  1. info_hash: 即种子hash
  2. peer_id:使用URL编码的20字节串。用于标识client的唯一ID,由client启动时生成。这个ID能够是随意值,甚至可能是二进制数据。眼下没有生成该ID的标准准则,尽管如此。实现者必须保证该ID对于本地主机是唯一的,因此能够通过包括进程ID和启动时记录的时间戳(timestamp)来达到这个目的
  3. port:client正在监听的port号。为BitTorrent协议保留的port号是6881-6889
  4. uploaded:peer已上传的总量(从start事件開始)
  5. downloaded:peer已下载的总量(从start事件開始)
  6. left:client还未下载的字节数。以十进制表示
  7. compact:设置为1,表示client接收压缩的响应包,这时peers列表将被peers串替换,peers串中每一个peer占6个字节(前4表示ip,后2表示port)
  8. numwant(可选):client希望从serve接受的peers数,如未设置,默觉得50
  9. ip(可选):client的IP地址。仅仅有当请求的IP地址不是client的IP地址时。才须要此參数(client通过代理的方式交互)
  10. key:当clientip改变时,可用该标识来证明自己的身份
  11. event:假设指定,必须是started/completed/stoped和空之中的一个,假设一个请求不指定event,表示它仅仅是每隔一段时间发送的请求,event取值例如以下

  • started:第一个发送到serve的请求,其event必须为此值
  • stopped:假设正常关闭client,必须发该事件到serve
  • completed: 假设下载完毕。必须发送该事件到serve。假设client启动前,就已完毕下载,则不是必需发送,serve仅仅基于此事件添加已完毕的下载数

    event指定了值,但值为空,那么它和event不指定值的效果一样(定期发送)

serve回复peer的请求

  1. failure reason:假设包括这个键(key)。那么其它的键(key)就不会出现,该键(key)相应的值是一个可读的错误消息,它告诉用户的请求为什么会失败
  2. interval:指定peer间隔发送请求的时间
  3. min interval(可选):最小的请求间隔,表示peer不能在这个时间间隔内向track重发请求
  4. complete:完毕整个文件下载的peers数(做种数)
  5. incomplete:未完毕文件下载的peers数(非做种数)
  6. peers:peers列表

peer列表数一般控制在30-55之间,当一个分片下载完毕后,client须要将have消息发送给大部分活跃的peers。结果是广播通信的代价和peers数目成正比,当这个数高于25以后,新添加的peers已不太可能提升下载速度


Peer端交互

一个client(client)必须维持其与每一个远程peer(端)连接的状态信息:

  1. choked: 远程peer(端)是否已经choke本client。当一个peer(端) choke本client后。它是在通知本client。除非它unchoke本client。否则它不会应答该client所发出的不论什么请求。本client也不应该试图向远程peer发送数据请求。而且应该觉得全部没有应答的请求已经被远程peer丢弃。
  2. interested: 远程peer(端)是否对本client提供的数据感兴趣。这是远程peer在通知本client。当本clientunchoke他们时,远程client将開始请求块(block)。

    注意这也意味着本client须要记录它是否对远程peer(端)感兴趣。以及它是否choke/unchoke远程peer。

    因此真正的列表看起来像这样:


  • am_choking: 本client正在choke远程peer
  • am_interested: 本client对远程peer感兴趣
  • peer_choking: 远程peer正choke本client
  • peer_interested: 远程peer对本client感兴趣。

    client连接開始时状态是choke和not interested(不感兴趣)。换句话就是:

    l am_choking = 1

    l am_interested = 0

    l peer_choking = 1

    l peer_interested = 0

    当一个client对一个远程peer感兴趣而且那个远程peer没有choke这个client。那么这个client就能够从远程peer下载块(block)。当一个client没有choke一个peer,而且那个peer对这个client这个感兴趣时,这个client就会上传块(block)。

    client必须不断通知它的peers,它是否对它们感兴趣,这一点是非常重要的。client和每一个端的状态信息必须保持最新。即使本client被choke。这同意全部的peer知道。当它们unchoke该client后。该client是否開始下载(反之亦然)。

握手

握手是一个必需的报文。而且必须是client发送的第一个报文。该握手报文的长度是(49+len(pstr))字节

握手的消息格式:handshake: <pstrlen><pstr><reserved><info_hash><peer_id>

  1. pstrlen: 的字符串长度。单个字节。

  2. pstr: 协议的标识符,字符串类型。

  3. reserved: 8个保留字节。当前的全部实现都使用全0.这些字节里面的每一个字节都能够用来改变协议的行为。

  4. info_hash: 种子hash
  5. peer_id: 用于唯一标识client的20字节字符串。

    连接的发起者应该马上发送握手报文。一旦接收方看到握手报文中的info_hash部分,接收方必须尽快响应

    假设一个client接收到一个握手报文。而且该client没有服务这个报文的info_hash,那么该client必须丢弃该连接。

    假设一个连接发起者接收到一个握手报文,而且该报文中peer_id与期望的peer_id不匹配,那么连接发起者应该丢弃该连接

报文

接下来协议的全部报文採用例如以下的结构:<length prefix><message ID><payload>

length prefix(长度前缀)是一个4字节的大端(big-endian)值。message ID是单个十进制值。playload与消息相关。

keep-alive

格式 :<len=0000>

keep-alive消息是一个0字节的消息,将length prefix设置成0。没有message ID和payload。假设peers在一个固定时间段内没有收到不论什么报文(keep-alive或其它不论什么报文),那么peers应该关掉这个连接。因此假设在一个给定的时间内没有发出不论什么命令的话,peers必须发送一个keep-alive报文保持这个连接激活。通常情况下。这个时间是2分钟

choke

格式 :<len=0001><id=0>

choke报文长度固定,而且没有payload。

unchoke

格式 :<len=0001><id=1>

unchoke报文长度固定。而且没有payload。

interested

格式 :<len=0001><id=2>

interested报文长度固定,而且没有payload。

not interested

格式 :<len=0001><id=3>

not interested报文长度固定,而且没有payload。

have

格式 :<len=0005><id=4><piece index>

have报文长度固定。payload是piece(片)的从零開始的索引。该片已经成功下载而且通过hash校验。

实现者注意:实际上,一些client必须严格实现该定义。由于peers不太可能下载他们已经拥有的piece(片),一个peer不应该通知还有一个peer它拥有一个piece(片),假设还有一个peer拥有这个piece(片)。最低限度”HAVE suppresion”会使用have报文数量减半。总的来说,大致降低25-35%的HAVE报文。同一时候,给一个拥有piece(片)的peer发送HAVE报文是值得的,由于这有助于决定哪个piece是稀缺的。

一个恶意的peer可能向其它的peer广播它们不可能下载的piece(片)。

bitfield

格式 : <len=0001+X><id=5><bitfield>

bitfield报文可能仅在握手序列发送之后。其它消息发送之前马上发送。

它是可选的,假设一个client没有piece(片),就不须要发送该报文。

bitfield报文长度可变。当中x是bitfield的长度。

payload是一个bitfield,该bitfield表示已经成功下载的piece(片)。

第一个字节的高位相当于piece索引0。设置为0的位表示一个没有的piece,设置为1的位表示有效的和可用的piece。末尾的冗余位设置为0。

长度不正确的bitfield将被觉得是一个错误。假设client接收到长度不正确的bitfield或者bitfield有任一冗余位集,它应该丢弃这个连接。

request

格式 : <len=0013><id=6><index><begin><length>

request报文长度固定,用于请求一个块(block)。payload包括例如以下信息:

  1. index: 整数,指定从零開始的piece索引。
  2. begin: 整数,指定piece中从零開始的字节偏移。
  3. length: 整数。指定请求的长度。

piece

格式 :<len=0009+X><id=7><index><begin><block>

piece报文长度可变,当中x是块的长度。payload包括例如以下信息:

1. index: 整数,指定从零開始的piece索引。

2. begin: 整数,指定piece中从零開始的字节偏移。

3. block: 数据块,它是由索引指定的piece的子集。

cancel

格式 :<len=0013><id<=8><index><begin><length>

cancel报文长度固定,用于取消块请求。

playload与request报文的playload相同。普通情况下用于结束下载。

BT协议1.0

BitTorrent Protocol Specifications v1.0 翻译

原文(原始版本号):http://www.bittorrent.org/protocol.html

更具体的版本号:http://wiki.theory.org/BitTorrentSpecification

注:

1) 本文是原始版本号的翻译。假设有晦涩不清的地方,请參考上面的第二个link(更具体的版本号)。

2) 由于没有不论什么实践,全然是基于文档的自己的理解,所以可能会有翻译和理解错误的地方。

3) 由于是老版本号的协议原文,所述内容和如今实际的BTclient肯定会有出入。

4) 协议扩展和DHT相关介绍在官网(http://www.bittorrent.org)上都有,须要请參考。

BitTorrent协议规范1.0

BitTorrent是一种用来传输文件的协议。

它通过URL来识别被传输的文件而且被设计成能够无缝的集成到网络里。 它相对于单纯的HTTP协议的优势是当多个下载者同一时候下载同一个文件时,下载者之间能够互相传输文件内容(注:提高了下载速度),这使得被下载源(注:也就是指通常http下载时的http server)能够在仅仅添加适量(少量/合理)负载的情况下支持非常大数量的下载者。

一个BitTorrent文件传输系统由下面几部分构成:

* 一个普通的webserver

* 一个静态的元信息文件(注:也就是.torrent文件)

* 一个BitTorrent的Tracker(注:也就是Trackerserver:用来追踪当前全部下载者信息的server。



* 一个原始的下载者(注:即种子。

或者叫原始上传者。

在BT的世界里,下载者也是上传者。



* 终端用户的网页浏览器(注:用来下载torrent文件)

* 终端用户下载者(注:能够理解为BT的Client端)

理想的情况下应该有非常多的终端用户在下载同一个文件。

(注:实际情况也正是如此!)

在网络上提供(公布)一个BT文件以供下载,须要进行下面的步骤:

1. 启动一个Trackerserver(通常情况下。已经有一个Trackerserver正在执行)。

2. 启动一个普通的webserver。比方apache,通常应该已经有一个webserver在执行。

3. 在webserver上将扩展名为.torrent文件的mimetype关联为application/x-bittorrent(注:使client下载后知道用什么应用程序打开.torrent文件)。

4. 依据源文件(注:将要下载的文件)和Trankerserver的URL生成元信息文件(.torrent文件)。

5. 将生成的元信息文件(.torrent文件)放到webserver上。

6. 在某个网页上添加该元信息文件(.torrent文件)的下载link。

7. 启动一个拥有完整文件的下载者(即源)(注:这时我们有了第一个上传源,而且它有完整的文件,也就是通常所说有一个种子)。

要開始通过BT下载文件,须要进行下面的步骤:

1. 安装BitTorrent(或者已经装好了)

2. 浏览网页(注:寻找你下载的文件的.torrent文件)

3. 点击.torrent文件的link(注:这是你的BT程序应该会自己主动执行)

4. 选择下载文件的保存路径,或者继续下载曾经未下载完毕的文件。

5. 等待下载完毕。

6. 结束BitTorrent程序(它将继续上传直到你手动停止它)。(注:结束前你就是一个种子)

传输的内容按例如以下方式编码(注:该编码方式称为bencode):

* 字符串编码为:其长度的10进制数加冒号加字符串本身。比如:4:span表示字符串’spam’

* 数字编码为’i’加10进制数加’e’。比如:i3e表示数字3,而i-3e表示数字-3。数字没有长度限制。

i-0e是非法的。全部以0开头的编码,比方i03e。都是非法的。除了i0e,非常自然的它表示数字0。

* 列表(List)编码为字母’l’加列表的元素(元素相同也是bencoded过的)加’e’。比如l4:spam4:eggs表示列表[‘spam’, ‘eggs’]。

* 字典(Dictionary)编码为字母’d’加key加value加’e’。比如d3:cow3:moo:4spam4:eggse表示{‘cow’:’moo’, ‘spam’:’eggs’}。而d4:spaml1:a1:bee表示{‘spam’: [‘a’, ‘b’]}。

key必须是字符串,而且必须依照排序后的顺序出现(依照key的原字串排序。不是字母顺序)。

(注:sorted as raw strings应该是指基于binary compare的排序,而不是依照某种语言,比方英文,法文。字面意思/逻辑上的顺序)

元信息文件(.torrent文件)是一个以bencoded方式编码过的字典,该字典包括下面的key(及key相应的value):

announce

Tracker的URL。

info

该key的value是一个map,包括下面的key(及其value):

name:其值是一个string,建议了被下载文件(或者目录)用什么名字保存。它仅仅是一个建议值。

piece length:其值是一个数字。定义了每一个文件块的大小,单位是byte。

为了传输数据的方便。文件被分成固定大小的块(piece),除了最后一个文件块。由于非常可能除不尽。

piece length一般是2的幂。最经常使用的是2的18幂=256KB(3.2曾经的版本号用的是2的20次幂=1MB作为缺省值)。

pieces:其值是一个长度是20的倍数的string,每一段string(长度为20)内容是其相应顺序的文件块的SH1的哈希值。

还有两个key是length和files,这两个key不能同一时候出现,也不能都不出现(也就是说必须也仅仅能出现一个)。

假设length出现则表示下载的是单个文件,否则就表示下载的是多个文件:以目录的形式。

单个文件的情况下。length就表示该文件的大小。单位是byte。

考虑到其它key的目的,多文件也作为一个文件处理;依照它们在目录里出现的顺序连接成一个文件。

这样的情况下。files的值是一个字典的列表(译者注:每一个字典代表一个文件),每一个字典包括下面的key(及其value):

length:其值是数字。定义文件的长度。单位是byte。

path:其值是string的列表,每一个string表示子目录的名字,最后一个string表示文件的名字(长度为零的列表是错误的)。

单文件的情况下。name就表示这个文件的名字。

多文件时,name表示目录的名字。

向Trackerserver以Get方式发送的请求中应包括下面的Key:

info_hash

.torrent文件中info字段(bencoded编码后的info字段)的SHA1哈希值,长度为20字节。

注意info字段仅仅是.torrent文件的一个子串(注:.torrent文件本身就是一个字符串)。

这20字节的哈希值必须要进行URL编码。

peer_id

长度为20的字符串,用作标识下载端的ID。

由每一个下载端在開始下载的时候随机生成(注:可理解为每次下载不同文件时都生成一个peer_id)。

该值也必须进行URL编码(注:还有一份更加具体的文档中则注明该值不能进行URL编码)。

ip

可选项。

表明当前peer所在的IP地址(或者DNS名)(注:应该是指DNS解析前的主机名)。

通经常使用于第一个种子,假设它和Trackerserver在同一台机器上。

port

当前peer所侦听的port号。

默认的行为是依次尝试侦听port6881到6889,假设全部这些port都被占用,则放弃侦听(注:也就是放弃下载)。

uploaded

总上传大小。用十进制ASCII字符表示。

(注:从发送”started”事件開始计算,单位应该是字节。)

downloaded

总下载大小,用十进制ASCII字符表示。(注:从发送”started”事件開始计算。单位应该是字节。)

left

未下载大小(注:还须要下载的内容)。用十进制ASCII字符表示(注:单位应该是字节)。

注意这个数值不能够用文件总字节数和已经下载字节数之差来计算。

某些已经下载但未完毕文件块会由于不能通过一致性检查而被又一次下载。

(注:应该是计算某文件块的哈希值,假设和已知的,即下载完毕的该块的哈希值不一样。则一致性检查失败。

从而觉得该文件块没有下载完毕。须要又一次下载。)

event

可选项。

其值能够是started,completed。stopped(或者是empty,值是empty和没有该key是一样的)。

假设没有该key,则本次request被觉得是普通的定期request中的一次。

当下载開始时。第一个request必须包括该key而且其值必须是started。

当下载完毕时,必须发送一个request包括该key而且值为completed。

可是在文件下载完毕后再開始该任务时(注:事实上就是做种子),不能发送包括completed的request。

当下载停止时。必须发送包括stopped的request给Trackerserver。

Trackerserver的response也是以bencoded方式编码过的字典。假设response中包括有名为failure reason的key,那么该key的值是一个人能够理解的字符串(注:含有语义的字符串)用来通知发出请求的BTclient该请求失败的原因,同一时候此时该response中不须要有其它不论什么key。否则,response中必须包括有两个key:interval,值为数字,单位是秒。用来告知BTclient通常的定期发出的request之间的最小间隔时间。peers,值为字典的列表。

当中每一个字典包括下面的key:peer id, ip和port。他们的值依次是BTclient的ID,IP地址或者DNS名。port号。

注意当有事件发生或者须要知道很多其它的peer时,BTclient能够向Tracker发送非定期的request。

假设你须要对元信息文件(.torrent文件)或者发送给Trackerserver的request的内容作不论什么扩充,请与Bram Cohen协商以确保全部的扩展是兼容的。

BitTorrent的peer协议构建在TCP协议之上。它运作效率非常高同一时候不须要做不论什么socket的设置。

peer之间的连接时对等的。从peer连接的两个方向上看,发送的消息时一样的。同一时候数据能够向连接的不论什么一个方向上传送。

peer协议是以元信息文件(.torrent文件)里描写叙述的排序的文件块(file piece)这一概念为基础的。排序的序号从0開始。当某个peer下载完某个文件块。并验证其hash值正确后。该peer就向与它连接着的其它peer宣布它拥有该文件块。

对于每一个连接连接来说,每一端的peer应该是4种状态之中的一个(可用2bit来定义这4种状态):一端是interested或者not interested。则还有一端是choking或者unchoking。反之亦然。Choking表示直到unchoking发生之前,都不会有不论什么数据传送给连接还有一端的peer。关于Choke的原因和相关使用技巧将在本文的后面解释。

注:为了便于后面的理解,有必要先解释一下choke和interest。举例:文件被分为0,1,2。3,4这5个文件块。

如今有两个peer建立了连接,分别叫A和B。下面的解释是站在A的立场上进行的。

interested - 假设A没有块1。而B有块1,就是说B有A须要的块。则A的状态就是interested。

not interested - 假设A已经有块1。而B仅仅有块1。就是说B没有A须要的快。

那么A的状态就是not interested。

choking - 假设由于某种原因。B将在某段时间内(惩处期)不给A发送不论什么数据。那么在这段时间,B的状态就是choking(choked)。

unchoking - 惩处期过后,choking状态解除,B的状态就变为unchoking(unchoked)。

peer之间的连接上要发生传输数据。那么peer的状态就必须是一端interested,还有一端unchoked。interested/not interested状态必须时时进行更新。当某个peer须要下载某个文件块时,它应该向状态时unchoked的peer明白表示它须要的文件块。同一时候忽视那些状态时choked的peer。

要正确地实现这个须要一些技巧(注:不是那么简单)。可是一旦正确地实现了,就能够让peer知道哪些与它相连的状态是unchoked的peer会立马開始给它传送它须要的数据。

连接刚建立时,默认的状态是自己是not interested,对方是choked。

当数据被传输时。BTclient须要维持在同一时间内始终有多个对不同文件块的request在排队,以保证能够取得最好的TCP(网络)性能(这被称作pipelining:管道排队)。

还有一方面。排队的request不应该立马被写入TCP的缓冲区里,该request队列应该由BTclient在自己的内存中保存,而不是保存到应该用程序级别的网络缓冲区里(注:也就是说应该BTclient自己维护这个队列,而不是交给TCP协议去维护)。这样做事为了当choke发生时,这些未发送同一时候又已经不是必需发送的request能够及时被抛弃掉。

peer wire协议由握手消息開始,接着便是没有结束标记的带有长度前缀的消息(注:”length-prefixed messages”应该就是指按bencoded方式编码过的字串。而”never-ending”应该是说假设须要结束。就直接断开连接)。握手以”19”这个字串開始,后面紧接着是”BitTorrent protocol”这个字串。开头的第一个字串是定长的(”19”这个字串的长度为2),这样做希望其它新的协议也遵守这一规则以便新协议在连接后的一開始就能够和当前协议区分开。(注:”BitTorrent protocol”这个字串的长度就是19,所以程序处理上应该首先固定读取2个长度的字符串,然后将其转换为数字,接着读取该数字表示的长度的字符串,这个字符串就是协议名称。那么通过连接后的第一个消息,就能够知道对方使用的是什么协议了。比方新的BT协议应该发送例如以下的消息:”22BitTorrent protocol v2”。)

全部兴许发送的数字应该使用4 bytes的big-endian编码方式。

(注:big-endian即高位字节在后的编码方式。比方这样的编码方式下,0x0010转换为十进制时应该这样:0x0110 -> 0x1001 -> 9)

固定的消息头之后是8个字节长的保留字节,眼下的实现里是全0。假设你希望利用这些保留自己进行功能扩充。请与Bram Cohen协商以确保全部的扩展是兼容的。

接着便是元信息文件中面info的SHA1哈希值,长度为20个字节。(这和发送给Trackerserver的request里面的info_hash基本是一样的,唯一的差别是这里就是SHA1哈希值,而info_hash还须要进行escape处理)(注:由于发送Trackerserver的request是用HTTP的GET方式传送的,所以必须进行escape处理。而这里是直接TCP连接,用的是BT自己的协议。所以不须要escape处理)。假设两方发送的值不一样。则切断连接(注:假设hash值不一样。则表示下载的不是同一个文件。也就不是必需继续了。所以这里应该是要断开连接的)。有一个例外就是假设某个BTclient希望通过一个port下载多个文件,那么它能够先等对方发送info的hash值,接收后,假设自己的下载列表里包括该文件,那么发送给对方相同的info的hash值就能够了(注:假设没有,则等同于两方hash值不一样,能够直接断开连接)。

info的hash值传输完之后是20byte的peer id。

每一个下载端的peer id在向Trackerserver发送request时被Trackerserver记录下来,同一时候包括在Trackerserver发送给BTclient的response里面的peer id列表里。所以假设接受方回应的peer id和期待的peer id不一样,就切断连接。

(注:具体解释下:開始下载时。会向Trackerserver依照前面所述各式发送request。接着从Trackerserver的response里,能够获得一系列[ip,peer id],那么通过ip地址和对方连接后,依照本协议。对方会发送它的peer id过来,假设它发来的peer id和从Trackerserver上获得的不一样,就切断连接。)

以上就是握手的过程,接下来就是带有长度前缀的消息流(注:以bencoded方式编码的消息流)。

长度为0的消息时用作保持连接(注:相似于心跳帧)。不会做进一步的处理。心跳消息通常每2分钟发送一次(注:也就是说,当没有实用的传输数据时,超时时间大约是2分钟),可是当发送数据消息时,超时可能会发生的更快(注:叶就是说超时时间可能会短的多)。

全部的非心跳消息以一个单字节开头,该字节用来定义消息类型。

可选的消息类型例如以下:

0 - choke

1 - unchoke

2 - interested

3 - not interested

4 - have

5 - bitfield

6 - request

7 - piece

8 - cancel

‘choke’, ‘unchoke’, ‘interested’, 和 ‘not interested’没有负载(注:也就是说没有具体的消息内容。由于消息类型就已经表达了全部的内容)。

‘bitfield’必须是握手之后的第一个消息,假设须要发送的话(注:由于该消息是可选的)。它的内容表明了发送方peer已经拥有了哪些文件块:拥有的文件块的序号所相应的bit设置为1。没有的设置为0。假设没有不论什么文件块,则能够跳过该消息(注:也就是不发送)’。bitfield’的第一个字节从高位到低位依次代表文件块0到7。第二个字节代表8到15,其余依次类推。结尾多余的bit设置为0。(注:举例来说假设一个文件被分为20块,某个peer開始下载时已经有了第1,4,7,18块,那么它发送的’bitfield’消息的内容就应该是010010010000000000100000)。

‘have’消息的内容是一个数字:刚刚下载完毕而且正确的通过hash值验证的文件块的序号(注:该消息用于通知连接那一头的peer)。

‘request’消的内容包括一个序号。開始位置和长度。位置和长度的单位是字节。长度一般是2的幂,除非由于到达文件末尾。当前的实现使用的值是2的15次幂(注:即32KB。这是最早的值,现行版本号的BT用的值请參考文章开头注明的另外一遍更具体的文档)。假设对方peer要求的值大于2的17次幂(注:即128KB)则断开连接。

(注:文件块并非在一次消息中全然被传输,实际上文件块又被细分为更小的块,为了说明方便,这里称之为cell。所以通常下载完一个文件块须要多个request消息,每一个request消息用来要求对方传输某个指定的cell。

request消息中的序号知名是哪个文件块,開始位置能够理解为cell的序号,长度是固定值。能够理解为cell的大小)。

‘cancel’ 消息的内容和request消息的内容一样。’cancel’ 消息通常仅仅在文件下载到最后的时候才有可能被发送。这一时期消息发送/处理方式被称为”游戏结束模式”。

当文件下载将近尾声时,通常都有这样的趋势:最后的少量数据会从某个单通道的modem线路上获得(注:”off a single hosed modem line”不知道怎样翻译好),而且速度会非常的慢。所以为了尽快取得最后的这些数据块。一旦要求这些数据块的requests准备好了,就会把这些requests发给全部正在向我发送数据的peer(要求他们发送我须要的数据块)。为了避免数据重发而导致的效率低下(注:这里的数据重发指的是不同的peer都会向我发送相同的数据),一旦我得到了某个数据块,我就向其它全部peer发送cancel消息。告诉它们我已经得到了某个数据块,不须要再向我发送了。

‘piece’消息的内容包括序号。開始和具体的数据。尽管消息的内容里没有明白说明,但该消息实际上是和request消息相关的(注:能够理解为该消息就是request消息的response消息,要来传输request消息中要求的文件数据)。须要注意的是有时候会收到非期望的’piece’消息,比方choke和unchoke这两个消息交替发生的太快,或者消息传输的太慢。

文件块的下载顺序一般是随机的。这样能够避免某个peer拥有的文件块是和它相连的peer的超集或者子集。从而使下载合理并有效。

Choking之所以存在有几个理由:首先是由于向很多连接同一时候发送数据的时候。TCP的阻塞控制做的非常差(所以BTclient须要自己做Choking,也就是阻塞,目的说白了就是要提高下载效率)。另外choking也让每一个peer採用一种轮回的算法以保证它们能够达到一致的下载速度(注:事实上就是通过choking控制,避免和自己相连的某些peer下载速度快。而其它peer则一直非常慢,简而言之,就是为了公平)。

下面描写叙述的choking算法是当前已经应用的一个。

非常重要的一点是:全部新的算法既要能在全部由新算法构成的BT网络里工作。也要能在大多数BTclient使用的是老算法的网络里工作。

一个好的算法应该达到下面几个标准。它应当能够限制一定数量的上传连接以获得好的TCP性能。它应该避免频繁的在choking和unchoking之间切换,这被叫做”纤维性颤动”。

它应当报答那些让它下载的peer(注:也就是不要choking下载源)。

最后,它应当时不时地尝试那些未曾unchoking过的peer(注:由于初始状态是choking。),以便能够找到比当前已经连接的效果更好的peer,这被称作乐观地unchoking。

当前实际应用的算法通过延迟10秒去改变某个peer的choking状态。以避免”纤维性颤动”。通过unchoking四个下载速度最好,而且是自己interested的peer。来报答这些peer和限制上传连接。同一时候,上传速度好,但不是interested的peer会被unchoking。可是假设它们变成interested状态,那么上传速度最差的peer会被choking。假设自己拥有完整的文件(注:自己是种子)。那么就用上传速度来决定谁应该被unchoke。

最佳畅通是指:一直都会有一个peer被unchoking而无论它的上传速度(假设它是自己interested。那么就作为4个被同意的下载者之中的一个)。最佳畅通每30秒循环一次。

为了让他们有机会下载一个完整的文件块(注:指某些最佳畅通的peer下载了某个文件块的一部分之后被choking了,为了能让他们下载完这个文件块),下次和它的建立连接的可能性将会是循环中其它普通的畅通的三倍(注:即30秒后,要再次进行最佳畅通时,该peer被选中的可能性将会3倍于其它peer。

假设这样的peer多于一个,那么它们之间的竞争则应该是平等的)。

P2P-BT对端管理协议(附BT协议1.0)的更多相关文章

  1. 代码管理git 工具的话可以使用GitHub桌面端管理git、码云上的代码

    git版本控制  廖雪峰老师的git教程 git是linus 1991年创建了开源的linux...已成为最大的服务器系统软件 集中式的版本控制器:CVS.SVN.ClearCase是IBM的收费软件 ...

  2. kafka web端管理工具 kafka-manager【转发】

    1,下载与安装 $ git clone https://github.com/yahoo/kafka-manager.git $ cd kafka-manager $ ./sbt clean dist ...

  3. BitTorrent协议与MagNet协议原理【转】

    转自:https://blog.csdn.net/u012785382/article/details/70674875 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog ...

  4. TCP/IP协议学习(四) 协议概述

    生活中有舒适区,借口成为懒惰的护身符,学习也有舒适区,逃避便是阻止进步的最大障碍. 经过半年多嵌入式方面的工作和学习,我提高了很多,但同时我也对自己所面临的问题逐渐清晰: 1. 偏于实践,理论基础不牢 ...

  5. 在网络7层协议中,如果想使用UDP协议达到TCP协议的效果,可以在哪层做文章?(QQ 为什么采用 UDP 协议,而不采用 TCP 协议实现?)

    为了解决这题,可以具体看看下面这个讨论. 解灵运工程师 185 人赞同 某次架构师大会上那个58同城做即时通信的人说:原因是因为当时没有epoll这种可以支持成千上万tcp并发连接的技术,所以他们使用 ...

  6. 协议系列UDP协议

    所述上部TCP虽然该协议提供了一个可靠的传输,但也有一个缺点.发送速度慢.是否有协议它可以以高速传送?这部分是将要讨论UDP协议,它提供了更加快了传输速度.而且在可靠性为代价,这是一个无连接的传输协议 ...

  7. 网络协议 20 - RPC 协议(上)- 基于XML的SOAP协议

    [前五篇]系列文章传送门: 网络协议 15 - P2P 协议:小种子大学问 网络协议 16 - DNS 协议:网络世界的地址簿 网络协议 17 - HTTPDNS:私人定制的 DNS 服务 网络协议 ...

  8. 网络协议 19 - RPC协议综述:远在天边近在眼前

    [前五篇]系列文章传送门: 网络协议 14 - 流媒体协议:要说爱你不容易 网络协议 15 - P2P 协议:小种子大学问 网络协议 16 - DNS 协议:网络世界的地址簿 网络协议 17 - HT ...

  9. TCP/IP协议、UDP协议、 Http协议

    开放式系统互联通信参考模型(Open System Interconnection Reference Model,缩写为 OSI),简称为OSI模型(OSI model),一种概念模型,由国际标准化 ...

随机推荐

  1. Linux系统中磁盘block和windos中的簇一个意思

    block就是几个连续扇区组成一个block,每个分区可以设置block大小,好比一个txt只有2字节,但是这个分区的block为4K,那么其实这个txt需要4k来存储(所以大文件block设置大比较 ...

  2. 辛星跟您玩转vim第二节之用vim命令移动光标

    首先值得一提的是,我的vim教程pdf版本号已经写完了,大家能够去下载.这里是csdn的下载地址:csdn下载.假设左边的下载地址挂掉了,也能够自行在浏览器以下输入例如以下地址进行下载:http:// ...

  3. YUYV格式到RGB格式的转换

    为什么YUYV格式要转到RGB格式,视频的显示调用的多数API都是基于RGB格式,所以需要进行格式的转换. YUYV格式如下: Y0U0Y1V0 Y2U1Y3V1.......... 说明:一个Y代表 ...

  4. 内核并发管理---spin lock

    自旋锁最初是为了在smp系统上使用而设计. 1.在单处理器非抢占模式下,自旋锁不做任何事情. #ifdef CONFIG_PREEMPT_COUNT     //支持抢占模式 #define pree ...

  5. vue 事件处理器

    事件处理器 1.监听事件 可以用v-on指令监听DOM事件来触发一些js代码. 2.方法事件处理器 许多事件处理的逻辑都很复杂,所以直接把js代码写在v-on指令中是不可行的.因此v-on可以接受一个 ...

  6. 使用 NGUI Toggle 制作单选框

    好久没写了,今天来把关于NGUI的做的简单功能发上来~ 这个是做单选框的.用了新版本的NGUI后,发现没有以前的Checkbox了,在网上查了之后才知道是用Toggle代替了以前的Checkbox.现 ...

  7. python第二周数据类型 字符编码 文件处理

    第一数据类型需要学习的几个点: 用途 定义方式 常用操作和内置的方法 该类型总结: 可以存一个值或者多个值 只能存储一个值 可以存储多个值,值都可以是什么类型 有序或者无序 可变或者不可变 二:数字整 ...

  8. python笔记2 生成器 文件读写

    生成器 一边循环一边计算的机制,称为生成器(Generator). 把一个列表生成式的[]改成(),就创建了一个generator: 创建了一个generator后,通过for循环来迭代它. 著名的斐 ...

  9. 如何解决HTML在各种浏览器的兼容性

    方法/步骤 不同浏览器对HTML标记所具有的内外边距属性具有不同的定义. 因此如果想消除这种差距,应该在相应的CSS部分加入以下CSS代码: *{margin:0px;padding:0px;} 借于 ...

  10. HTTPS请求 SSL证书验证

    import urllib2 url = "https://www.12306.cn/mormhweb/" headers = {"User-Agent": & ...