FTPSClient上传文件报 Remote host terminated the handshake

最近做了个项目,是要和哥伦比亚当地的软件商做接口传凭证。
那边给了接口方式,用sftp的方式给他们指定的服务器目录传txt格式的报文。然后他们再自己读取到自己系统里。
虽然不知道为什么要这么做,webservice它不香吗…还能实时返回调用结果…难道他们不会用?…那句俗话,乳香随俗吧。

现在自己电脑上搭了个ftp服务器,然后网上找了段轮子当demo,一测,还挺好使。就继续写业务逻辑了。

结果等他们给提供了ftp服务器地址之后,怎么连也连不上。然后给他们发了封邮件,过了两天回过来消息。说FTPClient不行,得用FTPSClient,然后丢过来一个stackoverflow网站的连接,一看就是谷歌刚查出来的那种。然后是他们用filezilla连接成功的截图。

我去下了个filezilla试了下,竟然能传。这整的就很尴尬了,刚开始我还心里想他们难道webservice都不会用,这会儿自己连个ftp都整不成功。没办法,自己研究吧。

用FTPSClient之后,确实登录不报错了,但是 ftpsClient.storeFile()就卡那儿了,网上查了下,要在前边加ftpsClient.enterLocalPassiveMode(); 设置成被动模式。

不报错了,程序直接就走完了。

真好。

去filezilla上刷新一看,毛都没有。

等了个寂寞。然后ftpsClient.storeFile()的值打印出来看。false。这就是没上传成功。难道是目录切错了?
listFiles一下,也是程序跑完,结果listFiles的数组里是空的。明明有文件。这接不到值。日狗。

遂去查 listFiles为空。刚好网上有人说两个服务器,什么跨国两台服务器语言不同,导致传日期格式的时候有问题。我一看,跨国,语言不同,为空,这不就是说的我嘛。

打起精神看人家怎么解决的:找到源码,继承某某类。看了半天把代码搬过来用也不行。

人家是跟源码解决的,那我这是不是跟下源码就能解决了。遂找地儿下来了commons源码。着重跟了半天博主说的语言格式的地儿。

一是工作原理没弄懂,二是md那么多源码绕来绕去的,咱这水平也跟不上。真较真就下班了。

中场休息。

下午回来一想,我是为了上传,应该去解决storeFile的问题啊。整半天listfile回头解决了也没啥用啊。

转头跟storeFile的源码逻辑。回主线。

发现FTPSClient是继承FTPClient的,然后在源码中socket是空的,所以下边判断返回了false
在这里插入图片描述
然后这里返回了521
在这里插入图片描述
这里的方法判断返回的值必须是在100-200,而此时sendcommmand返回的是521.

public static boolean isPositivePreliminary(int reply)
    {
        return (reply >= 100 && reply < 200);
    }

关键是sendcommand我也不知道是干啥的啊,再去网上查,然后查ftp返回521是什么意思,是我爱你的意思吗

写不下去了。。。太tm曲折了,也没时间再写了。

最后用谷到stackoverflow上有说
jre1.7和jre1.8对session的缓存处理有差异。

synchronized String getHost() {
    // Note that the host may be null or empty for localhost.
    if (host == null || host.length() == 0) {
        host = getInetAddress().getHostName();
    }
    return host;
}
synchronized String getHost() {
    // Note that the host may be null or empty for localhost.
    if (host == null || host.length() == 0) {
	if (!trustNameService) {
            // If the local name service is not trustworthy, reverse host
            // name resolution should not be performed for endpoint
            // identification.  Use the application original specified
            // hostname or IP address instead.
            host = getOriginalHostname(getInetAddress());
        } else {
            host = getInetAddress().getHostName();
        }
    }
    return host;
}

1.7.0中,返回的字符串是主机名称,类似"ec2-…compute.amazonaws.com"。但是,在 1.8.0 中,默认情况下会阻止反向主机名称,并返回主机ip,类似"123.45.67.89"。问题是,总是使用主机名称作为密钥的第一部分存储到缓存中.

大概就是错误的host导致要上传的时候算新会话,服务器不认。

自己定义一个类继承FTPSClient,重载_prepareDataSocket_(final Socket socket)方法

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Locale;

import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;

import org.apache.commons.net.ftp.FTPSClient;

public class SSLSessionReuseFTPSClient extends FTPSClient {

    // adapted from:
    // https://trac.cyberduck.io/browser/trunk/ftp/src/main/java/ch/cyberduck/core/ftp/FTPClient.java
    @Override
    protected void _prepareDataSocket_(final Socket socket) throws IOException {
        if (socket instanceof SSLSocket) {
            // Control socket is SSL
            final SSLSession session = ((SSLSocket) _socket_).getSession();
            if (session.isValid()) {
                final SSLSessionContext context = session.getSessionContext();
                try {
                    final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
                    sessionHostPortCache.setAccessible(true);
                    final Object cache = sessionHostPortCache.get(context);
                    final Method method = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
                    method.setAccessible(true);
                    method.invoke(cache, String
                            .format("%s:%s", socket.getInetAddress().getHostName(), String.valueOf(socket.getPort()))
                            .toLowerCase(Locale.ROOT), session);
                    method.invoke(cache, String
                            .format("%s:%s", socket.getInetAddress().getHostAddress(), String.valueOf(socket.getPort()))
                            .toLowerCase(Locale.ROOT), session);
                } catch (NoSuchFieldException e) {
                    throw new IOException(e);
                } catch (Exception e) {
                    throw new IOException(e);
                }
            } else {
                throw new IOException("Invalid SSL Session");
            }
        }
    }
}

不想写了,就这样的。
原问题链接:
https://stackoverflow.com/questions/32398754/how-to-connect-to-ftps-server-with-data-connection-using-same-tls-session

回头对计算机网络这块还是要认真学一下。

哦,还有两个事儿要交待
1.System.setProperty(“jdk.tls.useExtendedMasterSecret”, “false”);
这句话加代码里。
2.用eclipse跑demo自己加载个jdk,我用eclipse自带的跑不成.

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值