Added Simple true/false storage class to the utilities

Added socketSoTimeout
CHANGED RetransmissionTimer is now public
FIXED SimpleTimer has a way to be stopped, and reap it's children
FIXED Lots of javadoc additions, where I could
CLEANUP all code that needed to catch the timeout exception for socketSoTimeout
This commit is contained in:
sponge
2008-09-25 23:59:01 +00:00
parent ee2fd32a97
commit dd7d993631
13 changed files with 540 additions and 146 deletions

View File

@ -1,5 +1,6 @@
package net.i2p.client.streaming;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;

View File

@ -21,6 +21,7 @@ import net.i2p.util.SimpleTimer;
*
*/
public class ConnectionManager {
private I2PAppContext _context;
private Log _log;
private I2PSession _session;
@ -39,6 +40,7 @@ public class ConnectionManager {
private ConnectionOptions _defaultOptions;
private volatile int _numWaiting;
private Object _connectionLock;
private long SoTimeout;
public ConnectionManager(I2PAppContext context, I2PSession session, int maxConcurrent, ConnectionOptions defaultOptions) {
_context = context;
@ -58,6 +60,9 @@ public class ConnectionManager {
_maxConcurrentStreams = maxConcurrent;
_defaultOptions = defaultOptions;
_numWaiting = 0;
/** Socket timeout for accept() */
SoTimeout = -1;
_context.statManager().createRateStat("stream.con.lifetimeMessagesSent", "How many messages do we send on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("stream.con.lifetimeMessagesReceived", "How many messages do we receive on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("stream.con.lifetimeBytesSent", "How many bytes do we send on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
@ -75,6 +80,7 @@ public class ConnectionManager {
return (Connection)_connectionByInboundId.get(new Long(id));
}
}
/**
* not guaranteed to be unique, but in case we receive more than one packet
* on an inbound connection that we havent ack'ed yet...
@ -83,16 +89,34 @@ public class ConnectionManager {
synchronized (_connectionLock) {
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
Connection con = (Connection)iter.next();
if (DataHelper.eq(con.getSendStreamId(), id))
if(DataHelper.eq(con.getSendStreamId(), id)) {
return con;
}
}
}
return null;
}
/**
* Set the socket accept() timeout.
* @param x
*/
public void MsetSoTimeout(long x) {
SoTimeout = x;
}
/**
* Get the socket accept() timeout.
* @return
*/
public long MgetSoTimeout() {
return SoTimeout;
}
public void setAllowIncomingConnections(boolean allow) {
_connectionHandler.setActive(allow);
}
/** should we acceot connections, or just reject everyone? */
public boolean getAllowIncomingConnections() {
return _connectionHandler.getActive();
@ -113,9 +137,10 @@ public class ConnectionManager {
synchronized (_connectionLock) {
total = _connectionByInboundId.size();
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
if ( ((Connection)iter.next()).getIsConnected() )
if(((Connection)iter.next()).getIsConnected()) {
active++;
}
}
if (locked_tooManyStreams()) {
reject = true;
} else {
@ -135,9 +160,9 @@ public class ConnectionManager {
_context.statManager().addRateData("stream.receiveActive", active, total);
if (reject) {
if (_log.shouldLog(Log.WARN))
_log.warn("Refusing connection since we have exceeded our max of "
+ _maxConcurrentStreams + " connections");
if(_log.shouldLog(Log.WARN)) {
_log.warn("Refusing connection since we have exceeded our max of " + _maxConcurrentStreams + " connections");
}
PacketLocal reply = new PacketLocal(_context, synPacket.getOptionalFrom());
reply.setFlag(Packet.FLAG_RESET);
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
@ -163,7 +188,6 @@ public class ConnectionManager {
_context.statManager().addRateData("stream.connectionReceived", 1, 0);
return con;
}
private static final long DEFAULT_STREAM_DELAY_MAX = 10*1000;
/**
@ -176,15 +200,16 @@ public class ConnectionManager {
Connection con = null;
long receiveId = _context.random().nextLong(Packet.MAX_STREAM_ID-1)+1;
long expiration = _context.clock().now() + opts.getConnectTimeout();
if (opts.getConnectTimeout() <= 0)
if(opts.getConnectTimeout() <= 0) {
expiration = _context.clock().now() + DEFAULT_STREAM_DELAY_MAX;
}
_numWaiting++;
while (true) {
long remaining = expiration - _context.clock().now();
if (remaining <= 0) {
if (_log.shouldLog(Log.WARN))
_log.warn("Refusing to connect since we have exceeded our max of "
+ _maxConcurrentStreams + " connections");
if(_log.shouldLog(Log.WARN)) {
_log.warn("Refusing to connect since we have exceeded our max of " + _maxConcurrentStreams + " connections");
}
_numWaiting--;
return null;
}
@ -193,16 +218,18 @@ public class ConnectionManager {
if (locked_tooManyStreams()) {
// allow a full buffer of pending/waiting streams
if (_numWaiting > _maxConcurrentStreams) {
if (_log.shouldLog(Log.WARN))
_log.warn("Refusing connection since we have exceeded our max of "
+ _maxConcurrentStreams + " and there are " + _numWaiting
+ " waiting already");
if(_log.shouldLog(Log.WARN)) {
_log.warn("Refusing connection since we have exceeded our max of " + _maxConcurrentStreams + " and there are " + _numWaiting + " waiting already");
}
_numWaiting--;
return null;
}
// no remaining streams, lets wait a bit
try { _connectionLock.wait(remaining); } catch (InterruptedException ie) {}
try {
_connectionLock.wait(remaining);
} catch(InterruptedException ie) {
}
} else {
con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler, opts);
con.setRemotePeer(peer);
@ -224,35 +251,53 @@ public class ConnectionManager {
if (opts.getConnectDelay() <= 0) {
con.waitForConnect();
}
if (_numWaiting > 0)
if(_numWaiting > 0) {
_numWaiting--;
}
_context.statManager().addRateData("stream.connectionCreated", 1, 0);
return con;
}
private boolean locked_tooManyStreams() {
if (_maxConcurrentStreams <= 0) return false;
if (_connectionByInboundId.size() < _maxConcurrentStreams) return false;
if(_maxConcurrentStreams <= 0) {
return false;
}
if(_connectionByInboundId.size() < _maxConcurrentStreams) {
return false;
}
int active = 0;
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
Connection con = (Connection)iter.next();
if (con.getIsConnected())
if(con.getIsConnected()) {
active++;
}
}
if ( (_connectionByInboundId.size() > 100) && (_log.shouldLog(Log.INFO)) )
_log.info("More than 100 connections! " + active
+ " total: " + _connectionByInboundId.size());
if((_connectionByInboundId.size() > 100) && (_log.shouldLog(Log.INFO))) {
_log.info("More than 100 connections! " + active + " total: " + _connectionByInboundId.size());
}
return (active >= _maxConcurrentStreams);
}
public MessageHandler getMessageHandler() { return _messageHandler; }
public PacketHandler getPacketHandler() { return _packetHandler; }
public ConnectionHandler getConnectionHandler() { return _connectionHandler; }
public I2PSession getSession() { return _session; }
public PacketQueue getPacketQueue() { return _outboundQueue; }
public MessageHandler getMessageHandler() {
return _messageHandler;
}
public PacketHandler getPacketHandler() {
return _packetHandler;
}
public ConnectionHandler getConnectionHandler() {
return _connectionHandler;
}
public I2PSession getSession() {
return _session;
}
public PacketQueue getPacketQueue() {
return _outboundQueue;
}
/**
* Something b0rked hard, so kill all of our connections without mercy.
@ -279,11 +324,12 @@ public class ConnectionManager {
synchronized (_connectionLock) {
Object o = _connectionByInboundId.remove(new Long(con.getReceiveStreamId()));
removed = (o == con);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Connection removed? " + removed + " remaining: "
+ _connectionByInboundId.size() + ": " + con);
if (!removed && _log.shouldLog(Log.DEBUG))
if(_log.shouldLog(Log.DEBUG)) {
_log.debug("Connection removed? " + removed + " remaining: " + _connectionByInboundId.size() + ": " + con);
}
if(!removed && _log.shouldLog(Log.DEBUG)) {
_log.debug("Failed to remove " + con +"\n" + _connectionByInboundId.values());
}
_connectionLock.notifyAll();
}
if (removed) {
@ -309,9 +355,11 @@ public class ConnectionManager {
public boolean ping(Destination peer, long timeoutMs) {
return ping(peer, timeoutMs, true);
}
public boolean ping(Destination peer, long timeoutMs, boolean blocking) {
return ping(peer, timeoutMs, blocking, null, null, null);
}
public boolean ping(Destination peer, long timeoutMs, boolean blocking, SessionKey keyToUse, Set tagsToSend, PingNotifier notifier) {
Long id = new Long(_context.random().nextLong(Packet.MAX_STREAM_ID-1)+1);
PacketLocal packet = new PacketLocal(_context, peer);
@ -335,8 +383,12 @@ public class ConnectionManager {
if (blocking) {
synchronized (req) {
if (!req.pongReceived())
try { req.wait(timeoutMs); } catch (InterruptedException ie) {}
if(!req.pongReceived()) {
try {
req.wait(timeoutMs);
} catch(InterruptedException ie) {
}
}
}
synchronized (_pendingPings) {
@ -351,12 +403,15 @@ public class ConnectionManager {
}
interface PingNotifier {
public void pingComplete(boolean ok);
}
private class PingFailed implements SimpleTimer.TimedEvent {
private Long _id;
private PingNotifier _notifier;
public PingFailed(Long id, PingNotifier notifier) {
_id = id;
_notifier = notifier;
@ -366,29 +421,35 @@ public class ConnectionManager {
boolean removed = false;
synchronized (_pendingPings) {
Object o = _pendingPings.remove(_id);
if (o != null)
if(o != null) {
removed = true;
}
}
if (removed) {
if (_notifier != null)
if(_notifier != null) {
_notifier.pingComplete(false);
if (_log.shouldLog(Log.INFO))
}
if(_log.shouldLog(Log.INFO)) {
_log.info("Ping failed");
}
}
}
}
private class PingRequest {
private boolean _ponged;
private Destination _peer;
private PacketLocal _packet;
private PingNotifier _notifier;
public PingRequest(Destination peer, PacketLocal packet, PingNotifier notifier) {
_ponged = false;
_peer = peer;
_packet = packet;
_notifier = notifier;
}
public void pong() {
_log.debug("Ping successful");
_context.sessionKeyManager().tagsDelivered(_peer.getPublicKey(), _packet.getKeyUsed(), _packet.getTagsSent());
@ -396,10 +457,14 @@ public class ConnectionManager {
_ponged = true;
ConnectionManager.PingRequest.this.notifyAll();
}
if (_notifier != null)
if(_notifier != null) {
_notifier.pingComplete(true);
}
public boolean pongReceived() { return _ponged; }
}
public boolean pongReceived() {
return _ponged;
}
}
void receivePong(long pingId) {
@ -407,7 +472,8 @@ public class ConnectionManager {
synchronized (_pendingPings) {
req = (PingRequest)_pendingPings.remove(new Long(pingId));
}
if (req != null)
if(req != null) {
req.pong();
}
}
}

View File

@ -1,5 +1,8 @@
package net.i2p.client.streaming;
import java.net.SocketTimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.i2p.I2PException;
/**
@ -7,17 +10,46 @@ import net.i2p.I2PException;
*
*/
public class I2PServerSocketFull implements I2PServerSocket {
private I2PSocketManagerFull _socketManager;
/**
*
* @param mgr
*/
public I2PServerSocketFull(I2PSocketManagerFull mgr) {
_socketManager = mgr;
}
public I2PSocket accept() throws I2PException {
/**
*
* @return
* @throws net.i2p.I2PException
* @throws SocketTimeoutException
*/
public I2PSocket accept() throws I2PException, SocketTimeoutException {
return _socketManager.receiveSocket();
}
public void close() { _socketManager.getConnectionManager().setAllowIncomingConnections(false); }
public long getSoTimeout() {
return _socketManager.getConnectionManager().MgetSoTimeout();
}
public I2PSocketManager getManager() { return _socketManager; }
public void setSoTimeout(long x) {
_socketManager.getConnectionManager().MsetSoTimeout(x);
}
/**
* Close the connection.
*/
public void close() {
_socketManager.getConnectionManager().setAllowIncomingConnections(false);
}
/**
*
* @return _socketManager
*/
public I2PSocketManager getManager() {
return _socketManager;
}
}

View File

@ -11,6 +11,7 @@ import net.i2p.data.Destination;
*
*/
public class I2PSocketFull implements I2PSocket {
private Connection _connection;
private I2PSocket.SocketErrorListener _listener;
private Destination _remotePeer;
@ -24,9 +25,12 @@ public class I2PSocketFull implements I2PSocket {
}
}
public void close() throws IOException {
Connection c = _connection;
if (c == null) return;
if(c == null) {
return;
}
if (c.getIsConnected()) {
OutputStream out = c.getOutputStream();
if (out != null) {
@ -44,58 +48,71 @@ public class I2PSocketFull implements I2PSocket {
destroy();
}
Connection getConnection() { return _connection; }
Connection getConnection() {
return _connection;
}
public InputStream getInputStream() {
Connection c = _connection;
if (c != null)
if(c != null) {
return c.getInputStream();
else
} else {
return null;
}
}
public I2PSocketOptions getOptions() {
Connection c = _connection;
if (c != null)
if(c != null) {
return c.getOptions();
else
} else {
return null;
}
}
public OutputStream getOutputStream() throws IOException {
Connection c = _connection;
if (c != null)
if(c != null) {
return c.getOutputStream();
else
} else {
return null;
}
}
public Destination getPeerDestination() { return _remotePeer; }
public Destination getPeerDestination() {
return _remotePeer;
}
public long getReadTimeout() {
I2PSocketOptions opts = getOptions();
if (opts != null)
if(opts != null) {
return opts.getReadTimeout();
else
} else {
return -1;
}
}
public Destination getThisDestination() { return _localPeer; }
public Destination getThisDestination() {
return _localPeer;
}
public void setOptions(I2PSocketOptions options) {
Connection c = _connection;
if (c == null) return;
if (options instanceof ConnectionOptions)
if(c == null) {
return;
}
if(options instanceof ConnectionOptions) {
c.setOptions((ConnectionOptions)options);
else
} else {
c.setOptions(new ConnectionOptions(options));
}
}
public void setReadTimeout(long ms) {
Connection c = _connection;
if (c == null) return;
if(c == null) {
return;
}
c.getInputStream().setReadTimeout((int)ms);
c.getOptions().setReadTimeout(ms);
}
@ -116,14 +133,17 @@ public class I2PSocketFull implements I2PSocket {
Connection c = _connection;
_connection = null;
_listener = null;
if (c != null)
if(c != null) {
c.disconnectComplete();
}
}
public String toString() {
Connection c = _connection;
if (c == null)
if(c == null) {
return super.toString();
else
} else {
return c.toString();
}
}
}

View File

@ -1,6 +1,7 @@
package net.i2p.client.streaming;
import java.net.NoRouteToHostException;
import java.net.SocketTimeoutException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
@ -13,7 +14,6 @@ import net.i2p.client.I2PSessionException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* Centralize the coordination and multiplexing of the local client's streaming.
* There should be one I2PSocketManager for each I2PSession, and if an application
@ -23,6 +23,7 @@ import net.i2p.util.Log;
*
*/
public class I2PSocketManagerFull implements I2PSocketManager {
private I2PAppContext _context;
private Log _log;
private I2PSession _session;
@ -33,27 +34,41 @@ public class I2PSocketManagerFull implements I2PSocketManager {
private int _maxStreams;
private static int __managerId = 0;
private ConnectionManager _connectionManager;
/**
* How long to wait for the client app to accept() before sending back CLOSE?
* This includes the time waiting in the queue. Currently set to 5 seconds.
*/
private static final long ACCEPT_TIMEOUT_DEFAULT = 5*1000;
/**
*
*/
public I2PSocketManagerFull() {
_context = null;
_session = null;
}
/**
*
* @param context
* @param session
* @param opts
* @param name
*/
public I2PSocketManagerFull(I2PAppContext context, I2PSession session, Properties opts, String name) {
this();
init(context, session, opts, name);
}
/** how many streams will we allow at once? */
public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams";
/**
*
*
* @param context
* @param session
* @param opts
* @param name
*/
public void init(I2PAppContext context, I2PSession session, Properties opts, String name) {
_context = context;
@ -65,8 +80,9 @@ public class I2PSocketManagerFull implements I2PSocketManager {
String num = (opts != null ? opts.getProperty(PROP_MAX_STREAMS, "-1") : "-1");
_maxStreams = Integer.parseInt(num);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn("Invalid max # of concurrent streams, defaulting to unlimited", nfe);
}
_maxStreams = -1;
}
_name = name + " " + (++__managerId);
@ -76,44 +92,77 @@ public class I2PSocketManagerFull implements I2PSocketManager {
_serverSocket = new I2PServerSocketFull(this);
if (_log.shouldLog(Log.INFO)) {
_log.info("Socket manager created. \ndefault options: " + _defaultOptions
+ "\noriginal properties: " + opts);
_log.info("Socket manager created. \ndefault options: " + _defaultOptions + "\noriginal properties: " + opts);
}
}
public I2PSocketOptions buildOptions() { return buildOptions(null); }
/**
*
* @return
*/
public I2PSocketOptions buildOptions() {
return buildOptions(null);
}
/**
*
* @param opts
* @return
*/
public I2PSocketOptions buildOptions(Properties opts) {
ConnectionOptions curOpts = new ConnectionOptions(_defaultOptions);
curOpts.setProperties(opts);
return curOpts;
}
/**
*
* @return
*/
public I2PSession getSession() {
return _session;
}
/**
*
* @return
*/
public ConnectionManager getConnectionManager() {
return _connectionManager;
}
public I2PSocket receiveSocket() throws I2PException {
/**
*
* @return
* @throws net.i2p.I2PException
* @throws java.net.SocketTimeoutException
*/
public I2PSocket receiveSocket() throws I2PException, SocketTimeoutException {
verifySession();
Connection con = _connectionManager.getConnectionHandler().accept(-1);
if (_log.shouldLog(Log.DEBUG))
Connection con = _connectionManager.getConnectionHandler().accept(_connectionManager.MgetSoTimeout());
if(_log.shouldLog(Log.DEBUG)) {
_log.debug("receiveSocket() called: " + con);
}
if (con != null) {
I2PSocketFull sock = new I2PSocketFull(con);
con.setSocket(sock);
return sock;
} else {
if(_connectionManager.MgetSoTimeout() == -1) {
return null;
}
throw new SocketTimeoutException("I2PSocket timed out");
}
}
/**
* Ping the specified peer, returning true if they replied to the ping within
* the timeout specified, false otherwise. This call blocks.
*
*
* @param peer
* @param timeoutMs
* @return
*/
public boolean ping(Destination peer, long timeoutMs) {
return _connectionManager.ping(peer, timeoutMs);
@ -125,25 +174,47 @@ public class I2PSocketManagerFull implements I2PSocketManager {
*
* @param ms milliseconds to wait, maximum
*/
public void setAcceptTimeout(long ms) { _acceptTimeout = ms; }
public long getAcceptTimeout() { return _acceptTimeout; }
public void setAcceptTimeout(long ms) {
_acceptTimeout = ms;
}
/**
*
* @return
*/
public long getAcceptTimeout() {
return _acceptTimeout;
}
/**
*
* @param options
*/
public void setDefaultOptions(I2PSocketOptions options) {
_defaultOptions = new ConnectionOptions((ConnectionOptions) options);
}
/**
*
* @return
*/
public I2PSocketOptions getDefaultOptions() {
return _defaultOptions;
}
/**
*
* @return
*/
public I2PServerSocket getServerSocket() {
_connectionManager.setAllowIncomingConnections(true);
return _serverSocket;
}
private void verifySession() throws I2PException {
if (!_connectionManager.getSession().isClosed())
if(!_connectionManager.getSession().isClosed()) {
return;
}
_connectionManager.getSession().connect();
}
@ -159,20 +230,22 @@ public class I2PSocketManagerFull implements I2PSocketManager {
public I2PSocket connect(Destination peer, I2PSocketOptions options)
throws I2PException, NoRouteToHostException {
verifySession();
if (options == null)
if(options == null) {
options = _defaultOptions;
}
ConnectionOptions opts = null;
if (options instanceof ConnectionOptions)
if(options instanceof ConnectionOptions) {
opts = new ConnectionOptions((ConnectionOptions)options);
else
} else {
opts = new ConnectionOptions(options);
if (_log.shouldLog(Log.INFO))
_log.info("Connecting to " + peer.calculateHash().toBase64().substring(0,6)
+ " with options: " + opts);
}
if(_log.shouldLog(Log.INFO)) {
_log.info("Connecting to " + peer.calculateHash().toBase64().substring(0, 6) + " with options: " + opts);
}
Connection con = _connectionManager.connect(peer, opts);
if (con == null)
if(con == null) {
throw new TooManyStreamsException("Too many streams (max " + _maxStreams + ")");
}
I2PSocketFull socket = new I2PSocketFull(con);
con.setSocket(socket);
if (con.getConnectionError() != null) {
@ -187,6 +260,7 @@ public class I2PSocketManagerFull implements I2PSocketManager {
*
* @param peer Destination to connect to
*
* @return
* @throws NoRouteToHostException if the peer is not found or not reachable
* @throws I2PException if there is some other I2P-related problem
*/
@ -216,25 +290,49 @@ public class I2PSocketManagerFull implements I2PSocketManager {
/**
* Retrieve a set of currently connected I2PSockets, either initiated locally or remotely.
*
*
* @return
*/
public Set listSockets() {
Set connections = _connectionManager.listConnections();
Set rv = new HashSet(connections.size());
for (Iterator iter = connections.iterator(); iter.hasNext(); ) {
Connection con = (Connection)iter.next();
if (con.getSocket() != null)
if(con.getSocket() != null) {
rv.add(con.getSocket());
}
}
return rv;
}
public String getName() { return _name; }
public void setName(String name) { _name = name; }
/**
*
* @return
*/
public String getName() {
return _name;
}
/**
*
* @param name
*/
public void setName(String name) {
_name = name;
}
/**
*
* @param lsnr
*/
public void addDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
_connectionManager.getMessageHandler().addDisconnectListener(lsnr);
}
/**
*
* @param lsnr
*/
public void removeDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
_connectionManager.getMessageHandler().removeDisconnectListener(lsnr);
}

View File

@ -5,7 +5,7 @@ import net.i2p.util.SimpleTimer;
/**
*
*/
class RetransmissionTimer extends SimpleTimer {
public class RetransmissionTimer extends SimpleTimer {
private static final RetransmissionTimer _instance = new RetransmissionTimer();
public static final SimpleTimer getInstance() { return _instance; }
protected RetransmissionTimer() { super("StreamingTimer"); }