diff --git a/briar-core/src/net/sf/briar/reliability/Ack.java b/briar-core/src/net/sf/briar/reliability/Ack.java index a8c437bf94124beea0251308a1ac74e691223bdb..1d763528f2c7d99fff04634a107625fcfa5706c7 100644 --- a/briar-core/src/net/sf/briar/reliability/Ack.java +++ b/briar-core/src/net/sf/briar/reliability/Ack.java @@ -4,7 +4,7 @@ import net.sf.briar.util.ByteUtils; class Ack extends Frame { - static final int LENGTH = 12; + static final int LENGTH = 11; Ack() { super(new byte[LENGTH]); @@ -18,10 +18,10 @@ class Ack extends Frame { } int getWindowSize() { - return ByteUtils.readUint24(buf, 5); + return ByteUtils.readUint16(buf, 5); } void setWindowSize(int windowSize) { - ByteUtils.writeUint24(windowSize, buf, 5); + ByteUtils.writeUint16(windowSize, buf, 5); } } diff --git a/briar-core/src/net/sf/briar/reliability/Receiver.java b/briar-core/src/net/sf/briar/reliability/Receiver.java index ddb3394aa67663a3d1123d4f0946151a171e4dfa..9072dbba312b20d62591cc147b44451b5e1eb08c 100644 --- a/briar-core/src/net/sf/briar/reliability/Receiver.java +++ b/briar-core/src/net/sf/briar/reliability/Receiver.java @@ -10,7 +10,8 @@ import net.sf.briar.api.reliability.ReadHandler; class Receiver implements ReadHandler { - static final int MAX_WINDOW_SIZE = 8 * Data.MAX_PAYLOAD_LENGTH; + private static final int READ_TIMEOUT = 5 * 60 * 1000; // Milliseconds + private static final int MAX_WINDOW_SIZE = 8 * Data.MAX_PAYLOAD_LENGTH; private final Sender sender; private final SortedSet<Data> dataFrames; // Locking: this @@ -27,10 +28,11 @@ class Receiver implements ReadHandler { } synchronized Data read() throws IOException, InterruptedException { - while(valid) { + long now = System.currentTimeMillis(), end = now + READ_TIMEOUT; + while(now < end && valid) { if(dataFrames.isEmpty()) { // Wait for a data frame - wait(); + wait(end - now); } else { Data d = dataFrames.first(); if(d.getSequenceNumber() == nextSequenceNumber) { @@ -42,10 +44,12 @@ class Receiver implements ReadHandler { return d; } else { // Wait for the next in-order data frame - wait(); + wait(end - now); } } + now = System.currentTimeMillis(); } + if(valid) throw new IOException("Read timed out"); throw new IOException("Connection closed"); } diff --git a/briar-core/src/net/sf/briar/reliability/Sender.java b/briar-core/src/net/sf/briar/reliability/Sender.java index 313c1cb71b4c859a0760869092a828d996189a2a..7ae6f69f85b10db71c3b1a6935ed054fea845c3b 100644 --- a/briar-core/src/net/sf/briar/reliability/Sender.java +++ b/briar-core/src/net/sf/briar/reliability/Sender.java @@ -11,20 +11,23 @@ import net.sf.briar.api.reliability.WriteHandler; class Sender { // All times are in milliseconds - private static final int MIN_TIMEOUT = 1000; - private static final int MAX_TIMEOUT = 60 * 1000; + private static final int WRITE_TIMEOUT = 5 * 60 * 1000; + private static final int MIN_RTO = 1000; + private static final int MAX_RTO = 60 * 1000; private static final int INITIAL_RTT = 0; private static final int INITIAL_RTT_VAR = 3 * 1000; + private static final int MAX_WINDOW_SIZE = 64 * Data.MAX_PAYLOAD_LENGTH; private final WriteHandler writeHandler; private final LinkedList<Outstanding> outstanding; // Locking: this - private int outstandingBytes = 0; // Locking: this - private int windowSize = Data.MAX_PAYLOAD_LENGTH; // Locking: this - private int rtt = INITIAL_RTT, rttVar = INITIAL_RTT_VAR; // Locking: this - private int timeout = rtt + (rttVar << 2); // Locking: this - private long lastWindowUpdateOrProbe = Long.MAX_VALUE; // Locking: this - private boolean dataWaiting = false; // Locking: this + // All of the following are locking: this + private int outstandingBytes = 0; + private int windowSize = Data.MAX_PAYLOAD_LENGTH; + private int rtt = INITIAL_RTT, rttVar = INITIAL_RTT_VAR; + private int rto = rtt + (rttVar << 2); + private long lastWindowUpdateOrProbe = Long.MAX_VALUE; + private boolean dataWaiting = false; Sender(WriteHandler writeHandler) { this.writeHandler = writeHandler; @@ -62,15 +65,15 @@ class Sender { it.remove(); outstandingBytes -= o.data.getPayloadLength(); foundIndex = i; - // Update the round-trip time and retransmission timer + // Update the round-trip time and retransmission timeout if(!o.retransmitted) { int sample = (int) (now - o.lastTransmitted); int error = sample - rtt; rtt += (error >> 3); rttVar += (Math.abs(error) - rttVar) >> 2; - timeout = rtt + (rttVar << 2); - if(timeout < MIN_TIMEOUT) timeout = MIN_TIMEOUT; - else if(timeout > MAX_TIMEOUT) timeout = MAX_TIMEOUT; + rto = rtt + (rttVar << 2); + if(rto < MIN_RTO) rto = MIN_RTO; + else if(rto > MAX_RTO) rto = MAX_RTO; } break; } @@ -86,7 +89,7 @@ class Sender { lastWindowUpdateOrProbe = now; int oldWindowSize = windowSize; // Don't accept an unreasonably large window size - windowSize = Math.min(a.getWindowSize(), Receiver.MAX_WINDOW_SIZE); + windowSize = Math.min(a.getWindowSize(), MAX_WINDOW_SIZE); // If space has become available, notify any waiting writers if(windowSize > oldWindowSize || foundIndex != -1) notifyAll(); } @@ -101,22 +104,23 @@ class Sender { boolean sendProbe = false; synchronized(this) { if(outstanding.isEmpty()) { - if(dataWaiting && now - lastWindowUpdateOrProbe > timeout) { + if(dataWaiting && now - lastWindowUpdateOrProbe > rto) { sendProbe = true; - timeout <<= 1; - if(timeout > MAX_TIMEOUT) timeout = MAX_TIMEOUT; + rto <<= 1; + if(rto > MAX_RTO) rto = MAX_RTO; } } else { Iterator<Outstanding> it = outstanding.iterator(); while(it.hasNext()) { Outstanding o = it.next(); - if(now - o.lastTransmitted > timeout) { + if(now - o.lastTransmitted > rto) { it.remove(); if(retransmit == null) retransmit = new ArrayList<Outstanding>(); retransmit.add(o); - timeout <<= 1; - if(timeout > MAX_TIMEOUT) timeout = MAX_TIMEOUT; + // Update the retransmission timeout + rto <<= 1; + if(rto > MAX_RTO) rto = MAX_RTO; } } if(retransmit != null) { @@ -146,10 +150,14 @@ class Sender { int payloadLength = d.getPayloadLength(); synchronized(this) { // Wait for space in the window - while(outstandingBytes + payloadLength >= windowSize) { + long now = System.currentTimeMillis(), end = now + WRITE_TIMEOUT; + while(now < end && outstandingBytes + payloadLength >= windowSize) { dataWaiting = true; - wait(); + wait(end - now); + now = System.currentTimeMillis(); } + if(outstandingBytes + payloadLength >= windowSize) + throw new IOException("Write timed out"); outstanding.add(new Outstanding(d)); outstandingBytes += payloadLength; dataWaiting = false; diff --git a/briar-core/src/net/sf/briar/util/ByteUtils.java b/briar-core/src/net/sf/briar/util/ByteUtils.java index ec062ec817f245c8e109d31d871cda7df50030a5..d858b8f9b07f28d3eb6e2aefe96459bbc2cf8bdf 100644 --- a/briar-core/src/net/sf/briar/util/ByteUtils.java +++ b/briar-core/src/net/sf/briar/util/ByteUtils.java @@ -7,11 +7,6 @@ public class ByteUtils { */ public static final int MAX_16_BIT_UNSIGNED = 65535; // 2^16 - 1 - /** - * The maximum value that can be represented as an unsigned 24-bit integer. - */ - public static final int MAX_24_BIT_UNSIGNED = 16777215; // 2^24 - 1 - /** * The maximum value that can be represented as an unsigned 32-bit integer. */ @@ -32,15 +27,6 @@ public class ByteUtils { b[offset + 1] = (byte) (i & 0xFF); } - public static void writeUint24(long i, byte[] b, int offset) { - if(i < 0L) throw new IllegalArgumentException(); - if(i > MAX_24_BIT_UNSIGNED) throw new IllegalArgumentException(); - if(b.length < offset + 3) throw new IllegalArgumentException(); - b[offset] = (byte) (i >> 16); - b[offset + 1] = (byte) (i >> 8 & 0xFF); - b[offset + 2] = (byte) (i & 0xFF); - } - public static void writeUint32(long i, byte[] b, int offset) { if(i < 0L) throw new IllegalArgumentException(); if(i > MAX_32_BIT_UNSIGNED) throw new IllegalArgumentException(); @@ -56,12 +42,6 @@ public class ByteUtils { return ((b[offset] & 0xFF) << 8) | (b[offset + 1] & 0xFF); } - public static int readUint24(byte[] b, int offset) { - if(b.length < offset + 3) throw new IllegalArgumentException(); - return ((b[offset] & 0xFF) << 16) | ((b[offset + 1] & 0xFF) << 8) - | (b[offset + 2] & 0xFF); - } - public static long readUint32(byte[] b, int offset) { if(b.length < offset + 4) throw new IllegalArgumentException(); return ((b[offset] & 0xFFL) << 24) | ((b[offset + 1] & 0xFFL) << 16)