Updated Identifier, implemented simple NODES message

This commit is contained in:
senft-lap 2012-12-18 17:06:40 +01:00
parent 33163b3bea
commit e977c20024
7 changed files with 362 additions and 294 deletions

1
ws2012/P2P/uebungen/8/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
bin/

View File

@ -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();
}
}

View File

@ -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<Integer, Ack> acks = new HashMap<Integer, Ack>();
private Map<Integer, Ack> acks = new HashMap<Integer, Ack>();
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<BucketEntry> 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();
}
}

View File

@ -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<BucketEntry> readEntries = new HashSet<BucketEntry>();
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;
}
}

View File

@ -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<BucketEntry> entrys = new ArrayList<BucketEntry>();
public List<BucketEntry> entries = new ArrayList<BucketEntry>();
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<BucketEntry> getClosestNodesTo(Identifier id) {
@ -121,5 +121,4 @@ public class Bucket {
return result;
}
}

View File

@ -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();
}
}

View File

@ -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<BucketEntry> entries = new HashSet<BucketEntry>();
public void update(Identifier id, InetSocketAddress address) {
tree.update(id, address);
}
private int bucketSize;
public List<BucketEntry> 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<BucketEntry> getClosestNodesTo(Identifier id) {
Set<BucketEntry> result = new HashSet<BucketEntry>();
if (entries.size() <= bucketSize) {
result.addAll(entries);
return result;
} else {
// TODO get all nodes that are closest..
return null;
}
}
public void remove(Identifier node) {
}
}