2012-11-16 13:12:44 +01:00

227 lines
4.8 KiB
Java

package peer;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
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 = 256;
private DatagramChannel channel;
private ByteBuffer buf;
private String name = "Not initialized";
private List<SocketAddress> neighbors = new ArrayList<SocketAddress>();
private Thread thread;
private UDPListen udpListen;
public Node() {
try {
channel = DatagramChannel.open();
channel.socket().bind(null);
buf = ByteBuffer.allocate(BUF_SIZE);
this.name = channel.getLocalAddress().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() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
LOGGER.severe("Address not set yet.");
}
return null;
}
private byte[] addrToByte(SocketAddress addr) {
// TODO Wäre cool, die IP nicht einfach als String zu schicken.
// Was machen mit IPV4 vs IPV6?
InetSocketAddress a = (InetSocketAddress) addr;
if (a.getAddress() instanceof Inet6Address) {
// seems to be IPv6
} else if (a.getAddress() instanceof Inet4Address) {
// seems to be IPv4
} else {
// invalid ip address
}
return a.getAddress().getAddress();
}
/**
* 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) {
buf.clear();
buf.put(MessageType.NEW_NEIGHBOR_IPV6);
if (i == 0) {
buf.put(addrToByte(neighbors.get(1)));
buf.put(addrToByte(neighbors.get(neighbors.size() - 1)));
} else if (i == neighbors.size() - 1) {
buf.put(addrToByte(neighbors.get(0)));
buf.put(addrToByte(neighbors.get(i - 1)));
} else {
buf.put(addrToByte(neighbors.get(i - 1)));
buf.put(addrToByte(neighbors.get(i + 1)));
}
buf.flip();
try {
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 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() {
while (running) {
SocketAddress receivedFrom = null;
buf.clear();
try {
receivedFrom = channel.receive(buf);
} catch (IOException e) {
e.printStackTrace();
}
buf.flip();
byte messageType = buf.get();
switch (messageType) {
case MessageType.INVITE:
LOGGER.info(name + " received invite from "
+ receivedFrom.toString());
break;
case MessageType.LEAVE:
neighbors.remove(receivedFrom);
break;
case MessageType.NEW_NEIGHBOR_IPV4:
LOGGER.info(name + " received new IPV4 neighbor"
+ receivedFrom.toString() + ": "
+ new String(buf.array()));
break;
case MessageType.NEW_NEIGHBOR_IPV6:
LOGGER.info(name + " received new IPV6 neighbor"
+ receivedFrom.toString() + ": "
+ new String(buf.array()));
break;
default:
LOGGER.info(name + " received unknown command from "
+ receivedFrom.toString() + ": "
+ new String(buf.array()));
}
}
}
public void terminate() {
running = false;
}
}
}