Working on NodeLookups

This commit is contained in:
Joshua Kissoon 2014-02-19 15:54:15 +05:30
parent 58df7be300
commit 1de348fc72
13 changed files with 219 additions and 17 deletions

View File

@ -0,0 +1,20 @@
/**
* @author Joshua Kissoon
* @created 20140219
* @desc An exception used to indicate an unknown message type or communication identifier
*/
package kademlia.exceptions;
public class UnknownMessageException extends RuntimeException
{
public UnknownMessageException()
{
super();
}
public UnknownMessageException(String message)
{
super(message);
}
}

View File

@ -14,7 +14,7 @@ public class AcknowledgeMessage implements Message
{
private Node origin;
public static final byte CODE = 0x10;
public static final byte CODE = 0x01;
public AcknowledgeMessage(Node origin)
{

View File

@ -14,7 +14,7 @@ public class ConnectMessage implements Message
{
private Node origin;
public static final byte CODE = 0x11;
public static final byte CODE = 0x02;
public ConnectMessage(Node origin)
{

View File

@ -25,13 +25,20 @@ public class MessageFactory
{
switch (code)
{
default:
case SimpleMessage.CODE:
return new SimpleMessage(in);
case ConnectMessage.CODE:
return new ConnectMessage(in);
case AcknowledgeMessage.CODE:
return new AcknowledgeMessage(in);
case NodeReplyMessage.CODE:
return new NodeReplyMessage(in);
case NodeLookupMessage.CODE:
return new NodeLookupMessage(in);
default:
System.out.println("No Message handler found for message. Code: " + code);
return new SimpleMessage(in);
}
}
@ -44,6 +51,8 @@ public class MessageFactory
return new SimpleReceiver();
case ConnectMessage.CODE:
return new ConnectReceiver(server, this.localNode);
case NodeLookupMessage.CODE
return new NodeLookupReceiver();
}
}
}

View File

@ -17,7 +17,7 @@ public class NodeLookupMessage implements Message
private Node origin;
private NodeId lookupId;
public static final byte CODE = 0x13;
public static final byte CODE = 0x03;
/**
* A new NodeLookupMessage to find nodes

View File

@ -0,0 +1,91 @@
/**
* @author Joshua Kissoon
* @created 20140218
* @desc A message used to connect nodes
*/
package kademlia.message;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import kademlia.node.Node;
public class NodeReplyMessage implements Message
{
private Node origin;
public static final byte CODE = 0x04;
private ArrayList<Node> nodes;
public NodeReplyMessage(Node origin, ArrayList<Node> nodes)
{
this.origin = origin;
this.nodes = nodes;
}
public NodeReplyMessage(DataInput in) throws IOException
{
this.fromStream(in);
}
@Override
public final void fromStream(DataInput in) throws IOException
{
/* Read in the origin */
this.origin = new Node(in);
/* Get the number of incoming nodes */
int len = in.readInt();
this.nodes = new ArrayList<>(len);
/* Read in all nodes */
for (int i = 0; i < len; i++)
{
this.nodes.add(new Node(in));
}
}
@Override
public void toStream(DataOutput out) throws IOException
{
/* Add the origin node to the stream */
origin.toStream(out);
/* Add all other nodes to the stream */
int len = this.nodes.size();
if (len > 255)
{
throw new IndexOutOfBoundsException("Too many nodes in list to send in NodeReplyMessage. Size: " + len);
}
/* Writing the nodes to the stream */
out.writeInt(len);
for (Node n : this.nodes)
{
n.toStream(out);
}
}
public Node getOrigin()
{
return this.origin;
}
@Override
public byte code()
{
return CODE;
}
public ArrayList<Node> getNodes()
{
return this.nodes;
}
@Override
public String toString()
{
return "ConnectMessage[origin NodeId=" + origin.getNodeId() + "]";
}
}

View File

@ -13,7 +13,7 @@ public class SimpleMessage implements Message
{
/* Message constants */
public static final byte CODE = 0x01;
public static final byte CODE = 0x05;
private String content;

View File

@ -8,7 +8,6 @@ package kademlia.node;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

View File

@ -122,7 +122,6 @@ public class NodeId implements Streamable
public int getFirstSetBitIndex()
{
int prefixLength = 0;
System.out.println("Bytes: ");
for (byte b : this.keyBytes)
{

View File

@ -64,8 +64,9 @@ public class ConnectOperation implements Operation, Receiver
throw new RoutingException("Bootstrap node did not respond: " + bootstrapNode);
}
/* @todo Perform lookup for our own ID to get nodes close to us */
Operation lookup = new No
/* Perform lookup for our own ID to get nodes close to us */
Operation lookup = new NodeLookupOperation(this.server, this.localNode, this.localNode.getNodeId());
lookup.execute();
/* @todo Refresh buckets to get a good routing table */
return null;

View File

@ -19,8 +19,10 @@ import java.util.TreeMap;
import kademlia.core.Configuration;
import kademlia.core.KadServer;
import kademlia.exceptions.RoutingException;
import kademlia.exceptions.UnknownMessageException;
import kademlia.message.Message;
import kademlia.message.NodeLookupMessage;
import kademlia.message.NodeReplyMessage;
import kademlia.node.Node;
import kademlia.node.NodeId;
@ -149,7 +151,7 @@ public class NodeLookupOperation implements Operation, Receiver
return false;
}
/* Get unqueried nodes among the K closest seen */
/* Get unqueried nodes among the K closest seen that have not FAILED */
ArrayList<Node> unasked = this.closestNodesNotFailed(UNASKED);
if (unasked.isEmpty() && this.messagesTransiting.isEmpty())
@ -239,15 +241,57 @@ public class NodeLookupOperation implements Operation, Receiver
return closestNodes;
}
/**
* Receive and handle the incoming NodeReplyMessage
*
* @param comm
*
* @throws java.io.IOException
*/
@Override
public synchronized void receive(Message incoming, int comm)
public synchronized void receive(Message incoming, int comm) throws IOException
{
// NodeRepl
/* We receive a NodeReplyMessage with a set of nodes, read this message */
NodeReplyMessage msg = (NodeReplyMessage) incoming;
/* Add the origin node to our routing table */
Node origin = msg.getOrigin();
this.localNode.getRoutingTable().insert(origin);
/* Set that we've completed ASKing the origin node */
this.nodes.put(origin, ASKED);
/* Remove this msg from messagesTransiting since it's completed now */
this.messagesTransiting.remove(new Integer(comm));
/* Add the received nodes to our nodes list to query */
this.addNodes(msg.getNodes());
this.askNodesorFinish();
}
/**
* A node does not respond or a packet was lost, we set this node as failed
*
* @param comm
*
* @throws java.io.IOException
*/
@Override
public synchronized void timeout(int comm)
public synchronized void timeout(int comm) throws IOException
{
/* Get the node associated with this communication */
Node n = this.messagesTransiting.get(new Integer(comm));
if (n == null)
{
throw new UnknownMessageException("Unknown comm: " + comm);
}
/* Mark this node as failed */
this.nodes.put(n, FAILED);
this.localNode.getRoutingTable().remove(n);
this.messagesTransiting.remove(new Integer(comm));
this.askNodesorFinish();
}
}

View File

@ -43,6 +43,28 @@ public class KadBucket implements Bucket
}
}
/**
* Checks if this bucket contain a node
*
* @param n The node to check for
*
* @return boolean
*/
public boolean containNode(Node n)
{
return this.nodes.contains(n);
}
/**
* Remove a node from this bucket
*
* @param n The node to remove
*/
public void removeNode(Node n)
{
this.nodes.remove(n);
}
public int numNodes()
{
return this.nodes.size();

View File

@ -32,17 +32,34 @@ public class RoutingTable
}
/**
* Adds a new contact to the routing table
* Adds a new node to the routing table
*
* @param n The contact to add
*/
public void insert(Node n)
{
/* Find the prefix length of how far this node is away from the contact node */
int prefixLength = this.node.getNodeId().xor(n.getNodeId()).getFirstSetBitIndex();
/* Find the first set bit: how far this node is away from the contact node */
int bucketId = this.node.getNodeId().xor(n.getNodeId()).getFirstSetBitIndex();
/* Put this contact to the bucket that stores contacts prefixLength distance away */
this.buckets[prefixLength].insert(n);
this.buckets[bucketId].insert(n);
}
/**
* Remove a node from the routing table
*
* @param n The node to remove
*/
public void remove(Node n)
{
/* Find the first set bit: how far this node is away from the contact node */
int bucketId = this.node.getNodeId().xor(n.getNodeId()).getFirstSetBitIndex();
/* If the bucket has the contact, remove it */
if (this.buckets[bucketId].containNode(n))
{
this.buckets[bucketId].removeNode(n);
}
}
/**