package de.tum.in.www1.jReto.routing; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; import de.tum.in.www1.jReto.packet.Packet; import de.tum.in.www1.jReto.packet.PacketType; import de.tum.in.www1.jReto.routing.packets.FloodingPacket; /** * The FloodingPacketManager implements the Flooding algorithm used to distribute packets through the network. * When a packet is received, and it or a newer one has been seen before, it is discarded. This is accomplished by storing the last seen sequence number * for each sender. * If the packet is new, it is forwarded to all direct neighbors of the local peer. */ public class FloodingPacketManager { public static interface PacketHandler { Set<PacketType> getHandledPacketTypes(); void handlePacket(ByteBuffer data, PacketType type); } /** The Router responsible for this flooding packet manager. */ private final Router router; /** The packet handler registered with this flooding packet manager */ private final PacketHandler packetHandler; /** The next sequence number that will be used for packets sent from this peer. */ private int currentSequenceNumber = 0; /** The highest sequence number seen for each remote peer. */ private Map<UUID, Integer> sequenceNumbers = new HashMap<>(); /** Constructs a new FloodingPacketManager */ public FloodingPacketManager(PacketHandler packetHandler, Router router) { this.packetHandler = packetHandler; this.router = router; } public Set<PacketType> getHandledPacketTypes() { return new HashSet<>(Arrays.asList(PacketType.FLOODED_PACKET)); } /** Handles a received packet from a given source. If the packet is new, it is forwarded and handled, otherwise it is dismissed. */ public void handlePacket(ByteBuffer data, PacketType type, UUID sourceIdentifier) { FloodingPacket floodingPacket = FloodingPacket.deserialize(data); if (floodingPacket == null) { System.err.println("Received invalid flooded packet."); return; } if (this.sequenceNumbers.containsKey(floodingPacket.originIdentifier) && floodingPacket.sequenceNumber <= this.sequenceNumbers.get(floodingPacket.originIdentifier)) return; this.sequenceNumbers.put(floodingPacket.originIdentifier, floodingPacket.sequenceNumber); for (Node neighbor : this.router.getNeighborNodes()) { if (neighbor.getIdentifier().equals(sourceIdentifier)) continue; neighbor.sendPacket(floodingPacket); } PacketType subtype = PacketType.fromData(floodingPacket.payload); if (subtype == PacketType.UNKNOWN) { System.err.println("Flooded packet contains payload packet of unknown type (payload length: "+floodingPacket.payload.remaining()+")."); return; } if (this.packetHandler.getHandledPacketTypes().contains(subtype)) { this.packetHandler.handlePacket(floodingPacket.payload.slice().order(ByteOrder.LITTLE_ENDIAN), subtype); } else { System.err.println("No packet handler for flooded packet with type: "+subtype); } } /** Floods a new packet through the network. Increases the sequence number and sends the packet to all neighbors. */ public void floodPacket(Packet packet) { FloodingPacket floodingPacket = new FloodingPacket(this.router.getLocalNodeIdentifier(), this.currentSequenceNumber, packet.serialize()); this.sequenceNumbers.put(this.router.getLocalNodeIdentifier(), this.currentSequenceNumber); this.currentSequenceNumber++; for (Node neighbor : this.router.getNeighborNodes()) { neighbor.sendPacket(floodingPacket); } } }