/* * GBNTReceiver - Claudio Maggioni (2020) * NTW2020 HW2 * * No external source was used to create this file, other than regular course material. * * Note on implementation: change value of constant RECEIVER_TIMEOUT_MS to change the * value of the receiver disconnection timeout. */ import transport.Receiver; import transport.TimeoutAction; public class GBNTReceiver extends Receiver implements TimeoutAction { private char sequence; private boolean firstPacket = true; private static final int RECEIVER_TIMEOUT_MS = 15000; private State state = State.SETUP; enum State { SETUP, RECEIVING, CLOSED, } private static byte[] generateACK(char sequence) { byte[] dest = new byte[2]; dest[0] = (byte) ((sequence >> 8) & 0xFF); dest[1] = (byte) (sequence & 0xFF); return dest; } private static char readBigEndianChar(byte[] src, int offset) { return (char) (((src[offset] << 8) & 0xFF00) | (src[offset + 1] & 0xFF)); } private static char incrementAndRollover(char c) { return (char) ((c + 1) % 65536); } @Override public void timeoutAction() { System.out.println("Disconnect timeout triggered"); try { disconnect(); deliver(null, 0, END_OF_STREAM); state = State.CLOSED; } catch (java.lang.InterruptedException ex) { System.out.println("Thread interrupted. Exiting directly."); System.exit(0); } } @Override protected void unreliableReceive(byte[] bytes, int offset, int length) { if (state == State.CLOSED) return; cancelTimeout(this); setTimeout(RECEIVER_TIMEOUT_MS, this); switch(this.state) { case SETUP: if (length != 2) { if (!firstPacket) { state = State.RECEIVING; this.unreliableReceive(bytes, offset, length); return; } return; } sequence = readBigEndianChar(bytes, offset); System.out.println("Synchronization ACK: " + (int) sequence); unreliableSend(bytes, offset, 2); firstPacket = false; break; case RECEIVING: if (length == 2) { // if packet is a close packet char a = readBigEndianChar(bytes, offset); if (incrementAndRollover(this.sequence) != a) return; // ignore if not synchronized this.unreliableSend(bytes, offset, 2); System.out.println("Received valid FIN packet"); timeoutAction(); } if (length < 3) return; char seq = readBigEndianChar(bytes, offset); System.out.print("Read sequence: " + (int) seq + " Exp: " + (int) incrementAndRollover(sequence)+ " -> "); if (seq == incrementAndRollover(sequence)) { this.deliver(bytes, offset + 2, length - 2); sequence = incrementAndRollover(sequence); System.out.print("OK! "); } else { System.out.print("Drop "); } System.out.println("Sending ack: "+ (int) sequence); this.unreliableSend(generateACK(sequence), 0, 2); break; } } }