一個Gnutella客戶機通過與另一個當前在網路中的客戶機建立串連來使自己與網路相連。
一旦網路上的另一個客戶機的地址被擷取,一個與該客戶機的TCP/IP串連將被建立,以下的Gnutella串連請求字串(ASCII編碼)將被發送:
GNUTELLA CONNECT/<protocol version string>
Peercast定義:static const char *GNU_CONNECT = "GNUTELLA CONNECT/0.6";
一個客戶機願意接受串連的話必須回應
GNUTELLA OK
Peercast定義:static const char *GNU_OK = "GNUTELLA/0.6 200 OK";
一旦一個客戶機成功串連到網路上,他與其它客戶機通訊通過發送和接收Gnutella協議描述字。每一個描述符前都有一個以下位元組結構的描述頭,如下所示:
Descriptor Header
DescriptorID網路描述符:16個位元組的字串唯一標示網路的描述符號。
Payload Descriptor負載描述符:
0x00 = Ping
0x01 = Pong
0x40 = Push
0x80 = Query
0x81 = QueryHit
Peercast定義:
static const int GNU_FUNC_PING = 0;
static const int GNU_FUNC_PONG = 1;
static const int GNU_FUNC_QUERY = 128;
static const int GNU_FUNC_HIT = 129;
static const int GNU_FUNC_PUSH = 64;
TTL生存期:描述字元在刪除前在Gnutella網路中向前傳遞的次數。每個用戶端在將包向前傳遞前將TTL減一。當TTL等於0,描述符將不再被向前傳遞。
Hops描述符被向前傳遞的次數:作為一個描述符向前傳遞,頭部的TTL和Hops字必須滿足以下條件:
TTL(0) = TTL(i) + Hops(i)
Payload Length負載長度:表示緊接著頭部後面的描述符部分的長度。下一個描述符頭後的從頭部算起的Payload Length位元組數,也就是沒有間隔或保留字在Gnutella的資料流中。
TTL是網路中唯一的描述到期的機制。客戶機應該仔細檢查收到的描述符的TTL區並必要時減少它的值。濫用TTL區將會導致沒有必要的網路阻塞和差勁的網路效能。
Payload Length區是客戶機尋找輸入資料流中下一個描述符的唯一可靠方式。Gnutella協議不提供一個“監視”字串或任何其它的描述符同步的方式。因此,客戶機應該嚴格保證每一個收到的描述符的Payload Length區的有效性(至少為固定長度的描述符)。如果一個客戶機不能和輸入的流同步,它應該斷掉與這個輸入資料流有關的來自發送方的客戶機,不管是產生這個流還是向前傳遞這個流的無效的客戶機。緊接著描述頭的是一個有效裝載包含以下之一的描述符:
Peercast定義一個GnuPacket類來儲存包的資訊。
class GnuPacket
{
public:
unsigned char func; //描述符類型,包括Ping/Pong/Query/Hit/Push
unsigned char ttl; //生存周期
unsigned char hops; //記錄描述符被傳送的次數
unsigned int len; //資料長度
GnuID id; //描述符ID:由16位元組組成,用於唯一標識一個網路描述符
char data[MAX_DATA]; //實際資料
};
Ping (0x00)
Ping描述符沒有相關的有效裝載和資料長度為0。一個Ping只是簡單地有一個描述頭表述,它的有效裝載區是0x00和裝載長度區為0x00000000。
一個客戶機用Ping描述符
Peercast實現:void GnuPacket::initPing(int t)
只需簡單設定ping描述頭表述和TTL值(初始化為t)及hop值(初始化為0),並產生校正ID
Pong (0x01)
Port
Port:同意接收響應的客戶機的連接埠
IP Address:響應的客戶機的地址(此資料區高位位元組在後)
Number of Files Shared:本機共用檔案的數量
Number of Kilobytes Shared:本機所有共用檔案的空間大小,以K為單位
Peercast實現:void GnuPacket::initPong(Host &h, bool ownPong, GnuPacket &ping)
data.writeShort(h.port); // 寫入響應的客戶機連接埠
data.writeLong(SWAP4(h.ip)); // 響應的客戶機的地址
data.writeLong(chanMgr->numChannels()); //本機頻道的數量
data.writeLong(servMgr->totalOutput(false)); // 本機總輸出資料量大小,以K為單位
Query (0x80)
位元組位移0 1 2 …
Minimum Speed :最小響應速度,響應的客戶機的速度必須在此速度之上( 以K/秒為單位)
Search criteria:查詢關鍵字,一個零結尾的字串。這個字串的最大長度由描述頭的Payload Length負載長度規定。
Peercast實現:void GnuPacket::initFind(const char *str, XML *xml, int maxTTL)
mem.writeShort(0); // 最小響應速度為0
mem.write((void *)str,slen+1); // 寫入要搜尋的字串
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指令進行響應。
Peercast定義:bool GnuPacket::initHit(Host &h, Channel *ch, GnuPacket *query, bool push, bool busy, bool stable, bool tracker, int maxttl)
mem.writeChar(1); // 能接受串連的客戶機的連接埠
mem.writeShort(h.port); // 能接受串連的客戶機的連接埠
mem.writeLong(SWAP4(h.ip)); // 響應客戶機的地址(此資料區高位位元組在後)
mem.writeLong(0); // index
mem.writeShort(ch->getBitrate()); // 響應客戶機的連線速度(以K/秒為單位)
mem.writeShort(ch->localListeners()); // 聽眾數目
Push (0x40)
Servent Identifier:一個16位的字串用來唯一標示網路上的客戶機,該客戶機請求下載帶有File_Index的檔案。
File Index:下載勘探機的檔案的唯一標識,初始化的客戶機應該根據返回的QueryHit指令的File_Index中的標識設定。
IP Address:下載帶有File_Index的檔案的客戶機的地址(此資料區高位位元組在後)
Port:下載帶有File_Index的檔案的客戶機的連接埠
Peercast定義:void GnuPacket::initPush(ChanHit &ch, Host &sh)
data.write(ch.packetID.id,16); //一個16位的字串用來唯一標示網路上的客戶機,該客戶機請求下載帶有Channel_Index的檔案。
data.writeLong(ch.index); //下載勘探機的頻道的唯一標識
data.writeLong(SWAP4(sh.ip)); // 下載帶有Channel_Index的頻道的客戶機的地址(此資料區高位位元組在後)
data.writeShort(sh.port); // 下載帶有Channel_Index的頻道的客戶機的連接埠