Re: [JSch-users] Issue: One blocking channel blocks all Session communication
Status: Alpha
Brought to you by:
ymnk
From: B. S. S. <sc...@sm...> - 2011-12-30 02:41:44
|
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 |