Loading...

jsch-users@lists.sourceforge.net

[Prev] Thread [Next]  |  [Prev] Date [Next]

Re: [JSch-users] Issue: One blocking channel blocks all Session communication B. Scott Smith Fri Feb 03 05:00:06 2012

Hi ymnk,

I never heard back from you on this issue.
The problem is that the main Session loop blocks when calling 
channel.write() while handling incoming SSH_MSG_CHANNEL_DATA. When that 
happens, all channels in the entire Session stop communicating. To 
resolve this, I instead write the message data to a queue, and I create 
a background thread to process the queue and write the actual data.

You may not agree with my implementation, but it works for me, and I 
have tested it extensively.
Here is the patch diff to 0.1.45:

==== BEGIN PATCH =====
Index: src/main/java/com/jcraft/jsch/Channel.java
===================================================================
--- src/main/java/com/jcraft/jsch/Channel.java    (revision 253)
+++ src/main/java/com/jcraft/jsch/Channel.java    (working copy)
@@ -119,6 +119,8 @@
    volatile int reply=0;
    volatile int connectTimeout=0;

+  WriteQueue writeQueue = new WriteQueue();
+
    private Session session;

    int notifyme=0;
@@ -416,19 +418,19 @@
    }
    void write(byte[] foo, int s, int l) throws IOException {
      try{
-      io.put(foo, s, l);
+      writeQueue.add(new WriteMessage(foo,s,l,false));
      }catch(NullPointerException e){}
    }
    void write_ext(byte[] foo, int s, int l) throws IOException {
      try{
-      io.put_ext(foo, s, l);
+      writeQueue.add(new WriteMessage(foo,s,l,true));
      }catch(NullPointerException e){}
    }

    void eof_remote(){
      eof_remote=true;
      try{
-      io.out_close();
+      writeQueue.add(new WriteMessage(null,0,0,false));
      }
      catch(NullPointerException e){}
    }
@@ -557,7 +559,7 @@

        try{
          if(io!=null){
-          io.close();
+          writeQueue.add(null);
          }
        }
        catch(Exception e){
@@ -662,4 +664,106 @@
      catch(Exception e){
      }
    }
+
+  class WriteQueue implements Runnable{
+    final java.util.LinkedList writeList = new java.util.LinkedList();
+    Thread writeThread = null;
+
+    private synchronized void add(WriteMessage writeMessage){
+      if (writeThread ==null){
+        writeThread=new Thread(this,"WriteQueue thread for Channel: "+id);
+        writeThread.start();
+      }
+
+      synchronized(writeList){
+        writeList.add(writeMessage);
+        if (writeList.size()==1){
+          writeList.notify();
+        }
+      }
+    }
+    public void run(){
+      WriteMessage writeMessage = null;
+
+      try{
+        while(writeThread!=null){
+          synchronized(writeList){
+            if (writeList.size()<=0){
+              if(close){
+                writeThread=null;
+                break;
+              }
+              try{writeList.wait(10000);} catch(InterruptedException ie){}
+              if(writeList.size()<=0){continue;}
+            }
+
+            writeMessage=(WriteMessage)writeList.removeFirst();
+          }
+
+          if (writeMessage==null){
+            writeThread=null;
+            break;
+          }
+
+          writeMessage.write();
+        }
+      }
+      catch(Throwable t){
+        writeThread=null;
+      }
+
+      if (io!=null){
+        io.close();
+        io=null;
+      }
+    }
+  }
+  class WriteMessage{
+    boolean extData = false;
+    byte[] msg = null;
+
+    public WriteMessage(byte[] foo, int s, int l, boolean ext){
+        extData = ext;
+        if (l>0){
+          msg=new byte[l];
+          System.arraycopy(foo, s, msg, 0, l);
+        }
+    }
+    void write() throws Exception{
+      if (msg==null){
+        try{
+          io.out_close();
+        }catch(Throwable e){}
+        return;
+      }
+      else if (extData){
+        try{
+          io.put_ext(msg, 0, msg.length);
+        }catch(Throwable e){
+          try{disconnect();}catch(Throwable ee){}
+          return;
+        }
+      }
+      else{
+        try{
+          io.put(msg, 0, msg.length);
+        }catch(Throwable e){
+          try{disconnect();}catch(Throwable ee){}
+          return;
+        }
+      }
+
+      setLocalWindowSize(lwsize-msg.length);
+      if(lwsize<lwsize_max/2){
+        Buffer buf=new Buffer(100);
+        Packet packet=new Packet(buf);
+        packet.reset();
+        buf.putByte((byte)Session.SSH_MSG_CHANNEL_WINDOW_ADJUST);
+        buf.putInt(getRecipient());
+        buf.putInt(lwsize_max-lwsize);
+        getSession().write(packet);
+        setLocalWindowSize(lwsize_max);
+      }
+    }
+  }
  }
Index: src/main/java/com/jcraft/jsch/Session.java
===================================================================
--- src/main/java/com/jcraft/jsch/Session.java    (revision 253)
+++ src/main/java/com/jcraft/jsch/Session.java    (working copy)
@@ -1332,6 +1332,7 @@
    try{channel.disconnect();}catch(Exception ee){}
  break;
  }
+/*
        int len=length[0];
        channel.setLocalWindowSize(channel.lwsize-len);
         if(channel.lwsize<channel.lwsize_max/2){
@@ -1342,6 +1343,7 @@
          write(packet);
          channel.setLocalWindowSize(channel.lwsize_max);
        }
+*/
        break;

          case SSH_MSG_CHANNEL_EXTENDED_DATA:
@@ -1362,6 +1364,7 @@

        channel.write_ext(foo, start[0], length[0]);

+/*
        len=length[0];
        channel.setLocalWindowSize(channel.lwsize-len);
         if(channel.lwsize<channel.lwsize_max/2){
@@ -1372,6 +1375,7 @@
          write(packet);
          channel.setLocalWindowSize(channel.lwsize_max);
        }
+*/
        break;

      case SSH_MSG_CHANNEL_WINDOW_ADJUST:
==== END PATCH =====

On 10/21/2011 5:55 PM, B. Scott Smith wrote:
> Hi ymnk,
>
> I am experiencing an issue in which one slow client is effecting all 
> client performance. To simplify and demonstrate the issue, I setup a 
> test case in which I forward 2 ports: one to a web server and one to 
> another service, say telnet. I then issue a "wget" command to request 
> a large file from the web server. While the file is downloading, I 
> suspend the wget process (control Z). At this point, no other client 
> can connect to the web server, and also any active telnet clients stop 
> working. When I resume the wget process, everything works again. I try 
> this exact test with the native command-line ssh client, and it all 
> works fine.
>
> I have tried this with version 0.1.44 as well as an older version 
> (0.1.41).
>
> Any ideas?
> Thanks in advance.
>  - Scott

------------------------------------------------------------------------------
Ridiculously easy VDI. With Citrix VDI-in-a-Box, you don't need a complex
infrastructure or vast IT resources to deliver seamless, secure access to
virtual desktops. With this all-in-one solution, easily deploy virtual 
desktops for less than the cost of PCs and save 60% on VDI infrastructure 
costs. Try it free! http://p.sf.net/sfu/Citrix-VDIinabox
_______________________________________________
JSch-users mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/jsch-users