2005-04-20 jrandom
* In the SDK, we don't actually need to block when we're sending a message as BestEffort (and these days, we're always sending BestEffort). * Pass out client messages in fewer (larger) steps. * Have the InNetMessagePool short circuit dispatch requests. * Have the message validator take into account expiration to cut down on false positives at high transfer rates. * Allow configuration of the probabalistic window size growth rate in the streaming lib's slow start and congestion avoidance phases, and default them to a more conservative value (2), rather than the previous value (1). * Reduce the ack delay in the streaming lib to 500ms * Honor choke requests in the streaming lib (only affects those getting insanely high transfer rates) * Let the user specify an interface besides 127.0.0.1 or 0.0.0.0 on the I2PTunnel client page (thanks maestro^!) (plus minor udp tweaks)
This commit is contained in:
@ -22,6 +22,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
private int _inactivityAction;
|
||||
private int _inboundBufferSize;
|
||||
private int _maxWindowSize;
|
||||
private int _congestionAvoidanceGrowthRateFactor;
|
||||
private int _slowStartGrowthRateFactor;
|
||||
|
||||
public static final int PROFILE_BULK = 1;
|
||||
public static final int PROFILE_INTERACTIVE = 2;
|
||||
@ -45,6 +47,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
public static final String PROP_INACTIVITY_TIMEOUT = "i2p.streaming.inactivityTimeout";
|
||||
public static final String PROP_INACTIVITY_ACTION = "i2p.streaming.inactivityAction";
|
||||
public static final String PROP_MAX_WINDOW_SIZE = "i2p.streaming.maxWindowSize";
|
||||
public static final String PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR = "i2p.streaming.congestionAvoidanceGrowthRateFactor";
|
||||
public static final String PROP_SLOW_START_GROWTH_RATE_FACTOR = "i2p.streaming.slowStartGrowthRateFactor";
|
||||
|
||||
public ConnectionOptions() {
|
||||
super();
|
||||
@ -74,6 +78,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
setInactivityAction(opts.getInactivityAction());
|
||||
setInboundBufferSize(opts.getInboundBufferSize());
|
||||
setMaxWindowSize(opts.getMaxWindowSize());
|
||||
setCongestionAvoidanceGrowthRateFactor(opts.getCongestionAvoidanceGrowthRateFactor());
|
||||
setSlowStartGrowthRateFactor(opts.getSlowStartGrowthRateFactor());
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,13 +91,15 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
setRTT(getInt(opts, PROP_INITIAL_RTT, 10*1000));
|
||||
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
|
||||
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000));
|
||||
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 1000));
|
||||
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 500));
|
||||
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, 1));
|
||||
setMaxResends(getInt(opts, PROP_MAX_RESENDS, 5));
|
||||
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
|
||||
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
|
||||
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
|
||||
setInboundBufferSize((getMaxMessageSize() + 2) * Connection.MAX_WINDOW_SIZE);
|
||||
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
|
||||
setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 2));
|
||||
setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 2));
|
||||
|
||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
|
||||
@ -124,7 +132,11 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
|
||||
if (opts.containsKey(PROP_INACTIVITY_ACTION))
|
||||
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
|
||||
setInboundBufferSize((getMaxMessageSize() + 2) * Connection.MAX_WINDOW_SIZE);
|
||||
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
|
||||
if (opts.contains(PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR))
|
||||
setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 2));
|
||||
if (opts.contains(PROP_SLOW_START_GROWTH_RATE_FACTOR))
|
||||
setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 2));
|
||||
|
||||
if (opts.containsKey(PROP_CONNECT_TIMEOUT))
|
||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||
@ -257,6 +269,24 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
public int getInboundBufferSize() { return _inboundBufferSize; }
|
||||
public void setInboundBufferSize(int bytes) { _inboundBufferSize = bytes; }
|
||||
|
||||
/**
|
||||
* When we're in congestion avoidance, we grow the window size at the rate
|
||||
* of 1/(windowSize*factor). In standard TCP, window sizes are in bytes,
|
||||
* while in I2P, window sizes are in messages, so setting factor=maxMessageSize
|
||||
* mimics TCP, but using a smaller factor helps grow a little more rapidly.
|
||||
*/
|
||||
public int getCongestionAvoidanceGrowthRateFactor() { return _congestionAvoidanceGrowthRateFactor; }
|
||||
public void setCongestionAvoidanceGrowthRateFactor(int factor) { _congestionAvoidanceGrowthRateFactor = factor; }
|
||||
|
||||
/**
|
||||
* When we're in slow start, we grow the window size at the rate
|
||||
* of 1/(factor). In standard TCP, window sizes are in bytes,
|
||||
* while in I2P, window sizes are in messages, so setting factor=maxMessageSize
|
||||
* mimics TCP, but using a smaller factor helps grow a little more rapidly.
|
||||
*/
|
||||
public int getSlowStartGrowthRateFactor() { return _slowStartGrowthRateFactor; }
|
||||
public void setSlowStartGrowthRateFactor(int factor) { _slowStartGrowthRateFactor = factor; }
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("conDelay=").append(_connectDelay);
|
||||
|
@ -64,6 +64,15 @@ public class ConnectionPacketHandler {
|
||||
|
||||
con.packetReceived();
|
||||
|
||||
boolean choke = false;
|
||||
if (packet.isFlagSet(Packet.FLAG_DELAY_REQUESTED)) {
|
||||
if (packet.getOptionalDelay() > 60000) {
|
||||
// requested choke
|
||||
choke = true;
|
||||
con.getOptions().setRTT(con.getOptions().getRTT() + 10*1000);
|
||||
}
|
||||
}
|
||||
|
||||
long ready = con.getInputStream().getHighestReadyBockId();
|
||||
int available = con.getOptions().getInboundBufferSize() - con.getInputStream().getTotalReadySize();
|
||||
int allowedBlocks = available/con.getOptions().getMaxMessageSize();
|
||||
@ -72,9 +81,10 @@ public class ConnectionPacketHandler {
|
||||
_log.warn("Inbound buffer exceeded on connection " + con + " ("
|
||||
+ ready + "/"+ (ready+allowedBlocks) + "/" + available
|
||||
+ ": dropping " + packet);
|
||||
ack(con, packet.getAckThrough(), packet.getNacks(), null, false);
|
||||
con.getOptions().setChoke(5*1000);
|
||||
ack(con, packet.getAckThrough(), packet.getNacks(), null, false, choke);
|
||||
con.getOptions().setChoke(61*1000);
|
||||
packet.releasePayload();
|
||||
con.ackImmediately();
|
||||
return;
|
||||
}
|
||||
con.getOptions().setChoke(0);
|
||||
@ -107,7 +117,7 @@ public class ConnectionPacketHandler {
|
||||
} else {
|
||||
int delay = con.getOptions().getSendAckDelay();
|
||||
if (packet.isFlagSet(Packet.FLAG_DELAY_REQUESTED)) // delayed ACK requested
|
||||
delay += packet.getOptionalDelay();
|
||||
delay = packet.getOptionalDelay();
|
||||
con.setNextSendTime(delay + _context.clock().now());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Scheduling ack in " + delay + "ms for received packet " + packet);
|
||||
@ -142,7 +152,7 @@ public class ConnectionPacketHandler {
|
||||
// don't honor the ACK 0 in SYN packets received when the other side
|
||||
// has obviously not seen our messages
|
||||
} else {
|
||||
fastAck = fastAck || ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
|
||||
fastAck = ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew, choke);
|
||||
}
|
||||
con.eventOccurred();
|
||||
if (fastAck) {
|
||||
@ -159,7 +169,10 @@ public class ConnectionPacketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean ack(Connection con, long ackThrough, long nacks[], Packet packet, boolean isNew) {
|
||||
private boolean ack(Connection con, long ackThrough, long nacks[], Packet packet, boolean isNew, boolean choke) {
|
||||
if ( (nacks != null) && (nacks.length > 0) )
|
||||
con.getOptions().setRTT(con.getOptions().getRTT() + nacks.length*1000);
|
||||
|
||||
int numResends = 0;
|
||||
List acked = con.ackPackets(ackThrough, nacks);
|
||||
if ( (acked != null) && (acked.size() > 0) ) {
|
||||
@ -196,16 +209,16 @@ public class ConnectionPacketHandler {
|
||||
}
|
||||
|
||||
if (packet != null)
|
||||
return adjustWindow(con, isNew, packet.getSequenceNum(), numResends, (acked != null ? acked.size() : 0));
|
||||
return adjustWindow(con, isNew, packet.getSequenceNum(), numResends, (acked != null ? acked.size() : 0), choke);
|
||||
else
|
||||
return adjustWindow(con, false, -1, numResends, (acked != null ? acked.size() : 0));
|
||||
return adjustWindow(con, false, -1, numResends, (acked != null ? acked.size() : 0), choke);
|
||||
}
|
||||
|
||||
|
||||
private boolean adjustWindow(Connection con, boolean isNew, long sequenceNum, int numResends, int acked) {
|
||||
private boolean adjustWindow(Connection con, boolean isNew, long sequenceNum, int numResends, int acked, boolean choke) {
|
||||
boolean congested = false;
|
||||
if ( (!isNew) && (sequenceNum > 0) ) {
|
||||
// dup real packet
|
||||
// dup real packet, or they told us to back off
|
||||
int oldSize = con.getOptions().getWindowSize();
|
||||
con.congestionOccurred();
|
||||
oldSize >>>= 1;
|
||||
@ -235,12 +248,16 @@ public class ConnectionPacketHandler {
|
||||
|
||||
// we can't use newWindowSize += 1/newWindowSize, since we're
|
||||
// integers, so lets use a random distribution instead
|
||||
int shouldIncrement = _context.random().nextInt(newWindowSize);
|
||||
int shouldIncrement = _context.random().nextInt(con.getOptions().getCongestionAvoidanceGrowthRateFactor()*newWindowSize);
|
||||
if (shouldIncrement <= 0)
|
||||
newWindowSize += 1;
|
||||
} else {
|
||||
// slow start
|
||||
newWindowSize += 1;
|
||||
// slow start, but modified to take into account the fact
|
||||
// that windows in the streaming lib are messages, not bytes,
|
||||
// so we only grow 1 every N times (where N = the slow start factor)
|
||||
int shouldIncrement = _context.random().nextInt(con.getOptions().getSlowStartGrowthRateFactor());
|
||||
if (shouldIncrement <= 0)
|
||||
newWindowSize += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user