321 lines
7.4 KiB
Java
321 lines
7.4 KiB
Java
package peer;
|
|
|
|
import java.io.IOException;
|
|
import java.net.Inet4Address;
|
|
import java.net.Inet6Address;
|
|
import java.net.InetAddress;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.SocketAddress;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.channels.DatagramChannel;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.logging.Logger;
|
|
|
|
import common.MessageType;
|
|
|
|
public class Node {
|
|
|
|
private final static Logger LOGGER = Logger.getLogger(Node.class.getName());
|
|
|
|
private static final int BUF_SIZE = 512;
|
|
|
|
private DatagramChannel channel;
|
|
private ByteBuffer buf;
|
|
|
|
private String name = "Not initialized";
|
|
|
|
private List<SocketAddress> neighbors = new ArrayList<SocketAddress>();
|
|
|
|
private volatile Thread thread;
|
|
private UDPListen udpListen;
|
|
|
|
public Node() {
|
|
try {
|
|
channel = DatagramChannel.open();
|
|
channel.socket().bind(null);
|
|
channel.configureBlocking(false);
|
|
|
|
buf = ByteBuffer.allocate(BUF_SIZE);
|
|
|
|
this.name = channel.socket().getLocalSocketAddress().toString();
|
|
|
|
udpListen = new UDPListen();
|
|
thread = new Thread(udpListen);
|
|
thread.start();
|
|
|
|
LOGGER.info("Initialized node " + name);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create another peer, mutually link creator and spawn.
|
|
*
|
|
* @return
|
|
* @throws IOException
|
|
*/
|
|
public Node spawn() throws IOException {
|
|
LOGGER.info("Name: " + getName() + ", Spawning new node.");
|
|
|
|
// create a new node
|
|
Node newNode = new Node();
|
|
|
|
buf.clear();
|
|
buf.put(MessageType.INVITE);
|
|
buf.flip();
|
|
|
|
try {
|
|
channel.send(buf, newNode.getAddress());
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
neighbors.add(newNode.getAddress());
|
|
|
|
return newNode;
|
|
}
|
|
|
|
public SocketAddress getAddress() {
|
|
return channel.socket().getLocalSocketAddress();
|
|
}
|
|
|
|
private void putAddrInBuf(ByteBuffer buf, SocketAddress addr) {
|
|
buf.clear();
|
|
InetSocketAddress a = (InetSocketAddress) addr;
|
|
|
|
if (a.getAddress() instanceof Inet6Address) {
|
|
buf.put(MessageType.NEW_NEIGHBOR_IPV6);
|
|
buf.put(addr.toString().getBytes());
|
|
|
|
} else if (a.getAddress() instanceof Inet4Address) {
|
|
buf.put(MessageType.NEW_NEIGHBOR_IPV4);
|
|
// TODO: man braucht wahrscheinlich nicht mal .toString()
|
|
// a.getHostName() und a.getPort() sollten auch gehen.
|
|
|
|
for (String part : addr.toString().split(".")) {
|
|
if (!part.contains(":")) {
|
|
buf.put(Byte.valueOf(part));
|
|
} else {
|
|
// Last part (contains port)
|
|
String[] lastPart = part.split(":");
|
|
buf.put(Byte.valueOf(lastPart[0]));
|
|
buf.putInt(Integer.valueOf(lastPart[1]));
|
|
}
|
|
}
|
|
|
|
} else {
|
|
return;
|
|
}
|
|
buf.flip();
|
|
}
|
|
|
|
/**
|
|
* Circularly link all neighbors, remove itself form all neighbors and exit.
|
|
*/
|
|
public void leave() {
|
|
LOGGER.info("Name: " + getName() + ", Leaving...");
|
|
|
|
for (int i = 0; i < neighbors.size(); i++) {
|
|
|
|
if (neighbors.size() > 1) {
|
|
try {
|
|
if (i == 0) {
|
|
|
|
putAddrInBuf(buf, neighbors.get(1));
|
|
channel.send(buf, neighbors.get(i));
|
|
|
|
putAddrInBuf(buf, neighbors.get(neighbors.size() - 1));
|
|
channel.send(buf, neighbors.get(i));
|
|
|
|
} else if (i == neighbors.size() - 1) {
|
|
|
|
putAddrInBuf(buf, neighbors.get(0));
|
|
channel.send(buf, neighbors.get(i));
|
|
|
|
putAddrInBuf(buf, neighbors.get(i - 1));
|
|
channel.send(buf, neighbors.get(i));
|
|
|
|
} else {
|
|
|
|
putAddrInBuf(buf, neighbors.get(i - 1));
|
|
channel.send(buf, neighbors.get(i));
|
|
|
|
putAddrInBuf(buf, neighbors.get(i + 1));
|
|
channel.send(buf, neighbors.get(i));
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
buf.clear();
|
|
buf.put(MessageType.LEAVE);
|
|
buf.flip();
|
|
|
|
try {
|
|
channel.send(buf, neighbors.get(i));
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
try {
|
|
if (thread != null) {
|
|
udpListen.terminate();
|
|
thread.join();
|
|
}
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public boolean hasNeighbors() {
|
|
return (this.neighbors.size() > 0);
|
|
}
|
|
|
|
public boolean hasNeighbor(SocketAddress adr) {
|
|
for (SocketAddress n : neighbors) {
|
|
if (n.toString().equals(adr.toString())) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public String getName() {
|
|
return this.name;
|
|
}
|
|
|
|
public void printInfos() {
|
|
StringBuilder result = new StringBuilder(128);
|
|
result.append("Name: " + this.name + ", Neighbours: ");
|
|
for (int i = 0; i < this.neighbors.size(); i++) {
|
|
result.append(this.neighbors.get(i).toString() + ", ");
|
|
}
|
|
LOGGER.info(result.toString());
|
|
}
|
|
|
|
public class UDPListen implements Runnable {
|
|
private volatile boolean running = true;
|
|
|
|
public void run() {
|
|
StringBuilder sb_received;
|
|
|
|
while (running) {
|
|
SocketAddress receivedFrom = null;
|
|
buf.clear();
|
|
|
|
try {
|
|
receivedFrom = channel.receive(buf);
|
|
|
|
if (buf.remaining() != 512) {
|
|
buf.flip();
|
|
|
|
byte messageType = buf.get();
|
|
|
|
switch (messageType) {
|
|
case MessageType.INVITE:
|
|
LOGGER.info(name + " received invite from "
|
|
+ receivedFrom.toString());
|
|
neighbors.add(receivedFrom);
|
|
break;
|
|
|
|
case MessageType.LEAVE:
|
|
LOGGER.info(name + ": " + receivedFrom.toString()
|
|
+ " is leaving. Deleting...");
|
|
|
|
// search the neighbor in the list and remove him.
|
|
for (int i = 0; i < neighbors.size(); i++) {
|
|
if (neighbors.get(i).equals(receivedFrom)) {
|
|
neighbors.remove(i);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MessageType.NEW_NEIGHBOR_IPV4:
|
|
LOGGER.info(name + " received new IPV4 neighbor"
|
|
+ receivedFrom.toString() + ": "
|
|
+ new String(buf.array()));
|
|
|
|
byte[] byte_addr = new byte[7];
|
|
|
|
// Read 4 Bytes and 1 Int
|
|
for (int i = 0; i < 8; i = i + 2) {
|
|
byte_addr[i] = buf.get();
|
|
if (i < 7)
|
|
byte_addr[i + 1] = '.';
|
|
}
|
|
|
|
int port = buf.getInt();
|
|
|
|
InetSocketAddress new_neighbor = new InetSocketAddress(
|
|
new String(byte_addr), port);
|
|
|
|
// check, if we have the neighbor already.
|
|
if (!hasNeighbor(new_neighbor)) {
|
|
neighbors.add(new_neighbor);
|
|
}
|
|
|
|
// TODO: send PING to new neighbor and expect wait
|
|
// to
|
|
// get a
|
|
// PONG
|
|
|
|
break;
|
|
case MessageType.NEW_NEIGHBOR_IPV6:
|
|
LOGGER.info(name
|
|
+ " received new IPV6 neighbor from: "
|
|
+ receivedFrom.toString() + ": "
|
|
+ new String(buf.array()));
|
|
|
|
sb_received = new StringBuilder(512);
|
|
while (buf.hasRemaining()) {
|
|
sb_received.append((char) buf.get());
|
|
}
|
|
|
|
String str_received = sb_received.toString();
|
|
|
|
int startOfPort = str_received.lastIndexOf(":");
|
|
|
|
String new_hostname = str_received.substring(1,
|
|
startOfPort);
|
|
int new_port = Integer.valueOf(str_received
|
|
.substring(startOfPort + 1));
|
|
|
|
new_neighbor = new InetSocketAddress(new_hostname,
|
|
new_port);
|
|
|
|
// check, if we have the neighbor already.
|
|
if (!hasNeighbor(new_neighbor)) {
|
|
neighbors.add(new_neighbor);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
LOGGER.info(name
|
|
+ " received unknown command from "
|
|
+ receivedFrom.toString() + ": "
|
|
+ new String(buf.array()));
|
|
|
|
}
|
|
} else {
|
|
try {
|
|
Thread.sleep(10);
|
|
} catch (InterruptedException e) {
|
|
// TODO Auto-generated catch block
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void terminate() {
|
|
running = false;
|
|
}
|
|
}
|
|
} |