/// <summary> /// Returns what contact is blocking insertion (least promoted), or null if no contact is. /// </summary> /// <param name="toAdd">The node to add</param> /// <returns>The first element of the bucket</returns> public Contact Blocker(ID toAdd) { int bucket = BucketFor(toAdd); lock(buckets[bucket]) { // Nobody can move it while we're getting it if(buckets[bucket].Count < BUCKET_SIZE) { return null; } else { return buckets[bucket][0]; } } }
/// <summary> /// Make a new bucket list, for holding node contacts. /// </summary> /// <param name="ourID">The ID to center the list on.</param> public BucketList(ID ourID) { this.ourID = ourID; buckets = new List<List<Contact>>(NUM_BUCKETS); accessTimes = new List<DateTime>(); // Set up each bucket for(int i = 0; i < NUM_BUCKETS; i++) { buckets.Add(new List<Contact>(BUCKET_SIZE)); accessTimes.Add(default(DateTime)); } }
/// <summary> /// Determines the least significant bit at which the given ID differs from this one, from 0 through 8 * ID_LENGTH - 1. /// PRECONDITION: IDs do not match. /// </summary> /// <param name="other">The ID to compare to</param> /// <returns>The least significant bit where can be found the difference</returns> public int DifferingBit(ID other) { ID differingBits = this ^ other; int differAt = 8 * ID_LENGTH - 1; // Subtract 8 for every zero byte from the right int i = ID_LENGTH - 1; while(i >= 0 && differingBits.data[i] == 0) { differAt -= 8; i--; } // Subtract 1 for every zero bit from the right int j = 0; // 1 << j = pow(2, j) while(j < 8 && (differingBits.data[i] & (1 << j)) == 0) { j++; differAt--; } return differAt; }
/// <summary> /// Do we have the specified value for the given key? /// </summary> /// <param name="key"></param> /// <param name="hash"></param> /// <returns></returns> public bool Contains(ID key, ID hash) { lock(store) { return store.ContainsKey(key) && store[key].ContainsKey(hash); } }
/// <summary> /// Returns what bucket an ID maps to. /// PRECONDITION: ourID not passed. /// </summary> /// <param name="id">The id to check</param> /// <returns>The bucket number</returns> private int BucketFor(ID id) { return(ourID.DifferingBit(id)); }
/// <summary> /// Remove a contact. /// </summary> /// <param name="toRemove">The identificator od the contact to remove</param> public void Remove(ID toRemove) { int bucket = BucketFor(toRemove); lock(buckets[bucket]) { // Nobody can move it while we're removing it for(int i = 0; i < buckets[bucket].Count; i++) { if(buckets[bucket][i].NodeID == toRemove) { buckets[bucket].RemoveAt(i); return; } } } }
/// <summary> /// Return the number of nodes in the network closer to the key than us. /// This is a guess as described at http://xlattice.sourceforge.net/components/protocol/kademlia/specs.html /// </summary> /// <param name="key">The key to analize</param> /// <returns>the number of nodes found</returns> public int NodesToKey(ID key) { int j = BucketFor(key); // Count nodes in earlier buckets int inEarlierBuckets = 0; for(int i = 0; i < j; i++) { inEarlierBuckets += buckets[i].Count; } // Count closer nodes in actual bucket int inActualBucket = 0; lock(buckets[j]) { foreach(Contact c in buckets[j]) { if((c.NodeID ^ ourID) < (key ^ ourID)) { // Closer to us than key inActualBucket++; } } } return inEarlierBuckets + inActualBucket; }
/// <summary> /// See if we have a contact with the given ID. /// </summary> /// <param name="toCheck">The ID to find into the structure</param> /// <returns>true if the contact is found into the structure</returns> public bool Contains(ID toCheck) { return this.Get(toCheck) != null; }
/* IList<Uri> Get(ID tag_id) { return new List<Uri>(); } */ public DateTime GetPublicationTime(ID tag_id, Uri uri) { return new DateTime(); }
/// <summary> /// Gets a list of all value hashes for the given key /// It's a copy, iterate all you want. /// </summary> /// <param name="key"></param> /// <returns></returns> public IList<ID> GetHashes(ID key) { List<ID> toReturn = new List<ID>(); lock(store) { if(store.ContainsKey(key)) { foreach(ID hash in store[key].Keys) { toReturn.Add(hash); } } } return toReturn; }
/// <summary> /// Get a particular value by key and hash, or null. /// </summary> /// <param name="key"></param> /// <param name="hash"></param> /// <returns></returns> public string Get(ID key, ID hash) { lock(store) { if(this.Contains(key, hash)) { File.ReadAllText(PathFor(key, hash)); } } return null; }
/// <summary> /// Get all data values for the given key, or an empty list. /// </summary> /// <param name="key"></param> /// <returns></returns> public IList<string> Get(ID key) { List<string> toReturn = new List<string>(); lock(store) { if(ContainsKey(key)) { foreach(ID hash in store[key].Keys) { // Load the value and add it to the list toReturn.Add(File.ReadAllText(PathFor(key, hash))); } } } return toReturn; }
public bool ContainsUrl(ID tag_id, Uri uri) { return false; }
public bool ContainsTag(ID tag_id) { return false; }
/// <summary> /// Do we have any data for the given key? /// </summary> /// <param name="key"></param> /// <returns></returns> public bool ContainsKey(ID key) { return store.ContainsKey(key); }
/// <summary> /// Return a list of the BUCKET_SIZE contacts with IDs closest to /// target, not containing any contacts with the excluded ID. /// </summary> /// <param name="target">The target to find the close node to</param> /// <param name="excluded">The excluded ID</param> /// <returns>The list of contacts found</returns> public List<Contact> CloseContacts(ID target, ID excluded) { return CloseContacts(NUM_BUCKETS, target, excluded); }
/// <summary> /// Returns a list of the specified number of contacts with IDs closest /// to the given key, excluding the excluded ID. /// </summary> /// <param name="count">The number of contacts to found</param> /// <param name="target">The target node</param> /// <param name="excluded">The excluded node</param> /// <returns>The list of contacts found</returns> public List<Contact> CloseContacts(int count, ID target, ID excluded) { // These lists are sorted by distance. // Closest is first. List<Contact> found = new List<Contact>(); List<ID> distances = new List<ID>(); // For every Contact we have for(int i = 0; i < NUM_BUCKETS; i++) { lock(buckets[i]) { for(int j = 0; j < buckets[i].Count; j++) { Contact applicant = buckets[i][j]; // Exclude excluded contact if(applicant.NodeID == excluded) { continue; } // Add the applicant at the right place ID distance = applicant.NodeID ^ target; int addIndex = 0; while(addIndex < distances.Count && distances[addIndex] < distance) { addIndex++; } distances.Insert(addIndex, distance); found.Insert(addIndex, applicant); // Remove the last entry if we've grown too big if(distances.Count >= count) { distances.RemoveAt(distances.Count - 1); found.RemoveAt(found.Count - 1); } } } } // Give back the list of closest. return found; }
/// <summary> /// Returns when the given value was last inserted by /// its original publisher, or null if the value isn't present. /// </summary> /// <param name="key"></param> /// <returns></returns> public DateTime? GetPublicationTime(ID key, ID hash) { lock(store) { if(store.ContainsKey(key) && store[key].ContainsKey(hash)) { return store[key][hash].timestamp; } } return null; }
/// <summary> /// Return the contact with the given ID, or null if it's not found. /// </summary> /// <param name="toGet">The ID of the contact to Get</param> /// <returns>The contact found</returns> public Contact Get(ID toGet) { int bucket = BucketFor(toGet); lock(buckets[bucket]) { // Nobody can move it while we're getting it for(int i = 0; i < buckets[bucket].Count; i++) { if(buckets[bucket][i].NodeID == toGet) { return buckets[bucket][i]; } } } return null; }
public void Put(ID tag_id, Uri uri, DateTime publicationTime) { }
/// <summary> /// Move the contact with the given ID to the front of its bucket. /// </summary> /// <param name="toPromote">The identificator of the contact to promote</param> public void Promote(ID toPromote) { Contact promotee = Get(toPromote); int bucket = BucketFor(toPromote); lock(buckets[bucket]) { // Nobody can touch it while we move it. buckets[bucket].Remove(promotee); // Take out buckets[bucket].Add(promotee); // And put in at end } lock(accessTimes) { accessTimes[bucket] = DateTime.Now; } }
/// <summary> /// Store a key/value pair published originally at the given UTC timestamp. Value is kept until keepTime past timestamp. /// </summary> /// <param name="key"></param> /// <param name="hash">The hash of the value</param> /// <param name="val"></param> /// <param name="timestamp"></param> /// <param name="keepTime"></param> public void Put(ID key, ID hash, string val, DateTime timestamp, TimeSpan keepFor) { // Write the file CreatePath(Path.GetDirectoryName(PathFor(key, hash))); File.WriteAllText(PathFor(key, hash), val); // Record its existence Entry entry = new Entry(); entry.timestamp = timestamp; entry.keepFor = keepFor; lock(store) { if(!store.ContainsKey(key)) { store[key] = new SortedList<ID, Entry>(); } store[key][hash] = entry; } }
/// <summary> /// Report that a lookup was done for the given key. /// Key must not match our ID. /// </summary> /// <param name="key">The bucket that refer the bucket to touch</param> public void Touch(ID key) { lock(accessTimes) { accessTimes[BucketFor(key)] = DateTime.Now; } }
public void RefreshResource(ID tag_id, Uri uri, DateTime timestamp) { }
/// <summary> /// Change the timing information on an existing entry, if extant. /// </summary> /// <param name="key"></param> /// <param name="hash"></param> /// <param name="newStamp"></param> /// <param name="newKeep"></param> public void Restamp(ID key, ID hash, DateTime newStamp, TimeSpan newKeep) { lock(store) { if(store.ContainsKey(key) && store[key].ContainsKey(hash)) { store[key][hash].timestamp = newStamp; store[key][hash].keepFor = newKeep; } } }
/// <summary> /// Where should we save a particular value? /// </summary> /// <param name="key"></param> /// <param name="hash"></param> /// <returns></returns> private string PathFor(ID key, ID hash) { return Path.Combine(Path.Combine(storageRoot, key.ToPathString()), hash.ToPathString() + DATA_EXTENSION); }
/// <summary> /// Make a contact for a node with the given ID at the given location. /// </summary> /// <param name="id">The identificator of the node</param> /// <param name="endpoint">The address of the node</param> public Contact(ID id, Uri endpoint) { nodeID = id; nodeEndpoint = endpoint; }
public static void Main(string[] args) { Console.WriteLine("ID tests"); byte[] aData = new byte[20]; aData[3] = 10; byte[] bData = new byte[20]; bData[1] = 10; ID a = new ID(aData); ID b = new ID(bData); Debug.Assert(a != b); Debug.Assert(a == a); Debug.Assert(!(b == a)); Debug.Assert(a < b); Debug.Assert(b > a); Debug.Assert(a.Equals(a)); Debug.Assert(!a.Equals(b)); Debug.Assert(a.GetHashCode() != b.GetHashCode()); Debug.Assert(a.DifferingBit(b) == 4 * 8 - 2); // next to last bit of 4th byte differs Console.WriteLine("Test complete"); Console.WriteLine("Testing KademilaNode"); KademliaNode node1 = new KademliaNode(new EndpointAddress("soap.udp://localhost:8002/kademlia")); ServiceHost host1 = new ServiceHost(node1, new Uri("soap.udp://localhost:8002/kademlia")); KademliaNode node2 = new KademliaNode(new EndpointAddress("soap.udp://localhost:8001/kademlia")); ServiceHost host2 = new ServiceHost(node2, new Uri("soap.udp://localhost:8001/kademlia")); // node1.Bootstrap(); System.Threading.Thread.Sleep(50); // Wait for the other node to process its bucket queue node1.JoinNetwork(); node2.JoinNetwork(); // Do a big test List<KademliaNode> nl = new List<KademliaNode>(); KademliaNode lastNode = node1; for(int i = 0; i < 10; i++) { KademliaNode node = new KademliaNode(); node.Bootstrap(); lastNode = node; System.Threading.Thread.Sleep(50); // Wait for the other node to process its bucket queue node.JoinNetwork(); nl.Add(node); } host1.Close(); host2.Close(); Console.WriteLine("Connectivity tests complete"); /* // Do a store test ID babiesID = ID.RandomID(); node1.Put(babiesID, "=====I eat babies====="); Console.WriteLine("Store tests complete"); // Get it back IList<string> foundVals = node1.Get(babiesID); foreach(string s in foundVals) { Console.WriteLine("1 Found: " + s); } foundVals = node2.Get(babiesID); foreach(string s in foundVals) { Console.WriteLine("2 Found: " + s); } Console.WriteLine("Find tests complete"); Console.WriteLine("Testing DHT"); Dht dht = new Dht("C:\\Users\\seby\\Documents\\progetto_malgeri\\p2p-player\\nodes"+Console.ReadLine()+".xml"); dht.Put("A", "The value for A"); Console.WriteLine("A = " + dht.Get("A")); Console.WriteLine("Multi-value test"); dht.Put("Animal", "cat"); dht.Put("Animal", "dog"); dht.Put("Animal", "double-beaver"); dht.Put("Animal", "wombat"); dht.Put("Animal", "alot"); Console.WriteLine("Animal entry count " + node1.Get(ID.Hash("Animal")).Count); Console.WriteLine("Animal entry count " + node2.Get(ID.Hash("Animal")).Count); Console.WriteLine("Arbitrary animal = " + dht.Get("Animal")); */ Console.WriteLine("Testing complete! Press any key to exit the program."); Console.ReadLine(); }