在 Android 模拟器中从本地 FTP 服务器下载时出现错误“227 进入被动模式”/"Connection refused"
Error "227 Entering Passive Mode"/"Connection refused" when downloading from local FTP server in Android emulator
我正在尝试在 Android 模拟器中使用 Java FTPSClient
运行 从本地 FileZilla 服务器下载文件。
我写了这个帮助代码来下载一个文件:
public boolean downloadSingleFile(FTPSClient ftpClient,
String remoteFilePath, File downloadFile) {
OutputStream outputStream;
Log.i("t1", remoteFilePath + " - " + downloadFile.getAbsolutePath());
try {
outputStream = new BufferedOutputStream(new FileOutputStream(
downloadFile));
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
boolean retval = ftpClient.retrieveFile(remoteFilePath, outputStream);
outputStream.flush();
return retval;
} catch (Exception e) {
Log.e("dwFile", e.toString());
Log.e("dwFile", ftpClient.getReplyString());
} return false;
}
我这样调用这个函数:
FTPSClient dwClient = new FTPSClient();
dwClient.addProtocolCommandListener(
new PrintCommandListener(
new PrintWriter(new OutputStreamWriter(System.out, "UTF-8")), true));
dwClient.setConnectTimeout(30 * 1000);
dwClient.connect(OmsSettingsFunctions.getFTPServer());
Log.i("dwDB", dwClient.getReplyString());
if (dwClient.login(FPTuser, FTPpass)) {
Log.i("dwDB", dwClient.getReplyString());
dwClient.enterLocalPassiveMode();
File dwFile = new File(externalPath + "/Android/data/com.myapp/files/Documents/db.temp");
if(!downloadSingleFile(dwClient, "/DBs/db.txt", dwFile)) {
Log.e("dwDB", "Download could not finish (DB)");
Log.e("dwDB", dwClient.getReplyString());
}...
但是我一直收到这个错误:
I/System.out: 220-FileZilla Server version 0.9.41 beta
I/System.out: 220-written by Tim Kosse (Tim.Kosse@gmx.de)
220 Please visit http://sourceforge.net/projects/filezilla/
I/System.out: AUTH TLS
D/EGL_emulation: eglMakeCurrent: 0xa209dd60: ver 3 0 (tinfo 0x9f652ff0)
D/EGL_emulation: eglMakeCurrent: 0xa209dd60: ver 3 0 (tinfo 0x9f652ff0)
I/System.out: 234 Using authentication type TLS
I/dwDB: 234 Using authentication type TLS
I/Permission: Readingpermission is granted
I/Permission: Writingpermission is granted
I/System.out: USER *******
I/System.out: 331 Password required for omstest
I/System.out: PASS *******
I/System.out: 230 Logged on
I/dwDB: 230 Logged on
I/t1: /DBs/db.txt - /storage/0FF0-280B/Android/data/com.myapp/files/Documents/db.temp
I/System.out: TYPE I
I/System.out: 200 Type set to I
I/System.out: PASV
I/System.out: 227 Entering Passive Mode (127,0,0,1,199,113)
E/dwFile: java.net.ConnectException: Connection refused
227 Entering Passive Mode (127,0,0,1,199,113)
E/dwDB: Download could not finish (DB)
227 Entering Passive Mode (127,0,0,1,199,113)
我已经尝试使用 enterLocalActivemode()
而不是 enterLocalPassivmode()
但它没有帮助。 FTP 服务器在我的本地计算机上执行 TLS 和 运行。我通过 10.0.2.2(Android 环回)连接到它。我该如何解决这个问题?
虽然我不熟悉 Android 模拟器,但我假设您需要连接到 10.0.2.2 才能连接到模拟器主机。
在FTP 被动模式下,服务器发回一个IP 地址,FTP 客户端需要连接到该IP 地址以传输文件(或目录列表)。当您的 FTP 服务器侦听 127.0.0.1 时,它会发回该 IP 地址。但是 127.0.0.1 在您的 Android 代码的上下文中指的是(模拟的)Android 主机。因此“连接被拒绝”。
这与连接到 NAT 后面的 FTP 服务器的常见问题非常相似。参见
因此解决方案是相同的:
- 在 FileZilla Server 界面中,转到编辑 > 设置 > 被动模式设置 > 特定 IPv4 > 被动模式传输的外部服务器 IP 地址。并输入 10.0.2.2.
- 也许您还需要取消选中 “不要将外部 IP 用于本地连接”。
显然,这反过来会使 FTP 服务器无法用于普通客户端。
正如您正确评论的那样,此问题仅在从 Android 模拟器连接到模拟器主机上的 FTP 服务器 运行 时出现。
另一个解决方案是使用 FTPClient.setPassiveNatWorkaroundStrategy
。它接受 HostnameResolver
接口的实现。如果您以将 127.0.0.1 转换为 10.0.2.2 的方式实施,它将允许您的 Java 代码连接,即使服务器上没有任何更改。
public static class ServerResolverImpl implements HostnameResolver {
private FTPClient client;
public ServerResolverImpl(FTPClient client) {
this.client = client;
}
@Override
public String resolve(String hostname) throws UnknownHostException {
// Ignore "hostname" returned by the server.
// Instead always use the primary address of the FTP server.
return this.client.getRemoteAddress().getHostAddress();
}
}
我正在尝试在 Android 模拟器中使用 Java FTPSClient
运行 从本地 FileZilla 服务器下载文件。
我写了这个帮助代码来下载一个文件:
public boolean downloadSingleFile(FTPSClient ftpClient,
String remoteFilePath, File downloadFile) {
OutputStream outputStream;
Log.i("t1", remoteFilePath + " - " + downloadFile.getAbsolutePath());
try {
outputStream = new BufferedOutputStream(new FileOutputStream(
downloadFile));
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
boolean retval = ftpClient.retrieveFile(remoteFilePath, outputStream);
outputStream.flush();
return retval;
} catch (Exception e) {
Log.e("dwFile", e.toString());
Log.e("dwFile", ftpClient.getReplyString());
} return false;
}
我这样调用这个函数:
FTPSClient dwClient = new FTPSClient();
dwClient.addProtocolCommandListener(
new PrintCommandListener(
new PrintWriter(new OutputStreamWriter(System.out, "UTF-8")), true));
dwClient.setConnectTimeout(30 * 1000);
dwClient.connect(OmsSettingsFunctions.getFTPServer());
Log.i("dwDB", dwClient.getReplyString());
if (dwClient.login(FPTuser, FTPpass)) {
Log.i("dwDB", dwClient.getReplyString());
dwClient.enterLocalPassiveMode();
File dwFile = new File(externalPath + "/Android/data/com.myapp/files/Documents/db.temp");
if(!downloadSingleFile(dwClient, "/DBs/db.txt", dwFile)) {
Log.e("dwDB", "Download could not finish (DB)");
Log.e("dwDB", dwClient.getReplyString());
}...
但是我一直收到这个错误:
I/System.out: 220-FileZilla Server version 0.9.41 beta
I/System.out: 220-written by Tim Kosse (Tim.Kosse@gmx.de)
220 Please visit http://sourceforge.net/projects/filezilla/
I/System.out: AUTH TLS
D/EGL_emulation: eglMakeCurrent: 0xa209dd60: ver 3 0 (tinfo 0x9f652ff0)
D/EGL_emulation: eglMakeCurrent: 0xa209dd60: ver 3 0 (tinfo 0x9f652ff0)
I/System.out: 234 Using authentication type TLS
I/dwDB: 234 Using authentication type TLS
I/Permission: Readingpermission is granted
I/Permission: Writingpermission is granted
I/System.out: USER *******
I/System.out: 331 Password required for omstest
I/System.out: PASS *******
I/System.out: 230 Logged on
I/dwDB: 230 Logged on
I/t1: /DBs/db.txt - /storage/0FF0-280B/Android/data/com.myapp/files/Documents/db.temp
I/System.out: TYPE I
I/System.out: 200 Type set to I
I/System.out: PASV
I/System.out: 227 Entering Passive Mode (127,0,0,1,199,113)
E/dwFile: java.net.ConnectException: Connection refused
227 Entering Passive Mode (127,0,0,1,199,113)
E/dwDB: Download could not finish (DB)
227 Entering Passive Mode (127,0,0,1,199,113)
我已经尝试使用 enterLocalActivemode()
而不是 enterLocalPassivmode()
但它没有帮助。 FTP 服务器在我的本地计算机上执行 TLS 和 运行。我通过 10.0.2.2(Android 环回)连接到它。我该如何解决这个问题?
虽然我不熟悉 Android 模拟器,但我假设您需要连接到 10.0.2.2 才能连接到模拟器主机。
在FTP 被动模式下,服务器发回一个IP 地址,FTP 客户端需要连接到该IP 地址以传输文件(或目录列表)。当您的 FTP 服务器侦听 127.0.0.1 时,它会发回该 IP 地址。但是 127.0.0.1 在您的 Android 代码的上下文中指的是(模拟的)Android 主机。因此“连接被拒绝”。
这与连接到 NAT 后面的 FTP 服务器的常见问题非常相似。参见
因此解决方案是相同的:
- 在 FileZilla Server 界面中,转到编辑 > 设置 > 被动模式设置 > 特定 IPv4 > 被动模式传输的外部服务器 IP 地址。并输入 10.0.2.2.
- 也许您还需要取消选中 “不要将外部 IP 用于本地连接”。
显然,这反过来会使 FTP 服务器无法用于普通客户端。
正如您正确评论的那样,此问题仅在从 Android 模拟器连接到模拟器主机上的 FTP 服务器 运行 时出现。
另一个解决方案是使用 FTPClient.setPassiveNatWorkaroundStrategy
。它接受 HostnameResolver
接口的实现。如果您以将 127.0.0.1 转换为 10.0.2.2 的方式实施,它将允许您的 Java 代码连接,即使服务器上没有任何更改。
public static class ServerResolverImpl implements HostnameResolver {
private FTPClient client;
public ServerResolverImpl(FTPClient client) {
this.client = client;
}
@Override
public String resolve(String hostname) throws UnknownHostException {
// Ignore "hostname" returned by the server.
// Instead always use the primary address of the FTP server.
return this.client.getRemoteAddress().getHostAddress();
}
}