对于包是丢弃、广播或者是继续路由是通过设置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);