
net.i2p.client.streaming.impl.TCBShare Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of streaming Show documentation
Show all versions of streaming Show documentation
Implementation of a TCP-like set of sockets for communicating over I2P.
package net.i2p.client.streaming.impl;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
import static net.i2p.client.streaming.impl.I2PSocketOptionsImpl.getDouble;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2;
/**
* Share important TCP Control Block parameters across Connections
* to the same remote peer.
* This is intended for "temporal" sharing at connection open/close time,
* not "ensemble" sharing during a connection. Ref. RFC 2140.
*
* There is a TCB share per ConnectionManager (i.e. per local Destination)
* so that there is no information leakage to other Destinations on the
* same router.
*
*/
class TCBShare {
private final I2PAppContext _context;
private final Log _log;
private final Map _cache;
private final CleanEvent _cleaner;
private final double _rttDampening, _wdwDampening, _rttDevDampening;
private static final long EXPIRE_TIME = 15*60*1000;
private static final long CLEAN_TIME = 5*60*1000;
///// constants defined in rfc 2140
///// do not change unless you know what you're doing
private static final double RTT_DAMPENING = 0.75;
private static final double RTTDEV_DAMPENING = 0.75;
private static final double WDW_DAMPENING = 0.75;
private static final String RTT_DAMP_PROP="i2p.streaming.tcbcache.rttDampening";
private static final String WDW_DAMP_PROP="i2p.streaming.tcbcache.wdwDampening";
private static final String RTTDEV_DAMP_PROP="i2p.streaming.tcbcache.rttdevDampening";
/////
private static final int MAX_RTT = ((int) Connection.MAX_RESEND_DELAY) / 2;
private static final int MAX_RTT_DEV = (int) (MAX_RTT * 1.5);
private static final int MAX_WINDOW_SIZE = ConnectionPacketHandler.MAX_SLOW_START_WINDOW;
public TCBShare(I2PAppContext ctx, SimpleTimer2 timer) {
_context = ctx;
_log = ctx.logManager().getLog(TCBShare.class);
final Properties props = ctx.getProperties();
_rttDampening = getDouble(props, RTT_DAMP_PROP, RTT_DAMPENING);
_wdwDampening = getDouble(props, WDW_DAMP_PROP, WDW_DAMPENING);
_rttDevDampening = getDouble(props, RTTDEV_DAMP_PROP, RTTDEV_DAMPENING);
_cache = new ConcurrentHashMap(4);
_cleaner = new CleanEvent(timer);
_cleaner.schedule(CLEAN_TIME);
if (_log.shouldLog(Log.DEBUG)) {
String log = "Creating TCBCache with rttDamp=%s, rttDevDamp=%s, wdwDamp=%s, "+
"expire=%d, clean=%d";
log = String.format(log,_rttDampening,_rttDevDampening,_wdwDampening,
EXPIRE_TIME,CLEAN_TIME);
_log.debug(log);
}
}
/**
* Cannot be restarted.
*/
public void stop() {
_cleaner.cancel();
_cache.clear();
}
/** retrieve from cache */
public void updateOptsFromShare(Connection con) {
Destination dest = con.getRemotePeer();
if (dest == null)
return;
ConnectionOptions opts = con.getOptions();
if (opts == null)
return;
Entry e = _cache.get(dest);
if (e == null || e.isExpired())
return;
final int rtt, rttDev, wdw;
synchronized(e) {
rtt = e.getRTT();
rttDev = e.getRTTDev();
wdw = e.getWindowSize();
}
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("From cache: " +
con.getSession().getMyDestination().calculateHash().toBase64().substring(0, 4) +
'-' +
dest.calculateHash().toBase64().substring(0, 4) +
" RTT: " + rtt +
" RTTDev: "+ rttDev +
" wdw: " + wdw );
}
opts.loadFromCache(rtt,rttDev,wdw);
}
/** store to cache */
public void updateShareOpts(Connection con) {
Destination dest = con.getRemotePeer();
if (dest == null)
return;
if (con.getAckedPackets() <= 0)
return;
ConnectionOptions opts = con.getOptions();
if (opts == null)
return;
int old = -1;
int oldw = -1;
int oldDev = -1;
Entry e = _cache.get(dest);
if (e == null || e.isExpired()) {
e = new Entry(opts.getRTT(), opts.getWindowSize(), opts.getRTTDev());
_cache.put(dest, e);
} else {
synchronized(e) {
old = e.getRTT();
oldw = e.getWindowSize();
oldDev = e.getRTTDev();
e.setRTT(opts.getRTT());
e.setWindowSize(opts.getWindowSize());
e.setRTTDev(opts.getRTTDev());
}
}
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("To cache: " +
con.getSession().getMyDestination().calculateHash().toBase64().substring(0, 4) +
'-' +
dest.calculateHash().toBase64().substring(0, 4) +
" old: " + old + " con: " + opts.getRTT() + " new: " + e.getRTT() +
" oldDev: " + oldDev + " conDev: " + opts.getRTTDev() + " newDev: " + e.getRTTDev() +
" oldw: " + oldw + " conw: " + opts.getWindowSize() + " neww: " + e.getWindowSize());
}
}
private class Entry {
int _rtt;
int _wdw;
int _rttDev;
long _updated;
public Entry(int ms, int wdw, int rttDev) {
_rtt = ms;
_wdw = wdw;
_rttDev = rttDev;
_updated = _context.clock().now();
}
public synchronized int getRTT() { return _rtt; }
public synchronized void setRTT(int ms) {
_rtt = (int)(_rttDampening*_rtt + (1-_rttDampening)*ms);
if (_rtt > MAX_RTT)
_rtt = MAX_RTT;
_updated = _context.clock().now();
}
public synchronized int getRTTDev() { return _rttDev; }
public synchronized void setRTTDev(int count) {
_rttDev = (int)(_rttDevDampening*_rttDev + (1-_rttDevDampening)*count);
if (_rttDev > MAX_RTT_DEV)
_rttDev = MAX_RTT_DEV;
_updated = _context.clock().now();
}
public synchronized int getWindowSize() { return _wdw; }
public synchronized void setWindowSize(int wdw) {
_wdw = (int)(0.5 + _wdwDampening*_wdw + (1-_wdwDampening)*wdw);
if (_wdw > MAX_WINDOW_SIZE)
_wdw = MAX_WINDOW_SIZE;
_updated = _context.clock().now();
}
public synchronized boolean isExpired() {
return _updated < _context.clock().now() - EXPIRE_TIME;
}
}
private class CleanEvent extends SimpleTimer2.TimedEvent {
public CleanEvent(SimpleTimer2 timer) {
// Use router's SimpleTimer2
super(timer);
}
public void timeReached() {
for (Iterator iter = _cache.values().iterator(); iter.hasNext(); ) {
if (iter.next().isExpired())
iter.remove();
}
schedule(CLEAN_TIME);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy