對於包是丟棄、廣播或者是繼續路由是通過設定R_TYPE類型來判斷的。
這是在GnuStream類中定義的,其中GnuStream完成收包、發包、處理包等操作
enum R_TYPE
{
R_PROCESS,
R_DEAD,
R_DISCARD, //丟棄
R_ACCEPTED, //接受
R_BROADCAST, //廣播
R_ROUTE, //路由
R_DUPLICATE, /複製
R_BADVERSION,
R_DROP
};
通過GnuStream::R_TYPE processPacket(GnuPacket &, Servent *, GnuID &)這個函數來執行處理進程,下面我們就這個函數進行具體的分析:
接收到包時,首先把TTL值遞減,HOP值遞增
in.ttl--;
in.hops++;
通過讀取包中的func值來判斷命令的類型(Ping/Pong/Query/QueryHit/Push),再執行相應處理
switch(in.func)
{
case GNU_FUNC_PING:
case GNU_FUNC_PONG:
收到Ping訊息應返回一個Pong訊息,並將訊息設定為廣播方式
GnuPacket pong;
pong.initPong(sh,true,in);
serv->outputPacket(pong,true)) //發出訊息
ret = R_BROADCAST;
收到Pong訊息應進行判斷,若是對本機發出的Ping訊息的回複,則表明與遠端主機建立串連;若否,則路由返回
if (servMgr->isReplyID(in.id))
{
servMgr->addHost(h,ServHost::T_SERVENT,sys->getTime()); //建立串連
ret = R_ACCEPTED;
}else
ret = R_ROUTE;
收到Query訊息,首先應把訊息廣播出去.然後若本機存在Query要尋找的頻道,則返回一個QueryHit訊息
注意Gnutella中的檔案在Peercast中相對應的是一個頻道,因此檔案名稱相對應的是頻道的ID
ret = R_BROADCAST;
numHits = chanMgr->findChannels(info,hits,16); //info是一個頻道資訊結構,此時儲存著Query訊息中尋找的頻道資訊
for(int i=0; i<numHits; i++)
{
GnuPacket hit;
if (hit.initHit(sh,hits[i],&in,push,busy,stable,tracker,in.hops))
serv->outputPacket(hit,true);
}
收到QueryHit訊息,則表示頻道已找到,加入收聽.若位於防火牆後面,則加上PUSH訊息
if (hit.firewalled) strcat(flstr,"Push,");
readHit(data,hit,in.hops,in.id)
其中readHit()中加入收聽的執行語句為
if (info.id.isSet())
{
if (!chanMgr->findHitList(info))
chanMgr->addHitList(info);
}
收到Push訊息,則新分配servent類,按GIV方式進行傳送
Servent *s = servMgr->allocServent();
if (s)
s->initGIV(h,c->info.id);