SFTP相关的api说实话还是挺好用的,不过也有不少的坑。
直接上问题吧:timeout: socket is not established
当时是突然爆出来的问题,之前登陆sftp都没出过问题,突然生产报出来了。我透,这有点恶心人。
我在网上翻过挺多文章的,都没怎么看到解决方案,于是我就自力更生,看了下源码。算了再回头说下问题现状吧,
问题现状是:在登陆的时候,session.connect(30*1000);给会话设置了连接超时时间,当时都没多想,感觉没啥问题。
当问题出来,我跟进源码发现了有一个方法:
Util.createSocket(this.host, this.port, connectTimeout);
这个方法就是获取句柄的入口方法,我们继续跟下去:
static Socket createSocket(final String host, final int port, int timeout) throws JSchException {
Socket socket = null;
if (timeout == 0) {
try {
socket = new Socket(host, port);
return socket;
} catch (Exception var12) {
String message = var12.toString();
if (var12 instanceof Throwable) {
throw new JSchException(message, var12);
} else {
throw new JSchException(message);
}
}
} else {
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 var4) {
ee[0] = var4;
if (sockp[0] != null && sockp[0].isConnected()) {
try {
sockp[0].close();
} catch (Exception var3) {
;
}
}
sockp[0] = null;
}
}
});
tmp.setName("Opening Socket " + host);
tmp.start();
try {
tmp.join((long)timeout);
message = "timeout: ";
} catch (InterruptedException var11) {
;
}
//超时时,由于socket未完成实例化(或者未连接上sftp),主线程并行时,不符合条件抛出异常
if (sockp[0] != null && sockp[0].isConnected()) {
socket = sockp[0];
return socket;
} else {
message = message + "socket is not established";
if (ee[0] != null) {
message = ee[0].toString();
}
tmp.interrupt();
tmp = null;
throw new JSchException(message, ee[0]);
}
}
}
咱们看一下这个方法:根据设置的超时时间分为两个分支:
第一个分支没什么说的,就是新创建一个对象并返回。
当设置了超时时间时:方法采用的创建线程来处理的方式。
因为在此静态api中,不存在静态变量的操作,所以此方法是线程安全的。
创建的线程都是join(time)到主线程中的,也就是主线程等待至少time才执行。
这里是关键,如果在线程实例化Socket,连接sftp还未完成时,达到了超时时间time,此时主线程会和此线程并行!
并行的结果就是,主线程在句柄为实例化的情况下做了判断,导致抛出异常!
抛出的异常意思是:socket还没有创建出来!
这种情况下一般再调用一次就可以了。如果反复尝试都连不上的话,就有两种可能:
1:sftp端口没赋权导致连接超时
2:网络较差连接耗时大于time
如果是没有赋权,就打通下网络,如果连接耗时过长,我自己的解决方案是:不设置session的超时时间。那么主线程会一直等到线程执行完再执行。要么就报错。