Jsch session channel timeout 默认连接超时时间机制

前言

Jsch版本

<dependency>
    <groupId>com.jcraftgroupId>
    <artifactId>jschartifactId>
    <version>0.1.55version>
dependency>

代码示例

public ChannelSftp createChannel() throws JSchException {
    JSch jsch = new JSch();
    Session session = jsch.getSession(username, host, port);
    session.setPassword(password);
    session.setConfig("StrictHostKeyChecking", "no");
    //可设连接超时时间
    session.connect();
    ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
    //可设连接超时时间
    channel.connect();
    return channel;
}

上面的session和channel连接时都使用了无参构造函数,我看有的人说要穿连接超时限制,但是也没说默认连接超时时间是多久,所以自己得看看,可能他设个5000毫秒,也可能设个-1、0之类的永不超时

源码分析

Session

com.jcraft.jsch.Session

分析

默认时间是0

private int timeout=0;

public void connect() throws JSchException{
    connect(timeout);
}

有多种连接方式,如socket_factory、proxy,如我们看socket_factory的createSocket就行

也可以看到当connectTimeout>0时进行了setSoTimeout设置,所以说这个连接的connectTimeout和soTimeout是取得同一个值,你要网络不好、文件操作时间长,还真不能设短了,要按实际情况设置

public void connect(int connectTimeout) throws JSchException{
    //省略
	if(socket_factory==null){
          socket=Util.createSocket(host, port, connectTimeout);
	  in=socket.getInputStream();
	  out=socket.getOutputStream();
	}
    synchronized(proxy){
          proxy.connect(socket_factory, host, port, connectTimeout);
	  io.setInputStream(proxy.getInputStream());
	  io.setOutputStream(proxy.getOutputStream());
          socket=proxy.getSocket();
	}
      }

      if(connectTimeout>0 && socket!=null){
        socket.setSoTimeout(connectTimeout);
      }
        

这里在timeout为0时直接new Socket,返回socket后在外层设置,默认是0,就是没设置,使用Socket的默认超时时间

java.net.Socket中写了A timeout of zero is interpreted as an infinite timeout. The connection will then block until established or an error occurs,就是无限等待,直到建立连接或发生错误

if(connectTimeout>0 && socket!=null){
  socket.setSoTimeout(connectTimeout);
}

如果不是0,使用了thread的join方法,就是阻塞等待设置的超时时间,超过了就报错

com.jcraft.jsch.SocketFactory中

static Socket createSocket(String host, int port, int timeout) throws JSchException{
  Socket socket=null;
  if(timeout==0){
    try{
      socket=new Socket(host, port);
      return socket;
    }
    catch(Exception e){
      String message=e.toString();
      if(e instanceof Throwable)
        throw new JSchException(message, (Throwable)e);
      throw new JSchException(message);
    }
  }
  final String _host=host;
  final int _port=port;
  final Socket[] sockp=new Socket[1];
  final Exception[] ee=new Exception[1];
  String message="";
  Thread tmp=new Thread(new Runnable(){
      public void run(){
        sockp[0]=null;
        try{
          sockp[0]=new Socket(_host, _port);
        }
        catch(Exception e){
          ee[0]=e;
          if(sockp[0]!=null && sockp[0].isConnected()){
            try{
              sockp[0].close();
            }
            catch(Exception eee){}
          }
          sockp[0]=null;
        }
      }
    });
  tmp.setName("Opening Socket "+host);
  tmp.start();
  try{ 
    tmp.join(timeout);
    message="timeout: ";
  }
  catch(java.lang.InterruptedException eee){
  }
  if(sockp[0]!=null && sockp[0].isConnected()){
    socket=sockp[0];
  }
  else{
    message+="socket is not established";
    if(ee[0]!=null){
      message=ee[0].toString();
    }
    tmp.interrupt();
    tmp=null;
    throw new JSchException(message, ee[0]);
  }
  return socket;
} 

结论

Session默认使用java.net.Socket的默认超时时间0,会无限等待,直到建立连接或发生错误

Channel

com.jcraft.jsch.Channel

分析

channel直接写死了个0,然后赋给connectTimeout变量,同一个SDK的代码,风格都不一样。。

sendChannelOpen是com.jcraft.jsch.Channel自己的方法,用来打开连接

start是抽象方法,有四个实现:com.jcraft.jsch.ChannelSftp、com.jcraft.jsch.ChannelExec、com.jcraft.jsch.ChannelShell、com.jcraft.jsch.ChannelSubsystem

public void connect() throws JSchException{
  connect(0);
}
public void connect(int connectTimeout) throws JSchException{
    this.connectTimeout=connectTimeout;
    try{
      sendChannelOpen();
      start();
    }
    catch(Exception e){
      connected=false;
      disconnect();
      if(e instanceof JSchException) 
        throw (JSchException)e;
      throw new JSchException(e.toString(), e);
    }
  }

当timeout不是0,只尝试1次,使用Object的wait指定时间

是0的时候,连接成功全重试2000次,每次等10毫秒,也就是20秒

protected void sendChannelOpen() throws Exception {
  Session _session=getSession();
  if(!_session.isConnected()){
    throw new JSchException("session is down");
  }

  Packet packet = genChannelOpenPacket();
  _session.write(packet);

  int retry=2000;
  long start=System.currentTimeMillis();
  long timeout=connectTimeout;
  if(timeout!=0L) retry = 1;
  synchronized(this){
    while(this.getRecipient()==-1 &&
          _session.isConnected() &&
           retry>0){
      if(timeout>0L){
        if((System.currentTimeMillis()-start)>timeout){
          retry=0;
          continue;
        }
      }
      try{
        long t = timeout==0L ? 10L : timeout;
        this.notifyme=1;
        wait(t);
      }
      catch(java.lang.InterruptedException e){
      }
      finally{
        this.notifyme=0;
      }
      retry--;
    }
  }
  if(!_session.isConnected()){
    throw new JSchException("session is down");
  }
  if(this.getRecipient()==-1){  // timeout
    throw new JSchException("channel is not opened.");
  }
  if(this.open_confirmation==false){  // SSH_MSG_CHANNEL_OPEN_FAILURE
    throw new JSchException("channel is not opened.");
  }
  connected=true;
}

结论

Channel连接默认20秒超时

你可能感兴趣的:(性能优化,开发工具,技术,java,网络)