101 lines
3.4 KiB
Java
101 lines
3.4 KiB
Java
/*
|
|
* 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;
|
|
}
|
|
}
|
|
}
|