Routing Table Improvement

- Setup the RoutingTable to store contact instead of node
- This helps since we'll need more information in the routing to evict contacts, etc
This commit is contained in:
Joshua Kissoon 2014-04-25 16:04:05 +05:30
parent c902ba26ab
commit 21b6667eb8
8 changed files with 153 additions and 66 deletions

View File

@ -10,7 +10,7 @@ import java.io.File;
public class DefaultConfiguration implements KadConfiguration public class DefaultConfiguration implements KadConfiguration
{ {
private final static long RESTORE_INTERVAL = 60 * 1000; // Default at 1 hour private final static long RESTORE_INTERVAL = 10 * 1000; // Default at 1 hour
private final static long RESPONSE_TIMEOUT = 1500; private final static long RESPONSE_TIMEOUT = 1500;
private final static long OPERATION_TIMEOUT = 3000; private final static long OPERATION_TIMEOUT = 3000;
private final static int CONCURRENCY = 10; private final static int CONCURRENCY = 10;

View File

@ -36,7 +36,7 @@ public class KadServer
/* Server Objects */ /* Server Objects */
private final int udpPort; private final int udpPort;
private final DatagramSocket socket; private final DatagramSocket socket;
private boolean isRunning; private transient boolean isRunning;
private final Map<Integer, Receiver> receivers; private final Map<Integer, Receiver> receivers;
private final Timer timer; // Schedule future tasks private final Timer timer; // Schedule future tasks
private final Map<Integer, TimerTask> tasks; // Keep track of scheduled tasks private final Map<Integer, TimerTask> tasks; // Keep track of scheduled tasks
@ -109,7 +109,7 @@ public class KadServer
{ {
if (!isRunning) if (!isRunning)
{ {
throw new IllegalStateException("Kad Server is not running."); throw new IllegalStateException("Kad Server is not running on node " + this.localNode);
} }
/* Generate a random communication ID */ /* Generate a random communication ID */

View File

@ -4,7 +4,7 @@ import java.util.List;
import kademlia.node.Node; import kademlia.node.Node;
/** /**
* A bucket used to store Nodes in the routing table. * A bucket used to store Contacts in the routing table.
* *
* @author Joshua Kissoon * @author Joshua Kissoon
* @created 20140215 * @created 20140215
@ -13,12 +13,28 @@ public interface Bucket
{ {
/** /**
* Adds a new node to the bucket * Adds a contact to the bucket
* *
* @param n the new node * @param c the new contact
*/
public void insert(Contact c);
/**
* Create a new contact and insert it into the bucket.
*
* @param n The node to create the contact from
*/ */
public void insert(Node n); public void insert(Node n);
/**
* Checks if this bucket contain a contact
*
* @param c The contact to check for
*
* @return boolean
*/
public boolean containsContact(Contact c);
/** /**
* Checks if this bucket contain a node * Checks if this bucket contain a node
* *
@ -26,21 +42,28 @@ public interface Bucket
* *
* @return boolean * @return boolean
*/ */
public boolean containNode(Node n); public boolean containsNode(Node n);
/** /**
* Remove a node from this bucket * Remove a contact from this bucket
* *
* @param n The node to remove * @param c The contact to remove
*/
public void removeContact(Contact c);
/**
* Remove the contact object related to a node from this bucket
*
* @param n The node of the contact to remove
*/ */
public void removeNode(Node n); public void removeNode(Node n);
/** /**
* Counts the number of nodes in this bucket. * Counts the number of contacts in this bucket.
* *
* @return Integer The number of nodes in this bucket * @return Integer The number of contacts in this bucket
*/ */
public int numNodes(); public int numContacts();
/** /**
* @return Integer The depth of this bucket in the RoutingTable * @return Integer The depth of this bucket in the RoutingTable
@ -48,7 +71,7 @@ public interface Bucket
public int getDepth(); public int getDepth();
/** /**
* @return An Iterable structure with all nodes in this bucket * @return An Iterable structure with all contacts in this bucket
*/ */
public List<Node> getNodes(); public List<Contact> getContacts();
} }

View File

@ -0,0 +1,29 @@
package kademlia.routing;
import kademlia.node.Node;
/**
* Keeps information about contacts of the Node; Contacts are stored in the Buckets in the Routing Table.
*
* Contacts are used instead of nodes because more information is needed than just the node information.
* - Information such as
* -- Last alive time
*
* @author Joshua Kissoon
* @since 20140425
*/
public class Contact
{
private final Node n;
public Contact(Node n)
{
this.n = n;
}
public Node getNode()
{
return this.n;
}
}

View File

@ -17,11 +17,11 @@ public class KadBucket implements Bucket
{ {
private final int depth; private final int depth;
private final Map<NodeId, Node> nodes; private final Map<NodeId, Contact> contacts;
{ {
nodes = new HashMap<>(); contacts = new HashMap<>();
} }
/** /**
@ -33,49 +33,55 @@ public class KadBucket implements Bucket
} }
@Override @Override
public void insert(Node n) public void insert(Contact c)
{ {
/* @todo Check if the bucket is filled already and handle the situation */ /* @todo Check if the bucket is filled already and handle the situation */
/* Check if the contact is already in the bucket */ /* Check if the contact is already in the bucket */
if (this.nodes.containsKey(n.getNodeId())) if (this.contacts.containsKey(c.getNode().getNodeId()))
{ {
/* @todo If it is, then move it to the front */ /* @todo If it is, then move it to the front */
/* @todo Possibly use a doubly linked list instead of an ArrayList */ /* @todo Possibly use a doubly linked list instead of an ArrayList */
} }
else else
{ {
nodes.put(n.getNodeId(), n); contacts.put(c.getNode().getNodeId(), c);
} }
} }
/**
* Checks if this bucket contain a node
*
* @param n The node to check for
*
* @return boolean
*/
@Override @Override
public boolean containNode(Node n) public void insert(Node n)
{ {
return this.nodes.containsKey(n.getNodeId()); this.insert(new Contact(n));
}
@Override
public boolean containsContact(Contact c)
{
return this.contacts.containsKey(c.getNode().getNodeId());
}
@Override
public boolean containsNode(Node n)
{
return this.contacts.containsKey(n.getNodeId());
}
@Override
public void removeContact(Contact c)
{
this.contacts.remove(c.getNode().getNodeId());
} }
/**
* Remove a node from this bucket
*
* @param n The node to remove
*/
@Override @Override
public void removeNode(Node n) public void removeNode(Node n)
{ {
this.nodes.remove(n.getNodeId()); this.contacts.remove(n.getNodeId());
} }
@Override @Override
public int numNodes() public int numContacts()
{ {
return this.nodes.size(); return this.contacts.size();
} }
@Override @Override
@ -85,9 +91,9 @@ public class KadBucket implements Bucket
} }
@Override @Override
public List<Node> getNodes() public List<Contact> getContacts()
{ {
return new ArrayList<>(this.nodes.values()); return new ArrayList<>(this.contacts.values());
} }
@Override @Override
@ -96,10 +102,10 @@ public class KadBucket implements Bucket
StringBuilder sb = new StringBuilder("Bucket at depth: "); StringBuilder sb = new StringBuilder("Bucket at depth: ");
sb.append(this.depth); sb.append(this.depth);
sb.append("\n Nodes: \n"); sb.append("\n Nodes: \n");
for (Node n : this.nodes.values()) for (Contact n : this.contacts.values())
{ {
sb.append("Node: "); sb.append("Node: ");
sb.append(n.getNodeId().toString()); sb.append(n.getNode().getNodeId().toString());
sb.append("\n"); sb.append("\n");
} }

View File

@ -41,9 +41,19 @@ public class RoutingTable
} }
/** /**
* Adds a new node to the routing table based on how far it is from the LocalNode. * Adds a contact to the routing table based on how far it is from the LocalNode.
* *
* @param n The contact to add * @param c The contact to add
*/
public final void insert(Contact c)
{
this.buckets[this.getBucketId(c.getNode().getNodeId())].insert(c);
}
/**
* Adds a node to the routing table based on how far it is from the LocalNode.
*
* @param n The node to add
*/ */
public final void insert(Node n) public final void insert(Node n)
{ {
@ -60,7 +70,7 @@ public class RoutingTable
int bucketId = this.getBucketId(n.getNodeId()); int bucketId = this.getBucketId(n.getNodeId());
/* If the bucket has the contact, remove it */ /* If the bucket has the contact, remove it */
if (this.buckets[bucketId].containNode(n)) if (this.buckets[bucketId].containsNode(n))
{ {
this.buckets[bucketId].removeNode(n); this.buckets[bucketId].removeNode(n);
} }
@ -97,11 +107,11 @@ public class RoutingTable
int bucketIndex = this.getBucketId(target); int bucketIndex = this.getBucketId(target);
/* Add the contacts from this bucket to the return contacts */ /* Add the contacts from this bucket to the return contacts */
for (Node c : this.buckets[bucketIndex].getNodes()) for (Contact c : this.buckets[bucketIndex].getContacts())
{ {
if (closest.size() < numNodesRequired) if (closest.size() < numNodesRequired)
{ {
closest.add(c); closest.add(c.getNode());
} }
else else
{ {
@ -120,11 +130,11 @@ public class RoutingTable
*/ */
for (int i = 1; (bucketIndex - i) >= 0; i++) for (int i = 1; (bucketIndex - i) >= 0; i++)
{ {
for (Node c : this.buckets[bucketIndex - i].getNodes()) for (Contact c : this.buckets[bucketIndex - i].getContacts())
{ {
if (closest.size() < numNodesRequired) if (closest.size() < numNodesRequired)
{ {
closest.add(c); closest.add(c.getNode());
} }
else else
{ {
@ -149,11 +159,11 @@ public class RoutingTable
*/ */
for (int i = 1; (bucketIndex + i) < NodeId.ID_LENGTH; i++) for (int i = 1; (bucketIndex + i) < NodeId.ID_LENGTH; i++)
{ {
for (Node c : this.buckets[bucketIndex + i].getNodes()) for (Contact c : this.buckets[bucketIndex + i].getContacts())
{ {
if (closest.size() < numNodesRequired) if (closest.size() < numNodesRequired)
{ {
closest.add(c); closest.add(c.getNode());
} }
else else
{ {
@ -180,12 +190,30 @@ public class RoutingTable
for (Bucket b : this.buckets) for (Bucket b : this.buckets)
{ {
nodes.addAll(b.getNodes()); for (Contact c : b.getContacts())
{
nodes.add(c.getNode());
}
} }
return nodes; return nodes;
} }
/**
* @return List A List of all Nodes in this RoutingTable
*/
public final List getAllContacts()
{
List<Contact> contacts = new ArrayList<>();
for (Bucket b : this.buckets)
{
contacts.addAll(b.getContacts());
}
return contacts;
}
/** /**
* @return Bucket[] The buckets in this Kad Instance * @return Bucket[] The buckets in this Kad Instance
*/ */
@ -210,12 +238,12 @@ public class RoutingTable
StringBuilder sb = new StringBuilder("\nPrinting Routing Table Started ***************** \n"); StringBuilder sb = new StringBuilder("\nPrinting Routing Table Started ***************** \n");
for (Bucket b : this.buckets) for (Bucket b : this.buckets)
{ {
if (b.numNodes() > 0) if (b.numContacts() > 0)
{ {
sb.append("# nodes in Bucket with depth "); sb.append("# nodes in Bucket with depth ");
sb.append(b.getDepth()); sb.append(b.getDepth());
sb.append(": "); sb.append(": ");
sb.append(b.numNodes()); sb.append(b.numContacts());
sb.append("\n"); sb.append("\n");
sb.append(b.toString()); sb.append(b.toString());
sb.append("\n"); sb.append("\n");

View File

@ -79,7 +79,10 @@ public class SaveStateTest
Kademlia kadR2 = Kademlia.loadFromFile("JoshuaK"); Kademlia kadR2 = Kademlia.loadFromFile("JoshuaK");
System.out.println(kadR2); System.out.println(kadR2);
} }
catch (IllegalStateException e)
{
}
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace(); e.printStackTrace();

View File

@ -13,6 +13,7 @@ import kademlia.routing.RoutingTable;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.List; import java.util.List;
import kademlia.node.Node; import kademlia.node.Node;
import kademlia.routing.Contact;
/** /**
* A KadSerializer that serializes routing tables to JSON format * A KadSerializer that serializes routing tables to JSON format
@ -43,6 +44,10 @@ public class JsonRoutingTableSerializer implements KadSerializer<RoutingTable>
private final Gson gson; private final Gson gson;
Type contactCollectionType = new TypeToken<List<Contact>>()
{
}.getType();
{ {
gson = new Gson(); gson = new Gson();
@ -58,15 +63,11 @@ public class JsonRoutingTableSerializer implements KadSerializer<RoutingTable>
/* Write the basic RoutingTable */ /* Write the basic RoutingTable */
gson.toJson(data, RoutingTable.class, writer); gson.toJson(data, RoutingTable.class, writer);
/* Now Store the Nodes */ /* Now Store the Contacts */
Type collectionType = new TypeToken<List<Node>>() gson.toJson(data.getAllContacts(), contactCollectionType, writer);
{
}.getType();
gson.toJson(data.getAllNodes(), collectionType, writer);
writer.endArray(); writer.endArray();
} }
} }
@Override @Override
@ -80,16 +81,13 @@ public class JsonRoutingTableSerializer implements KadSerializer<RoutingTable>
/* Read the basic RoutingTable */ /* Read the basic RoutingTable */
RoutingTable tbl = gson.fromJson(reader, RoutingTable.class); RoutingTable tbl = gson.fromJson(reader, RoutingTable.class);
/* Now get the nodes and add them back to the RoutingTable */ /* Now get the Contacts and add them back to the RoutingTable */
Type collectionType = new TypeToken<List<Node>>() List<Contact> contacts = gson.fromJson(reader, contactCollectionType);
{
}.getType();
List<Node> nodes = gson.fromJson(reader, collectionType);
tbl.initialize(); tbl.initialize();
for (Node n : nodes) for (Contact c : contacts)
{ {
tbl.insert(n); tbl.insert(c);
} }
reader.endArray(); reader.endArray();