diff --git a/src/kademlia/core/DefaultConfiguration.java b/src/kademlia/core/DefaultConfiguration.java index 1cc6520..0d6343e 100644 --- a/src/kademlia/core/DefaultConfiguration.java +++ b/src/kademlia/core/DefaultConfiguration.java @@ -10,7 +10,7 @@ import java.io.File; public class DefaultConfiguration implements KadConfiguration { - private final static long RESTORE_INTERVAL = 10 * 1000; // Default at 1 hour + private final static long RESTORE_INTERVAL = 1000 * 1000; // Default at 1 hour private final static long RESPONSE_TIMEOUT = 1500; private final static long OPERATION_TIMEOUT = 3000; private final static int CONCURRENCY = 10; diff --git a/src/kademlia/routing/Contact.java b/src/kademlia/routing/Contact.java index 48d7630..3e10b5c 100644 --- a/src/kademlia/routing/Contact.java +++ b/src/kademlia/routing/Contact.java @@ -7,23 +7,67 @@ import kademlia.node.Node; * * Contacts are used instead of nodes because more information is needed than just the node information. * - Information such as - * -- Last alive time + * -- Last seen time * * @author Joshua Kissoon * @since 20140425 + * @updated 20140426 */ -public class Contact +public class Contact implements Comparable { private final Node n; + private long lastSeen; + /** + * Create a contact object + * + * @param n The node associated with this contact + */ public Contact(Node n) { this.n = n; + this.lastSeen = System.currentTimeMillis() / 1000L; } public Node getNode() { return this.n; } + + /** + * When a Node sees a contact a gain, the Node will want to update that it's seen recently, + * this method updates the last seen timestamp for this contact. + */ + public void setSeenNow() + { + this.lastSeen = System.currentTimeMillis() / 1000L; + } + + /** + * When last was this contact seen? + * + * @return long The last time this contact was seen. + */ + public long lastSeen() + { + return this.lastSeen; + } + + public boolean equals(Contact c) + { + return c.getNode().equals(this.getNode()); + } + + @Override + public int compareTo(Contact o) + { + if (this.getNode().equals(o.getNode())) + { + return 0; + } + + return (this.lastSeen() > o.lastSeen()) ? 1 : -1; + } + } diff --git a/src/kademlia/routing/ContactLastSeenComparator.java b/src/kademlia/routing/ContactLastSeenComparator.java new file mode 100644 index 0000000..6c1d097 --- /dev/null +++ b/src/kademlia/routing/ContactLastSeenComparator.java @@ -0,0 +1,34 @@ +package kademlia.routing; + +import java.util.Comparator; + +/** + * A Comparator to compare 2 contacts by their last seen time + * + * @author Joshua Kissoon + * @since 20140426 + */ +public class ContactLastSeenComparator implements Comparator +{ + + /** + * Compare two contacts to determine their order in the Bucket, + * Contacts are ordered by their last seen timestamp. + * + * @param c1 Contact 1 + * @param c2 Contact 2 + */ + @Override + public int compare(Contact c1, Contact c2) + { + if (c1.getNode().equals(c2.getNode())) + { + return 0; + } + else + { + /* We may have 2 different contacts with same last seen values so we can't return 0 here */ + return c1.lastSeen() > c2.lastSeen() ? 1 : -1; + } + } +} diff --git a/src/kademlia/routing/KadBucket.java b/src/kademlia/routing/KadBucket.java index 7f039f9..e320138 100644 --- a/src/kademlia/routing/KadBucket.java +++ b/src/kademlia/routing/KadBucket.java @@ -1,11 +1,10 @@ package kademlia.routing; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; import kademlia.node.Node; -import kademlia.node.NodeId; /** * A bucket in the Kademlia routing table @@ -17,11 +16,11 @@ public class KadBucket implements Bucket { private final int depth; - private final Map contacts; + private final Map contacts; { - contacts = new HashMap<>(); + contacts = new TreeMap<>(new ContactLastSeenComparator()); } /** @@ -36,15 +35,19 @@ public class KadBucket implements Bucket public void insert(Contact c) { /* @todo Check if the bucket is filled already and handle the situation */ - /* Check if the contact is already in the bucket */ - if (this.contacts.containsKey(c.getNode().getNodeId())) + if (this.contacts.containsKey(c)) { - /* @todo If it is, then move it to the front */ - /* @todo Possibly use a doubly linked list instead of an ArrayList */ + /** + * If the contact is already in the bucket, lets update that we've seen it + * We need to remove and re-add the contact to get the Sorted Set to update sort order + */ + Contact tmp = this.contacts.remove(c); + tmp.setSeenNow(); + this.contacts.put(tmp, tmp); } else { - contacts.put(c.getNode().getNodeId(), c); + contacts.put(c, c); } } @@ -57,25 +60,25 @@ public class KadBucket implements Bucket @Override public boolean containsContact(Contact c) { - return this.contacts.containsKey(c.getNode().getNodeId()); + return this.contacts.containsKey(c); } @Override public boolean containsNode(Node n) { - return this.contacts.containsKey(n.getNodeId()); + return this.containsContact(new Contact(n)); } @Override public void removeContact(Contact c) { - this.contacts.remove(c.getNode().getNodeId()); + this.contacts.remove(c); } @Override public void removeNode(Node n) { - this.contacts.remove(n.getNodeId()); + this.removeContact(new Contact(n)); } @Override diff --git a/src/kademlia/routing/RoutingTable.java b/src/kademlia/routing/RoutingTable.java index ac11dd6..23c2c4a 100644 --- a/src/kademlia/routing/RoutingTable.java +++ b/src/kademlia/routing/RoutingTable.java @@ -45,7 +45,7 @@ public class RoutingTable * * @param c The contact to add */ - public final void insert(Contact c) + public synchronized final void insert(Contact c) { this.buckets[this.getBucketId(c.getNode().getNodeId())].insert(c); } @@ -55,7 +55,7 @@ public class RoutingTable * * @param n The node to add */ - public final void insert(Node n) + public synchronized final void insert(Node n) { this.buckets[this.getBucketId(n.getNodeId())].insert(n); } diff --git a/src/kademlia/tests/RoutingTableSimulation.java b/src/kademlia/tests/RoutingTableSimulation.java new file mode 100644 index 0000000..04bec6e --- /dev/null +++ b/src/kademlia/tests/RoutingTableSimulation.java @@ -0,0 +1,54 @@ +package kademlia.tests; + +import kademlia.Kademlia; +import kademlia.node.NodeId; +import kademlia.routing.RoutingTable; + +/** + * Testing how the routing table works and checking if everything works properly + * + * @author Joshua Kissoon + * @since 20140426 + */ +public class RoutingTableSimulation +{ + + public RoutingTableSimulation() + { + try + { + /* Setting up 2 Kad networks */ + Kademlia kad1 = new Kademlia("JoshuaK", new NodeId("ASF45678947584567463"), 12049); + Kademlia kad2 = new Kademlia("Crystal", new NodeId("ASF45678947584567464"), 4585); + Kademlia kad3 = new Kademlia("Shameer", new NodeId("ASF45678947584567465"), 8104); + Kademlia kad4 = new Kademlia("Lokesh", new NodeId("ASF45678947584567466"), 8335); + Kademlia kad5 = new Kademlia("Chandu", new NodeId("ASF45678947584567467"), 13345); + + RoutingTable rt = kad1.getNode().getRoutingTable(); + + rt.insert(kad2.getNode()); + rt.insert(kad3.getNode()); + rt.insert(kad4.getNode()); + System.out.println(rt); + + rt.insert(kad5.getNode()); + System.out.println(rt); + + rt.insert(kad3.getNode()); + System.out.println(rt); + } + catch (IllegalStateException e) + { + + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public static void main(String[] args) + { + new RoutingTableSimulation(); + } +}