本篇内容基本是以《BitTorrent Protocol Specification》和 《Incentives Build Robustness in BitTorrent》这两篇的内容,以及对部分libtorrent代码的阅读为基础的。前面那两篇很容易从谷歌上获得。对于以下内容,在术语以及措辞方面,可能会引起您的不快,请见谅。希望我的描述能够容易理解。
从“BitTorrent Protocol Specification"和"Incentives Build Robustness in BitTorrent",以及有些书籍上读到的内容,可以有以下分析,之所以说是分析,因为没有地方有直白的说明。 简单的对部分情况下的流程进行说明:
0,client启动,随机生成peer_id(可能根据启动线程的ID,启动时间等),打开种子文件,解析提取info_hash,tracker等信息。
1,client向tracker建立连接,发送带有peer_id和info_hash等信息的请求报文,tracker回复,返回peers在内的信息。(注意,整个下载过程中,client都可以向tracker发送请求,以更新自己有的peers,但这个请求的发送是有时间间隔要求的,过于频繁的请求会被tracker忽略。至于client作为seed时的情况,未查明)
2,client获取peers后,会逐个向peers发送包含本地peer_id和info_hash等信息在内的握手报文,尝试建立连接,即握手。
3,握手报文由连接的发起者发出(即这里的client),作为连接的接收者(即这里的peer)会根据对info_hash数据位的检查迅速对连接请求作出回应:接收建立连接,或者丢弃。peer对握手报文中的info_hash数据位检查,如果在自己维护的torrent中找到与info_hash对应的,那么就接收连接请求,并以握手报文回复,回复的握手报文中的peer_id为peer的peer_id。client收到握手报文后,也会进行检查,对info_hash和peer_id进行检查,如果两个数据位都无法匹配,则丢弃。
4,连接建立后,双方会发送BITFIELD报文,该报文含有本地对torrent的片的持有情况。(注意,BITFIELD报文只在连接建立后发送一次,之后如果连接的双发有新的片的获得了,需要更新bitfield,那么可以通过HAVE报文来通知对方)
5,在获得对方的bitfield后,连接的双方根据是否对对方提供的数据感兴趣,进行数据传输。
6,连接建立后的两个节点之间的报文还有很多,这里不再说明。
以上,能直接从前面所说的文字资料中分析出来的内容关于流程的东西就这么多了。之后需要借助具体实现,来分析一些细节,以及将流程的其他内容补充清楚。在进一步之前,需要说明一下所谓的”对对方提供的数据感兴趣“是指什么?
0,每个peer对建立起的连接都会有(来自“BitTorrent Protocol Specifiaction"):am_choking, am_interested, peer_choking, peer_interested 这四个状态标记,分别表明:本地choke远程节点,本地对远程节点感兴趣,远程节点choke本地,远程节点对本地感兴趣。
1,当一个节点A choke 另一个节点B 时,A不会对B上传数据。
2,当一个节点A unchoke 另一个节点B,并且B对A感兴趣时,A会为B上传数据。
3,choke是指针对的数据,只是所共享文件的数据,并不包括用于节点网络维护的基本数据,即使一个节点choke另一个节点,但连接仍然可以建立,一些状态更新数据仍可以传输。
接下来,以具体实现(libtorrent)的代码分析,对其他一些细节进行不完全的补充(因为libtorrent还有不少内容没有看):
0,peers。文字资料中表明,tracker一次向client返回30个peers,已经很多了。高于55个后,client会拒绝与tracker交互以更新数据;低于25个,新加入的peer不太可能提升下载速度;低于30个,peer会积极的与tracker交互,以更新数据。在这么多peers的情况下,client没有必要一下与之都建立连接,毕竟client还有可能接受一些后进入的client不知道的节点的连接请求。libtorrent下,session_impl中有m_max_connections数据成员,并且peer_connection也有是否说明当前连接的状态数据。
1,interesed和choke。前面所说的 am_choking 等四个数据,在libtorrent中表现为:m_choked, m_interesting, m_peer_choked, m_peer_interested,代表的内容没有变。在之前的文字资料中无法获知 choke和interested 是否有关联,没有说明有,也没有说明没有。因此需要通过阅读源码来发现。 peer_connection 中,用于peer间发送 choke,unchoke,interested, not interested 的报文有:send_choke(), send_unchoke(), send_interested(), send_not_interested(), incoming_choke(), incoming_unchoke(), incoming_interested(), incoming_not_interested()。这8个方法中,需要主要学习的是incoming_interested()和incoming_not_interested(),然后需要了解的是send_not_interested(), send_choke(), incoming_unchoke(), incoming_unchoke()。另外还有需要学习的方法有disconnect_if_redundant() 和 ignore_unchoke_slots()。
这里不具体说方法的内部了,只简要说明一些主要发现:在incoming_interested()中,如果本地choke了远程节点,那么本地节点会在不需要考虑资源的情况下unchoke远程节点,这里所说的不需要考虑资源的情况例如有本地下载完成进入做种;在incoming_not_interested()中,本地有可能choke远程节点,也有可能根据对方为seed,或者本地为superseed模式做出相应调整;在disconnect_if_redundant()中,断开的双方都彼此不感兴趣的连接状态有upload-upload 和 upload-uninterested 两种。
总结下来就是,interested 和 choke 两种状态会有关联,就目前的分析来看,interested 会更明显的影响 choke,根据一些判断条件有可能发生:如果远程节点对本地感兴趣,那么本地节点会unchoke对方(如果之前为choke对方);如果远程节点对本地不感兴趣,那么本地节点会choke对方。当然,libtorrent会根据其他丰富的条件、状态等做出策略调整。
2,suggest_piece和busy_block。libtorrent中有这两者的设置,但在前面的文字资料中,没有读到这个内容。suggest_piece是指,即使节点A choke了节点B,但只要B对A感兴趣并且感兴趣的片是suggest_piece,那么只要资源允许,A也会将这些片上传给B。busy_block是指对于一个block,一个节点同一时间内只能对一个节点提供上传,这会产生一种即使远程节点持有本地请求的block,但远程节点也不会马上为本地提供上传的现象。
3,抽象数据关系:(file/torrent)-(peers/peer_connection)-piece-block。虽然file被划分为piece,并且piece也是表征file在节点网络中分布的基本单元,但节点间的数据传输是以block为基本单元的,在interested和choke支持的情况下,节点间的数据请求也是以block为基本单元的。对于block的区分是以piece索引和本piece内block的索引来进行区分的。假如一个file被分为100个piece,每个piece又被分为10个block,那么就至少可以用(3 · 4 )这个元组表示索引为3的piece下索引为4的block。
4,当一个节点加入后,下载一个新的文件时,会为这个文件创建一个torrent(为简化描述,忽略类与实例)。在由前所述的获得对于这个文件的peers后,为每个远程节点/peers 各建立一个peer_connection(为便于描述,这里包括该类的子类,如bt_peer_connection),有peer_connection具体负责与远程节点的传输交互。因为一开始client没有任何关于下载文件的数据,所以可以不用发送BITFIELD报文("BitTorrent Protocol Specification"中是这样描述的)。采用随机的第一片策略,因为有suggest_piece,seed,optimistically unchoke 以及可以向tracker发送请求以更新peers的方法/条件,所以client始终可以获得随机的第一片。之后转为最少优先,既可以进入正常的下载流程。
其他补充:
0,最少优先。在获得peers后,client需要轮询自己有的peers才能知道一个局部的最少片的结果,并且局部最少结果是有意义的。
1,time_now()。libtorrent中有time_now()方法,就目前的了解来看,主要用于choke和unchoke报文的交互,对于一个peer_connection实例(为便于描述,这里包括该类的子类实例,如bt_peer_connection实例),在处理choke和unchoke报文时,libtorrent会更新时间记录,时间记录会在每10s的对于choked的连接更新(以选择新的unchoke对象)和在选择optimistically unchoke时使用。
2,一个节点的完成下载是指其感兴趣的片下载完毕,而不是指文件的完整下载。
3,当一个节点进入seed状态时,不在以被提供的下载速率为标准去提供上传服务,而是以自己能对外提供的最大上传速率为标准去提供上传服务。
4,super-seeding 是指一种为了优化本地上传带宽(减少上传带宽浪费),而采用的一种诱导远程节点去下载本地提供的指定片的策略。只推荐早期做中节点使用。
5,client初始进入时的状态时"choked"和"not interested",即(以libtorrent为例)m_choked = 1, m_peer_choked = 1, m_interested = 0, m_peer_interested = 0。