• P2P-BT对端管理协议(附BT协议1.0)


    对端管理

    指的是远端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状态的转换图例如以下:
    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多于一个,那么它们之间的竞争则应该是平等的)。

  • 相关阅读:
    HTML语义化
    OKAY take it away `electron-builder`一直提示这个
    gitbash选中不了自己想要的选择
    vue挂载
    vue关闭eslint
    第二天-5大浏览器内核和浏览器的组成
    第一天-JavaScript简介与历史
    bootstrap模态框遇到做复制的功能失效
    对象的key【键】和分别获取数组的key【键】和值
    AngularJS教程
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/7340356.html
Copyright © 2020-2023  润新知