记录一次FTP遇到的坑-主动模式与被动模式

一、背景

公司要接入一个三方的应用,需要使用FTP下载对方的数据文件,因为对方测试环境没有提供FTP的下载渠道,所有开发过程为了模拟FTP下载,我在自己的阿里云linux上搭建了一个FTP服务器(采用vsftpd),起初在本机windows上测试一切正常,发布到公司测试环境后,FTPClient.listFiles一直是空,并且ftpClient.retrieveFile下载不到文件

二、最初的FTP代码

		FTPClient ftpClient = new FTPClient();
		ftpClient.connect(ftp.getUrl(), ftp.getPort());
		ftpClient.setControlEncoding(ftp.getCharset());
		ftpClient.login(ftp.getUser(), ftp.getPassword());
		ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
		FTPFile[] ftpFiles = ftpClient.listFiles("/");
		log.info("文件个数:{}", ftpFiles.length);
		
		for (FTPFile ftpFile : ftpFiles) {
			log.info("FTP文件名:{},文件大小:{}", ftpFile.getName(), ftpFile.getSize());
		}
		
		File file = new File(ftp.getLocalFileName().substring(0, ftp.getLocalFileName().lastIndexOf(File.separator)));
		if (!file.exists()) {
			file.mkdirs();
		}
		
		log.info("FTP下载开始");
		FileOutputStream outputStream = new FileOutputStream(new File(ftp.getLocalFileName()));
		ftpClient.retrieveFile(ftp.getTargetFileName(), outputStream);
		log.info("FTP下载结束");

这段代码在我本机windows运行一切正常,但发布到公司的测试环境后,读不到FTP服务器的文件,公司的测试环境是linux,使用docker部署

二、主动模式与被动模式

上网搜了一下,有些文章讲述了类似经历,解决方案是调整FTP为被动模式,即在进行FTP操作前,添加下面一行代码:

		ftpClient.enterLocalPassiveMode();

调整后,本地测了几下,发现程序会随机地卡在这行代码,既不报错也不继续执行,而通过这行代码后,下载到的文件也有可能是0KB,小概率下会正常下载完毕,于是找文章仔细看了下FTP主动模式与被动模式的区别,大致如下:

FTP连接会建立两条TCP连接,一条用来发布命令,一条用来传输数据,对于FTP服务端而言,命令通道默认是21端口,数据通道默认是20端口,具体分为主动模式和被动模式:

主动模式

客户端通过端口N连接到服务器的21端口,并告诉服务端,我开启了一个N+1端口(实际可能不是N+1,但会和N比较接近)用来传输数据,然后服务端通过20端口,主动连接客户端的N+1端口,进行数据传输
该模式为FTP的默认模式

被动模式

客户端通过端口N连接到服务器的21端口,并告诉服务端,我采用的是被动模式,请提供一个端口给我用来传输数据,然后服务器告诉客户端:“OK,我给你分配的端口是M,可以开始传输数据”。然后客户端通过N+1(同上,可能不是但比较接近)端口主动连接服务器的M端口,进行数据传输

问题排查

通过比较,可以发现两种方式的区别,主要在于传输数据的连接由谁来建立,如果由服务器端来建立(主动模式),那么对于客户端来说,需要N+1端口对外开放,由于这个端口是随机的,所以客户端需要开放一定范围的端口,这在网络限制比较严格的生产环境比较困难,也有风险。当然这种模式对于服务器端比较方便,因为服务器端只需要开通21端口的入方向和20端口的出方向;如果由客户端来建立(被动模式),那么客户端通过N+1端口访问服务端,一般不会有网络问题,因为正常情况下不会限制出方向的访问,而服务器端需要开放更多端口,这个可以通过FTP服务器配置,指定端口的范围

由于调整为被动模式后,本地连接FTP成功与否具有一定随机性,所以猜测会不会是服务器端提供的端口M不通,导致客户端建立数据通道的时候连接失败,先到阿里云上看下安全策略:
在这里插入图片描述
之前为了测试方便,把入方向的端口范围调整为15-30000,正常来讲这个范围的端口已经满足各种组件的应用了,但是FTP的端口是随机的,有可能超出了30000,导致客户端无法正常连接,这里的解决方案是设定FTP服务端被动模式的端口范围
打开vsftp的配置文件vsftpd.conf,添加以下配置

pasv_enable=YES
pasv_min_port=3000
pasv_max_port=5000

开启被动模式,配置被动模式的端口范围为3000到5000,即当客户端以被动模式连接服务端时,服务端会在3000-5000的范围内随机分配一个端口,供客户端连接并进行数据传输
重启FTP服务器:

systemctl restart vsftpd.service

再次调用下载接口,程序可以稳定地完成下载

你可能感兴趣的:(java,ftp)