日期:2004-5-22
BitTorrent
是一种分发文件的协议。它通过URL来识别内容,并且可以无缝的和web进行交互。它基于HTTP协议,它的优势是:如果有多个下载者并发的下载同一个文件,那么,每个下载者也同时为其它下载者上传文件,这样,文件源可以支持大量的用户进行下载,而只带来适当的负载的增长。(译注:因为大量的负载被均衡到整个系统中,所以提供源文件的机器的负载只有少量增长)
一个BT文件分布系统由下列实体组成:
一个普通的web服务器
一个静态的“元信息”文件
一个跟踪(tracker)服务器
终端用户的web浏览器
终端下载者
理想的情况是多个终端用户在下载同一个文件。
要提供文件共享,那么一台主机需要执行以下步骤:
Ø运行一个 tracker服务器(或者,已经有一个tracker服务器在运行了也可以)
Ø运行一个web服务器,例如apache,或者已经有一个web服务器在运行了。
Ø在web服务器上,将文件扩展名.torrent 和MIME类型 application/x-bittorrent关联起来(或者已经关联了)
Ø根据 tracker服务器的 URL 和要共享的文件来创建一个“元信息”文件(.torrent)。
Ø将“元信息”文件发布到web服务器上
Ø在某个web页面上,添加一个到“元信息”文件的链接。
Ø运行一个已经拥有完整文件的下载者(被成为’origin’,或者’seed’,种子)
要开始下载文件,那么终端用户执行以下步骤:
Ø安装 BT(或者已经安装)
Ø访问提供 .torrent 文件的web服务器
Ø点击到 .torrent 文件的链接(译注:这时候,bt会弹出一个对话框)
Ø选择要把下载的文件保存到哪里?或者是一次断点续传
Ø等待下载的完成。
Ø结束bt程序的运行(如果不主动结束,那么bt会一直为其它人提供文件上传)
各个部分之间的连通性如下:
网站负责提供一个静态的文件,而把BT辅助程序(客户端)放在客户端机器上。
Trackers从所有下载者处接收信息,并返回给它们一个随机的peers的列表。这种交互是通过HTTP或HTTPS协议来完成的。
下载者周期性的向tracker登记,使得tracker能了解它们的进度;下载者之间通过直接连接进行数据的上传和下载。这种连接使用的是 BitTorrent 对等协议,它基于TCP。
Origin只负责上传,从不下载,因为它已经拥有了完整的文件。Origin是必须的。
元文件和tracker的响应都采用的是一种简单、有效、可扩展的格式,被称为bencoding,它可以包含字符串和整数。由于对不需要的字典关键字可以忽略,所以这种格式具有可扩展性,其它选项以后可以方便的加进来。
Bencoding格式如下:
对于字符串,首先是一个字符串的长度,然后是冒号,后面跟着实际的字符串,例如:4:spam,就是“ spam”
整数编码如下,以 ‘i’ 开始,然后10进制的整数值,最后以’e’结尾。例如,i3e表示3,I-3e表示-3。整数没有大小限制。I-0e是无效的。除了 i0e外,所以以0起始的整数都无效。I0e当然表示0。
列表编码如下,以’l’开始,接下来是列表值的编码(也采用bencoded编码),最后以’e’结束。例如:l4:spam4:eggse 表示 [‘spam’, ‘eggs’]。
字典编码如下,以’d’开始,接下来是可选的keys和它对应的值,最户以’e’结束。例如:d3:cow3:moo4:spam4:eggse,表示
{‘cow’:’moo’,’spam’:’eggs’},而d4:spaml1:al:bee 表示
{‘spam’:[‘a’,’b’]}。键值必须是字符串,而且已经排序(并非是按照字母顺序排序,而是根据原始的字符串进行排序)。
元文件是采用bencoded编码的字典,包括以下关键字:
announce tracker的服务器
info 它实际上是一个字典,包括以下关键字:
Name:
一个字符串,在保存文件的时候,作为一个建议值。仅仅是个建议而已,你可以用别的名字保存文件。
Piece length:
为了更好的传输,文件被分隔成等长的片断,除了最后一个片断以外,这个值就是片断的大小。片断大小几乎一直都是2的幂,最常用的是 256k(BT的前一个版本3.2,用的是1M作为默认大小)
Pieces:
一个长度为20的整数倍的字符串。它将再被分隔为20字节长的字符串,每个子串都是相应片断的hash值。
此外,还有一个length或files的关键字,这两个关键字只能出现一个。如果是length,那么表示要下载的仅仅是单个文件,如果是files那么要下载的是一个目录中的多个文件。
如果是单个文件,那么length是该文件的长度。
为了能支持其它关键字,对于多个文件的情况,也把它当作一个文件来看,也就是按照文件出现的顺序,把每个文件的信息连接起来,形成一个字符串。每个文件的信息实际上也是一个字典,包括以下关键字:
Length:文件长度
Path:子目录名称的列表,列表最后一项是文件的实际名称。(不允许出现列表为空的情况)。
Name:在单文件情况下,name是文件的名称,而在多文件情况下,name是目录的名称。
Tracker查询。Trakcer通过HTTP的GET命令的参数来接收信息,而响应给对方(也就是下载者)的是经过bencoded编码的消息。注意,尽管当前的tracker的实现需要一个web服务器,它实际上可以运行的更轻便一些,例如,作为apache的一个模块。
Tracker GET requests have the following keys:
发送给Tracker的GET请求,包含以下关键字:
Info_hash:
元文件中info部分的sha hash,20字节长。这个字符创几乎肯定需要被转义(译注:在URL中,有些字符不能出现,必须通过unicode进行编码)
Peer_id:
下载者的id,一个20字节长的字符串。每个下载者在开始一次新的下载之前,需要随机创建这个id。这个字符串通常也需要被转义。
Ip:
一个可选的参数,给出了peer的ip地址(或者dns名称?)。通常用在origin身上,如果它和tracker在同一个机器上。
Port:
peer所监听的端口。下载者通常在在 6881 端口上监听,如果该端口被占用,那么会一直尝试到 6889,如果都被占用,那么就放弃监听。
Uploaded:
已经上载的数据大小,十进制表示。
Downloaded:
已经下载的数据大小,十进制表示
该peer还有多少数据没有下载完,十进制表示。注意,这个值不能根据文件长度和已下载数据大小计算出来,因为很可能是断点续传,如果因为检查文件完整性失败而必须重新下载的时候,这也提供了一个机会。
Event:
一个可选的关键字,值是started、compted或者stopped之一(也可以为空,不做处理)。如果不出现该关键字,。在一次下载刚开始的时候,该值被设置为started,在下载完成之后,设置为completed。如果下载者停止了下载,那么该值设置为stopped。
Tracker的响应是用bencoded编码的字典。如果tracker的响应中有一个关键字failure
reason,那么它对应的是一个字符串,用来解释查询失败的原因,其它关键字都不再需要了。否则,它必须有两个关键字:Interval:下载者在两次发送请求之间的时间间隔。Peers:一个字典的列表,每个字典包括以下关键字:Peer
id,Ip,Port,分别对应peer所选择的id、ip地址或者dns名称、端口号。注意,如果某些事件发生,或者需要更多的peers,那么下载者可能不定期的发送请求,
(downloader 通过 HTTP 的GET 命令来向 tracker 发送查询请求,tracker 响应一个peers 的列表)
如果你想对元信息文件或者tracker查询进行扩展,那么需要同Bram Cohen协调,以确保所有的扩展都是兼容的。
BT对等协议基于TCP,它很有效率,并不需要设置任何socket选项。(译注:BT对等协议指的是peer与peer之间交换信息的协议)
对等的两个连接是对称的,消息在两个方向上同样的传递,数据也可以在任何一个方向上流动。
一旦某个peer下载完了一个片断,并且也检查了它的完整性,那么它就向它所有的peers宣布它拥有了这个片断。
连接的任何一端都包含两比特的状态信息:是否choked,是否感兴趣。Choking是通知对方,没有数据可以发送,除非unchoking发生。Choking的原因以及技术后文解释。
一旦一端状态变为interested,而另一端变为非choking,那么数据传输就开始了。(也就是说,一个peer,如果想从它的某个peer那里得到数据,那么,它首先必须将它两之间的连接设置为
interested,其实就是发一个消息过去,而另一个peer,要检查它是否应该给这个家伙发送数据,如果它对这个家伙是
unchoke,那么就可以给它发数据,否则还是不能给它数据)Interested状态必须一直被设置――任何时候。要用点技巧才能比较好的实现这个目的,但它使得下载者能够立刻知道哪些peers将开始下载。
对等协议由一个握手开始,后面是循环的消息流,每个消息的前面,都有一个数字来表示消息的长度。握手的过程首先是先发送19,然后发送“BitTorrent protocol”。19就是“BitTorrent protocol”的长度。
后续的所有的整数,都采用big-endian 来编码为4个字节
在协议名称之后,是8个保留的字节,这些字节当前都设置为0。
接下来对元文件中的 info 信息,通过 sha1 计算后得到的 hash值,20个字节长。接收消息方,也会对 info 进行一个 hash 运算,如果这两个结果不一样,那么说明对方要的文件,并不是自己所要提供的,所以切断连接。
接下来是20个字节的 peer id。
这就是握手过程
接下来就是以消息长度开始的消息流,这是可选的。长度为0 的消息,用于保持连接的活动状态,被忽略。通常每隔2分钟发送一个这样的消息。
其它类型的消息,都有一个字节长的消息类型,可能的值如下:
‘choke’, ‘unchoe’, ‘interested’, not interested’类型的消息不再含有其它数据了。
‘bitfield’永远也仅仅是第一个被发送的消息。它的数据实际是一个位图,如果downloader已经发送了某个片断,那么对应的位置1,否则置0。Downloaders如果一个片断也没有,可以忽略这个消息。(通过这个消息,能知道什么了?)
‘have’类型的消息,后面的数据是一个简单的数字,它是下载者刚刚下载完并检查过完整性的片断的索引。(由此,可以看到,peer通过这种消息,很快就相互了解了谁都有什么片断)
‘request’类型的消息,后面包含索引、开始位置和长度)长度是2的幂。当前的实现都用的是215 ,而关闭连接的时候,请求一个超过2 17的长度。(这种类型的消息,就是当一个peer希望另一个peer给它提供片断的时候,发出的请求)
‘cancel’类型的消息,它的数据和’request’消息一样。它们通常只在下载趋向完成的时候发送,也就是在‘结束模式“阶段发送。在一次下载接近完成的时候,最后的几个片断需要很长时间才能下载完。为了确保最后几个片断尽快下载完,它向所有的peers发送下载请求。为了保证这不带来可怕的低效,一旦某个片断下载完成,它就其它peers发送’cancel’消息。(意思就是说,我不要这个片断了,你要是准备好了,也不用给我发了,可以想象,如果对方还是把数据发送过来了,那么这边必须忽略这些重复的数据)。
‘piece’类型的消息,后面保护索引号、开始位置和实际的数据。注意,这种类型的消息和
‘request’消息之间有潜在的联系(译注:因为通常有了request消息之后,才会响应‘piece’消息)。如果choke和unchoke消息发送的过于迅速,或者,传输速度变的很慢,那么可能会读到一些并不是所期望的片断。(
也就是说,有时候读到了一些片断,但这些片断并不是所想要的)
1.随机的第一个片断 最简单,你可以采用随机的选择。在p2p中,当需要一个调度策略的时候,有时候随机直接采用随机,达到的效果也还不错,而且随机实现很简单。
2.最少的优先
一个比较好的策略是,在网络中越是稀有的Piece越是优先下载,当然,如果计算得出的稀有程度相同,这时候就随机就可以了。这种办法可以提高整个系统的性能,因为如果全部随机的话,那万一大家一段时间都没有随机到某个piece,而这时候种子又下线了,那么所有人都下不到完整资源了。而且BT客户端下载了一个稀有piece后,就可以马上为其他人提供这个稀有的piece了。那么我们怎样计算一个piece的稀有程度呢,最理想的是整个网络全局的稀有程度,那最好了,但是在p2p网络中,单个peer是很难知道全局信息的,所以BitTorrent的下载策略计算稀有程度是在peer的它连接的所有其他peers中计算的,也就是一个局部的信息。即便这样,这种方法仍然比随机选择要好的多。
3.严格的优先级 由于piece又被分成多个block,请求和传输的最小单位是block,所以当某个piece的一个block获得了或者发出去了请求,那么剩下的block的优先级将最高。这样做是为了尽可能快获取一个完整的piece。
4.最后阶段模式
下载的最后阶段。BT客户端应当向它连接的所有peers都发出请求,也就是冗余的请求,以便尽快完成下载。如果某个block接收到了,BT客户端应当立即向其他peers发出cancel包,取消请求。那这里也存在一个关键问题,如何来界定是否到达最后阶段了?这个问题也一直被很多人讨论,用百分比?用剩余block个数?等等。个人认为,用剩余block个数来界定是比较合理的。有一种就是取剩余block个数小于正在传输的block个数并且不超过20个的情况作为判断条件。
5.BT阻塞算法(下面这些内容来自互联网)
BT提倡peer之间尽可能多的相互共享。对于合作者,提供上传服务,对于不合作的,就阻塞对方。所以说,阻塞是一种临时的拒绝上传策略,虽然上传停止了,但是下载仍然继续。在阻塞停止的时候,连接并不需要重新建立。阻塞算法并不属于BT对等协议(指peers
之间交互的协议)的技术部分,但是对提高性能是必要的。一个好的阻塞算法应该利用所有可用的资源,为所有下载者提供一致可靠的下载速率,并适当惩罚那些只下载而不上传的peers。
从技术层面上说,BT的每个peer一直与固定数量的其它 peers
保持疏通(通常是4个,(这个也太少了吧)),所以问题就变成了哪些peers应该保持疏通?这种方法使得TCP的拥塞控制性能能够可靠的饱和上传容量。(也就是说,尽量让整个系统的上传能力达到最大)。
严格的根据当前的下载速率来决定哪些peers应该保持疏通。令人惊讶的是,计算当前下载速率是个大难题。当前的实现实质上是一个每隔20秒的轮询。而原来的算法是对一个长时间的网络传输进行总计,但这种方法很差劲,因为由于资源可用或者不可用,带宽会变化的很快。
为了避免因为频繁的阻塞和疏通 peers造成的资源浪费,BT每隔10秒计算一次哪个peer需要被阻塞,然后将这种状态保持到下一个10秒。10秒已经足够使得TCP来调整它的传输性能到最大。
optimistic unchoking
如果只是简单的为提供最好的下载速率的peers们提供上载,那么就没有办法来发现那些空闲的连接是否比当前正使用的连接更好。为了解决这个问题,在任何时候,每个peer都拥有一个称为“optimistic
unchoking”的连接,这个连接总是保持疏通状态,而不管它的下载速率是怎样。每隔30秒,重新计算一次哪个连接应该是“optimistic
unchoking”。30秒足以让上载能力达到最大,下载能力也相应的达到最大。这种和针锋相对类似的思想非常的伟大。“optimistic
unchoking”非常和谐的与“囚徒困境”合作。
反对歧视
某些情况下,一个peer可能被它所有的peers都阻塞了,这种情况下,它将会保持较低的下载速率直到通过“optimistic
unchoking”找到更好peers。为了减轻这种问题,如果一段时间过后,从某个peer那里一个片断也没有得到,那么这个peer认为自己被对方
“怠慢”了,于是不再为对方提供上传,除非对方是“optimistic
unchoking”。这种情况频繁发生,会导致多于一个的并发的“optimistic unchoking”。
仅仅上传
一旦某个peer完成了下载,它不能再通过下载速率(因为下载速率已经为0了)来决定为哪些 peers 提供上载了。目前采用的解决办法是,优先选择那些从它这里得到更好的上载速率的peers。这样的理由是可以尽可能的利用上载带宽。
一个BT的客户端(本地节点)对于每个与它连接上的节点都维护两种状态信息,choked(阻塞)和interested(关注),它们的含义分别是:
choked(阻塞):当一个远端节点把BT客户端(本地节点)阻塞住的时候,BT客户端向远端节点发送的所有请求block的请求包将不会被回应,当BT客户端发现自己被远端节点阻塞的时候,就不应当向该节点发送请求。
interested(关注):当BT客户端(本地节点)被远端节点关注时,远端节点会向BT客户端发送请求(当然如果BT客户端把远端节点阻塞住的话,就不会),这说明BT客户端(本地节点)有远端节点所需要的数据。
上面的解释中都只说了一方主动,另一方被动的情况,其实“作用”是相互的,对于本地节点和远端节点其实是这样的情况:
am_choking:本地节点把远端节点阻塞
am_interested:本地节点关注远端节点
peer_choking:本地节点被远端节点阻塞
peer_interested:本地节点被远端节点关注
在连接初始建立的时候,状态是双方互相被阻塞,互相不关注。
对于这些状态的表示,我们可以用每个Bit表示一个状态,用&操作取出状态值。
只有在本地节点关注远端节点并且远端节点没有阻塞本地节点的时候,本地节点才能从远端节点下载block,同样,只有在本地节点被远端节点关注并且本地节点没有把远端节点阻塞的时候,本地节点才会向远端节点上传数据。
需要注意的是,尽管节点被阻塞或者不关注,连接还是不关闭,而是要保持更新状态,除非客户端(本地节点)在一些调度算法上把没用的节点给淘汰关闭。至于多少个Peer后才需要淘汰,那是属于调度策略的事情,各个客户端实现可能不一样,一般30个peer已经够用,建议不要超过55个peer,节点再增多,不见得能把速度提高多少。
当两个Peer连接后,要首先向对方发送握手包,如果握手失败,连接将关闭。握手包的格式是:
字符串长度+字符串+保留字段+info_hash+peer_id
字符串长度:后面的字符串长度,1个Byte
字符串:标识协议的字符串。在1.0的bt协议中为"BitTorrent protocol"
保留字段:8个字节,用于协议扩展,无扩展的时候全为0,使用的时候从后向前开始使用。
info_hash:20个字节,就是用于向tracker请求的那个info_hash
peer_id:20个字节,各个BT软件对peer_id的命名方法不一样,大致有几种类型,这个以后再说。
当本地节点主动连接到远端节点后,就应当立即向远端节点发送握手包,远端节点接收连接后等等握手包的到来,当握手包接收后立即回应握手包。当远端节点接收到握手包后,它会首先看info_hash字段所代表的任务是否存在,若不存在,那就应该关闭连接。当远端节点回应握手包后,本地节点应当判断该节点的
peer_id,如果它有从tracker那里得知远端节点的peer_id,若发现pere_id不符合,本地节点应当关闭连接。
keep-alive:
keep-alive包(保活/心跳包)就是一个包长度设置为0的包,也没有包类型字段,其内容就是0000,它的时间间隔是2分钟。
choke:
把接收方阻塞
unchoke:
把接收方取消阻塞
interested:
关注接收方
not interested:
取消关注接收方
have:
告诉接收方的Peer,发送方拥有某一个piece的数据,piece的index是以0开始的
这个包是非常频繁的包,BT客户端的实现应该设计一些技巧在不减少信息发布的情况下减少这个包的传输。比如某个Peer已经有该piece了,显然就不应该向它发送了。或者某个Peer它选择性下载,根本不需要下载这个piece,那也不要给它发送了。或者已经告诉过它一次了,就不要再告诉它了,等等。
bitfield:
bitfield包一般在两个节点完成握手之后就要发送,当然,如果节点一个piece都没有,就没有必要发送了。这个包是变长的,取决于有多少个
piece,X就是bitfield的长度,bitfield里第一个字节的高位bit就对应着第一个piece(index=0)。由于piece的个数不一定能被8整除,所以最后可能有没有用到的bit,那些bit全设为0就行了。peer在接收到该包后,要判断bitfield是否合法,即
piece个数是否合法,不合法则要关闭连接。
request:
数据请求包,index:整型,表示piece
index,begin:整型,表示offset in the
piece。length:整型,表示请求的数据的长度。关于这个block的长度,众说纷纭,有的说16K,有的32K,有的说只要不超过128K就好,等等,我觉得,这个得看实现BT协议的人,如果它的客户端做的就只支持16K,它有很多用户了,你再做BT客户端,你要跟它兼容,你就得支持16K,所以我觉得,自己在程序中控制只要小于128K的请求都回应,向其他人发送request的时候,就根据它是什么客户端,默认就给它发个16K的这种就可以了,整这么复杂干啥~
piece:
回应的Block数据,X便是block的长度,index是piece index,begin是block在piece中的offset,block便是二进制数据了。
cancel:
这个包跟request包一样,就是type不一样,它就表示把对应的那个request给取消掉。
port:
port包,是用来告诉接收方节点,发送方节点支持DHT,并且DHT监听的端口是listen port。
从torrent文件中得到了tracker列表后,接下来的工作就是获取peer列表.
tracker使用http协议.客户端向服务器发送标准的GET请求,就可以得到这个列表.tracker返回的信息是bencode编码.
向tracker发送的GET请求有如下一些参数:
info_hash(必须):
torrent文件中info字段的sha1.torrent文件解析器中已经计算此值,保存在CTorrentParser的m_Infohash成员中.
peer_id(必须):
节点ID,长20字节.通常每一个下载产生一个相应的ID.通过peer_id可以识别大多数客户端类型.
ip(可选):
客户端指定的期望其他节点与本地交互时连接的IP.一般来说不用指定此参数,除非客户端使用了代理或者端口映射.
port(可选):
本地侦听的端口.
uploaded:
已经上传的字节数.
download:
已经下载的字节数.
left:
未下载的字节数.
event(可选):
此参数可以是如下的值:
started:下载开始
completed:下载完毕
stopped:下载停止
compact(可选):
此参数值为1,表示期望得到紧凑模式的节点列表.
否则表示期望得到普通模式的节点列表.
no_peer_id(可选):
其值为1,表示不需要节点id信息.
如果返回的bencode编码中包含failure reason字段,则表示处理请求失败,此字段的值即为失败原因.
如果请求成功,则有两个字段是必须出现的:
peers:节点列表
interval:服务器期望的下次查询间隔时间,单位为秒
通常还会有如下一些字段出现:
done peers:下载完毕的节点个数
num peers或者incomplete: 当前下载的节点个数
下面是普通模式的回复例子:
d8:intervali3600e5:peersld2:ip13:192.168.24.527:peer
id20:{peer_id}4:porti2001eed2:ip11:192.168.0.37:peer
id20:{peer_id}4:porti6889eeee
struct PEER
{
DWORD IP;//节点IP
WORD Port;//节点端口
};
例如:192.168.24.52:2001 => 0xC0 0xA8 0x18 0x34 0xD1 0x07
d8:intervali3600e5:peers12:{12 characters of binary data}e
一 BT系统的组成结构
datei1175422660e8:encoding3:GBK4:infod6:lengthi7080818e4:name18:gettingoveryou.mp310:name.utf-818:gettingoveryou.mp312:piece
lengthi1048576e6:pieces140:琚瀲⒂!堯??M挷咲i?屩轩@鏋EU轒50-?鷰靀F?@憸%l?Iy~
??R?襉軦d[1]f岤\>@陗罏?樯燐#o?翟木懣槾"霡瓼W?棈Dk?鰛殴鴞?チY庖}0!$苶7:privatei1ee13:publisher-url7:http://19:publisher-url.utf-87:http://e
Table)技术时,对等体之间的互相发现需要通过Tracker服务器,因此如果没有Tracker服务器,BT客户端就不会获得新加入的用户的信息,速度会受很大影响甚至根本无法下载。现在很多BT软件采用DHT技术的Kad算法,可以不通过服务器实现对等体之间的相互定位与发现,例如电驴就采用了kad网络。
BitTorrent
是一种分发文件的协议。它通过URL来识别内容,并且可以无缝的和web进行交互。它基于HTTP协议,它的优势是:如果有多个下载者并发的下载同一个文件,那么,每个下载者也同时为其它下载者上传文件,这样,文件源可以支持大量的用户进行下载,而只带来适当的负载的增长。(译注:因为大量的负载被均衡到整个系统中,所以提供源文件的机器的负载只有少量增长)
一个普通的web服务器
一个静态的“元信息”文件
一个跟踪(tracker)服务器
终端用户的web浏览器
终端下载者
要提供文件共享,那么一台主机需要执行以下步骤:
?运行一个 tracker服务器(或者,已经有一个tracker服务器在运行了也可以)
?运行一个web服务器,例如apache,或者已经有一个web服务器在运行了。
?在web服务器上,将文件扩展名.torrent 和MIME类型 application/x-bittorrent关联起来(或者已经关联了)
?根据 tracker服务器的 URL 和要共享的文件来创建一个“元信息”文件(.torrent)。
?将“元信息”文件发布到web服务器上
?在某个web页面上,添加一个到“元信息”文件的链接。
?运行一个已经拥有完整文件的下载者(被成为’origin’,或者’seed’,种子)
?安装 BT(或者已经安装)
?访问提供 .torrent 文件的web服务器
?点击到 .torrent 文件的链接(译注:这时候,bt会弹出一个对话框)
?选择要把下载的文件保存到哪里?或者是一次断点续传
?等待下载的完成。
?结束bt程序的运行(如果不主动结束,那么bt会一直为其它人提供文件上传)
网站负责提供一个静态的文件,而把BT辅助程序(客户端)放在客户端机器上。
Trackers从所有下载者处接收信息,并返回给它们一个随机的peers的列表。这种交互是通过HTTP或HTTPS协议来完成的。
下载者周期性的向tracker登记,使得tracker能了解它们的进度;下载者之间通过直接连接进行数据的上传和下载。这种连接使用的是 BitTorrent 对等协议,它基于TCP。
Origin只负责上传,从不下载,因为它已经拥有了完整的文件。Origin是必须的。
对于字符串,首先是一个字符串的长度,然后是冒号,后面跟着实际的字符串,例如:4:spam,就是“ spam”
整数编码如下,以 ‘i’ 开始,然后10进制的整数值,最后以’e’结尾。例如,i3e表示3,I-3e表示-3。整数没有大小限制。I-0e是无效的。除了 i0e外,所以以0起始的整数都无效。I0e当然表示0。
列表编码如下,以’l’开始,接下来是列表值的编码(也采用bencoded编码),最后以’e’结束。例如:l4:spam4:eggse 表示 [‘spam’, ‘eggs’]。
字典编码如下,以’d’开始,接下来是可选的keys和它对应的值,最户以’e’结束。例如:d3:cow3:moo4:spam4:eggse,表示{‘cow’:’moo’,’spam’:’eggs’},而d4:spaml1:al:bee
表示 {‘spam’:[‘a’,’b’]}。键值必须是字符串,而且已经排序(并非是按照字母顺序排序,而是根据原始的字符串进行排序)。
一个字符串,在保存文件的时候,作为一个建议值。仅仅是个建议而已,你可以用别的名字保存文件。
Piece length:
为了更好的传输,文件被分隔成等长的片断,除了最后一个片断以外,这个值就是片断的大小。片断大小几乎一直都是2的幂,最常用的是 256k
Pieces:
一个长度为20的整数倍的字符串。它将再被分隔为20字节长的字符串,每个子串都是相应片断的hash值。
如果是单个文件,那么length是该文件的长度。
Length:文件长度
Path:子目录名称的列表,列表最后一项是文件的实际名称。(不允许出现列表为空的情况)。
Name:在单文件情况下,name是文件的名称,而在多文件情况下,name是目录的名称。
Tracker GET requests have the following keys:
元文件中info部分的sha hash,20字节长。这个字符创几乎肯定需要被转义(译注:在URL中,有些字符不能出现,必须通过unicode进行编码)
下载者的id,一个20字节长的字符串。每个下载者在开始一次新的下载之前,需要随机创建这个id。这个字符串通常也需要被转义。
一个可选的参数,给出了peer的ip地址(或者dns名称?)。通常用在origin身上,如果它和tracker在同一个机器上。
peer所监听的端口。下载者通常在在 6881 端口上监听,如果该端口被占用,那么会一直尝试到 6889,如果都被占用,那么就放弃监听。
已经上载的数据大小,十进制表示。
已经下载的数据大小,十进制表示
该peer还有多少数据没有下载完,十进制表示。注意,这个值不能根据文件长度和已下载数据大小计算出来,因为很可能是断点续传,如果因为检查文件完整性失败而必须重新下载的时候,这也提供了一个机会。
一个可选的关键字,值是started、compted或者stopped之一(也可以为空,不做处理)。如果不出现该关键字,。在一次下载刚开始的时候,该值被设置为started,在下载完成之后,设置为completed。如果下载者停止了下载,那么该值设置为stopped。
reason,那么它对应的是一个字符串,用来解释查询失败的原因,其它关键字都不再需要了。否则,它必须有两个关键字:Interval:下载者在两次发送请求之间的时间间隔。Peers:一个字典的列表,每个字典包括以下关键字:Peer
id,Ip,Port,分别对应peer所选择的id、ip地址或者dns名称、端口号。注意,如果某些事件发生,或者需要更多的peers,那么下载者可能不定期的发送请求,
对等的两个连接是对称的,消息在两个方向上同样的传递,数据也可以在任何一个方向上流动。
一旦某个peer下载完了一个片断,并且也检查了它的完整性,那么它就向它所有的peers宣布它拥有了这个片断。
连接的任何一端都包含两比特的状态信息:是否choked,是否感兴趣。Choking是通知对方,没有数据可以发送,除非unchoking发生。Choking的原因以及技术后文解释。
interested,其实就是发一个消息过去,而另一个peer,要检查它是否应该给这个家伙发送数据,如果它对这个家伙是
unchoke,那么就可以给它发数据,否则还是不能给它数据)Interested状态必须一直被设置――任何时候。要用点技巧才能比较好的实现这个目的,但它使得下载者能够立刻知道哪些peers将开始下载。
后续的所有的整数,都采用big-endian 来编码为4个字节
在协议名称之后,是8个保留的字节,这些字节当前都设置为0。
接下来对元文件中的 info 信息,通过 sha1 计算后得到的 hash值,20个字节长。接收消息方,也会对 info 进行一个 hash 运算,如果这两个结果不一样,那么说明对方要的文件,并不是自己所要提供的,所以切断连接。
这就是握手过程
‘request’消息之间有潜在的联系(译注:因为通常有了request消息之后,才会响应‘piece’消息)。如果choke和unchoke消息发送的过于迅速,或者,传输速度变的很慢,那么可能会读到一些并不是所期望的片断。(
也就是说,有时候读到了一些片断,但这些片断并不是所想要的)
BT种子文件使用了一种叫bencoding的编码方法来保存数据。
bencoding有四种类型的数据:srings(字符串),integers(整数),lists(列表),dictionaries(字典)
编码规则如下: (1)strings(字符串)编码为:<字符串长度>:<字符串> 例如: 4:test 表示为字符串"test" 4:例子 表示为字符串“例子” 字符串长度单位为字节 没开始或结束标记 (2)integers(整数)编码为:i<整数>e
开始标记i,结束标记为e 例如: i1234e 表示为整数1234 i-1234e 表示为整数-1234 整数没有大小限制 i0e 表示为整数0 i-0e 为非法 以0开头的为非法如: i01234e 为非法 (3)lists(列表)编码为:l>e
开始标记为l,结束标记为e 列表里可以包含任何bencoding编码类型,包括整数,字符串,列表,字典。 例如: l4:test5:abcdee 表示为二个字符串["test","abcde"] (4)dictionaries(字典)编码为d>e
开始标记为d,结束标记为e 关键字必须为bencoding字符串 值可以为任何bencoding编码类型 例如: d3:agei20ee 表示为{"age"=20} d4:path3:C:"8:filename8:test.txte 表示为{"path"="C:"","filename"="test.txt"}
(5)具体文件结构如下:
全部内容必须都为bencoding编码类型。 整个文件为一个字典结构,包含如下关键字 announce:tracker服务器的URL(字符串) announce-list(可选):备用tracker服务器列表(列表) creation date(可选):种子创建的时间,Unix标准时间格式,从1970 1月1日 00:00:00到创建时间的秒数(整数) comment(可选):备注(字符串) created by(可选):创建人或创建程序的信息(字符串) info:一个字典结构,包含文件的主要信息,为分二种情况:单文件结构或多文件结构 单文件结构如下: length:文件长度,单位字节(整数) md5sum(可选):长32个字符的文件的MD5校验和,BT不使用这个值,只是为了兼容一些程序所保留!(字符串) name:文件名(字符串) piece length:每个块的大小,单位字节(整数) pieces:每个块的20个字节的SHA1 Hash的值(二进制格式) 多文件结构如下: files:一个字典结构 length:文件长度,单位字节(整数) md5sum(可选):同单文件结构中相同 path:文件的路径和名字,是一个列表结构,如"test"test.txt 列表为l4:test8test.txte name:最上层的目录名字(字符串) piece length:同单文件结构中相同 pieces:同单文件结构中相同 (6)实例: 用记事本打开一个.torrent可以看来类似如下内容 d8:announce35:http://www.manfen.net:7802/announce13:creation datei1076675108e4:infod6:lengthi17799e4:name62:MICROSOFT.WINDOWS.2000.AND.NT4.SOURCE.CODE-SCENELEADER.torrent12:piece 很容易看出
announce=http://www.manfen.net:7802/announce creation date=1076675108秒(02/13/04 20:25:08) 文件名=MICROSOFT.WINDOWS.2000.AND.NT4.SOURCE.CODE-SCENELEADER.torrent 文件大小=17799字节 文件块大小=32768字节 |