diff --git a/ws2012/P2P/uebungen/8/.gitignore b/ws2012/P2P/uebungen/8/.gitignore new file mode 100644 index 00000000..e660fd93 --- /dev/null +++ b/ws2012/P2P/uebungen/8/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/ws2012/P2P/uebungen/8/src/node/Identifier.java b/ws2012/P2P/uebungen/8/src/node/Identifier.java index 7bd748a7..ce4b024f 100644 --- a/ws2012/P2P/uebungen/8/src/node/Identifier.java +++ b/ws2012/P2P/uebungen/8/src/node/Identifier.java @@ -1,58 +1,69 @@ package node; -import java.nio.ByteBuffer; +import java.math.BigInteger; +import java.util.BitSet; import java.util.Random; public class Identifier { - private static Random random = new Random(System.currentTimeMillis()); + private static Random random = new Random(System.currentTimeMillis()); - private int id; - private boolean bits[] = new boolean[Node.ID_BITS]; + private BitSet bits; - public Identifier(int id) { - this.id = id; + public Identifier(byte[] bytes) { + this.bits = BitSet.valueOf(bytes); + } - String bitstring = Integer.toBinaryString(id); - for (int i = bitstring.length(); i > 0; i--) { - bits[bits.length - i] = bitstring.charAt(bitstring.length() - i) == '1'; - } - } + public Identifier(BitSet bits) { + this.bits = bits; + } - public static Identifier getRandomIdentifier() { - int max_value = (int) Math.pow(2, Node.ID_BITS); - int id = (int) Math.abs((Math.round((max_value / 2) - + random.nextGaussian() * (max_value / 2)) % max_value)); - return new Identifier(id); - } + public Identifier() { + BitSet middle = new BitSet(Node.ID_BITS); + middle.set(0); + this.bits = middle; + } - public int getId() { - return id; - } + public static Identifier getRandomIdentifier() { + BitSet bits = new BitSet(Node.ID_BITS); - public int distanceTo(Identifier otherID) { - return id ^ otherID.getId(); - } + for (int i = 0; i < Node.ID_BITS; i++) { + double threshold = random.nextGaussian(); + if (threshold > 0) { + bits.set(i); + } + } - public boolean isBitSetAt(int index) { - return bits[index]; - } + return new Identifier(bits); + } - public byte[] getBytes() { - ByteBuffer result = ByteBuffer.allocate(4); - result.putInt(id); - return result.array(); - } + public BitSet distanceTo(Identifier otherID) { + BitSet distance = (BitSet) bits.clone(); + distance.xor(otherID.getBitSet()); + return distance; + } - @Override - public boolean equals(Object o) { - if (!(o instanceof Identifier)) { - return false; - } else { - return id == ((Identifier) o).getId(); - } - } + public boolean isBitSetAt(int index) { + return bits.get(index); + } - public String toString() { - return String.valueOf(id); - } + public byte[] getBytes() { + return bits.toByteArray(); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Identifier)) { + return false; + } else { + return bits.equals(((Identifier) o).getBitSet()); + } + } + + public BitSet getBitSet() { + return bits; + } + + public String toString() { + return new BigInteger(bits.toByteArray()).toString(); + } } diff --git a/ws2012/P2P/uebungen/8/src/node/Node.java b/ws2012/P2P/uebungen/8/src/node/Node.java index 828c23ba..3f72dab5 100644 --- a/ws2012/P2P/uebungen/8/src/node/Node.java +++ b/ws2012/P2P/uebungen/8/src/node/Node.java @@ -2,183 +2,189 @@ package node; import java.io.IOException; import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; import message.Ack; import message.MessageType; +import routingtable.BucketEntry; import routingtable.RoutingTable; public class Node { - /** - * Size of ID space - */ - public static final int ID_BITS = 8; + /** + * Size of ID space + */ + public static final int ID_BITS = 8; - /** - * The bucket size - */ - private static final int BUCKET_SIZE = 2; + /** + * The bucket size + */ + private static final int BUCKET_SIZE = 2; - /** - * The first node is always spawned on port 9999 - */ - private static final int INITIAL_PORT = 9999; - private static final Identifier INITIAL_ID = new Identifier((int) Math.pow( - 2, ID_BITS) / 2); + /** + * The first node is always spawned on port 50000 + */ + private static final int INITIAL_PORT = 50000; + private static final Identifier INITIAL_ID = new Identifier(); - private final static Logger LOGGER = Logger.getLogger(Node.class.getName()); + private final static Logger LOGGER = Logger.getLogger(Node.class.getName()); - private InetSocketAddress address; - private DatagramChannel channel; + private InetSocketAddress address; + private DatagramChannel channel; - private Map acks = new HashMap(); + private Map acks = new HashMap(); - private Thread thread; - private UDPHandler udpListen; + private Thread thread; + private UDPHandler udpListen; - private Identifier nodeID = Identifier.getRandomIdentifier(); + private Identifier nodeID = Identifier.getRandomIdentifier(); - private RoutingTable rt = new RoutingTable(BUCKET_SIZE); + private RoutingTable rt = new RoutingTable(BUCKET_SIZE); - public Node() { - System.setProperty("java.net.preferIPv4Stack", "true"); + public Node() { + System.setProperty("java.net.preferIPv4Stack", "true"); - try { - channel = DatagramChannel.open(); + try { + channel = DatagramChannel.open(); - try { - address = new InetSocketAddress("localhost", INITIAL_PORT); - channel.socket().bind(address); + try { + address = new InetSocketAddress("localhost", INITIAL_PORT); + channel.socket().bind(address); - this.nodeID = INITIAL_ID; - } catch (SocketException e) { - // Port 9999 is already bound -> pick a random port - channel.socket().bind(new InetSocketAddress("localhost", 0)); - address = (InetSocketAddress) channel.getLocalAddress(); - } + this.nodeID = INITIAL_ID; + } catch (SocketException e) { + // Port 9999 is already bound -> pick a random port + channel.socket().bind(new InetSocketAddress("localhost", 0)); + address = (InetSocketAddress) channel.getLocalAddress(); + } - channel.configureBlocking(false); + channel.configureBlocking(false); - udpListen = new UDPHandler(this); - thread = new Thread(udpListen); - thread.start(); + udpListen = new UDPHandler(this); + thread = new Thread(udpListen); + thread.start(); - LOGGER.log(Level.INFO, "Initialized node {0} on {1}", new Object[] { - getName(), address.toString() }); + LOGGER.log(Level.INFO, "Initialized node {0} on {1}", new Object[] { + getName(), address.toString() }); - if (address.getPort() != INITIAL_PORT) { - joinNetworkVia( - new InetSocketAddress("localhost", INITIAL_PORT), - INITIAL_ID); - } + if (address.getPort() != INITIAL_PORT) { + joinNetworkVia( + new InetSocketAddress("localhost", INITIAL_PORT), + INITIAL_ID); + } - } catch (IOException e) { - e.printStackTrace(); - } - } + } catch (IOException e) { + e.printStackTrace(); + } + } - private void joinNetworkVia(InetSocketAddress receiver, Identifier id) { - LOGGER.log(Level.INFO, "Trying to join network via node {0} ({1})", - new Object[] { id, receiver }); + private void joinNetworkVia(InetSocketAddress receiver, Identifier id) { + LOGGER.log(Level.INFO, "Trying to join network via node {0} ({1})", + new Object[] { id, receiver }); - rt.update(id, receiver); - // Send a FIND_NODE. The node to find is my own id (bootstrapping...) - sendFindNode(receiver, nodeID); - } + rt.update(id, receiver); + // Send a FIND_NODE. The node to find is my own id (bootstrapping...) + sendFindNode(receiver, nodeID); + } - void sendFindNode(InetSocketAddress receiver, Identifier idToFind) { - ByteBuffer buffer = ByteBuffer.allocate(5 + 4 + 4); + void sendFindNode(InetSocketAddress receiver, Identifier idToFind) { + ByteBuffer buffer = ByteBuffer.allocate(5 + 4 + 4); - Identifier rpc_id = Identifier.getRandomIdentifier(); + Identifier rpc_id = Identifier.getRandomIdentifier(); - buffer.put(MessageType.FIND_NODE); - buffer.put(nodeID.getBytes()); - buffer.put(rpc_id.getBytes()); - buffer.put(idToFind.getBytes()); + buffer.put(MessageType.FIND_NODE); + buffer.put(nodeID.getBytes()); + buffer.put(rpc_id.getBytes()); + buffer.put(idToFind.getBytes()); - if (send(buffer, receiver)) { - LOGGER.log(Level.INFO, "Sending [FIND_NODE {0}] to node {1}", - new Object[] { idToFind, receiver }); - } - } + if (send(buffer, receiver)) { + LOGGER.log(Level.INFO, "Sending [FIND_NODE {0}] to node {1}", + new Object[] { idToFind, receiver }); + } + } - void sendClosestNodesTo(InetSocketAddress receiver, Identifier receiverId, - Identifier idToFind, Identifier rpc_id) { + void sendClosestNodesTo(InetSocketAddress receiver, Identifier receiverId, + Identifier idToFind, Identifier rpc_id) { - ByteBuffer buffer = ByteBuffer.allocate(5 + 4 + 4); + // TODO: how big?! + ByteBuffer buffer = ByteBuffer.allocate(512); - buffer.put(MessageType.NODES); - buffer.put(nodeID.getBytes()); - buffer.put(rpc_id.getBytes()); + buffer.put(MessageType.NODES); + buffer.put(nodeID.getBytes()); + buffer.put(rpc_id.getBytes()); - if (send(buffer, receiver)) { - LOGGER.log(Level.INFO, - "Sending [NODES XX] to node {0}. TODO: not implemented", - new Object[] { receiverId }); - } - } + Set entries = rt.getClosestNodesTo(idToFind); + for (BucketEntry e : entries) { + if (!receiverId.equals(e.id)) + buffer.put(e.getBytes()); + } - private boolean send(ByteBuffer buffer, InetSocketAddress to) { - buffer.flip(); - try { - channel.send(buffer, to); - return true; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } + if (send(buffer, receiver)) { + LOGGER.log(Level.INFO, "Sending [NODES {0}] to node {1}.", + new Object[] { idToFind, receiverId }); + } + } - public String getName() { - return nodeID.toString(); - } + private boolean send(ByteBuffer buffer, InetSocketAddress to) { + buffer.flip(); + try { + channel.send(buffer, to); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } - public boolean hasAcks() { - return !acks.isEmpty(); - } + public String getName() { + return nodeID.toString(); + } - protected boolean hasAck(int ack_id) { - return acks.containsKey(ack_id); - } + public boolean hasAcks() { + return !acks.isEmpty(); + } - protected Ack getAck(int ack_id) { - return acks.get(ack_id); - } + protected boolean hasAck(int ack_id) { + return acks.containsKey(ack_id); + } - protected void removeAck(int ack_id) { - acks.remove(ack_id).setReceived(); - } + protected Ack getAck(int ack_id) { + return acks.get(ack_id); + } - public DatagramChannel getChannel() { - return channel; - } + protected void removeAck(int ack_id) { + acks.remove(ack_id).setReceived(); + } - public void updateBuckets(Identifier id, InetSocketAddress from) { - rt.update(id, from); - } + public DatagramChannel getChannel() { + return channel; + } - public Identifier getID() { - return nodeID; - } + public void updateBuckets(Identifier id, InetSocketAddress from) { + rt.update(id, from); + } - public static void main(String[] args) { - System.setProperty("java.util.logging.config.file", - "logging.properties"); + public Identifier getID() { + return nodeID; + } - try { - LogManager.getLogManager().readConfiguration(); - } catch (Exception e) { - e.printStackTrace(); - } + public static void main(String[] args) { + System.setProperty("java.util.logging.config.file", + "logging.properties"); - new Node(); - } + try { + LogManager.getLogManager().readConfiguration(); + } catch (Exception e) { + e.printStackTrace(); + } + + new Node(); + } } diff --git a/ws2012/P2P/uebungen/8/src/node/UDPHandler.java b/ws2012/P2P/uebungen/8/src/node/UDPHandler.java index f8adff43..228ff11a 100644 --- a/ws2012/P2P/uebungen/8/src/node/UDPHandler.java +++ b/ws2012/P2P/uebungen/8/src/node/UDPHandler.java @@ -3,119 +3,143 @@ package node; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; +import java.util.HashSet; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import message.MessageType; +import routingtable.BucketEntry; public class UDPHandler implements Runnable { - private final static Logger LOGGER = Logger.getLogger(UDPHandler.class - .getName()); + private final static Logger LOGGER = Logger.getLogger(UDPHandler.class + .getName()); - public static final int BUF_SIZE = 512; + public static final int BUF_SIZE = 512; - private volatile boolean running = true; - private ByteBuffer buffer = ByteBuffer.allocate(BUF_SIZE); + private volatile boolean running = true; + private ByteBuffer buffer = ByteBuffer.allocate(BUF_SIZE); - private Node node; + private Node node; - public UDPHandler(Node node) { - this.node = node; - } + 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 readIPFromBuffer() { - 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); - } + /** + * 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); + } - public void run() { - InetSocketAddress from = null; + private Identifier getIDFromBuffer() { + int numBytes = Node.ID_BITS / 8; + byte[] result = new byte[numBytes]; - // Run until I get killed, and all my acks have been answered - while (running || node.hasAcks()) { - try { - from = (InetSocketAddress) node.getChannel().receive(buffer); + for (int i = 0; i < numBytes; i++) { + result[i] = buffer.get(); + } - // 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(); + return new Identifier(result); + } - byte messageType = buffer.get(); + public void run() { + InetSocketAddress from = null; - // TODO: this should read exactly ID_BITS bits not just an - // int - Identifier fromID = new Identifier(buffer.getInt()); - Identifier rpc_id = new Identifier(buffer.getInt()); + // Run until I get killed, and all my acks have been answered + while (running || node.hasAcks()) { + try { + from = (InetSocketAddress) node.getChannel().receive(buffer); - switch (messageType) { - case MessageType.FIND_NODE: - receiveFindNode(from, fromID, rpc_id); - break; - case MessageType.NODES: - receiveNodes(from, fromID, rpc_id); - break; - case MessageType.PING: - LOGGER.log(Level.INFO, "Received [ping] from {0}", - new Object[] { from.toString() }); - break; - default: - LOGGER.log(Level.INFO, - "Received unknown command from {0}: [{1}]{2}", - new Object[] { from.toString(), messageType, - new String(buffer.array()) }); - } + // 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(); - node.updateBuckets(fromID, from); - } else { - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + byte messageType = buffer.get(); - buffer.clear(); + Identifier fromID = getIDFromBuffer(); + Identifier rpc_id = getIDFromBuffer(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } + switch (messageType) { + case MessageType.FIND_NODE: + receiveFindNode(from, fromID, rpc_id); + break; + case MessageType.NODES: + receiveNodes(from, fromID, rpc_id); + break; + case MessageType.PING: + LOGGER.log(Level.INFO, "Received [ping] from {0}", + new Object[] { from.toString() }); + break; + default: + LOGGER.log(Level.INFO, + "Received unknown command from {0}: [{1}]{2}", + new Object[] { from.toString(), messageType, + new String(buffer.array()) }); + } - private void receiveNodes(InetSocketAddress from, Identifier fromID, - Identifier rpc_id) { + node.updateBuckets(fromID, from); + } else { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } - LOGGER.log(Level.INFO, "Received [NODES XX] from Node {0} ({1})", - new Object[] { fromID, from.toString() }); - } + buffer.clear(); - private void receiveFindNode(InetSocketAddress from, Identifier fromID, - Identifier rpc_id) { - Identifier idToFind = new Identifier(buffer.getInt()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } - LOGGER.log(Level.INFO, "Received [FIND_NODE {0}] from Node {1} ({2})", - new Object[] { idToFind, fromID, from.toString() }); + private void receiveNodes(InetSocketAddress from, Identifier fromID, + Identifier rpc_id) { - node.sendClosestNodesTo(from, fromID, idToFind, rpc_id); - } + Set readEntries = new HashSet(); - public void terminate() { - running = false; - } + while (buffer.hasRemaining()) { + InetSocketAddress address = getIPFromBuffer(); + Identifier id = getIDFromBuffer(); + BucketEntry entry = new BucketEntry(id, address); + readEntries.add(entry); + + // TODO: Just add all nodes?! + node.updateBuckets(id, address); + } + + LOGGER.log(Level.INFO, "Received {0} [NODES] from Node {1} ({2})", + new Object[] { readEntries.size(), fromID, from.toString() }); + } + + private void receiveFindNode(InetSocketAddress from, Identifier fromID, + Identifier rpc_id) { + Identifier idToFind = getIDFromBuffer(); + + LOGGER.log(Level.INFO, "Received [FIND_NODE {0}] from Node {1} ({2})", + new Object[] { idToFind, fromID, from.toString() }); + + node.sendClosestNodesTo(from, fromID, idToFind, rpc_id); + } + + public void terminate() { + running = false; + } } diff --git a/ws2012/P2P/uebungen/8/src/routingtable/Bucket.java b/ws2012/P2P/uebungen/8/src/routingtable/Bucket.java index d9270054..310f449b 100644 --- a/ws2012/P2P/uebungen/8/src/routingtable/Bucket.java +++ b/ws2012/P2P/uebungen/8/src/routingtable/Bucket.java @@ -1,6 +1,5 @@ package routingtable; -import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -14,7 +13,7 @@ public class Bucket { private int size; - public List entrys = new ArrayList(); + public List entries = new ArrayList(); public Bucket left = null; public Bucket right = null; @@ -25,41 +24,42 @@ public class Bucket { this.size = size; } - public void update(Identifier id, InetSocketAddress address) { - update(this, id, address); + public void update(BucketEntry entry) { + update(this, entry); } - private void update(Bucket bucket, Identifier id, InetSocketAddress address) { + private void update(Bucket bucket, BucketEntry newEntry) { if (!bucket.isLeaf()) { - if (id.isBitSetAt(level)) { + if (newEntry.id.isBitSetAt(level)) { - update(bucket.right, id, address); + update(bucket.right, newEntry); } else { - update(bucket.left, id, address); + update(bucket.left, newEntry); } } else if (bucket.isLeaf() && bucket.hasSpace()) { - BucketEntry newEntry = new BucketEntry(id, address); - - if (entrys.contains(newEntry)) { + if (entries.contains(newEntry)) { // Move to beginning LOGGER.log( Level.INFO, "Node {0} ({1}) already in routing table. Move to end of bucket.", - new Object[] { id, address }); - entrys.remove(newEntry); - entrys.add(newEntry); + new Object[] { newEntry.id, newEntry.address }); + if (entries.size() > 1) { + entries.remove(newEntry); + entries.add(newEntry); + } } else { LOGGER.log(Level.INFO, "Added new node {0} ({1}) to routing table.", - new Object[] { id, address }); - entrys.add(newEntry); + new Object[] { newEntry.id, newEntry.address }); + entries.add(newEntry); } } else { + // TODO: only split if necessary // Leaf, but full -> split Bucket newLeft = new Bucket(size, level + 1); @@ -67,18 +67,18 @@ public class Bucket { // Add the new entry and in the following loop distribute all // existing entries to left/right - entrys.add(new BucketEntry(id, address)); + entries.add(newEntry); - for (BucketEntry entry : entrys) { + for (BucketEntry entry : entries) { if (entry.id.isBitSetAt(level)) { - newLeft.entrys + newLeft.entries .add(new BucketEntry(entry.id, entry.address)); } else { - newRight.entrys + newRight.entries .add(new BucketEntry(entry.id, entry.address)); } } - bucket.entrys = null; + bucket.entries = null; bucket.left = newLeft; bucket.right = newRight; @@ -91,7 +91,7 @@ public class Bucket { private boolean hasNode(Bucket bucket, Identifier idToFind) { if (bucket.isLeaf()) { - for (BucketEntry entry : bucket.entrys) { + for (BucketEntry entry : bucket.entries) { if (entry.id.equals(idToFind)) { return true; } @@ -111,7 +111,7 @@ public class Bucket { } private boolean hasSpace() { - return entrys.size() < size; + return entries.size() < size; } public List getClosestNodesTo(Identifier id) { @@ -121,5 +121,4 @@ public class Bucket { return result; } - } \ No newline at end of file diff --git a/ws2012/P2P/uebungen/8/src/routingtable/BucketEntry.java b/ws2012/P2P/uebungen/8/src/routingtable/BucketEntry.java index 47e1c191..de137f28 100644 --- a/ws2012/P2P/uebungen/8/src/routingtable/BucketEntry.java +++ b/ws2012/P2P/uebungen/8/src/routingtable/BucketEntry.java @@ -1,8 +1,12 @@ package routingtable; import java.net.InetSocketAddress; +import java.nio.ByteBuffer; + +import util.BufferUtil; import node.Identifier; +import node.Node; public class BucketEntry { public Identifier id; @@ -21,4 +25,11 @@ public class BucketEntry { return id.equals(entry.id) && address.equals(entry.address); } } + + public byte[] getBytes() { + ByteBuffer result = ByteBuffer.allocate(8 + (Node.ID_BITS / 8)); + result.put(BufferUtil.addrToBytes(address)); + result.put(id.getBytes()); + return result.array(); + } } diff --git a/ws2012/P2P/uebungen/8/src/routingtable/RoutingTable.java b/ws2012/P2P/uebungen/8/src/routingtable/RoutingTable.java index 56287a9e..48b7de33 100644 --- a/ws2012/P2P/uebungen/8/src/routingtable/RoutingTable.java +++ b/ws2012/P2P/uebungen/8/src/routingtable/RoutingTable.java @@ -1,27 +1,43 @@ package routingtable; import java.net.InetSocketAddress; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import node.Identifier; public class RoutingTable { - private Bucket tree; + private Bucket tree; - public RoutingTable(int bucketSize) { - this.tree = new Bucket(bucketSize, 0); - } + private Set entries = new HashSet(); - public void update(Identifier id, InetSocketAddress address) { - tree.update(id, address); - } + private int bucketSize; - public List getClosestNodesTo(Identifier id) { - return tree.getClosestNodesTo(id); - } + public RoutingTable(int bucketSize) { + this.tree = new Bucket(bucketSize, 0); + this.bucketSize = bucketSize; + } - public void remove(Identifier node) { + public void update(Identifier id, InetSocketAddress address) { + BucketEntry newEntry = new BucketEntry(id, address); + entries.add(newEntry); + tree.update(newEntry); + } - } + public Set getClosestNodesTo(Identifier id) { + Set result = new HashSet(); + + if (entries.size() <= bucketSize) { + result.addAll(entries); + return result; + } else { + // TODO get all nodes that are closest.. + return null; + } + } + + public void remove(Identifier node) { + + } } \ No newline at end of file