Gnutella 协议

Gnutella协议中文版

Gnutella2是一份关于发布检索的协议。虽然Gnutella协议也支持传统的客户端/中心服务器的检索规范,但Gnutella协议更主要是支持点对点的,没有中心的检索。在这个模型中,所有的客户端也是一个服务器,同样反之亦然。这些所谓的Gnutella客户机正常情况下执行联系服务器和客户端的任务。他们提供客户端的接口使用户可以发出查询请求和看检索结果。同时他们也接收来自其它客户机的请求,检查他们自己的数据中匹配的部分,返回可用的结果。因为具有天然的分布性,一个执行Gnutella协议的网络是具有高度容错的,比如当部分客户机离线,网络服务不会被中断。

协议定义

  Gnutella协议定义客户机通过网络通讯的方式。其中包括定义了通过客户机进行数据通讯的描述符号集和内部客户机相互交互的一些规则。以下是定义的内容:

描述定义

指令

说明

Ping指令

用于激活发现网络上的客户机。一个客户机收到一个Ping的描述符表示希望回应一个或多个Pong描述符。

Pong指令

用于回应Ping。包括一个被连接的Gnutella客户机的地址和他能提供的数据供共享的信息。

Query指令

首要的分布式网络检索机制。一个客户机收到一个Query描述符后,如果在自己的数据集中发现一个匹配的数据将回应一个QueryHit

QueryHit指令

用于回应Query。这个描述符提供足够的信息来获取匹配Query请求的数据。

Push指令

一个用于允许防火墙中的客户端向网络提供基于文件的数据文件的机制。

  一个Gnutella客户机通过与另一个当前在网络中的客户机建立连接来使自己与网络相连。获取另一个客户机的地址不在这个协议的定义中,这里将不作描述(客户机地址缓冲保存是当前用强制方式自动获得Gnutella客户机地址的方式)。

  一旦网络上的另一个客户机的地址被获取,一个与该客户机的TCP/IP连接将被创建,以下的Gnutella连接请求字符串(ASCII编码)将被发送:

GNUTELLA CONNECT/<protocol version string>\n\n

<protocol version string>在当前版本中定义文ASCII字符串“0.4”(或者,同样可以是” \x30\x2e\x34”)是指当前的协议规范的版本。

注明:

1.这份文件代表事实的标准的Gnutella0.4协议,然而,有些协议的实现方案扩展了组成协议的描述符并在Gnutella网络上传送描述符时附加了额外的规则。已知的协议扩展列在本文件末尾的附录中,但可能有些在实际应用中出现的变量没有在文件中说明。

2.Gnutella的发音是new-tella

一个客户机愿意接受连接的话必须回应

GNUTELLA OK\n\n

任何其它的回应表示客户机不愿意接受连接。一个客户机拒绝连接可能有以下几种原因-一个客户机的连接缓冲池已经满了,或者他不支持同样版本的协议来作为一个响应客户机。仅此举例。

一旦一个客户机成功连接到网络上,他与其它客户机通讯通过发送和接收Gnutella协议描述字。每一个描述符前都有一个以下字节结构的描述头,如下所示:

注意:1.以下所有结构内字符次序都是在低位在后,除非另作说明。

2.所有的地址都是IP4格式

例如:

表示的IP地址为:208.17.50.4

Descriptor Header

DescriptorID网络描述符:16个字节的字符串唯一标示网络的描述符号。

Payload Descriptor负载描述符:

0x00 = Ping
0x01 = Pong
0x40 = Push
0x80 = Query
0x81 = QueryHit

TTL生存期:描述字符在删除前在Gnutella网络中向前传递的次数。每个客户端在将包向前传递前将TTL减一。当TTL等于0,描述符将不再被向前传递。

Hops描述符被向前传递的次数:作为一个描述符向前传递,头部的TTLHops字必须满足以下条件:

TTL(0) = TTL(i) + Hops(i)

Payload Length负载长度:表示紧接着头部后面的描述符部分的长度。下一个描述符头后的从头部算起的Payload Length字节数,也就是没有间隔或保留字在Gnutella的数据流中。

TTL是网络中唯一的描述过期的机制。客户机应该仔细检查收到的描述符的TTL区并必要时减少它的值。滥用TTL区将会导致没有必要的网络阻塞和差劲的网络性能。

Payload Length区是客户机查找输入流中下一个描述符的唯一可靠方式。Gnutella协议不提供一个监视字符串或任何其它的描述符同步的方式。因此,客户机应该严格保证每一个收到的描述符的Payload Length区的有效性(至少为固定长度的描述符)。如果一个客户机不能和输入的流同步,它应该断掉与这个输入流有关的来自发送方的客户机,不管是产生这个流还是向前传递这个流的无效的客户机。紧接着描述头的是一个有效装载包含以下之一的描述符:

Ping (0x00)

Ping描述符没有相关的有效装载和数据长度为0。一个Ping只是简单地有一个描述头表述,它的有效装载区是0x00和装载长度区为0x00000000

一个客户机用Ping描述符
Pong (0x01)

Port

Port:同意接收响应的客户机的端口

IP Address:响应的客户机的地址(此数据区高位字节在后)

Number of Files Shared:本机共享文件的数量

Number of Kilobytes Shared:本机所有共享文件的空间大小,以K为单位

Query (0x80)

字节偏移0 1 2 …

Minimum Speed :最小响应速度,响应的客户机的速度必须在此速度之上(K/秒为单位)

Search criteria:查询关键字,一个零结尾的字符串。这个字符串的最大长度由描述头的Payload Length负载长度规定。

QueryHit (0x81)

Number of Hits:符合搜索条件的结果数

Port:能接受连接的客户机的端口

IP Address :响应客户机的地址(此数据区高位字节在后)

Speed :响应客户机的连线速度(以K/秒为单位)

Result Set :响应查询的结果集。其中包含一个Number_of_Hits的部分,其中每个都包含以下结构

  File Index:一个数字,由响应的客户机指定,用来唯一标示响应的文件结果

  File Size:与File index相符的文件的大小

  File Name:已双零结尾的与File index相符的文件的名字

Result Set的长度由描述头的Payload Length负载长度规定。

Servent Identifier:一个16位的字符串用来唯一标示网络上的客户机。功能上用来标示客户机的网络地址。用在Push指令上。

QueryHit指令只有在收到一个Query指令后响应才发出。一个客户机只有在它严格符合查询关键字时才对一个Query指令进行响应。

Push (0x40)

Servent Identifier:一个16位的字符串用来唯一标示网络上的客户机,该客户机请求下载带有File_Index的文件。

File Index:下载目标客户机的文件的唯一标识,初始化的客户机应该根据返回的QueryHit指令的File_Index中的标识设置。

IP Address:下载带有File_Index的文件的客户机的地址(此数据区高位字节在后)

Port:下载带有File_Index的文件的客户机的端口

描述符路由

Gnutella网络的点对点本质要求客户机合适地路由网络(包括查询、查询响应、推送文件请求等)。一个好的客户机应该根据以下的规则路由协议的描述符:

1.    Pong描述符应该只沿进入的Ping描述符的路径发送。这样可以保证只有路由Ping描述符的客户机将看到的Pong描述符作为响应返回。一个客户机如果收到一个带有描述符IDnPong描述符,但没有看到一个带有描述符ID=nPing描述符的,应该把Pong描述符从网络中删除。

2.    QueryHit描述符应该只沿进入的Query描述符的路径发送。这样可以保证只有路由Query描述符的客户机将看到的Pong描述符作为响应返回。一个客户机如果收到一个带有描述符IDnQueryHit描述符,但没有看到一个带有描述符ID=nQuery描述符的,应该把Pong描述符从网络中删除。

3.    Push描述符应该只沿进入的Query描述符的路径发送。这样可以保证只有路由QueryHit描述符的客户机将看到的Pong描述符作为响应返回。一个客户机如果收到一个带有描述符IDnPush描述符,但没有看到一个带有描述符ID=nQueryHit描述符的,应该把Push描述符从网络中删除。一个客户机如果收到一个带有客户机IDnPush描述符,但没有看到一个带有客户机ID=nQueryHit描述符的,应该把Push描述符从网络中删除。Push描述符通过客户机ID进行路由,而不是通过描述符ID

4.    一个客户机将通过进来的PingQuery描述符向前到达所有与它直接相连的客户机,但负责传递进入的PingQuery的那些客户机除外。

5.    一个客户机将在它向前传递描述符到与它直接相连的客户机前,减少一个描述头的TTL区,并增加Hops区。如果,减少头部的TTL区后,TTL中的值等于0,描述符将不再向前传递到任何连接。

6.    一个客户机收到一个与它之前接收过的描述符具有相同有效描述符和描述符ID的描述符,应该避免再向前传递这个描述符到其它的连接。它已经接收过这样一个描述符,再把它传递出去只会浪费带宽。

 

文件下载

  一旦一个客户机收到一个QueryHit描述符,它将初始化直接下载描述符的结果集其中的一个文件。文件将不通过Gnutella的网络进行下载,一个源客户机和目标客户机直接建立连接进行数据的传输。文件数据从来不会通过Gnutella网络进行传送。

文件下载协议是HTTP协议。初始化下载的客户机发送一个请求字符串到目标客户机,格式如下:

GET /get/<File Index>/<File Name>/ HTTP/1.0\r\n
Connection: Keep-Alive\r\n
Range: bytes=0-\r\n
User-Agent: Gnutella\r\n3\r\n

这里的<File Index><File Name>是一个QueryHit描述符结果集中的File Index/File Name对中的其中之一。例如,如果QueryHit描述符中包含入口

File Index

2468

File Size

4356789

File Name

Foobar.mp3\x00\x00

那么一个通过这个入口下载这个文件的描述请求应该如下:
GET /get/2468/Foobar.mp3/ HTTP/1.0\r\n
Connection: Keep-Alive\r\n
Range: bytes=0-\r\n
User-Agent: Gnutella\r\n

服务器收到下载请求将回应于HTTP1.0兼容的头,例如:
HTTP 200 OK\r\n
Server: Gnutella\r\n
Content-type: application/binary\r\n
Content-length: 4356789\r\n
\r\n

文件数据随后将被读出,包括HTTP响应中提供的Gontent-length中描述的字节数。

Gnutella协议提供HTTP规范中的参数的支持,所以一个中断的下载可以按照中断的位置重连。

防火墙后的客户机

  并非总是在初始化一个文件下载后都可以与Gnutella客户机建立直接连接。客户机可能在防火墙后并不允许通过它的Gnutella端口进入的连接。如果一个直接连接不能建立,客户机若想下载文件可能会请求共享文件的客户机采用推送方式来代替。一个客户机可以通过发送一个Push文件推送请求到发送QueryHit请求的客户机处来实现。作为Push请求目标的客户机(在客户机标志区标示一个Push的描述符)应该接收Push描述符,尝试建立一个新的TCP/IP连接到请求客户机(在Push描述符中标示有IP地址和端口)。如果直接连接不能建立,那么可能发起Push请求的客户机自己也在防火墙后。这种情况,文件传输将不能进行。

  如果一个直接连接可以从防火墙后的客户机建立到发起Push请求的客户机,防火墙后的客户机应该立刻发送以下的:

GIV <File Index>:<Servent Identifier>/<File Name>\n\n

这里的<File Index>:<Servent Identifier>Push请求头中的的文件索引和客户机标示,<File Name>是本地文件表中文件索引为<File Index>的文件。客户机收到GIV请求头(Push请求者)应该从头中取出<File Index><File Name>并构造一个如下的HTTP GET请求:
GET /get/<File Index>/<File Name>/ HTTP/1.0\r\n
Connection: Keep-Alive\r\n
Range: bytes=0-\r\n
User-Agent: Gnutella\r\n3
\r\n

余下的下载过程和上面所述的文件下载内容一致。

可允许的用户-代理字符串由HTTP标准定义。客户机开发者不能对这里使用的值做自己的假定。其中的值“Gnutella”只是用来演示举例而已。

附录1 Gnutella 协议扩展

扩展的Query Hit( 描述更新03/15/2001) 首先以BearShare v1.3.0 为例,扩展的QueryHit 描述符通过在原先的Gnutella QueryHit 描述符的Servent ID标识符和双null结尾的文件名之间放置额外的数据进行扩展。一个扩展的QueryHit 描述符将有以下的结构:

QueryHit (0x81)

BearShareTrailer区的机构如下:

Trailer

Vendor Code

四个大小写敏感的字母代表一个供应商代号。已经确认的代号如下:

Open Data Size

包含Open Data 域的长度(以字节计算)

Open Data

包含2个单字节的标志域,,具有以下的数据分布和次序

flags

flagUploadSpeed=1 当且仅当flags2中的flagUploadSpeed标志有意义时

flagHaveUploaded=1 当且仅当flags2中的flagHaveUploaded标志有意义时

flagBusy=1 当且仅当flags2中的flagBusy标志有意义时

flagPush=1 当且仅当客户机处于防火墙后或者未接受进入的连接时

r=保留字,将来使用

flags2

flagUploadSpeed=1 当且仅当QueryHit描述符中的Speed域包含上10次上传的最高平均速率时

flagHaveUploaded=1 当且仅当客户机成功上传至少一次文件时

flagBusy=1 当且仅当客户机的上传插槽满时

flagPush=1当且仅当flags中的flagPush标志有意义时

r=保留字,将来使用

Private Data

未作正式文档说明的BearShare的数据,这个区域的数据长度可以根据以下决定

Query Hit Descriptor Payload Size-(Open Data Size+4+1)

开发者处理扩展的一种方式为

(1) 意识到一进来的QueryHit 可以或者可能不包含附加数据,在结果下落之后,和在Servent 标识符以前。没有完整说明适合是可能在场的字节的数量存在,或者他们内容。

使用负载长度领域并且算字节,当他们被从这条溪读确定是否扩展字节在场。

(2) 如果他们是,从排除16 字节Servent 标识符的输入流中读取数据。

(3) 象往常一样处理QueryHit

Gnotella

  Gnotella客户端软件的版本至早在0.73(2000730发布)时把额外的数据放置在QueryHit 描述符中。根据Gnutella 0.4 协议说明,每一个QueryHit 描述符中的结果集要用双nul结尾。 Gnotella 可以把额外的数据安置在两nuls 之间。虽然它的精确的数据格式未知,但是这些数据代表比特率,采样率,和MP3 文件的播放时间,通过结果集的入口处来描述。如果结果集入口处所描述的不是一个MP3文件的话,没有数据被放置在nuls 之间。

  在没有不利的结果的情况下,一些客户机通过简单得抛弃nuls之间的数据来扩展GnotellaQueryHit描述符。另外的一些客户机可能,由于发现结果中的数据没有向预期那样结束,不能正确得读取描述符。一旦读错,引起后来一系列的误处理,并且它的连接可能会断开。

Gnutella节点的结构及进程处理

Gnutella节点的结构:
  
在一个Gnutella节点中最多有四类并发的进程存在:连接处理器(Connection)、协调进程(Coordination Instance)、下载进程(Download Instance)和上载进程(Upload Instance)
   
连接处理器负责管理所有连接的建立工作,包括自身发起的以及其它节点发起的连接。每个协调进程则负责处理某一条具休的用于握手、查询的连接的活动以及状态。当这个节点需要上载文件或者下载文件时,一条相应的上载连接或下载连接将被建立,该连接的所有活动、状态也将由一个上载进程或下载进程来进行监控。
   
值得注意的是,在同一时间,节点中有且仅有一个连接处理器,但可能会有0个或者多个协调进程、下载进程或上载进程。

Gnutella节点的连接处理器:
   
连接处理器共有六个状态:offlinewaiting for ackping online searchcoonection request。当Gnutella的节点同一个或多个其它节点建立连接时,它的状态就是online。同时连接处理器还存有一个全局变量ccount,用来保存当前的连接数。

Gnutella节点的协调进程:
   
协调进程有两个状态waiting for messageterminate
   
当一个用于节点间握手通讯的连接建立起来的时候,一个直辖市进程同时也被创建,用于管理该新连接的所有活动和状态。
   
协调进程在创建初期处于wait for message状态,在这个状态下,协调进程会收到五种消息,它们分别是PingQuery(Search)PongQueryHit(Search Result)Push
   
当协调进程收到一个Ping消息时,首先返回一个Pong消息给发来Ping消息的节点,然后判断Ping消息中的TTL字段的值是不是大于0,如果大于零,则将TTL的值递减后转发给其它相邻的节点。在这个过程事,发出Ping消息的节点的全局标志和相应的返回 Pong信息的路由将被记录下来。
   
当协调进程收到一个Query消息时,该节点会判断其TTL值,对于大于0的,将TTL值递减后转发给其它相邻的节点。然后判断本地是否含有同Query消息匹配文件,如果有则返回相应的QueryHit消息。在这个过程中,发出Ping消息的节点的全局标志和相应的返回 Pong信息的路由将被记录下来。
   
当协调进程收到一个QueryHit消息时,首先通过消息中带的全局标志判断该消息是否响应了本节点发出的Query消息,如果不是,则按照记录下来的相应的返回QueryHit消息的路由转发该QueryHit消息。
   
当协调进程收到一个Push消息时,一条新的指向发出Push消息结点的连接将被建立,一个HttpGIV消息将被发送,当新的连接创建完成之后,该新连接将被移交给一个新的上载进程。
   
对于协调进程管理的连接两端的任意一方中断了连接,该协调进程就进入"terminate"状态
   

    Gnutella节点的下载进程
   
下载进程具有五个状态:connect to peerwait for filedownload filedownload finishedterminate
   
当本地节点请求下载远端节点的文件或者远端节点应本地的Push请求发送文件过来时,下载进程就被创建了。
   
下载进程在创建初期处于connect to peer状态。为了开始下载文件,本地节点发送包含有需要下载文件信息的HttpGET消息,同时将下载进程的状态转化为wait for file
   
当连接发生错误时,下载进程的状态会从wait for file直接转变成download finished。如果连接保持,并且开始接收从远端传来的数据,那么下载进程的状态会从wait for file转变为download file
   
当文件的内容源源不断的从远端发送过来,下载进程的状态将始终保持为download file,直到所有的文件信息都被传送过来,连接中没有数据包存在,下载进程的状态就转变为download finished
   
当本地节点或者远端节点的任意一方中断了连接,该下载进程就转入了terminate状态。


Gnutella
节点的上载进程
   
上载进程具有四个状态:send HTTP headerupload fileupload finishedterminate
   
当本地节点接收到远端节点的的Push信息,需要将本地文件传给远端节点时,上载进程就被创建了。
   
上载进程在创建初期处于send HTTP header状态。当收到远端发出的HttpGET消息,同时将上载进程的状态转化为upload file
   
当文件的内容源源不断的从发送给远端,下载进程的状态将始终保持为upload file,直到所有的文件信息都被传送完成,下载进程的状态就转变为upload finished
   
当本地节点或者远端节点的任意一方中断了连接,该下载进程就转入了terminate状态。 

 

你可能感兴趣的:(Gnutella 协议)