📄 queryroutetable.cs
字号:
// QueryRouteTable.cs// Copyright (C) 2002 Matt Zyzik (www.FileScope.com)// // This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.// // This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.// // You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USAusing System;using System.Timers;using System.Collections;using ICSharpCode.SharpZipLib.Zip.Compression;namespace FileScope.Gnutella2{ /// <summary> /// Class dedicated to G2 query hash/route tables. /// In G2 each bit represents a word hash value, 0 for filled, 1 for empty. /// </summary> public class QueryRouteTable { //our local query hash table public static BitArray ourQHT = null; //all leafs and I public static BitArray aggregateQHT = null; //send table every time qhtUpdate is true and/or send aggregate table once in a while, when hub public static bool qhtUpdate = false; public static GoodTimer qhtSend = new GoodTimer(20000); public static void PrepareTable() { if(ourQHT != null) { lock(ourQHT) ourQHT = new BitArray(1048576, true); } else { ourQHT = new BitArray(1048576, true); qhtSend.AddEvent(new ElapsedEventHandler(qhtSend_Tick)); qhtSend.Start(); } } public static void AddWords(string filename) { lock(ourQHT) { string[] words = Keywords.GetKeywords(ref filename); //set these hashes in the table to "not empty" for(int x = 0; x < words.Length; x++) { ourQHT[Hashing.Hash(ref words[x], words[x].Length, 20)] = false; if(words[x].Length >= 5) { ourQHT[Hashing.Hash(ref words[x], words[x].Length-1, 20)] = false; ourQHT[Hashing.Hash(ref words[x], words[x].Length-2, 20)] = false; } } } } public static void AddHash(ref string hash) { lock(ourQHT) {#if DEBUG// System.Diagnostics.Debug.WriteLine("g2 qht AddHash: " + hash);#endif string urnSha1 = "urn:sha1:" + hash; ourQHT[Hashing.Hash(ref urnSha1, urnSha1.Length, 20)] = false; } } public static void SendQRT() { lock(ourQHT) qhtUpdate = true; } static void qhtSend_Tick(object sender, ElapsedEventArgs e) { lock(ourQHT) { if(Stats.Updated.Gnutella2.ultrapeer) { /* * right now we are just taking tables our size n=20; later we'll move on to this: * * qhts of n > 30 or n < 5 should be blocked * making the aggregate table: * let ndiff = difference in n bits * 1. if we're adding some large table, foreach value: shift to the right by ndiff and set that value in table to true * 2. if we're adding some small table, foreach value: shift to left by ndiff and set pos equal to that; then for(int count=0; count < (2 to power of ndiff); count++, pos++) and set those pos values in table to true */ //if we're ultrapeer, we'll send aggregate tables every once in a while, regardless of qhtUpdate if(GUID.rand.Next(0, 3) == 0) { //System.Diagnostics.Debug.WriteLine("start qht combine"); aggregateQHT = (BitArray)ourQHT.Clone(); foreach(Sck sck in Sck.scks) if(sck != null) if(sck.mode == G2Mode.Leaf && sck.inQHT != null) { if(sck.inQHT.Length == aggregateQHT.Length) { //sck.inQHT is unaffected, only aggregateQHT changes in this operation aggregateQHT.And(sck.inQHT); } else System.Diagnostics.Debug.WriteLine("g2 leaf's qht size: " + sck.inQHT.Length.ToString()); } //System.Diagnostics.Debug.WriteLine("end qht combine"); foreach(Sck sck in Sck.scks) if(sck != null) if(sck.state == Condition.hndshk3 && sck.mode == G2Mode.Ultrapeer && !sck.browseHost && !sck.webCache) FillWithPatches(sck); } } else { //as a leaf node, we'll just send our table on every update if(qhtUpdate) { qhtUpdate = false; foreach(Sck sck in Sck.scks) if(sck != null) if(sck.state == Condition.hndshk3 && !sck.browseHost && !sck.webCache) FillWithPatches(sck); } } commonOldQHT = null; } } public static int maxPatchSize = 1024; //common/reoccuring patch related variables static BitArray commonOldQHT = null; static int commonDataSizeDeflate; static int commonDataSizeNonDeflate; static byte[] commonDataDeflate = null; static byte[] commonDataNonDeflate = null; /// <summary> /// Update sck with our new QHT. /// </summary> public static void FillWithPatches(Sck sck) { lock(ourQHT) { bool withReset; bool deflate; int dataSize; byte[] data; //latest qht that we want the host to have BitArray baToSend; if(Stats.Updated.Gnutella2.ultrapeer) { if(aggregateQHT == null) aggregateQHT = (BitArray)ourQHT.Clone(); baToSend = aggregateQHT; } else baToSend = ourQHT; /* * check to see if we already have a patch set ready to update this host * this happens if we're updating several ultrapeers in a row and we can use a common patch set for all of them */ if(sck.lastQHT == commonOldQHT && commonOldQHT != null) { if(sck.deflateOut && commonDataDeflate != null) { withReset = false; deflate = true; dataSize = commonDataSizeDeflate; data = commonDataDeflate; sck.lastQHT = baToSend; goto begin; } if(!sck.deflateOut && commonDataNonDeflate != null) { withReset = false; deflate = false; dataSize = commonDataSizeNonDeflate; data = commonDataNonDeflate; sck.lastQHT = baToSend; goto begin; } } else if(sck.lastQHT != null) { //we have to clear these because a new commonOldQHT is gonna be set commonDataDeflate = null; commonDataNonDeflate = null; } //the "update" data to be sent in patches BitArray patches = new BitArray(baToSend); if(sck.lastQHT == null) { withReset = true; patches.Not(); sck.lastQHT = baToSend; } else { withReset = false; commonOldQHT = sck.lastQHT; patches.Xor(sck.lastQHT); sck.lastQHT = baToSend; } deflate = false; dataSize = patches.Length/8; data = new byte[dataSize]; patches.CopyTo(data, 0); //if we're not already sending deflate, we'll compress this data if(!sck.deflateOut) { byte[] dataCompressed = new byte[dataSize]; Deflater dfer = new Deflater(); dfer.Reset(); dfer.SetInput(data); dfer.Finish(); int num = dfer.Deflate(dataCompressed); //if the compression actually did something... if(num < data.Length) { data = dataCompressed; dataSize = num; deflate = true; } } //if we're sending an update in patches, perhaps all other updates will use these patches if(!withReset) { if(deflate) { commonDataSizeDeflate = dataSize; commonDataDeflate = data; } else { commonDataSizeNonDeflate = dataSize; commonDataNonDeflate = data; } } begin: //make a reset packet first, if necessary if(withReset) { OQHT oqhtreset = new OQHT(); oqhtreset.reset = true; sck.SendPacket(oqhtreset); } //patches int fragNum = 1; int fragCount; //fragments are sent 1kb each if(dataSize % maxPatchSize == 0) fragCount = (int)(dataSize / maxPatchSize); else fragCount = (int)(Math.Ceiling(dataSize / maxPatchSize + 1)); for(int x = 0; x < dataSize; x += maxPatchSize) { int len = Math.Min(maxPatchSize, dataSize-x); OQHT oqhtpatch = new OQHT(); oqhtpatch.reset = false; oqhtpatch.deflate = 0x00; if(deflate) oqhtpatch.deflate = 0x01; oqhtpatch.fragNum = (byte)fragNum; oqhtpatch.fragCount = (byte)fragCount; oqhtpatch.indexindata = x; oqhtpatch.lenofdata = len; oqhtpatch.data = data; sck.SendPacket(oqhtpatch); fragNum++; } } } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -