diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
index ac764961aa460deb441d25cf5dfb4b038af08730..93fb6412147fec7f76f090ec76abfb4287d827f5 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
@@ -74,6 +74,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
     public static String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
     public static String listenHost = host;
 
+    public static long readTimeout = -1;
+
     private static final String nocli_args[] = { "-nocli", "-die"};
 
     private List tasks = new ArrayList();
@@ -201,6 +203,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
             runConfig(args, l);
         } else if ("listen_on".equals(cmdname)) {
             runListenOn(args, l);
+        } else if ("read_timeout".equals(cmdname)) {
+            runReadTimeout(args, l);
         } else if ("genkeys".equals(cmdname)) {
             runGenKeys(args, l);
         } else if ("gentextkeys".equals(cmdname)) {
@@ -235,6 +239,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
         l.log("Command list:");
         l.log("config <i2phost> <i2pport>");
         l.log("listen_on <ip>");
+        l.log("read_timeout <msecs>");
         l.log("owndest yes|no");
         l.log("ping <args>");
         l.log("server <host> <port> <privkeyfile>");
@@ -291,10 +296,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
                 notifyEvent("serverTaskId", new Integer(-1));
                 return;
             }
-            I2PTunnelTask task;
-            task = new I2PTunnelServer(serverHost, portNum, privKeyFile, args[2], l, (EventDispatcher) this);
-            addtask(task);
-            notifyEvent("serverTaskId", new Integer(task.getId()));
+            I2PTunnelServer serv = new I2PTunnelServer(serverHost, portNum, privKeyFile, args[2], l, (EventDispatcher) this);
+            serv.setReadTimeout(readTimeout);
+            serv.startRunning();
+            addtask(serv);
+            notifyEvent("serverTaskId", new Integer(serv.getId()));
             return;
         } else {
             l.log("server <host> <port> <privkeyfile>");
@@ -336,10 +342,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
                 return;
             }
 
-            I2PTunnelTask task;
-            task = new I2PTunnelServer(serverHost, portNum, args[2], l, (EventDispatcher) this);
-            addtask(task);
-            notifyEvent("serverTaskId", new Integer(task.getId()));
+            I2PTunnelServer serv = new I2PTunnelServer(serverHost, portNum, args[2], l, (EventDispatcher) this);
+            serv.setReadTimeout(readTimeout);
+            serv.startRunning();
+            addtask(serv);
+            notifyEvent("serverTaskId", new Integer(serv.getId()));
         } else {
             l.log("textserver <host> <port> <privkey>");
             l.log("  creates a server that sends all incoming data\n" + "  of its destination to host:port.");
@@ -510,7 +517,31 @@ public class I2PTunnel implements Logging, EventDispatcher {
         } else {
             l.log("listen_on <ip>");
             l.log("  sets the interface to listen for the I2PClient.");
+            notifyEvent("listen_onResult", "error");
+        }
+    }
+
+    /**
+     * Specify the read timeout going to be used for newly-created I2PSockets
+     *
+     * Sets the event "read_timeoutResult" = "ok" or "error" after the interface has been specified
+     *
+     * @param args {hostname}
+     * @param l logger to receive events and output
+     */
+    public void runReadTimeout(String args[], Logging l) {
+        if (args.length == 1) {
+            try {
+                readTimeout = Long.parseLong(args[0]);
+            } catch (NumberFormatException e) {
+                notifyEvent("read_timeoutResult", "error");
+            }
             notifyEvent("listen_onResult", "ok");
+        } else {
+            l.log("read_timeout <msecs>");
+            l.log("  sets the read timeout (in milliseconds) for I2P connections\n"
+                  +"  Negative values will make the connections wait forever");
+            notifyEvent("read_timeoutResult", "error");
         }
     }
 
@@ -1000,4 +1031,4 @@ public class I2PTunnel implements Logging, EventDispatcher {
     public Object waitEventValue(String n) {
         return _event.waitEventValue(n);
     }
-}
\ No newline at end of file
+}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java
index ad2246ed4eca0829e44d4394c3fc3e2cedd44c64..b81ee71ca82795fc0961bb16173162cd331bfd8d 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java
@@ -6,6 +6,7 @@ package net.i2p.i2ptunnel;
 import java.io.BufferedOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InterruptedIOException;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.net.SocketException;
@@ -116,7 +117,7 @@ public class I2PTunnelRunner extends I2PThread {
             _log.error("Interrupted", ex);
         } catch (IOException ex) {
             ex.printStackTrace();
-            _log.error("Error forwarding", ex);
+            _log.debug("Error forwarding", ex);
         } finally {
             try {
                 if (s != null) s.close();
@@ -163,11 +164,13 @@ public class I2PTunnelRunner extends I2PThread {
             } catch (SocketException ex) {
                 // this *will* occur when the other threads closes the socket
                 synchronized (finishLock) {
-                    if (!finished)
-                        _log.error("Error reading and writing", ex);
-                    else
-                        _log.warn("You may ignore this", ex);
+                    if (!finished) {
+                        _log.debug("Socket closed - error reading and writing",
+                                   ex);
+                    }
                 }
+            } catch (InterruptedIOException ex) {
+                _log.debug("Socket read timed out - closing StreamForwarder");
             } catch (IOException ex) {
                 if (!finished)
                     _log.error("Error forwarding", ex);
@@ -188,4 +191,4 @@ public class I2PTunnelRunner extends I2PThread {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
index e3401ebd109bde15c70ce79ab197092d8166333d..a219b4e40049d0da4812a1f8b302631b7d2dc880 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
@@ -40,6 +40,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
 
     private Logging l;
 
+    private long readTimeout = -1;
+
     public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis) {
         super(host + ":" + port + " <- " + privData, notifyThis);
         ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(privData));
@@ -57,8 +59,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
         }
     }
 
-    public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l,
-                           EventDispatcher notifyThis) {
+    public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l,  EventDispatcher notifyThis) {
         super(host + ":" + port + " <- " + privkeyname, notifyThis);
         init(host, port, privData, privkeyname, l);
     }
@@ -78,11 +79,37 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
         l.log("Ready!");
         notifyEvent("openServerResult", "ok");
         open = true;
+    }
+
+    /**
+     * Start running the I2PTunnelServer.
+     *
+     */
+    public void startRunning() {
         Thread t = new I2PThread(this);
         t.setName("Server");
         t.start();
     }
 
+    /**
+     * Set the read idle timeout for newly-created connections (in
+     * milliseconds).  After this time expires without data being reached from
+     * the I2P network, the connection itself will be closed.
+     */
+    public void setReadTimeout(long ms) {
+        readTimeout = ms;
+    }
+    
+    /**
+     * Get the read idle timeout for newly-created connections (in
+     * milliseconds).
+     *
+     * @return The read timeout used for connections
+     */
+    public long getReadTimeout() {
+        return readTimeout;
+    }
+
     public boolean close(boolean forced) {
         if (!open) return true;
         synchronized (lock) {
@@ -115,6 +142,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
                 //local is fast, so synchronously. Does not need that many
                 //threads.
                 try {
+                    i2ps.setReadTimeout(readTimeout);
                     Socket s = new Socket(remoteHost, remotePort);
                     new I2PTunnelRunner(s, i2ps, slock, null);
                 } catch (SocketException ex) {