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 neighbors = new ArrayList(); 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; } } }