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
{
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 OPERATION_TIMEOUT = 3000;
private final static int CONCURRENCY = 10;

View File

@ -36,7 +36,7 @@ public class KadServer
/* Server Objects */
private final int udpPort;
private final DatagramSocket socket;
private boolean isRunning;
private transient boolean isRunning;
private final Map<Integer, Receiver> receivers;
private final Timer timer; // Schedule future tasks
private final Map<Integer, TimerTask> tasks; // Keep track of scheduled tasks
@ -109,7 +109,7 @@ public class KadServer
{
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 */

View File

@ -4,7 +4,7 @@ import java.util.List;
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
* @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);
/**
* 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
*
@ -26,21 +42,28 @@ public interface Bucket
*
* @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);
/**
* 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
@ -48,7 +71,7 @@ public interface Bucket
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 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
public void insert(Node n)
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.nodes.containsKey(n.getNodeId()))
if (this.contacts.containsKey(c.getNode().getNodeId()))
{
/* @todo If it is, then move it to the front */
/* @todo Possibly use a doubly linked list instead of an ArrayList */
}
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
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
public void removeNode(Node n)
{
this.nodes.remove(n.getNodeId());
this.contacts.remove(n.getNodeId());
}
@Override
public int numNodes()
public int numContacts()
{
return this.nodes.size();
return this.contacts.size();
}
@Override
@ -85,9 +91,9 @@ public class KadBucket implements Bucket
}
@Override
public List<Node> getNodes()
public List<Contact> getContacts()
{
return new ArrayList<>(this.nodes.values());
return new ArrayList<>(this.contacts.values());
}
@Override
@ -96,10 +102,10 @@ public class KadBucket implements Bucket
StringBuilder sb = new StringBuilder("Bucket at depth: ");
sb.append(this.depth);
sb.append("\n Nodes: \n");
for (Node n : this.nodes.values())
for (Contact n : this.contacts.values())
{
sb.append("Node: ");
sb.append(n.getNodeId().toString());
sb.append(n.getNode().getNodeId().toString());
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)
{
@ -60,7 +70,7 @@ public class RoutingTable
int bucketId = this.getBucketId(n.getNodeId());
/* 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);
}
@ -97,11 +107,11 @@ public class RoutingTable
int bucketIndex = this.getBucketId(target);
/* 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)
{
closest.add(c);
closest.add(c.getNode());
}
else
{
@ -120,11 +130,11 @@ public class RoutingTable
*/
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)
{
closest.add(c);
closest.add(c.getNode());
}
else
{
@ -149,11 +159,11 @@ public class RoutingTable
*/
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)
{
closest.add(c);
closest.add(c.getNode());
}
else
{
@ -180,12 +190,30 @@ public class RoutingTable
for (Bucket b : this.buckets)
{
nodes.addAll(b.getNodes());
for (Contact c : b.getContacts())
{
nodes.add(c.getNode());
}
}
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
*/
@ -210,12 +238,12 @@ public class RoutingTable
StringBuilder sb = new StringBuilder("\nPrinting Routing Table Started ***************** \n");
for (Bucket b : this.buckets)
{
if (b.numNodes() > 0)
if (b.numContacts() > 0)
{
sb.append("# nodes in Bucket with depth ");
sb.append(b.getDepth());
sb.append(": ");
sb.append(b.numNodes());
sb.append(b.numContacts());
sb.append("\n");
sb.append(b.toString());
sb.append("\n");

View File

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

View File

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