diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandler.java b/apps/sam/java/src/net/i2p/sam/SAMHandler.java index 3a1f4aaa7c18d060610913dfeb9d3ff8052ef9f0..e2bcb84fb709d4ad2d3005fa0a9c57c02f5d3df9 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMHandler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMHandler.java @@ -102,7 +102,10 @@ abstract class SAMHandler implements Runnable, Handler { } } - static public void writeBytes(ByteBuffer data, SocketChannel out) throws IOException { + /** + * Caller must synch + */ + private static void writeBytes(ByteBuffer data, SocketChannel out) throws IOException { while (data.hasRemaining()) out.write(data); out.socket().getOutputStream().flush(); } @@ -132,7 +135,10 @@ abstract class SAMHandler implements Runnable, Handler { } } - /** @return success */ + /** + * Unsynchronized, use with caution + * @return success + */ public static boolean writeString(String str, SocketChannel out) { try { diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java index ff17f07b801bf831ccb894d63eed09f5cc4d1e39..9cc4a500c752a710125ec09d1db5d3ab601e3fed 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java @@ -315,7 +315,7 @@ class SAMv3Handler extends SAMv1Handler } else if (_lastPing < 0) { if (_log.shouldWarn()) _log.warn("2nd timeout"); - writeString("XXX STATUS RESULT=I2P_ERROR MESSAGE=\"command timeout, bye\"\n"); + writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"command timeout, bye\"\n"); break; } else { // don't clear buffer, don't send ping, diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java index fedd4699814626e771c5b2cd590ded34cc2cdbc9..b582fa8069fa9bdcfbd2017d1314e8150a26f5db 100644 --- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java +++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java @@ -15,6 +15,7 @@ import gnu.getopt.Getopt; import net.i2p.I2PAppContext; import net.i2p.data.Base32; import net.i2p.data.DataHelper; +import net.i2p.util.I2PAppThread; import net.i2p.util.Log; import net.i2p.util.VersionComparator; @@ -35,6 +36,7 @@ public class SAMStreamSink { private String _conOptions; private SAMReader _reader, _reader2; private boolean _isV3; + private boolean _isV32; private String _v3ID; //private boolean _dead; /** Connection id (Integer) to peer (Flooder) */ @@ -127,6 +129,11 @@ public class SAMStreamSink { throw new IOException("handshake failed"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Handshake complete. we are " + ourDest); + if (_isV32) { + _log.debug("Starting pinger"); + Thread t = new Pinger(out); + t.start(); + } if (_isV3 && mode != V1DG && mode != V1RAW) { Socket sock2 = connect(isSSL); out = sock2.getOutputStream(); @@ -146,6 +153,32 @@ public class SAMStreamSink { _log.error("Unable to connect to SAM at " + _samHost + ":" + _samPort, e); } } + + private static class Pinger extends I2PAppThread { + private final OutputStream _out; + + public Pinger(OutputStream out) { + super("SAM Sink Pinger"); + setDaemon(true); + _out = out; + } + + public void run() { + while (true) { + try { + Thread.sleep(127*1000); + synchronized(_out) { + _out.write(DataHelper.getASCII("PING " + System.currentTimeMillis() + '\n')); + _out.flush(); + } + } catch (InterruptedException ie) { + break; + } catch (IOException ioe) { + break; + } + } + } + } private class SinkEventHandler extends SAMEventHandler { @@ -353,6 +386,7 @@ public class SAMStreamSink { _isV3 = VersionComparator.comp(hisVersion, "3") >= 0; String dest; if (_isV3) { + _isV32 = VersionComparator.comp(hisVersion, "3.2") >= 0; // we use the filename as the name in sam.keys // and read it in ourselves File keys = new File("sam.keys");