基于pyftpdlib搭建的FTP服务器无法外网访问求助
解决NAT环境下pyftpdlib FTP服务器公网访问问题
问题背景
我基于pyftpdlib官方文档写了FTP服务器代码,本地用私有IP或者localhost能正常访问,但用公网IP从外网设备连接就超时。目前是连的手机热点(处于NAT环境),已经给21端口加了防火墙允许规则,但没用,试了网上同类方案也没解决。想实现外网通过公网IP访问,同时搞清楚不同场景下的IP使用规则。
现有代码与运行日志
服务器代码
import os from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import FTPHandler from pyftpdlib.servers import FTPServer def main(): # Instantiate a dummy authorizer for managing 'virtual' users authorizer = DummyAuthorizer() # Define a new user having full r/w permissions and a read-only # anonymous user authorizer.add_user('user', '12345', '.', perm='elradfmwMT') authorizer.add_anonymous(os.getcwd()) # Instantiate FTP handler class handler = FTPHandler handler.authorizer = authorizer # Define a customized banner (string returned when client connects) handler.banner = "pyftpdlib based ftpd ready." # Specify a masquerade address and the range of ports to use for # passive connections. Decomment in case you're behind a NAT. handler.masquerade_address = 'My Public IP checked online' handler.passive_ports = range(20, 65535) # Instantiate FTP server class and listen on 0.0.0.0:2121 address = ('0.0.0.0', 21) server = FTPServer(address, handler) # set a limit for connections server.max_cons = 256 server.max_cons_per_ip = 5 # start ftp server server.serve_forever() if __name__ == '__main__': main()
运行日志
[I 2020-05-11 20:27:13] concurrency model: async [I 2020-05-11 20:27:13] masquerade (NAT) address: X.X.X.X [I 2020-05-11 20:27:13] passive ports: 20->65534 [I 2020-05-11 20:27:13] >>> starting FTP server on 0.0.0.0:21, pid=9072 <<< [I 2020-05-11 20:28:07] 127.0.0.1:5054-[] FTP session opened (connect) [I 2020-05-11 20:28:07] 127.0.0.1:5054-[anonymous] USER 'anonymous' logged in. [I 2020-05-11 20:28:07] 127.0.0.1:5054-[anonymous] CWD C:\Users\Dell\Desktop\ftp_server 250
问题分析
出现公网访问超时的核心原因主要有这几点:
- 手机热点的CGNAT限制:大部分手机运营商提供的是共享公网IP(Carrier-Grade NAT),你的设备并没有独立的公网IP,外网直接访问这个公网IP无法定位到你的设备。
- 运营商端口屏蔽:很多运营商会默认屏蔽21这类标准FTP端口,避免用户搭建公共服务。
- 被动模式端口未正确转发:FTP被动模式需要开放一段端口范围,你代码里设置了20到65535的大范围,但防火墙没开放这么多端口,而且手机热点一般不支持端口转发,导致外网无法连接被动端口。
- 防火墙/安全软件拦截:除了入站规则,出站规则或者第三方安全软件可能拦截了FTP流量。
分步解决方案
1. 更换非标准FTP端口
先避开运营商屏蔽的21端口,修改代码里的监听端口为非标准端口(比如2121):
address = ('0.0.0.0', 2121)
同时在系统防火墙里添加2121端口的入站允许规则。
2. 缩小被动端口范围
代码里的被动端口范围太大,既不安全也难以配置,改成一个小范围(比如50000-50100):
handler.passive_ports = range(50000, 50100)
然后在防火墙里添加这个端口范围的入站允许规则。
3. 解决CGNAT问题(核心)
因为手机热点是CGNAT环境,没有独立公网IP,需要用内网穿透工具来实现外网访问,比如frp、ngrok这类工具:
- 部署一个内网穿透客户端,将你的2121端口和50000-50100端口映射到公网服务器的对应端口。
- 外网设备使用穿透工具提供的公网IP+映射端口来连接FTP服务器。
如果能换成有独立公网IP的宽带网络,那可以在路由器上做端口转发:
- 把路由器的2121端口转发到你的设备私有IP的2121端口。
- 把路由器的50000-50100端口范围转发到你的设备私有IP的对应端口范围。
4. 检查防火墙与安全软件
- 确认系统防火墙的入站规则不仅开放了2121和被动端口范围,出站规则也没有限制FTP相关流量。
- 暂时关闭Windows Defender或者第三方杀毒软件,测试是否能连接,能的话把pyftpdlib程序加入信任列表。
不同场景IP使用说明
- 本机访问:用
localhost或者127.0.0.1,直接连接本地服务。 - 局域网内其他设备访问:用你的设备私有IP(比如
192.168.43.x,手机热点的局域网段一般是这个)。 - 外网访问(有独立公网IP):用你的公网IP,配合路由器端口转发和防火墙规则。
- 外网访问(CGNAT环境):用内网穿透工具提供的公网IP+映射端口,或者工具分配的域名。
内容的提问来源于stack exchange,提问作者Kush Choudhary




