Java 使用FTP工具 425 Cannot prepare for data connection.

文章讲述了在使用Java的FTPClient连接FileZilla搭建的FTP服务时遇到的连接超时问题,原因是FTPClient的passiveHost属性解析错误。提供了修改FileZilla设置、自定义HostnameResolver以及使用ApacheCommonsNet库的方法来解决这个问题。

Java 使用FTP工具 425 Cannot prepare for data connection.

背景

最近搞了一台新的服务器,服务器存在两个IP,一个公网Ip,一个内网Ip,采用 FileZilla 搭建FTP服务,服务搭建好后,经测试,防火墙,服务模式均没问题,可以连接至FTP服务,但获取目录下文件列表时报错连接超时。

定位异常

public static FTPFile[] getFTPDirectoryFiles(FTPClient ftpClient, String path) {
      FTPFile[] files = null;
      try {
          //让客户端告诉服务端开通一个端口用来数据传输(必须要 不然会一直卡死)
          ftpClient.enterLocalPassiveMode();

          ftpClient.changeWorkingDirectory(path);
          files = ftpClient.listFiles();
      } catch (Exception e) {
          log.error(ExceptionUtils.getStackTrace(e));
      }
      return files;
  }

首先确认了FTP所需端口全部放行,并服务器已设置被动模式,再次尝试问题任然存在。
继续调试发现,在执行完 listFiles() 方法后 FTPClient 类中有一个内部属性 passiveHost会被赋值,这个属性存储的是被动模式(PASV)连接时的服务器主机地址,可以看到连接 FTP 时,指定的 Ip 地址是 19,而 passiveHost 中存储的地址是 64,而在执行 listFiles() 时使用的地址是passiveHost,所以会出现连接超时的异常。
Host
在这里插入图片描述
passiveHost
在这里插入图片描述

进一步查看代码发现 passivePort 属性基本都是使用当前 Socket 连接的远程主机的Ip地址,也就是说 Socket 解析到了错误的Ip。

this.getRemoteAddress().getHostAddress();

解决方法

解决方法可以通过 FileZilla 修改设置或使用工具类解决。

1. 修改设置

高版本 FileZilla,在设置 External Server IP Address for passive 时,不要使用 Default 模式,使用 Use the following IpRetrieve external Ip address from
没有新版 FileZilla  截图,从别的地方截的图,清泉山
注:没有新版的 FileZilla 从别的老师的博客上截的图,清泉山。

低版本 FileZilla,在设置中找到 Protocols settings --> FTP and FTP over TLS (FTPS) --> Passive mode,
修改 **Use the following host (leave empty to keep the default one)😗*为指定的Ip
取消勾选 Use the default host for local connections 选项在这里插入图片描述

2. 使用配置类

在 Apache Commons Net 的 FTPClient 类中,HostnameResolver 是一个接口,它用于解析服务器在被动模式下提供的 IP 地址。通常,当使用 FTP 的被动模式时,服务器会响应一个包含 IP 地址和端口号的 PASV 命令。这个 IP 地址可能是服务器的内部 IP 地址,对于客户端来说可能无法直接访问。因此,HostnameResolver 接口允许客户端自定义如何解析这个 IP 地址。

HostnameResolver 接口定义了一个方法 resolveHostname(String hostname),该方法接受一个字符串参数(即服务器提供的 IP 地址或主机名),并返回一个 InetAddress 对象,该对象表示客户端应该连接到的实际地址。

FTPClient 类在内部使用这个接口来解析服务器提供的地址。可以通过调用 FTPClient 的 setHostnameResolver 方法来提供自定义的 HostnameResolver 实现。

package i.kun;

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

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @describe: 自定义 ftp 主机名称解析
 * @author: xiaowuler
 * @createTime: 2024-03-22 10:49
 */
public class CustomHostnameResolver implements FTPClient.HostnameResolver {
    private final FTPClient client;
    private String ip;

    public CustomHostnameResolver(final FTPClient client, String ip) {
        this.client = client;
        this.ip = ip;
    }

    @Override
    public String resolve(final String hostname) {
        return ip;
    }
}

3.使用方法

public static FTPClient connection(String hostname, int port, String username, String password) {
    FTPClient ftp = new FTPClient();
    CustomHostnameResolver customHostnameResolver = new CustomHostnameResolver(ftp, hostname);
    ftp.setPassiveNatWorkaroundStrategy(customHostnameResolver);

    try {
      //连接FTP服务器
      ftp.connect(hostname, port);
      //下面三行代码必须要,而且不能改变编码格式,否则不能正确下载中文文件
      ftp.setControlEncoding("GBK");
      FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
      conf.setServerLanguageCode("zh");
      //登录ftp
      ftp.login(username, password);
      if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
        ftp.disconnect();
      }
    } catch (Exception e) {
      log.error(ExceptionUtils.getStackTrace(e));
    }
    return ftp;
  }

END…

/* * fn static void ftp_multi_up(LOAD_MULTI_DATA *pData) * brief ftp upload * details * * param[in] * param[out] * * * return * retval T/F * * note */ static void *ftp_multi_up(LOAD_MULTI_DATA *pData) { int up_fd = -1; int up_fdd = -1; int len =0; int index =0; UINT32 test_byte_should_up = 0; char filename[BUFLEN_512] = {0}; struct sockaddr_in addr; #ifdef INCLUDE_DIAG_TR143_SPEEDTEST_IPV6 struct sockaddr_in6 addrV6; #endif /* INCLUDE_DIAG_TR143_SPEEDTEST_IPV6 */ LOAD_CONN_PARAM load_conn; LOAD_MULTI_CFG *pCfg = NULL; LOAD_MULTI_RESULT *pRes = NULL; if (NULL == pData) { TR143_ERROR("pData(%p)", pData); return (void *)0; } pCfg = pData->pCfg; pRes = pData->pRes; if (NULL == pCfg || NULL == pRes) { TR143_ERROR("pCfg(%p) pRes(%p)", pCfg, pRes); return (void *)0; } TR143_DEBUG("start ftp multi upload!"); memset(&load_conn, 0, sizeof(LOAD_CONN_PARAM)); TR143_SET_STATE(&load_conn, TR143_EREQUEST); #ifdef INCLUDE_TR143_TX_ZERO_COPY char buf[BUFLEN_256] = {0}; int sndfd = 0; #else /* INCLUDE_TR143_TX_ZERO_COPY */ char *buf = NULL; buf = malloc(TR143_TRANS_LEN); if (NULL == buf) { TR143_ERROR("malloc buf fail\n"); TR143_SET_STATE(&load_conn, TR143_EINTERNAL); goto SEND_MSG; } #endif /* INCLUDE_TR143_TX_ZERO_COPY */ cidLock(); if(0 == pRes->thread_num) { CUTIL_STR_STRNCPY(filename, pCfg->pFileName, sizeof(filename)); /* http://ispproject.rd.tp-link.net/redmine/issues/15110 */ if (pCfg->num_of_conn) { /* Fix Bug 679867 Supplement the remainder that cannot be divided completely */ test_byte_should_up = pCfg->testFileLength / pCfg->num_of_conn + pCfg->testFileLength % pCfg->num_of_conn; } else { test_byte_should_up = pCfg->testFileLength; } } else { snprintf(filename, sizeof(filename), "%s.%d", pCfg->pFileName, pRes->thread_num); if (pCfg->num_of_conn) { test_byte_should_up = pCfg->testFileLength / pCfg->num_of_conn; } else { test_byte_should_up = pCfg->testFileLength; } } pRes->thread_num++; cidUnlock(); tr143_adjust_ethOffload_or_cpuAffinity(pCfg->ifName, DIR_UP, ENABLE); /* 1. init control socket */ if (TRUE != tr143_loadSocketInit(&up_fd, pCfg, FALSE, TRUE)) { TR143_ERROR("init control socket failed"); TR143_SET_STATE(&load_conn, TR143_ECONFAIL); goto SEND_MSG; } /* 2. connect to load server */ #ifdef INCLUDE_DIAG_TR143_SPEEDTEST_IPV6 if (2 == pCfg->protoVerFlag) { /* 2. connect to load server */ if (TRUE != connect_with_check(up_fd, (struct sockaddr *)&(pCfg->dstIpv6Addr), sizeof(pCfg->dstIpv6Addr))) { TR143_ERROR("socket connect server failed."); TR143_SET_STATE(&load_conn, TR143_ECONFAIL); goto SEND_MSG; } /* 3. FTP get IP and Port */ if (TRUE != tr143_ftpGetIpAndPortV6(up_fd, pCfg, &addrV6, buf)) { TR143_ERROR("FTP get IP and Port failed"); TR143_SET_STATE(&load_conn, TR143_ENOPASV); goto SEND_MSG; } } else if (1 == pCfg->protoVerFlag) #endif /* INCLUDE_DIAG_TR143_SPEEDTEST_IPV6 */ { if (TRUE != connect_with_check(up_fd, (struct sockaddr *)&(pCfg->dstAddr), sizeof(pCfg->dstAddr))) { TR143_ERROR("socket connect server failed."); TR143_SET_STATE(&load_conn, TR143_ECONFAIL); goto SEND_MSG; } /* 3. FTP get IP and Port */ if (TRUE != tr143_ftpGetIpAndPort(up_fd, pCfg, &addr, buf)) { TR143_ERROR("FTP get IP and Port failed"); TR143_SET_STATE(&load_conn, TR143_ENOPASV); goto SEND_MSG; } } /* 4. init data socket */ if (TRUE != tr143_loadSocketInit(&up_fdd, pCfg, TRUE, TRUE)) { TR143_ERROR("init data socket failed"); TR143_SET_STATE(&load_conn, TR143_ECONFAIL); goto SEND_MSG; } getDateTime(load_conn.tcpOpenreqTime); /* 5.data socket connect to load server */ #ifdef INCLUDE_DIAG_TR143_SPEEDTEST_IPV6 if (2 == pCfg->protoVerFlag) { if (TRUE != connect_with_check(up_fdd, (struct sockaddr *)&addrV6, sizeof(addrV6))) { TR143_ERROR("socket connect server failed."); TR143_SET_STATE(&load_conn, TR143_ECONFAIL); goto SEND_MSG; } } else if (1 == pCfg->protoVerFlag) #endif /* INCLUDE_DIAG_TR143_SPEEDTEST_IPV6 */ { if(TRUE != connect_with_check(up_fdd, (struct sockaddr *)&addr, sizeof(addr))) { TR143_ERROR("data socket connect server failed."); TR143_SET_STATE(&load_conn, TR143_ECONFAIL); goto SEND_MSG; } } getDateTime(load_conn.tcpOpenresTime); /* 6. save load start time and TX/RX bytes */ for (index = 0; index < 2; index++) { #ifdef INCLUDE_TR143_TX_ZERO_COPY len = snprintf(buf, BUFLEN_256, "%s %s\r\n", FSEQ_UL[index].pCmd, (index > 0) ? filename: pCfg->pPathName); #else /* INCLUDE_TR143_TX_ZERO_COPY */ len = snprintf(buf, TR143_TRANS_LEN, "%s %s\r\n", FSEQ_UL[index].pCmd, (index > 0) ? filename: pCfg->pPathName); #endif /* INCLUDE_TR143_TX_ZERO_COPY */ if (len == tr143_read_write_with_retry(up_fd, buf, len, TR143_WRITE_TYPE)) { if (1 == index) { getDateTime(load_conn.ROMTIME); } #ifdef INCLUDE_TR143_TX_ZERO_COPY if (6 < tr143_read_write_with_retry(up_fd, buf, BUFLEN_256, TR143_READ_TYPE)) #else /* INCLUDE_TR143_TX_ZERO_COPY */ if (6 < tr143_read_write_with_retry(up_fd, buf, TR143_TRANS_LEN, TR143_READ_TYPE)) #endif /* INCLUDE_TR143_TX_ZERO_COPY */ { /* MAY reply "125 Data connection already open..." on Windows IIS. Just check 1yz. */ if (CUTIL_SAME_NSTR(buf, FSEQ_UL[index].pReplyCode, 1)) { continue; } } } } #ifdef INCLUDE_TR143_TX_ZERO_COPY if (0 >= (sndfd = prepare_sendfile(filename))) { TR143_ERROR("Prepare for send file failed(%s)", strerror(errno)); goto SEND_MSG; } #endif /* INCLUDE_TR143_TX_ZERO_COPY */ tr143_saveStartTimeAndBytes(&load_conn, pCfg, pRes); /* 7. upload test */ if(0 == pCfg->time_base_dura) { #ifdef INCLUDE_TR143_TX_ZERO_COPY if (TRUE != tr143_uploadByBytes(&up_fdd, &load_conn, pCfg, pRes, test_byte_should_up, buf, sndfd)) #else /* INCLUDE_TR143_TX_ZERO_COPY */ if (TRUE != tr143_uploadByBytes(&up_fdd, &load_conn, pCfg, pRes, test_byte_should_up, buf)) #endif /* INCLUDE_TR143_TX_ZERO_COPY */ { #ifdef INCLUDE_LOAD_DIAG_INTERRUPT if (TRUE == g_interruptFlag) { g_interruptFlag = FALSE; TR143_ERROR("tfp upload by bytes is interrupted"); TR143_SET_STATE(&load_conn, TR143_EOTHER); goto SEND_MSG; } #endif /*INCLUDE_LOAD_DIAG_INTERRUPT*/ TR143_ERROR("ftp upload by bytes failed"); TR143_SET_STATE(&load_conn, TR143_ETRANSFAIL); goto SEND_MSG; } } else { #ifdef INCLUDE_TR143_TX_ZERO_COPY if (TRUE != tr143_uploadByTime(&up_fdd, &load_conn, pCfg, pRes, buf, sndfd)) #else /* INCLUDE_TR143_TX_ZERO_COPY */ if (TRUE != tr143_uploadByTime(&up_fdd, &load_conn, pCfg, pRes, buf)) #endif /* INCLUDE_TR143_TX_ZERO_COPY */ { #ifdef INCLUDE_LOAD_DIAG_INTERRUPT if (TRUE == g_interruptFlag) { g_interruptFlag = FALSE; TR143_ERROR("tfp upload by time is interrupted"); TR143_SET_STATE(&load_conn, TR143_EOTHER); goto SEND_MSG; } #endif /*INCLUDE_LOAD_DIAG_INTERRUPT*/ TR143_ERROR("ftp upload by time failed"); TR143_SET_STATE(&load_conn, TR143_ETRANSFAIL); goto SEND_MSG; } } /* 8. save load end time and TX/RX bytes */ tr143_saveEndTimeAndBytes(&load_conn, pCfg, pRes); /* 9. update upload result */ if(TRUE != tr143_set_upconn_result(load_conn, pCfg, pRes)) { TR143_ERROR("set obj failed"); TR143_SET_STATE(&load_conn, TR143_EINTERNAL); goto SEND_MSG; } TR143_SET_STATE(&load_conn, TR143_ECOMPELETED); TR143_DEBUG("set obj finish"); SEND_MSG: #ifdef INCLUDE_TR143_TX_ZERO_COPY release_sendfile(sndfd, filename); #else /* INCLUDE_TR143_TX_ZERO_COPY */ TR143_FREE(buf); #endif /* INCLUDE_TR143_TX_ZERO_COPY */ TR143_CLOSESOCKET(up_fd); TR143_CLOSESOCKET(up_fdd); tr143_updateAndSendLoadStatus(pData, load_conn.diagnosticsState); return (void *)0; } 在这个过程中 这个宏起到了什么作用,这个宏是我在测速过程中的性能优化与加速,告诉我这个在上传过程中起到了什么作用然后我要做一页PPT简单展示这个宏的作用 给我构思一个展示优化思路的内容
最新发布
11-09
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值