mirror of
https://github.com/ChronosX88/KademliaDHT.git
synced 2024-11-22 10:12:19 +00:00
Got system state storage and retrieval of state to work!
This commit is contained in:
parent
e1e6e4e40d
commit
e2ca9326c9
@ -9,7 +9,6 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
@ -26,6 +25,7 @@ import kademlia.operation.Operation;
|
|||||||
import kademlia.operation.KadRefreshOperation;
|
import kademlia.operation.KadRefreshOperation;
|
||||||
import kademlia.operation.StoreOperation;
|
import kademlia.operation.StoreOperation;
|
||||||
import kademlia.routing.RoutingTable;
|
import kademlia.routing.RoutingTable;
|
||||||
|
import kademlia.serializer.JsonRoutingTableSerializer;
|
||||||
import kademlia.serializer.JsonSerializer;
|
import kademlia.serializer.JsonSerializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,6 +69,7 @@ public class Kademlia
|
|||||||
* @param ownerId The Name of this node used for storage
|
* @param ownerId The Name of this node used for storage
|
||||||
* @param localNode The Local Node for this Kad instance
|
* @param localNode The Local Node for this Kad instance
|
||||||
* @param udpPort The UDP port to use for routing messages
|
* @param udpPort The UDP port to use for routing messages
|
||||||
|
* @param dht The DHT for this instance
|
||||||
*
|
*
|
||||||
* @throws IOException If an error occurred while reading id or local map
|
* @throws IOException If an error occurred while reading id or local map
|
||||||
* from disk <i>or</i> a network error occurred while
|
* from disk <i>or</i> a network error occurred while
|
||||||
@ -120,24 +121,40 @@ public class Kademlia
|
|||||||
* @return A Kademlia instance loaded from a stored state in a file
|
* @return A Kademlia instance loaded from a stored state in a file
|
||||||
*
|
*
|
||||||
* @throws java.io.FileNotFoundException
|
* @throws java.io.FileNotFoundException
|
||||||
|
* @throws java.lang.ClassNotFoundException
|
||||||
*
|
*
|
||||||
* @todo Boot up this Kademlia instance from a saved file state
|
* @todo Boot up this Kademlia instance from a saved file state
|
||||||
*/
|
*/
|
||||||
public static void loadFromFile(String ownerId) throws FileNotFoundException, IOException, ClassNotFoundException
|
public static Kademlia loadFromFile(String ownerId) throws FileNotFoundException, IOException, ClassNotFoundException
|
||||||
{
|
{
|
||||||
/* Setup the file in which we store the state */
|
DataInputStream din;
|
||||||
DataInputStream din = new DataInputStream(new FileInputStream(getStateStorageFolderName() + File.separator + ownerId + ".kns"));
|
|
||||||
|
|
||||||
/* Read the UDP Port that this app is running on */
|
/**
|
||||||
Integer rPort = new JsonSerializer<Integer>().read(din);
|
* @section Read Basic Kad data
|
||||||
|
*/
|
||||||
|
din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "kad.kns"));
|
||||||
|
Kademlia ikad = new JsonSerializer<Kademlia>().read(din);
|
||||||
|
|
||||||
/* Read the node state */
|
/**
|
||||||
// Node rN = new JsonSerializer<Node>().read(din);
|
* @section Read the routing table
|
||||||
|
*/
|
||||||
|
din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "routingtable.kns"));
|
||||||
|
RoutingTable irtbl = new JsonRoutingTableSerializer().read(din);
|
||||||
|
|
||||||
/* Read the DHT */
|
/**
|
||||||
DHT rDht = new JsonSerializer<DHT>().read(din);
|
* @section Read the node state
|
||||||
|
*/
|
||||||
|
din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "node.kns"));
|
||||||
|
Node inode = new JsonSerializer<Node>().read(din);
|
||||||
|
inode.setRoutingTable(irtbl);
|
||||||
|
|
||||||
//return new Kademlia(ownerId, rN, rPort, rDht);
|
/**
|
||||||
|
* @section Read the DHT
|
||||||
|
*/
|
||||||
|
din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "dht.kns"));
|
||||||
|
DHT idht = new JsonSerializer<DHT>().read(din);
|
||||||
|
System.out.println("Finished reading data.");
|
||||||
|
return new Kademlia(ownerId, inode, ikad.getPort(), idht);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -242,12 +259,20 @@ public class Kademlia
|
|||||||
return this.ownerId;
|
return this.ownerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Integer The port on which this kad instance is running
|
||||||
|
*/
|
||||||
|
public int getPort()
|
||||||
|
{
|
||||||
|
return this.udpPort;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Here we handle properly shutting down the Kademlia instance
|
* Here we handle properly shutting down the Kademlia instance
|
||||||
*
|
*
|
||||||
* @throws java.io.FileNotFoundException
|
* @throws java.io.FileNotFoundException
|
||||||
*/
|
*/
|
||||||
public void shutdown() throws FileNotFoundException, IOException, ClassNotFoundException
|
public void shutdown() throws FileNotFoundException, IOException
|
||||||
{
|
{
|
||||||
/* Shut down the server */
|
/* Shut down the server */
|
||||||
this.server.shutdown();
|
this.server.shutdown();
|
||||||
@ -257,11 +282,7 @@ public class Kademlia
|
|||||||
{
|
{
|
||||||
/* Save the system state */
|
/* Save the system state */
|
||||||
this.saveKadState();
|
this.saveKadState();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Now we store the content locally in a file */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,7 +313,7 @@ public class Kademlia
|
|||||||
* This will cause a serialization recursion, and in turn a Stack Overflow
|
* This will cause a serialization recursion, and in turn a Stack Overflow
|
||||||
*/
|
*/
|
||||||
dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId) + File.separator + "routingtable.kns"));
|
dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId) + File.separator + "routingtable.kns"));
|
||||||
new JsonSerializer<RoutingTable>().write(this.localNode.getRoutingTable(), dout);
|
new JsonRoutingTableSerializer().write(this.localNode.getRoutingTable(), dout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @section Save the DHT
|
* @section Save the DHT
|
||||||
|
@ -24,7 +24,7 @@ public class Node implements Streamable
|
|||||||
private InetAddress inetAddress;
|
private InetAddress inetAddress;
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
private transient final RoutingTable routingTable;
|
private transient RoutingTable routingTable;
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -119,6 +119,16 @@ public class Node implements Streamable
|
|||||||
return this.routingTable;
|
return this.routingTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new routing table to this node, mainly used when we retrieve the node from a saved state
|
||||||
|
*
|
||||||
|
* @param tbl The routing table to use
|
||||||
|
*/
|
||||||
|
public void setRoutingTable(RoutingTable tbl)
|
||||||
|
{
|
||||||
|
this.routingTable = tbl;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
|
@ -17,7 +17,7 @@ import kademlia.message.Streamable;
|
|||||||
public class NodeId implements Streamable
|
public class NodeId implements Streamable
|
||||||
{
|
{
|
||||||
|
|
||||||
public final static int ID_LENGTH = 160;
|
public final transient static int ID_LENGTH = 160;
|
||||||
private byte[] keyBytes;
|
private byte[] keyBytes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
/**
|
|
||||||
* @author Joshua Kissoon
|
|
||||||
* @created 20140215
|
|
||||||
* @desc A bucket in the Kademlia routing table
|
|
||||||
*/
|
|
||||||
package kademlia.routing;
|
package kademlia.routing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -12,13 +7,18 @@ import java.util.Map;
|
|||||||
import kademlia.node.Node;
|
import kademlia.node.Node;
|
||||||
import kademlia.node.NodeId;
|
import kademlia.node.NodeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A bucket in the Kademlia routing table
|
||||||
|
*
|
||||||
|
* @author Joshua Kissoon
|
||||||
|
* @created 20140215
|
||||||
|
*/
|
||||||
public class KadBucket implements Bucket
|
public class KadBucket implements Bucket
|
||||||
{
|
{
|
||||||
|
|
||||||
private final int depth;
|
private final int depth;
|
||||||
private final Map<NodeId, Node> nodes;
|
private final Map<NodeId, Node> nodes;
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
nodes = new HashMap<>();
|
nodes = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
/**
|
|
||||||
* @author Joshua Kissoon
|
|
||||||
* @created 20140215
|
|
||||||
* @desc Implementation of a Kademlia routing table
|
|
||||||
*/
|
|
||||||
package kademlia.routing;
|
package kademlia.routing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -10,11 +5,20 @@ import java.util.List;
|
|||||||
import kademlia.node.Node;
|
import kademlia.node.Node;
|
||||||
import kademlia.node.NodeId;
|
import kademlia.node.NodeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of a Kademlia routing table
|
||||||
|
*
|
||||||
|
* @author Joshua Kissoon
|
||||||
|
* @created 20140215
|
||||||
|
*
|
||||||
|
* @todo Make the KadBucket represent the Bucket interface
|
||||||
|
* @todo Change the code to reflect the bucket interface and not the specific KadBucket implementation
|
||||||
|
*/
|
||||||
public class RoutingTable
|
public class RoutingTable
|
||||||
{
|
{
|
||||||
|
|
||||||
private final Node localNode; // The current node
|
private final Node localNode; // The current node
|
||||||
private final KadBucket[] buckets;
|
private transient KadBucket[] buckets;
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -26,10 +30,10 @@ public class RoutingTable
|
|||||||
this.localNode = localNode;
|
this.localNode = localNode;
|
||||||
|
|
||||||
/* Initialize all of the buckets to a specific depth */
|
/* Initialize all of the buckets to a specific depth */
|
||||||
for (int i = 0; i < NodeId.ID_LENGTH; i++)
|
this.initializeBuckets();
|
||||||
{
|
|
||||||
buckets[i] = new KadBucket(i);
|
/* @todo Insert the local node */
|
||||||
}
|
//this.insert(localNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,10 +41,10 @@ public class RoutingTable
|
|||||||
*
|
*
|
||||||
* @param n The contact to add
|
* @param n The contact to add
|
||||||
*/
|
*/
|
||||||
public void insert(Node n)
|
public final void insert(Node n)
|
||||||
{
|
{
|
||||||
/* bucketId is the distance between these nodes */
|
/* bucketId is the distance between these nodes */
|
||||||
int bucketId = this.localNode.getNodeId().getDistance(n.getNodeId()) - 1;
|
int bucketId = this.localNode.getNodeId().getDistance(n.getNodeId());
|
||||||
|
|
||||||
//System.out.println(this.localNode.getNodeId() + " Adding Node " + n.getNodeId() + " to bucket at depth: " + bucketId);
|
//System.out.println(this.localNode.getNodeId() + " Adding Node " + n.getNodeId() + " to bucket at depth: " + bucketId);
|
||||||
|
|
||||||
@ -53,7 +57,7 @@ public class RoutingTable
|
|||||||
*
|
*
|
||||||
* @param n The node to remove
|
* @param n The node to remove
|
||||||
*/
|
*/
|
||||||
public void remove(Node n)
|
public final void remove(Node n)
|
||||||
{
|
{
|
||||||
/* Find the first set bit: how far this node is away from the contact node */
|
/* Find the first set bit: how far this node is away from the contact node */
|
||||||
int bucketId = this.localNode.getNodeId().getDistance(n.getNodeId());
|
int bucketId = this.localNode.getNodeId().getDistance(n.getNodeId());
|
||||||
@ -73,7 +77,7 @@ public class RoutingTable
|
|||||||
*
|
*
|
||||||
* @return List A List of contacts closest to target
|
* @return List A List of contacts closest to target
|
||||||
*/
|
*/
|
||||||
public List<Node> findClosest(NodeId target, int num)
|
public final List<Node> findClosest(NodeId target, int num)
|
||||||
{
|
{
|
||||||
List<Node> closest = new ArrayList<>(num);
|
List<Node> closest = new ArrayList<>(num);
|
||||||
|
|
||||||
@ -146,7 +150,7 @@ public class RoutingTable
|
|||||||
/**
|
/**
|
||||||
* @return List A List of all Nodes in this RoutingTable
|
* @return List A List of all Nodes in this RoutingTable
|
||||||
*/
|
*/
|
||||||
public List getAllNodes()
|
public final List getAllNodes()
|
||||||
{
|
{
|
||||||
List<Node> nodes = new ArrayList<>();
|
List<Node> nodes = new ArrayList<>();
|
||||||
|
|
||||||
@ -158,8 +162,38 @@ public class RoutingTable
|
|||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Bucket[] The buckets in this Kad Instance
|
||||||
|
*/
|
||||||
|
public final KadBucket[] getBuckets()
|
||||||
|
{
|
||||||
|
return this.buckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the KadBuckets of this routing table, mainly used when retrieving saved state
|
||||||
|
*
|
||||||
|
* @param buckets
|
||||||
|
*/
|
||||||
|
public final void setBuckets(KadBucket[] buckets)
|
||||||
|
{
|
||||||
|
this.buckets = buckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the kadBuckets to be empty
|
||||||
|
*/
|
||||||
|
public final void initializeBuckets()
|
||||||
|
{
|
||||||
|
this.buckets = new KadBucket[NodeId.ID_LENGTH];
|
||||||
|
for (int i = 0; i < NodeId.ID_LENGTH; i++)
|
||||||
|
{
|
||||||
|
buckets[i] = new KadBucket(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public final String toString()
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder("\nPrinting Routing Table Started ***************** \n");
|
StringBuilder sb = new StringBuilder("\nPrinting Routing Table Started ***************** \n");
|
||||||
for (KadBucket b : this.buckets)
|
for (KadBucket b : this.buckets)
|
||||||
|
100
src/kademlia/serializer/JsonRoutingTableSerializer.java
Normal file
100
src/kademlia/serializer/JsonRoutingTableSerializer.java
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package kademlia.serializer;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import kademlia.routing.RoutingTable;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.List;
|
||||||
|
import kademlia.node.Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A KadSerializer that serializes routing tables to JSON format
|
||||||
|
* The generic serializer is not working for routing tables
|
||||||
|
*
|
||||||
|
* Why a RoutingTable specific serializer?
|
||||||
|
* The routing table structure:
|
||||||
|
* - RoutingTable
|
||||||
|
* -- Buckets[]
|
||||||
|
* --- Map<NodeId, Node>
|
||||||
|
* ---- NodeId:KeyBytes
|
||||||
|
* ---- Node: NodeId, InetAddress, Port
|
||||||
|
*
|
||||||
|
* The above structure seems to be causing some problem for Gson,
|
||||||
|
* especially at the Map part.
|
||||||
|
*
|
||||||
|
* Solution
|
||||||
|
* - Make the Buckets[] transient
|
||||||
|
* - Simply store all Nodes in the serialized object
|
||||||
|
* - When reloading, re-add all nodes to the RoutingTable
|
||||||
|
*
|
||||||
|
* @author Joshua Kissoon
|
||||||
|
*
|
||||||
|
* @since 20140225
|
||||||
|
*/
|
||||||
|
public class JsonRoutingTableSerializer implements KadSerializer<RoutingTable>
|
||||||
|
{
|
||||||
|
|
||||||
|
private final Gson gson;
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
gson = new Gson();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(RoutingTable data, DataOutputStream out) throws IOException
|
||||||
|
{
|
||||||
|
try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(out)))
|
||||||
|
{
|
||||||
|
writer.beginArray();
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
writer.endArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RoutingTable read(DataInputStream in) throws IOException, ClassNotFoundException
|
||||||
|
{
|
||||||
|
try (DataInputStream din = new DataInputStream(in);
|
||||||
|
JsonReader reader = new JsonReader(new InputStreamReader(in)))
|
||||||
|
{
|
||||||
|
reader.beginArray();
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
tbl.initializeBuckets();
|
||||||
|
|
||||||
|
for (Node n : nodes)
|
||||||
|
{
|
||||||
|
tbl.insert(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.endArray();
|
||||||
|
/* Read and return the Content*/
|
||||||
|
return tbl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -58,7 +58,11 @@ public class JsonSerializer<T> implements KadSerializer<T>
|
|||||||
String className = gson.fromJson(reader, String.class);
|
String className = gson.fromJson(reader, String.class);
|
||||||
|
|
||||||
/* Read and return the Content*/
|
/* Read and return the Content*/
|
||||||
return gson.fromJson(reader, Class.forName(className));
|
T ret = gson.fromJson(reader, Class.forName(className));
|
||||||
|
|
||||||
|
reader.endArray();
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,9 @@ public class SaveStateTest
|
|||||||
System.out.println("\n\n\nShutting down Kad instance");
|
System.out.println("\n\n\nShutting down Kad instance");
|
||||||
kad1.shutdown();
|
kad1.shutdown();
|
||||||
|
|
||||||
//System.out.println("\n\n\nReloading down Kad instance from file");
|
System.out.println("\n\n\nReloading down Kad instance from file");
|
||||||
//Kademlia.loadFromFile("JoshuaK");
|
Kademlia kad3 = Kademlia.loadFromFile("JoshuaK");
|
||||||
//System.out.println(kad3);
|
System.out.println(kad3);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user