2004-12-03 jrandom

* Toss in a small pool of threads (3) to execute the events queued up with
      the SimpleTimer, as we do currently see the occational event
      notification spiking up to a second or so.
    * Implement a SAM client API in java, useful for event based streaming (or
      for testing the SAM bridge)
    * Added support to shut down the SAM bridge on OOM (useful if the SAM
      bridge is being run outside of the router).
    * Include the SAM test code in the sam.jar
    * Remove an irrelevent warning message from SAM, which was caused by
      perfectly normal operation due to a session being closed.
    * Removed some unnecessary synchronization in the streaming lib's
      PacketQueue
    * More quickly clean up the memory used by the streaming lib by
      immediately killing each packet's resend job as soon as it is ACKed (or
      cancelled), so that there are no longer any valid pointers to the
      (potentially 32KB) packet.
    * Fixed the timestamps dumped to stdout when debugging the PacketHandler.
    * Drop packets that would expand our inbound window beyond our maximum
      buffer size (default 32 messages)
    * Always read the ACK/NACK data from the verified packets received, even
      if we are going to drop them
    * Always adjust the window when there are messages ACKed, though do not
      change its size except as before.
    * Streamlined some synchronization in the router's I2CP handling
    * Streamlined some memory allocation in the SAM bridge
    * Default the streaming lib to disconnect on inactivity, rather than send
      an empty message.
this still doesnt get the BT to where it needs to be, or fix the timeout problem,
but i dont like having so many commits outstanding and these updates are sound
This commit is contained in:
jrandom
2004-12-04 23:40:50 +00:00
committed by zzz
parent f54687f398
commit 1a30cd5f4a
29 changed files with 1287 additions and 186 deletions

View File

@ -10,7 +10,7 @@
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac
srcdir="./src"
srcdir="./src:./test"
debug="true" deprecation="on" source="1.3" target="1.3"
destdir="./build/obj"
classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />

View File

@ -28,8 +28,10 @@ import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Base64;
import net.i2p.data.ByteArray;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.ByteCache;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
@ -199,7 +201,7 @@ public class SAMStreamSession {
*
* @return True if the data was sent, false otherwise
*/
public boolean sendBytes(int id, byte[] data) {
public boolean sendBytes(int id, InputStream in, int size) {
Destination d = new Destination();
SAMStreamSessionSocketHandler handler = getSocketHandler(id);
@ -208,7 +210,7 @@ public class SAMStreamSession {
return false;
}
return handler.sendBytes(data);
return handler.sendBytes(in, size);
}
/**
@ -313,8 +315,7 @@ public class SAMStreamSession {
}
if (removed == null) {
_log.error("BUG! Trying to remove inexistent SAM STREAM session socket handler " + id);
recv.stopStreamReceiving();
// ignore - likely the socket handler was already removed by removeAllSocketHandlers
} else {
removed.stopRunning();
_log.debug("Removed SAM STREAM session socket handler " + id);
@ -462,17 +463,29 @@ public class SAMStreamSession {
*
* @return True if data has been sent without errors, false otherwise
*/
public boolean sendBytes(byte[] data) {
public boolean sendBytes(InputStream in, int size) { // byte[] data) {
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Handler " + id + ": sending " + data.length
_log.debug("Handler " + id + ": sending " + size
+ " bytes");
}
ByteCache cache = ByteCache.getInstance(1024, 4);
ByteArray ba = cache.acquire();
try {
i2pSocketOS.write(data);
int sent = 0;
byte buf[] = ba.getData();
while (sent < size) {
int read = in.read(buf);
if (read == -1)
throw new IOException("Insufficient data from the SAM client (" + sent + "/" + size + ")");
i2pSocketOS.write(buf, 0, read);
sent += read;
}
//i2pSocketOS.flush();
} catch (IOException e) {
_log.error("Error sending data through I2P socket", e);
return false;
} finally {
cache.release(ba);
}
return true;

View File

@ -44,6 +44,9 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
private SAMDatagramSession datagramSession = null;
private SAMStreamSession streamSession = null;
private long _id;
private static volatile long __id = 0;
/**
* Create a new SAM version 1 handler. This constructor expects
* that the SAM HELLO message has been still answered (and
@ -68,6 +71,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
*/
public SAMv1Handler(Socket s, int verMajor, int verMinor, Properties i2cpProps) throws SAMException, IOException {
super(s, verMajor, verMinor, i2cpProps);
_id = ++__id;
_log.debug("SAM version 1 handler instantiated");
if ((this.verMajor != 1) || (this.verMinor != 0)) {
@ -82,7 +86,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
StringTokenizer tok;
Properties props;
this.thread.setName("SAMv1Handler");
this.thread.setName("SAMv1Handler " + _id);
_log.debug("SAM handling started");
try {
@ -550,12 +554,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
try {
DataInputStream in = new DataInputStream(getClientSocketInputStream());
byte[] data = new byte[size];
in.readFully(data);
if (!streamSession.sendBytes(id, data)) {
if (!streamSession.sendBytes(id, getClientSocketInputStream(), size)) { // data)) {
_log.error("STREAM SEND failed");
boolean rv = writeString("STREAM CLOSED RESULT=CANT_REACH_PEER ID=" + id + " MESSAGE=\"Send of " + size + " bytes failed\"\n");
streamSession.closeConnection(id);
@ -801,7 +800,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
public void stopStreamReceiving() {
_log.debug("stopStreamReceiving() invoked");
_log.debug("stopStreamReceiving() invoked", new Exception("stopped"));
if (streamSession == null) {
_log.error("BUG! Got stream receiving stop, but session is null!");

View File

@ -0,0 +1,18 @@
package net.i2p.sam.client;
import java.util.Properties;
/**
* Basic noop client event listener
*/
public class SAMClientEventListenerImpl implements SAMReader.SAMClientEventListener {
public void destReplyReceived(String publicKey, String privateKey) {}
public void helloReplyReceived(boolean ok) {}
public void namingReplyReceived(String name, String result, String value, String message) {}
public void sessionStatusReceived(String result, String destination, String message) {}
public void streamClosedReceived(String result, int id, String message) {}
public void streamConnectedReceived(String remoteDestination, int id) {}
public void streamDataReceived(int id, byte[] data, int offset, int length) {}
public void streamStatusReceived(String result, int id, String message) {}
public void unknownMessageReceived(String major, String minor, Properties params) {}
}

View File

@ -0,0 +1,127 @@
package net.i2p.sam.client;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* Simple helper implementation of a the SAMClientEventListener
*
*/
public class SAMEventHandler extends SAMClientEventListenerImpl {
private I2PAppContext _context;
private Log _log;
private Boolean _helloOk;
private Object _helloLock = new Object();
private Boolean _sessionCreateOk;
private Object _sessionCreateLock = new Object();
private Object _namingReplyLock = new Object();
private Map _namingReplies = new HashMap();
public SAMEventHandler(I2PAppContext ctx) {
_context = ctx;
_log = ctx.logManager().getLog(getClass());
}
public void helloReplyReceived(boolean ok) {
synchronized (_helloLock) {
if (ok)
_helloOk = Boolean.TRUE;
else
_helloOk = Boolean.FALSE;
_helloLock.notifyAll();
}
}
public void sessionStatusReceived(String result, String destination, String msg) {
synchronized (_sessionCreateLock) {
if (SAMReader.SAMClientEventListener.SESSION_STATUS_OK.equals(result))
_sessionCreateOk = Boolean.TRUE;
else
_sessionCreateOk = Boolean.FALSE;
_sessionCreateLock.notifyAll();
}
}
public void namingReplyReceived(String name, String result, String value, String msg) {
synchronized (_namingReplyLock) {
if (SAMReader.SAMClientEventListener.NAMING_REPLY_OK.equals(result))
_namingReplies.put(name, value);
else
_namingReplies.put(name, result);
_namingReplyLock.notifyAll();
}
}
public void unknownMessageReceived(String major, String minor, Properties params) {
_log.error("wrt, [" + major + "] [" + minor + "] [" + params + "]");
}
//
// blocking lookup calls below
//
/**
* Wait for the connection to be established, returning true if everything
* went ok
*/
public boolean waitForHelloReply() {
while (true) {
try {
synchronized (_helloLock) {
if (_helloOk == null)
_helloLock.wait();
else
return _helloOk.booleanValue();
}
} catch (InterruptedException ie) {}
}
}
/**
* Wait for the session to be created, returning true if everything went ok
*
*/
public boolean waitForSessionCreateReply() {
while (true) {
try {
synchronized (_sessionCreateLock) {
if (_sessionCreateOk == null)
_sessionCreateLock.wait();
else
return _sessionCreateOk.booleanValue();
}
} catch (InterruptedException ie) {}
}
}
/**
* Return the destination found matching the name, or null if the key was
* not able to be retrieved.
*
* @param name name to be looked for, or "ME"
*/
public String waitForNamingReply(String name) {
while (true) {
try {
synchronized (_namingReplyLock) {
String val = (String)_namingReplies.remove(name);
if (val == null) {
_namingReplyLock.wait();
} else {
if (SAMReader.SAMClientEventListener.NAMING_REPLY_INVALID_KEY.equals(val))
return null;
else if (SAMReader.SAMClientEventListener.NAMING_REPLY_KEY_NOT_FOUND.equals(val))
return null;
else
return val;
}
}
} catch (InterruptedException ie) {}
}
}
}

View File

@ -0,0 +1,253 @@
package net.i2p.sam.client;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;
/**
* Read from a socket, producing events for any SAM message read
*
*/
public class SAMReader {
private Log _log;
private InputStream _inRaw;
private SAMClientEventListener _listener;
private boolean _live;
public SAMReader(I2PAppContext context, InputStream samIn, SAMClientEventListener listener) {
_log = context.logManager().getLog(SAMReader.class);
_inRaw = samIn;
_listener = listener;
}
public void startReading() {
_live = true;
I2PThread t = new I2PThread(new Runner(), "SAM reader");
t.start();
}
public void stopReading() { _live = false; }
/**
* Async event notification interface for SAM clients
*
*/
public interface SAMClientEventListener {
public static final String SESSION_STATUS_OK = "OK";
public static final String SESSION_STATUS_DUPLICATE_DEST = "DUPLICATE_DEST";
public static final String SESSION_STATUS_I2P_ERROR = "I2P_ERROR";
public static final String SESSION_STATUS_INVALID_KEY = "INVALID_KEY";
public static final String STREAM_STATUS_OK = "OK";
public static final String STREAM_STATUS_CANT_REACH_PEER = "CANT_REACH_PEER";
public static final String STREAM_STATUS_I2P_ERROR = "I2P_ERROR";
public static final String STREAM_STATUS_INVALID_KEY = "INVALID_KEY";
public static final String STREAM_STATUS_TIMEOUT = "TIMEOUT";
public static final String STREAM_CLOSED_OK = "OK";
public static final String STREAM_CLOSED_CANT_REACH_PEER = "CANT_REACH_PEER";
public static final String STREAM_CLOSED_I2P_ERROR = "I2P_ERROR";
public static final String STREAM_CLOSED_PEER_NOT_FOUND = "PEER_NOT_FOUND";
public static final String STREAM_CLOSED_TIMEOUT = "CLOSED";
public static final String NAMING_REPLY_OK = "OK";
public static final String NAMING_REPLY_INVALID_KEY = "INVALID_KEY";
public static final String NAMING_REPLY_KEY_NOT_FOUND = "KEY_NOT_FOUND";
public void helloReplyReceived(boolean ok);
public void sessionStatusReceived(String result, String destination, String message);
public void streamStatusReceived(String result, int id, String message);
public void streamConnectedReceived(String remoteDestination, int id);
public void streamClosedReceived(String result, int id, String message);
public void streamDataReceived(int id, byte data[], int offset, int length);
public void namingReplyReceived(String name, String result, String value, String message);
public void destReplyReceived(String publicKey, String privateKey);
public void unknownMessageReceived(String major, String minor, Properties params);
}
private class Runner implements Runnable {
public void run() {
Properties params = new Properties();
ByteArrayOutputStream baos = new ByteArrayOutputStream(80);
while (_live) {
try {
int c = -1;
while ((c = _inRaw.read()) != -1) {
if (c == '\n') {
break;
}
baos.write(c);
}
if (c == -1) {
_log.error("Error reading from the SAM bridge");
return;
}
} catch (IOException ioe) {
_log.error("Error reading from SAM", ioe);
}
String line = new String(baos.toByteArray());
baos.reset();
if (line == null) {
_log.info("No more data from the SAM bridge");
break;
}
_log.debug("Line read from the bridge: " + line);
StringTokenizer tok = new StringTokenizer(line);
if (tok.countTokens() < 2) {
_log.error("Invalid SAM line: [" + line + "]");
_live = false;
return;
}
String major = tok.nextToken();
String minor = tok.nextToken();
params.clear();
while (tok.hasMoreTokens()) {
String pair = tok.nextToken();
int eq = pair.indexOf('=');
if ( (eq > 0) && (eq < pair.length() - 1) ) {
String name = pair.substring(0, eq);
String val = pair.substring(eq+1);
while ( (val.charAt(0) == '\"') && (val.length() > 0) )
val = val.substring(1);
while ( (val.length() > 0) && (val.charAt(val.length()-1) == '\"') )
val = val.substring(0, val.length()-1);
params.setProperty(name, val);
}
}
processEvent(major, minor, params);
}
}
}
/**
* Big ugly method parsing everything. If I cared, I'd factor this out into
* a dozen tiny methods.
*
*/
private void processEvent(String major, String minor, Properties params) {
if ("HELLO".equals(major)) {
if ("REPLY".equals(minor)) {
String result = params.getProperty("RESULT");
if ("OK".equals(result))
_listener.helloReplyReceived(true);
else
_listener.helloReplyReceived(false);
} else {
_listener.unknownMessageReceived(major, minor, params);
}
} else if ("SESSION".equals(major)) {
if ("STATUS".equals(minor)) {
String result = params.getProperty("RESULT");
String dest = params.getProperty("DESTINATION");
String msg = params.getProperty("MESSAGE");
_listener.sessionStatusReceived(result, dest, msg);
} else {
_listener.unknownMessageReceived(major, minor, params);
}
} else if ("STREAM".equals(major)) {
if ("STATUS".equals(minor)) {
String result = params.getProperty("RESULT");
String id = params.getProperty("ID");
String msg = params.getProperty("MESSAGE");
if (id != null) {
try {
_listener.streamStatusReceived(result, Integer.parseInt(id), msg);
} catch (NumberFormatException nfe) {
_listener.unknownMessageReceived(major, minor, params);
}
} else {
_listener.unknownMessageReceived(major, minor, params);
}
} else if ("CONNECTED".equals(minor)) {
String dest = params.getProperty("DESTINATION");
String id = params.getProperty("ID");
if (id != null) {
try {
_listener.streamConnectedReceived(dest, Integer.parseInt(id));
} catch (NumberFormatException nfe) {
_listener.unknownMessageReceived(major, minor, params);
}
} else {
_listener.unknownMessageReceived(major, minor, params);
}
} else if ("CLOSED".equals(minor)) {
String result = params.getProperty("RESULT");
String id = params.getProperty("ID");
String msg = params.getProperty("MESSAGE");
if (id != null) {
try {
_listener.streamClosedReceived(result, Integer.parseInt(id), msg);
} catch (NumberFormatException nfe) {
_listener.unknownMessageReceived(major, minor, params);
}
} else {
_listener.unknownMessageReceived(major, minor, params);
}
} else if ("RECEIVED".equals(minor)) {
String id = params.getProperty("ID");
String size = params.getProperty("SIZE");
if (id != null) {
try {
int idVal = Integer.parseInt(id);
int sizeVal = Integer.parseInt(size);
byte data[] = new byte[sizeVal];
int read = DataHelper.read(_inRaw, data);
if (read != sizeVal) {
_listener.unknownMessageReceived(major, minor, params);
} else {
_listener.streamDataReceived(idVal, data, 0, sizeVal);
}
} catch (NumberFormatException nfe) {
_listener.unknownMessageReceived(major, minor, params);
} catch (IOException ioe) {
_live = false;
_listener.unknownMessageReceived(major, minor, params);
}
} else {
_listener.unknownMessageReceived(major, minor, params);
}
} else {
_listener.unknownMessageReceived(major, minor, params);
}
} else if ("NAMING".equals(major)) {
if ("REPLY".equals(minor)) {
String name = params.getProperty("NAME");
String result = params.getProperty("RESULT");
String value = params.getProperty("VALUE");
String msg = params.getProperty("MESSAGE");
_listener.namingReplyReceived(name, result, value, msg);
} else {
_listener.unknownMessageReceived(major, minor, params);
}
} else if ("DEST".equals(major)) {
if ("REPLY".equals(minor)) {
String pub = params.getProperty("PUB");
String priv = params.getProperty("PRIV");
_listener.destReplyReceived(pub, priv);
} else {
_listener.unknownMessageReceived(major, minor, params);
}
} else {
_listener.unknownMessageReceived(major, minor, params);
}
}
}

View File

@ -0,0 +1,312 @@
package net.i2p.sam;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;
import net.i2p.sam.client.SAMEventHandler;
import net.i2p.sam.client.SAMClientEventListenerImpl;
import net.i2p.sam.client.SAMReader;
/**
* Sit around on a SAM destination, receiving lots of data and sending lots of
* data to whomever talks to us.
*
* Usage: TestSwarm samHost samPort myKeyFile [peerDestFile ]*
*
*/
public class TestSwarm {
private I2PAppContext _context;
private Log _log;
private String _samHost;
private String _samPort;
private String _destFile;
private String _peerDestFiles[];
private String _conOptions;
private Socket _samSocket;
private OutputStream _samOut;
private InputStream _samIn;
private SAMReader _reader;
private boolean _dead;
private SAMEventHandler _eventHandler;
/** Connection id (Integer) to peer (Flooder) */
private Map _remotePeers;
public static void main(String args[]) {
if (args.length < 3) {
System.err.println("Usage: TestSwarm samHost samPort myDestFile [peerDestFile ]*");
return;
}
I2PAppContext ctx = new I2PAppContext();
String files[] = new String[args.length - 3];
System.arraycopy(args, 3, files, 0, files.length);
TestSwarm swarm = new TestSwarm(ctx, args[0], args[1], args[2], files);
swarm.startup();
}
public TestSwarm(I2PAppContext ctx, String samHost, String samPort, String destFile, String peerDestFiles[]) {
_context = ctx;
_log = ctx.logManager().getLog(TestSwarm.class);
_dead = false;
_samHost = samHost;
_samPort = samPort;
_destFile = destFile;
_peerDestFiles = peerDestFiles;
_conOptions = "";
_eventHandler = new SwarmEventHandler(_context);
_remotePeers = new HashMap();
}
public void startup() {
_log.debug("Starting up");
boolean ok = connect();
_log.debug("Connected: " + ok);
if (ok) {
_reader = new SAMReader(_context, _samIn, _eventHandler);
_reader.startReading();
_log.debug("Reader created");
String ourDest = handshake();
_log.debug("Handshake complete. we are " + ourDest);
if (ourDest != null) {
boolean written = writeDest(ourDest);
_log.debug("Dest written");
if (written) {
connectWithPeers();
_log.debug("connected with peers");
}
}
}
}
private class SwarmEventHandler extends SAMEventHandler {
public SwarmEventHandler(I2PAppContext ctx) { super(ctx); }
public void streamClosedReceived(String result, int id, String message) {
Flooder flooder = null;
synchronized (_remotePeers) {
flooder = (Flooder)_remotePeers.remove(new Integer(id));
}
if (flooder != null) {
flooder.closed();
_log.debug("Connection " + flooder.getConnectionId() + " closed to " + flooder.getDestination());
} else {
_log.error("wtf, not connected to " + id + " but we were just closed?");
}
}
public void streamDataReceived(int id, byte data[], int offset, int length) {
Flooder flooder = null;
synchronized (_remotePeers) {
flooder = (Flooder)_remotePeers.get(new Integer(id));
}
long value = DataHelper.fromLong(data, 0, 4);
if (flooder != null) {
flooder.received(length, value);
} else {
_log.error("wtf, not connected to " + id + " but we received " + value + "?");
}
}
public void streamConnectedReceived(String dest, int id) {
_log.debug("Connection " + id + " received from " + dest);
Flooder flooder = new Flooder(id, dest);
synchronized (_remotePeers) {
_remotePeers.put(new Integer(id), flooder);
}
I2PThread t = new I2PThread(flooder, "Flood " + id);
t.start();
}
}
private boolean connect() {
try {
_samSocket = new Socket(_samHost, Integer.parseInt(_samPort));
_samOut = _samSocket.getOutputStream();
_samIn = _samSocket.getInputStream();
return true;
} catch (Exception e) {
_log.error("Unable to connect to SAM at " + _samHost + ":" + _samPort, e);
return false;
}
}
private String handshake() {
synchronized (_samOut) {
try {
_samOut.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
_samOut.flush();
_log.debug("Hello sent");
boolean ok = _eventHandler.waitForHelloReply();
_log.debug("Hello reply found: " + ok);
if (!ok)
throw new IOException("wtf, hello failed?");
String req = "SESSION CREATE STYLE=STREAM DESTINATION=" + _destFile + " " + _conOptions + "\n";
_samOut.write(req.getBytes());
_samOut.flush();
_log.debug("Session create sent");
ok = _eventHandler.waitForSessionCreateReply();
_log.debug("Session create reply found: " + ok);
req = "NAMING LOOKUP NAME=ME\n";
_samOut.write(req.getBytes());
_samOut.flush();
_log.debug("Naming lookup sent");
String destination = _eventHandler.waitForNamingReply("ME");
_log.debug("Naming lookup reply found: " + destination);
if (destination == null) {
_log.error("No naming lookup reply found!");
return null;
} else {
_log.info(_destFile + " is located at " + destination);
}
return destination;
} catch (Exception e) {
_log.error("Error handshaking", e);
return null;
}
}
}
private boolean writeDest(String dest) {
try {
FileOutputStream fos = new FileOutputStream(_destFile);
fos.write(dest.getBytes());
fos.close();
return true;
} catch (Exception e) {
_log.error("Error writing to " + _destFile, e);
return false;
}
}
private void connectWithPeers() {
if (_peerDestFiles != null) {
for (int i = 0; i < _peerDestFiles.length; i++) {
try {
FileInputStream fin = new FileInputStream(_peerDestFiles[i]);
byte dest[] = new byte[1024];
int read = DataHelper.read(fin, dest);
String remDest = new String(dest, 0, read);
int con = 0;
Flooder flooder = null;
synchronized (_remotePeers) {
con = _remotePeers.size() + 1;
flooder = new Flooder(con, remDest);
_remotePeers.put(new Integer(con), flooder);
}
byte msg[] = ("STREAM CONNECT ID=" + con + " DESTINATION=" + remDest + "\n").getBytes();
synchronized (_samOut) {
_samOut.write(msg);
_samOut.flush();
}
I2PThread flood = new I2PThread(flooder, "Flood " + con);
flood.start();
_log.debug("Starting flooder with peer from " + _peerDestFiles[i] + ": " + con);
} catch (IOException ioe) {
_log.error("Unable to read the peer from " + _peerDestFiles[i]);
}
}
}
}
private class Flooder implements Runnable {
private int _connectionId;
private String _remoteDestination;
private boolean _closed;
private long _started;
private long _totalSent;
private long _totalReceived;
private long _lastReceived;
private long _lastReceivedOn;
private boolean _outOfSync;
public Flooder(int conId, String remDest) {
_connectionId = conId;
_remoteDestination = remDest;
_closed = false;
_outOfSync = false;
_lastReceived = -1;
_lastReceivedOn = _context.clock().now();
_context.statManager().createRateStat("swarm." + conId + ".totalReceived", "Data size received", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
_context.statManager().createRateStat("swarm." + conId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
_context.statManager().createRateStat("swarm." + conId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
_context.statManager().createRateStat("swarm." + conId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
}
public int getConnectionId() { return _connectionId; }
public String getDestination() { return _remoteDestination; }
public void closed() {
_closed = true;
long lifetime = _context.clock().now() - _started;
_context.statManager().addRateData("swarm." + _connectionId + ".lifetime", lifetime, lifetime);
}
public void run() {
_started = _context.clock().now();
_context.statManager().addRateData("swarm." + _connectionId + ".started", 1, 0);
byte data[] = new byte[32*1024];
long value = 0;
long lastSend = _context.clock().now();
while (!_closed) {
byte msg[] = ("STREAM SEND ID=" + _connectionId + " SIZE=" + data.length + "\n").getBytes();
DataHelper.toLong(data, 0, 4, value);
try {
synchronized (_samOut) {
_samOut.write(msg);
_samOut.write(data);
_samOut.flush();
}
} catch (IOException ioe) {
_log.error("Error talking to SAM", ioe);
return;
}
_totalSent += data.length;
_context.statManager().addRateData("swarm." + _connectionId + ".totalSent", _totalSent, 0);
value++;
try { Thread.sleep(20); } catch (InterruptedException ie) {}
long now = _context.clock().now();
_log.debug("Sending " + value + " on " + _connectionId + " after " + (now-lastSend));
lastSend = now;
}
}
public void received(int len, long value) {
_totalReceived += len;
if ( (!_outOfSync) && (len % 32*1024 != 0) ) {
_outOfSync = true;
if (_log.shouldLog(Log.ERROR))
_log.error("Out of sync (len=" + len + " after " + (_totalReceived-len) + ")");
}
_context.statManager().addRateData("swarm." + getConnectionId() + ".totalReceived", _totalReceived, 0);
if (value != _lastReceived + 1) {
if (!_outOfSync)
_log.error("Received " + value + " when expecting " + (_lastReceived+1) + " on "
+ _connectionId + " with " + _remoteDestination.substring(0,6));
else
_log.debug("(out of sync) Received " + value + " when expecting " + (_lastReceived+1) + " on "
+ _connectionId + " with " + _remoteDestination.substring(0,6));
} else {
_log.debug("Received " + value + " on " + _connectionId + " after " + (_context.clock().now()-_lastReceivedOn)
+ "ms with " + _remoteDestination.substring(0,6));
}
_lastReceived = value;
_lastReceivedOn = _context.clock().now();
}
}
}