使用Apache FTPClient连接Docker FTP服务器时遇连接关闭异常
Scala FTP客户端代码
import org.apache.commons.net.ftp.FTPClient import java.io.IOException object FtpClient { def main(args: Array[String]): Unit = { val ftpClient = new FTPClient() try { ftpClient.connect("localhost", 21) val success = ftpClient.login("one", "1234") if (success) { println("Success connect") } } catch { case e: IOException => throw new RuntimeException(e) } finally { // } } }
Docker启动FTP服务器命令
docker run -d -p 21:21 -p 21000-21010:21000-21010 -e USERS="one|1234" -e ADDRESS=ftp.site.domain delfer/alpine-ftp-server
客户端错误信息
Exception in thread "main" java.lang.RuntimeException: org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
at ru.spb.client.FtpClient$.main(FtpClient.scala:23)
at ru.spb.client.FtpClient.main(FtpClient.scala)
Caused by: org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
at org.apache.commons.net.ftp.FTP.getReply(FTP.java:568)
at org.apache.commons.net.ftp.FTP.getReply(FTP.java:556)
Docker日志信息
Changing password for one
New password:
Bad password: too short
Retype password:
adduser: /ftp/one: No such file or directory
passwd: password for one changed by root
seems like pidfd_open syscall does not work, falling back to
polling
failed to watch for direct child exit (pidfd_open error): Operation not permitted
提问:是否遗漏了某些重要的连接设置?
从Docker日志和客户端错误来看,核心问题出在FTP服务器配置和客户端连接设置两方面:
1. 修复FTP服务器配置
日志里的两个关键错误直接导致用户无法正常登录:
Bad password: too short:该镜像默认要求密码至少6位,你设置的1234不符合规则adduser: /ftp/one: No such file or directory:用户目录创建失败,服务器会直接断开连接
修改Docker启动命令,使用符合要求的密码并调整地址适配本地测试:
docker run -d \ -p 21:21 \ -p 21000-21010:21000-21010 \ -e USERS="one|123456" \ -e ADDRESS=localhost \ delfer/alpine-ftp-server
2. 完善客户端连接设置
原客户端代码缺少FTP连接的关键配置,补充以下内容:
- 开启被动模式(Docker环境下被动模式更易穿透端口映射)
- 检查服务器连接响应
- 添加连接关闭的收尾逻辑
修改后的代码:
import org.apache.commons.net.ftp.FTPClient import java.io.IOException object FtpClient { def main(args: Array[String]): Unit = { val ftpClient = new FTPClient() try { ftpClient.connect("localhost", 21) // 检查服务器连接响应 val replyCode = ftpClient.getReplyCode() if (!ftpClient.isPositiveCompletion(replyCode)) { throw new IOException("FTP server refused connection") } // 启用被动模式 ftpClient.enterLocalPassiveMode() val success = ftpClient.login("one", "123456") if (success) { println("Success connect") // 示例:列出服务器目录文件 val files = ftpClient.listFiles() files.foreach(f => println(s"File: ${f.getName}")) } else { println("Login failed") } } catch { case e: IOException => throw new RuntimeException(e) } finally { // 关闭连接的收尾操作 if (ftpClient.isConnected) { try { ftpClient.logout() ftpClient.disconnect() } catch { case e: IOException => e.printStackTrace() } } } } }
3. 验证修复效果
重新启动FTP服务器,运行修改后的客户端代码,即可正常连接并登录。
内容的提问来源于stack exchange,提问作者Jelly

