From 6e35a68c4729fcbaf53276adb1a8814900de1305 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 17 Feb 2018 19:09:15 +0100 Subject: [PATCH] Initial commit --- build.xml | 73 ++ build/.gitignore | 1 + logging.properties | 7 + manifest.mf | 3 + nbproject/.gitignore | 1 + nbproject/build-impl.xml | 1403 ++++++++++++++++++++++++ nbproject/genfiles.properties | 8 + nbproject/project.properties | 84 ++ nbproject/project.xml | 13 + src/CLI.java | 78 ++ src/message/Ack.java | 141 +++ src/message/MessageCallback.java | 22 + src/message/MessageType.java | 19 + src/node/ChunkIdentifier.java | 36 + src/node/FileIdentifier.java | 28 + src/node/Identifier.java | 111 ++ src/node/Node.java | 476 ++++++++ src/node/NodeIdentifier.java | 35 + src/node/UDPHandler.java | 353 ++++++ src/routingtable/Bucket.java | 145 +++ src/routingtable/IRoutingTable.java | 19 + src/routingtable/RoutingTableImpl.java | 79 ++ src/util/BufferUtil.java | 28 + u5-1.pdf | Bin 0 -> 87016 bytes 24 files changed, 3163 insertions(+) create mode 100644 build.xml create mode 100644 build/.gitignore create mode 100644 logging.properties create mode 100644 manifest.mf create mode 100644 nbproject/.gitignore create mode 100644 nbproject/build-impl.xml create mode 100644 nbproject/genfiles.properties create mode 100644 nbproject/project.properties create mode 100644 nbproject/project.xml create mode 100644 src/CLI.java create mode 100644 src/message/Ack.java create mode 100644 src/message/MessageCallback.java create mode 100644 src/message/MessageType.java create mode 100644 src/node/ChunkIdentifier.java create mode 100644 src/node/FileIdentifier.java create mode 100644 src/node/Identifier.java create mode 100644 src/node/Node.java create mode 100644 src/node/NodeIdentifier.java create mode 100644 src/node/UDPHandler.java create mode 100644 src/routingtable/Bucket.java create mode 100644 src/routingtable/IRoutingTable.java create mode 100644 src/routingtable/RoutingTableImpl.java create mode 100644 src/util/BufferUtil.java create mode 100644 u5-1.pdf diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..793fefc --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project Kademlia. + + + diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..0a00d70 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1 @@ +*/ \ No newline at end of file diff --git a/logging.properties b/logging.properties new file mode 100644 index 0000000..2157f57 --- /dev/null +++ b/logging.properties @@ -0,0 +1,7 @@ +handlers=java.util.logging.ConsoleHandler + +.level=FINEST + +java.util.logging.SimpleFormatter.format=[%1$tF %1$tr] %3$s %4$s: %5$s %n +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level = FINEST diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/nbproject/.gitignore b/nbproject/.gitignore new file mode 100644 index 0000000..623f09e --- /dev/null +++ b/nbproject/.gitignore @@ -0,0 +1 @@ +/private \ No newline at end of file diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..a4a9efa --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..3a2424f --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=4d688e4d +build.xml.script.CRC32=c3cd04bd +build.xml.stylesheet.CRC32=8064a381@1.78.1.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=4d688e4d +nbproject/build-impl.xml.script.CRC32=269076b4 +nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..019f285 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,84 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=Kademlia +application.vendor=rylon +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/Kademlia.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.11-src=src +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.external.vm=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit.classpath}:\ + ${libs.junit_4.classpath} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=defaut +jnlp.offline-allowed=false +jnlp.signed=false +main.class=CLI +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=${file.reference.11-src} diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..a87d938 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,13 @@ + + + org.netbeans.modules.java.j2seproject + + + Kademlia + + + + + + + diff --git a/src/CLI.java b/src/CLI.java new file mode 100644 index 0000000..40de48c --- /dev/null +++ b/src/CLI.java @@ -0,0 +1,78 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetSocketAddress; +import java.nio.MappedByteBuffer; +import java.util.logging.LogManager; +import node.FileIdentifier; + +import node.Identifier; +import node.Node; +import node.NodeIdentifier; + +public class CLI { + + public static void main(String[] args) throws IOException { + System.setProperty("java.util.logging.config.file", + "logging.properties"); + + try { + LogManager.getLogManager().readConfiguration(); + } catch (Exception e) { + e.printStackTrace(); + } + + Node node = new Node(); + + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + String s; + while ((s = in.readLine()) != null && s.length() != 0) { + String[] splitted = s.split(" "); + + String cmd = splitted[0]; + + switch (cmd) { + //status + case "status": + for (NodeIdentifier id : node.getNeighbors()) { + System.out.println(id); + } + break; + //lookup fileID + case "lookup": + String fileID = splitted[1]; + // TODO not implemented + // Zum testen: + FileIdentifier fileIDToFind = new FileIdentifier(1, fileID.getBytes()); + node.findValue(fileIDToFind); + break; + //request fileID + case "request": + String fileID3 = splitted[1]; + FileIdentifier fileIDToFind2 = new FileIdentifier(1, fileID3.getBytes()); + node.sendDataReq(fileIDToFind2); + break; + //leave + case "leave": + node.leave(); + break; + //store fileID data + case "store": + String fileID2 = splitted[1]; + String data = splitted[2]; + // TODO not implemented + // Zum testen: + FileIdentifier fileIDToStore = new FileIdentifier(1,fileID2.getBytes()); + node.store(fileIDToStore); + node.storeData(fileIDToStore,data); + break; + default: + System.out.println("Unknown command."); + break; + } + } + } +} \ No newline at end of file diff --git a/src/message/Ack.java b/src/message/Ack.java new file mode 100644 index 0000000..f1426e6 --- /dev/null +++ b/src/message/Ack.java @@ -0,0 +1,141 @@ +package message; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.util.logging.Level; +import java.util.logging.Logger; + +import node.Identifier; +import node.NodeIdentifier; +import util.BufferUtil; + +public class Ack { + private final static Logger LOGGER = Logger.getLogger(Ack.class.getName()); + + /** + * timeout in seconds + */ + private static final int TIMEOUT = 1000; + + /** + * Maximum number of retries + */ + private static final int MAX_RETRIES = 3; + + private Identifier rpcId; + + private NodeIdentifier receiver; + + private ByteBuffer buffer; + + private int numRetries = 0; + + private TimeoutThread timeout; + private Thread thread; + + // The channel to re-send the message on + private DatagramChannel channel; + + private MessageCallback callback; + + public Ack(Identifier id, NodeIdentifier receiver, DatagramChannel channel, + ByteBuffer buffer, MessageCallback cb) { + this.rpcId = id; + this.receiver = receiver; + this.channel = channel; + this.buffer = BufferUtil.clone(buffer); + this.callback = cb; + startThread(); + } + + private void startThread() { + LOGGER.log(Level.FINEST, "Starting timeout thread for RPC " + rpcId); + timeout = new TimeoutThread(); + thread = new Thread(timeout); + thread.start(); + } + + public Identifier getID() { + return rpcId; + } + + public boolean check(NodeIdentifier fromID) { + return fromID.equals(receiver); + } + + public ByteBuffer getBuf() { + return buffer; + } + + public void setBuf(ByteBuffer buf) { + this.buffer = buf; + } + + public void setReceived() { + // Stop thread + try { + if (thread != null) { + timeout.terminate(); + thread.join(); + } + } catch (InterruptedException e) { + } + } + + private class TimeoutThread implements Runnable { + private volatile boolean notReceived = true; + + // When do we stop expecting the ack + private long timeToStop = System.currentTimeMillis() + TIMEOUT; + + @Override + public void run() { + while (notReceived && System.currentTimeMillis() < timeToStop) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + // Timeout hit! + + if (notReceived) { + + if (numRetries < MAX_RETRIES) { + try { + LOGGER.log( + Level.FINE, + "Didn't receive RPC Ack {0} by now. Resending... ", + new Object[] { rpcId }); + LOGGER.log(Level.INFO, receiver.getAddress().toString()); + channel.send(buffer, receiver.getAddress()); + } catch (IOException e) { + e.printStackTrace(); + } + + startThread(); + numRetries++; + } else { + + LOGGER.log(Level.INFO, "Absent RPC ack {0}.", + new Object[] { rpcId }); + + if (callback != null) { + callback.onTimeout(); + } + } + } else { + // Message has been received in time + if (callback != null) { + callback.onReceive(); + } + } + } + + public void terminate() { + notReceived = false; + } + } +} \ No newline at end of file diff --git a/src/message/MessageCallback.java b/src/message/MessageCallback.java new file mode 100644 index 0000000..d88b392 --- /dev/null +++ b/src/message/MessageCallback.java @@ -0,0 +1,22 @@ +package message; + +/** + * A callback to create asynchronous events that get triggered when a message + * (ack/answer) is received. + * + * @author jln + * + */ +public interface MessageCallback { + + /** + * Called when the awaited message arrives. + */ + public void onReceive(); + + /** + * Called when the awaited message doesn't arrive (even after possible + * retries). + */ + public void onTimeout(); +} diff --git a/src/message/MessageType.java b/src/message/MessageType.java new file mode 100644 index 0000000..40cb09c --- /dev/null +++ b/src/message/MessageType.java @@ -0,0 +1,19 @@ +package message; + +public class MessageType { + public final static byte FIND_NODE = 0; + public final static byte NODES = 1; + + public final static byte PING = 11; + public final static byte PONG = 12; + + public final static byte LEAVE = 2; + + public final static byte FIND_VALUE = 4; + public final static byte STORE = 5; + public final static byte DATA = 6; + public final static byte DATA_REQ = 7; + public final static byte VALUE_NODES = 8; + public final static byte FOUND_VALUE = 9; + public final static byte ACK = 10; +} diff --git a/src/node/ChunkIdentifier.java b/src/node/ChunkIdentifier.java new file mode 100644 index 0000000..3bc9143 --- /dev/null +++ b/src/node/ChunkIdentifier.java @@ -0,0 +1,36 @@ +package node; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class ChunkIdentifier extends Identifier { + + private String chunkID; + private FileIdentifier fileID; + + public ChunkIdentifier(int size, byte[] bytes, FileIdentifier fileID, String chunkID) { + super(size, bytes); + + this.fileID = fileID; + + //calculate SHA-256 Hash of chunckID + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(chunkID.getBytes()); + this.chunkID = md.digest().toString(); + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + public String getChunkID() { + return this.chunkID; + } + + public FileIdentifier getFileID(){ + return this.fileID; + } + +} diff --git a/src/node/FileIdentifier.java b/src/node/FileIdentifier.java new file mode 100644 index 0000000..0cd699e --- /dev/null +++ b/src/node/FileIdentifier.java @@ -0,0 +1,28 @@ +package node; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class FileIdentifier extends Identifier { + + private String fileID; + + public FileIdentifier(int size, byte[] fileID) { + super(size, fileID); + + /*//calculate SHA-256 Hash of key + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(fileID.getBytes()); + this.fileID = md.digest().toString(); + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + }*/ + + } + + public String getKey() { + return this.fileID; + } +} diff --git a/src/node/Identifier.java b/src/node/Identifier.java new file mode 100644 index 0000000..fb44ff8 --- /dev/null +++ b/src/node/Identifier.java @@ -0,0 +1,111 @@ +package node; + +import java.math.BigInteger; +import java.util.BitSet; +import java.util.Random; + +/** + * A Kademlia identifier. Can be used for identifying files as well as nodes + * (but for nodes check {@see NodeIdentifier}). + * + * @author jln + * + */ +public class Identifier { + private static Random random = new Random(System.currentTimeMillis()); + + protected BitSet bits; + + private int size; + + public Identifier(int size, byte[] bytes) { + this.size = size; + this.bits = BitSet.valueOf(bytes); + } + + private Identifier(int size, BitSet bits) { + this.size = size; + this.bits = bits; + } + + /** + * Creates an ID exactly "in the middle" of the ID space. (If the ID space + * is 8 bit wide, this returns an ID valued 128). + * + * @param size + * the size of the id space + * @return an Identifier + */ + public static Identifier getStaticIdentifier(int size) { + BitSet middle = new BitSet(size); + middle.set(size - 1); + return new Identifier(size, middle); + } + + /** + * Creates a random ID for the given id space size. + * + * @param size + * the size of the id space + * @return a random Identifier + */ + public static Identifier getRandomIdentifier(int size) { + BitSet bits = new BitSet(size); + + for (int i = 0; i < size; i++) { + double threshold = random.nextGaussian(); + if (threshold > 0) { + bits.set(i); + } + } + + return new Identifier(size, bits); + } + + public BigInteger distanceTo(Identifier otherID) { + BitSet distance = (BitSet) bits.clone(); + distance.xor(otherID.bits); + return new BigInteger(1, distance.toByteArray()); + } + + /** + * Returns whether the bit at the given position is set or not. The MSB is + * at position 0. + * + * @param index + * the index to check + * @return true if the bit is set + */ + public boolean isBitSetAt(int index) { + BigInteger intValue = new BigInteger(1, bits.toByteArray()); + int numOfTrimmedZeros = size - intValue.bitLength(); + + if (index < numOfTrimmedZeros) { + return false; + } + + return bits.get(bits.length() - (index + numOfTrimmedZeros) - 1); + } + + public byte[] getBytes() { + return bits.toByteArray(); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Identifier)) { + return false; + } else { + return bits.equals(((Identifier) o).bits); + } + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + public String toString() { + return new BigInteger(1, bits.toByteArray()).toString(); + } +} diff --git a/src/node/Node.java b/src/node/Node.java new file mode 100644 index 0000000..7436792 --- /dev/null +++ b/src/node/Node.java @@ -0,0 +1,476 @@ +package node; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import message.Ack; +import message.MessageCallback; +import message.MessageType; +import routingtable.IRoutingTable; +import routingtable.RoutingTableImpl; + +public class Node { + + private final static Logger LOGGER = Logger.getLogger(Node.class.getName()); + + /** + * Size of ID space (has to be a multiple of 8) + */ + public static final int ID_BITS = 8; + /** + * The bucket size + */ + public static final int BUCKET_SIZE = 2; + /** + * The first node is always spawned on port 50000 + */ + private static final int INITIAL_PORT = 50000; + private static final Identifier INITIAL_ID = Identifier + .getStaticIdentifier(ID_BITS); + private static final int BUFFER_SIZE = 512; + private static final int CHUNK_SIZE = 4; + /** + * The size of an IP address (in bytes) + */ + public static final int SIZE_IP_ADDRESS = 8; + + private InetSocketAddress address; + private DatagramChannel channel; + + public NodeIdentifier lastlookup = null; + + private Map> rpcs = new HashMap>(); + private Map values = new HashMap(); + + private Identifier searchID = null; + + private Thread thread; + private UDPHandler udpListen; + + private Identifier nodeID = Identifier.getRandomIdentifier(ID_BITS); + private IRoutingTable routingTable = new RoutingTableImpl(BUCKET_SIZE, this); + + private Map data = new HashMap();; + + public Node() { + System.setProperty("java.net.preferIPv4Stack", "true"); + + try { + channel = DatagramChannel.open(); + + try { + address = new InetSocketAddress("localhost", INITIAL_PORT); + channel.socket().bind(address); + + this.nodeID = INITIAL_ID; + } catch (SocketException e) { + // The initial port is already bound -> let the system pick a + // port + channel.socket().bind(new InetSocketAddress("localhost", 0)); + address = (InetSocketAddress) channel.getLocalAddress(); + } + + channel.configureBlocking(false); + + udpListen = new UDPHandler(this); + thread = new Thread(udpListen); + thread.start(); + + LOGGER.log(Level.INFO, "{0}: Initialized node {1} on {2}", + new Object[] { this.nodeID, getName(), address.toString() }); + + if (address.getPort() != INITIAL_PORT) { + // The port of this node is not the "INITIAL_PORT" (so it's not + // the first node in the network). So we try to join the network + // via the first node. + NodeIdentifier viaNode = new NodeIdentifier(ID_BITS, + INITIAL_ID.getBytes(), new InetSocketAddress( + "127.0.0.1", INITIAL_PORT)); + joinNetworkVia(viaNode); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void joinNetworkVia(NodeIdentifier viaNode) { + LOGGER.log(Level.INFO, "Trying to join network via node {0}", + new Object[] { viaNode }); + + routingTable.insert(viaNode); + sendFindNode(viaNode, this.nodeID); + } + + /** + * Creates and returns new ID (usually used as a RPC ID). This makes sure + * the ID is not yet used (in this node). + * + * @return an ID + */ + private Identifier createRPCID() { + Identifier rpcID = Identifier.getRandomIdentifier(ID_BITS); + while (rpcs.containsKey(rpcID)) { + rpcID = Identifier.getRandomIdentifier(ID_BITS); + } + return rpcID; + } + + void sendFindNode(NodeIdentifier receiver, Identifier idToFind) { + boolean successful = send(receiver, MessageType.FIND_NODE, + idToFind.getBytes(), true, null); + + if (successful) { + LOGGER.log(Level.INFO, "Sending [FIND_NODE {0}] to node {1}", + new Object[] { idToFind, receiver }); + } + } + + void sendFindValue(NodeIdentifier receiver, Identifier idToFind) { + // need to save the fileID because we need it for future searches + this.searchID = idToFind; + + LOGGER.log(Level.INFO, "Sending [FIND_VALUE {0}] to node {1}",new Object[] { idToFind, receiver }); + boolean successful = send(receiver, MessageType.FIND_VALUE, + idToFind.getBytes(), true, null); + + if (successful) { + LOGGER.log(Level.INFO, "Sent [FIND_VALUE {0}] to node {1}", + new Object[] { idToFind, receiver }); + } + } + + void sendFoundValue(NodeIdentifier receiver, Identifier idToFind, + Identifier rpcID) { + boolean successful = send(receiver, MessageType.FOUND_VALUE, rpcID, + values.get(idToFind).getBytes(), false, null); + + if (successful) { + LOGGER.log(Level.INFO, "Sending [FOUND_VALUE {0} -> {1}] to node {2}", + new Object[] { idToFind, values.get(idToFind), receiver }); + } + } + + /** + * Gets all nodes of this nodes routing table, that are close to a given + * node/fileID and sends that list to a specific node. + * + * @param receiver + * The node to receive the list of nodes + * @param idToFind + * The ID to find close nodes of + * @param rpcID + * An RPC ID (because this is always an answer to a FIND_NODE + * RPC) + * @param nodeType + * If true, we search a specific node, else a fileID + */ + void sendClosestNodesTo(NodeIdentifier receiver, Identifier idToFind, + Identifier rpcID, boolean nodeType) { + byte msgtype = 0; + if (nodeType) { + msgtype = MessageType.NODES; + } else { + msgtype = MessageType.VALUE_NODES; + } + + Set closeNodes = routingTable + .getClosestNodesTo(idToFind); + int numNodes = closeNodes.size(); + + ByteBuffer nodes = ByteBuffer.allocate(numNodes * (ID_BITS / 8) + + numNodes * SIZE_IP_ADDRESS); + + for (NodeIdentifier idToSend : closeNodes) { + // Don't send the node to itself + if (!receiver.equals(idToSend)) { + nodes.put(idToSend.getTripleAsBytes()); + } + } + + boolean successful = send(receiver, msgtype, rpcID, nodes.array(), + false, null); + + if (successful) { + LOGGER.log( + Level.INFO, + "Sending {0} nodes to to node {1} [FIND_NODE {2}] (rpcID={3})", + new Object[] { closeNodes.size(), receiver, idToFind, rpcID }); + } + } + + public void sendStore(NodeIdentifier receiver, Identifier fileID) { + boolean successful = send(receiver, MessageType.STORE, + fileID.getBytes(), true, null); + + if (successful) { + LOGGER.log(Level.INFO, "Sending [STORE {0}] to node {1}", + new Object[] { fileID, receiver }); + } + } + + public void sendAck(NodeIdentifier receiver, Identifier rpcID) { + send(receiver, MessageType.ACK, rpcID, null, false, null); + } + + public void sendDataReq(FileIdentifier fileID){ + //TODO + if(lastlookup == null){ + new Exception("lookup first!").printStackTrace(); + return;} + //String id = "128"; + //NodeIdentifier receiver = new NodeIdentifier(8, id.getBytes(), new InetSocketAddress("localhost", INITIAL_PORT)); + send(lastlookup, MessageType.DATA_REQ, fileID.getBytes(), true, null); + } + + public void sendData(NodeIdentifier receiver, Identifier fileID) { + + String data = this.data.get(fileID); + if(data == null){ + //TODO We dont have that data. -> DOES NOT WORK PROPERLY! + new Exception().printStackTrace(); + return; + } + int CHUNK_COUNT = data.length()/CHUNK_SIZE; + + for(int i = 0; i()); + rpcs.get(rpcID).add(newAck); + } + } + } + return successful; + } + + public String getName() { + return nodeID.toString(); + } + + public boolean hasAcks() { + return !rpcs.isEmpty(); + } + + public DatagramChannel getChannel() { + return channel; + } + + public void updateBuckets(NodeIdentifier id) { + routingTable.insert(id); + } + + public Identifier getID() { + return nodeID; + } + + public Set getNeighbors() { + return routingTable.getEntries(); + } + + public void storePair(Identifier key, Identifier nodeid) { + values.put(key, nodeid); + } + + public void store(Identifier key) { + + storePair(key,this.nodeID); + + Set nodes = routingTable.getClosestNodesTo(key); + + + for (NodeIdentifier node : nodes) { + sendStore(node, key); + } + } + + public void findValue(Identifier key) { + Set nodes = routingTable.getClosestNodesTo(key); + + + for (NodeIdentifier node : nodes) { + sendFindValue(node, key); + } + } + + public boolean hasKey(Identifier key) { + return values.containsKey(key); + } + + public Identifier getSearchID() { + return this.searchID; + } + + public boolean receivedRPC(NodeIdentifier fromID, Identifier rpcID) { + List rpcsFromID = rpcs.get(rpcID); + boolean removedAck = false; + + // wohl unschön, hier auf != null zu prüfen, da Fehler wo anders ist. + if (rpcsFromID != null) { + for (Ack ack : rpcsFromID) { + if (ack.check(fromID)) { + ack.setReceived(); + rpcsFromID.remove(ack); + removedAck = true; + + LOGGER.log(Level.FINEST, "Received RPC ack " + rpcID); + + break; + } + } + } + + if (!removedAck) { + LOGGER.log(Level.WARNING, + "Received RPC ack {0}, but didn't expect that", + new Object[] { rpcID }); + } + + return removedAck; + } + + public void leave() { + for (NodeIdentifier n : getNeighbors()) { + sendLeave(n); + } + System.exit(0); + } + + private boolean sendLeave(NodeIdentifier n) { + return send(n, MessageType.LEAVE, null, false, null); + } + + public void storeData(FileIdentifier id, String data) { + this.data.put(id, data); + LOGGER.log(Level.INFO, "Stored Data [{0}] as [{1}])", + new Object[] { data, id}); + } + + + public void sendFile(NodeIdentifier nodeID, File file) { + + + } +} \ No newline at end of file diff --git a/src/node/NodeIdentifier.java b/src/node/NodeIdentifier.java new file mode 100644 index 0000000..37ca438 --- /dev/null +++ b/src/node/NodeIdentifier.java @@ -0,0 +1,35 @@ +package node; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; + +import util.BufferUtil; + +/** + * Same as a {@link Identifier}, but this also stores an IP address. + * + * @author jln + * + */ +public class NodeIdentifier extends Identifier { + + private InetSocketAddress address; + + public NodeIdentifier(int size, byte[] bytes, InetSocketAddress address) { + super(size, bytes); + this.address = address; + } + + public byte[] getTripleAsBytes() { + ByteBuffer result = ByteBuffer.allocate(Node.SIZE_IP_ADDRESS + + (Node.ID_BITS / 8)); + + result.put(BufferUtil.addrToBytes(address)); + result.put(bits.toByteArray()); + return result.array(); + } + + public InetSocketAddress getAddress() { + return address; + } +} \ No newline at end of file diff --git a/src/node/UDPHandler.java b/src/node/UDPHandler.java new file mode 100644 index 0000000..ef592fa --- /dev/null +++ b/src/node/UDPHandler.java @@ -0,0 +1,353 @@ +package node; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import message.MessageType; + +public class UDPHandler implements Runnable { + private final static Logger LOGGER = Logger.getLogger(UDPHandler.class + .getName()); + + public static final int BUF_SIZE = 512; + + private volatile boolean running = true; + private ByteBuffer buffer = ByteBuffer.allocate(BUF_SIZE); + + private Node node; + + HashMap > chunklist = new HashMap >(); + + public UDPHandler(Node node) { + this.node = node; + } + + /** + * Takes the buffer of this UDPHandler as is and tries to read an IP address + * (4 bytes and 1 int) from it. If there is no/incomplete or wrong data, + * this will fail. + * + * @return the address that has been read + */ + private InetSocketAddress getIPFromBuffer() { + StringBuilder theAddr = new StringBuilder(); + // Read 4 Bytes and 1 Integer = 1 IP address + for (int i = 0; i < 4; i++) { + theAddr.append(buffer.get()); + if (i < 3) { + theAddr.append("."); + } + } + int port = buffer.getInt(); + return new InetSocketAddress(theAddr.toString(), port); + } + + private Identifier getIDFromBuffer() { + int numBytes = Node.ID_BITS / 8; + byte[] result = new byte[numBytes]; + for (int i = 0; i < numBytes; i++) { + result[i] = buffer.get(); + } + return new Identifier(Node.ID_BITS, result); + } + + /** + * Reads a triple from the channel and returns a + * {@link node.NodeIdentifier}. + * + * @return the read node ID + */ + private NodeIdentifier getNodeTripleFromBuffer() { + InetSocketAddress address = getIPFromBuffer(); + + int numBytes = Node.ID_BITS / 8; + byte[] result = new byte[numBytes]; + for (int i = 0; i < numBytes; i++) { + result[i] = buffer.get(); + } + LOGGER.log(Level.INFO,"Read Buffer id: "+Node.ID_BITS+" result: "+result+" addr: "+address); + return new NodeIdentifier(Node.ID_BITS, result, address); + } + + public void run() { + InetSocketAddress from = null; + + // Run until it gets killed, and all my Acks have been answered + while (running || node.hasAcks()) { + try { + // Flag that indicates whether the routing table should be + // updated with the node we just received a message from. This + // needs to be done, because some messages trigger a direct + // answer. For example we send a PING to a node. That node + // answers with a PONG. Because we received a message from that + // node we will update our routing table and see that we already + // know this node. So we will PING that node... + boolean updateRT = true; + + // The address of the node that sent this message + from = (InetSocketAddress) node.getChannel().receive(buffer); + + // channel.receive() is non-blocking. So we need to check if + // something actually has been written to the buffer + if (buffer.remaining() != BUF_SIZE) { + buffer.flip(); + + byte messageType = buffer.get(); + + NodeIdentifier fromID = new NodeIdentifier(Node.ID_BITS, + getIDFromBuffer().getBytes(), from); + + Identifier rpcID = getIDFromBuffer(); + + switch (messageType) { + case MessageType.FIND_NODE: + receiveFindNode(fromID, rpcID); + break; + case MessageType.NODES: + receiveNodes(fromID, rpcID); + break; + case MessageType.PING: + updateRT = false; + receivePing(fromID, rpcID); + break; + case MessageType.PONG: + updateRT = false; + receivePong(fromID, rpcID); + break; + case MessageType.LEAVE: + // We don't have to do anything here because, after this + // switch block we call node.updateBuckets(...) which + // will try to ping the node we received this leave + // message from. That node will not answered because it + // directly shut down after sending the leave message. + // So the node will be removed from this routing table. + LOGGER.log(Level.INFO, "Received leave from {0}", + new Object[] { from.toString() }); + break; + case MessageType.FIND_VALUE: + receiveFindValue(fromID, rpcID); + break; + case MessageType.VALUE_NODES: + receiveValueNodes(fromID, rpcID); + break; + case MessageType.FOUND_VALUE: + receiveFoundValue(fromID, rpcID); + break; + case MessageType.STORE: + receiveStore(fromID, rpcID); + break; + case MessageType.DATA: + receiveData(fromID, rpcID); + LOGGER.log(Level.INFO, "Received DATA from {0}", + new Object[] { from.toString() }); + break; + case MessageType.DATA_REQ: + receiveDataReq(fromID,rpcID); + LOGGER.log(Level.INFO, "Received DATA_REQ from {0}", + new Object[] { from.toString() }); + break; + case MessageType.ACK: + receiveAck(fromID, rpcID); + break; + default: + LOGGER.log(Level.INFO, + "Received unknown command from {0}: [{1}]{2}", + new Object[] { from.toString(), messageType, + new String(buffer.array()) }); + } + + if (updateRT) { + node.updateBuckets(new NodeIdentifier(Node.ID_BITS, + fromID.getBytes(), from)); + } + + } else { + // If nothing has been read/received wait and read/receive + // again + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + buffer.clear(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private void receiveAck(NodeIdentifier fromID, Identifier rpcID) { + // This should be the either answer to a prior STORE or FOUND_VALUE -> + // mark this RPC ID as received + node.receivedRPC(fromID, rpcID); + } + + private void receiveFoundValue(NodeIdentifier fromID, Identifier rpcID) { + Identifier idToFind = getIDFromBuffer(); + node.lastlookup = fromID; + // TODO Auto-generated method stub + // Node kontaktieren, damit Datei gesendet werden kann. + + // This should be the answer to a prior FIND_VALUE -> mark this RPC ID + // as received + node.receivedRPC(fromID, rpcID); + + LOGGER.log(Level.INFO, "Received [FOUND VALUE on Node {0}] from Node {1}", + new Object[] { idToFind, fromID }); + } + + private void receiveValueNodes(NodeIdentifier fromID, Identifier rpcID) { + int numReceived = 0; + + // This is just for the log message + StringBuilder nodes = new StringBuilder(); + + while (buffer.hasRemaining()) { + NodeIdentifier newID = getNodeTripleFromBuffer(); + node.sendFindValue(newID, node.getSearchID()); + nodes.append(newID).append(", "); + numReceived++; + } + + // This should be the answer to a prior FIND_VALUE -> mark this RPC ID + // as + // received + node.receivedRPC(fromID, rpcID); + + LOGGER.log(Level.INFO, + "Received {0} [VALUE NODES] [{1}] from Node {2})", + new Object[] { numReceived, nodes.toString(), fromID }); + } + + private void receiveData(NodeIdentifier fromID, Identifier rpcID) { + + String data = new String(buffer.array()); + String parts[] = data.split("-"); + + String fileID = parts[0]; + int chunkCount = Integer.parseInt(parts[1]); + int chunkID = Integer.parseInt(parts[2]); + String chunkContent = parts[3]; + LOGGER.log(Level.INFO,"recieved Chunk file: "+fileID+" count: "+chunkCount+" id: "+chunkID); + + FileIdentifier fid = new FileIdentifier(1,fileID.getBytes()); + if(chunklist.get(fid) == null){ + chunklist.put(fid, new HashMap()); + } + + if(chunklist.get(fid).get(chunkID) == null){ + chunklist.get(fid).put(chunkID, chunkContent); + } + + if(chunklist.get(fid).size() >= chunkCount){ + LOGGER.log(Level.INFO,"FILE complete file: "+fileID+" count: "+chunkCount+" id: "+chunkID); + String file = ""; + for(int i=0; i mark this RPC ID as + // received + node.receivedRPC(fromID, rpcID); + } + + private void receivePing(NodeIdentifier fromID, Identifier rpcID) { + LOGGER.log(Level.INFO, "Received [PING] from {0}", + new Object[] { fromID }); + node.sendPong(fromID, rpcID); + } + + private void receiveNodes(NodeIdentifier fromID, Identifier rpcID) { + + int numReceived = 0; + + // This is just for the log message + StringBuilder nodes = new StringBuilder(); + + while (buffer.hasRemaining()) { + NodeIdentifier newID = getNodeTripleFromBuffer(); + node.updateBuckets(newID); + nodes.append(newID).append(", "); + numReceived++; + } + + // This should be the answer to a prior FIND_NODE -> mark this RPC ID as + // received + node.receivedRPC(fromID, rpcID); + + LOGGER.log(Level.INFO, "Received {0} [NODES] [{1}] from Node {2})", + new Object[] { numReceived, nodes.toString(), fromID }); + } + + private void receiveFindNode(NodeIdentifier fromID, Identifier rpc_id) { + Identifier idToFind = getIDFromBuffer(); + + LOGGER.log(Level.INFO, "Received [FIND_NODE {0}] from Node {1}", + new Object[] { idToFind, fromID }); + + node.sendClosestNodesTo(fromID, idToFind, rpc_id, true); + } + + private void receiveStore(NodeIdentifier fromID, Identifier rpcID) { + Identifier fileID = getIDFromBuffer(); + + LOGGER.log(Level.INFO, "Received [STORE {0}] from Node {1}", + new Object[] { fileID, fromID }); + + node.storePair(fileID, fromID); + + node.sendAck(fromID, rpcID); + } + + private void receiveFindValue(NodeIdentifier fromID, Identifier rpcID) { + Identifier fileID = getIDFromBuffer(); + + LOGGER.log(Level.INFO, "Received [FIND VALUE {0}] from Node {1}", + new Object[] { fileID, fromID }); + + if (node.hasKey(fileID)) { + node.sendFoundValue(fromID, fileID, rpcID); + } else { + node.sendClosestNodesTo(fromID, fileID, rpcID, false); + } + } + + public void terminate() { + running = false; + } +} diff --git a/src/routingtable/Bucket.java b/src/routingtable/Bucket.java new file mode 100644 index 0000000..271d063 --- /dev/null +++ b/src/routingtable/Bucket.java @@ -0,0 +1,145 @@ +package routingtable; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import message.MessageCallback; +import node.Node; +import node.NodeIdentifier; + +public class Bucket { + private final static Logger LOGGER = Logger.getLogger(Bucket.class + .getName()); + + private Bucket left; + private Bucket right; + + private List entries; + + private int bucketSize; + private int level; + + private Node node; + + public Bucket(int bucketSize, int level, Node node) { + this.bucketSize = bucketSize; + this.level = level; + this.node = node; + entries = new ArrayList(); + } + + /** + * Returns the nodes of this very bucket. + * + * @return + */ + public List getNodes() { + return entries; + } + + public boolean contains(NodeIdentifier id) { + if (!isLeaf()) { + return left.contains(id) || right.contains(id); + } + return entries.contains(id); + } + + /** + * Tries to update the given node. + * + * @param id + * @return true if the node is still available, else false + */ + public void update(final NodeIdentifier id) { + if (!isLeaf()) { + if (id.isBitSetAt(level)) { + left.update(id); + } else { + right.update(id); + } + } else { + node.sendPing(id, new MessageCallback() { + @Override + public void onReceive() { + LOGGER.log(Level.INFO, + "Node answered in time, moving to top of list."); + entries.remove(id); + entries.add(0, id); + } + + @Override + public void onTimeout() { + LOGGER.log(Level.INFO, "Node didnt answer in time."); + // TODO: this should be propagated to the "upper" Routing + // Table, not just to this specific bucket + entries.remove(id); + } + }); + } + } + + public void insert(NodeIdentifier newId) { + insert(newId, ""); + } + + public void insert(NodeIdentifier newId, String path) { + if (isLeaf()) { + if (entries.size() < bucketSize) { + LOGGER.log(Level.INFO, + "Added node {0} to RT [{1}] on level {2}", + new Object[] { newId, path, level }); + entries.add(newId); + } else { + LOGGER.log(Level.INFO, "Split on level " + level + + " while adding " + newId); + + LOGGER.log(Level.INFO, + "Distributing present nodes to lower buckets"); + + Bucket newLeft = new Bucket(bucketSize, level + 1, node); + Bucket newRight = new Bucket(bucketSize, level + 1, node); + + // Add the new entry and in the following loop distribute all + // existing entries to left/right + entries.add(newId); + + for (NodeIdentifier id : entries) { + if (id.isBitSetAt(level)) { + newLeft.insert(id, path + "1"); + } else { + newRight.insert(id, path + "0"); + } + } + + this.entries = null; + this.left = newLeft; + this.right = newRight; + } + } else { + if (newId.isBitSetAt(level)) { + left.insert(newId, path + "1"); + } else { + right.insert(newId, path + "0"); + } + } + + } + + private boolean isLeaf() { + return left == null && right == null; + } + + public void remove(NodeIdentifier node) { + if (isLeaf()) { + entries.remove(node); + } else { + if (node.isBitSetAt(level)) { + left.remove(node); + } else { + right.remove(node); + } + } + } +} \ No newline at end of file diff --git a/src/routingtable/IRoutingTable.java b/src/routingtable/IRoutingTable.java new file mode 100644 index 0000000..8dd53f0 --- /dev/null +++ b/src/routingtable/IRoutingTable.java @@ -0,0 +1,19 @@ +package routingtable; + +import java.util.Set; + +import node.Identifier; +import node.NodeIdentifier; + +public interface IRoutingTable { + + public void insert(NodeIdentifier id); + + public Set getClosestNodesTo(Identifier id); + + public boolean contains(NodeIdentifier node); + + public void remove(NodeIdentifier node); + + public Set getEntries(); +} \ No newline at end of file diff --git a/src/routingtable/RoutingTableImpl.java b/src/routingtable/RoutingTableImpl.java new file mode 100644 index 0000000..5a36c49 --- /dev/null +++ b/src/routingtable/RoutingTableImpl.java @@ -0,0 +1,79 @@ +package routingtable; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import node.Identifier; +import node.Node; +import node.NodeIdentifier; + +public class RoutingTableImpl implements IRoutingTable { + private Set entries = new HashSet(); + + private Bucket root; + + private int bucketSize; + + public RoutingTableImpl(int bucketSize, Node node) { + this.bucketSize = bucketSize; + this.root = new Bucket(bucketSize, 0, node); + } + + @Override + public void insert(NodeIdentifier id) { + if (root.contains(id)) { + root.update(id); + } else { + entries.add(id); + root.insert(id); + } + } + + @Override + public Set getClosestNodesTo(final Identifier id) { + Set result = new HashSet(); + + if (entries.size() <= bucketSize) { + result.addAll(entries); + + } else { + List temp = new ArrayList(entries); + + Collections.sort(temp, new Comparator() { + @Override + public int compare(NodeIdentifier o1, NodeIdentifier o2) { + BigInteger dist1 = id.distanceTo(o1); + BigInteger dist2 = id.distanceTo(o2); + return dist1.compareTo(dist2); + } + }); + + for (int i = 0; i < bucketSize; i++) { + result.add(temp.get(i)); + } + result = new HashSet(temp.subList(0, + Node.BUCKET_SIZE)); + } + return result; + } + + @Override + public boolean contains(NodeIdentifier node) { + return root.contains(node); + } + + @Override + public void remove(NodeIdentifier node) { + + } + + @Override + public Set getEntries() { + return entries; + } +} \ No newline at end of file diff --git a/src/util/BufferUtil.java b/src/util/BufferUtil.java new file mode 100644 index 0000000..182ce91 --- /dev/null +++ b/src/util/BufferUtil.java @@ -0,0 +1,28 @@ +package util; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; + +public class BufferUtil { + + public static ByteBuffer clone(ByteBuffer original) { + ByteBuffer clone = ByteBuffer.allocate(original.capacity()); + + int oldPosition = original.position(); + original.rewind();// copy from the beginning + clone.put(original); + // original.rewind(); + original.position(oldPosition); + clone.flip(); + return clone; + } + + public static byte[] addrToBytes(InetSocketAddress addr) { + ByteBuffer buffer = ByteBuffer.allocate(8); + for (String part : addr.getHostString().split("\\.")) { + buffer.put(Byte.valueOf(part)); + } + buffer.putInt(addr.getPort()); + return buffer.array(); + } +} diff --git a/u5-1.pdf b/u5-1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ce6dd9256f02588917c456d55dbae36d5526ac1b GIT binary patch literal 87016 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 XKx|0h>8`aP>ROuYQc^}JWA=XoAnXpY literal 0 HcmV?d00001