发布时间2026-05-20 | 阅读时间约15分钟 | 标签FTP协议、文件传输、网络协议“在HTTP一统天下的今天FTP这个1971年出生的’老古董’依然活跃在服务器管理、文件同步、网站部署的第一线。它就像一位经验丰富的老邮差——虽然装备不够时髦但送文件这件事它真的专业。”一、FTP是什么——两个电话线的故事FTPFile Transfer Protocol文件传输协议诞生于1971年比TCP/IP协议族还要早。在那个网络还是奢侈品、HTTP还没出生的年代FTP就是互联网文件传输的标准答案。但FTP有一个让初学者抓狂的特性它用两条TCP连接来完成一次文件传输。这就像你要收快递结果快递员给你打了两个电话 核心比喻FTP就像两个人打电话商量事情——控制连接是打电话商量“我要发文件了”、“发完了”、“还有什么事”数据连接是实际送货真正的文件内容走这条线。1.1 双连接模型详解┌─────────────────────────────────────────────────────────────┐ │ FTP 双连接模型示意图 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 客户端 服务器 │ │ ┌─────────┐ ┌─────────┐ │ │ │ 控制连接 │◄──────────────►│ 端口21 │ ← 命令通道 │ │ │ (随机端口)│ TCP连接 │ │ 始终开启 │ │ └─────────┘ └─────────┘ │ │ │ │ │ │ │ 我要下载文件 │ │ │ │ 用户名密码是啥 │ │ │ │ 下载完成 │ │ │ ▼ ▼ │ │ ┌─────────┐ ┌─────────┐ │ │ │ 数据连接 │◄──────────────►│ 端口20或 │ ← 传输通道 │ │ │ (随机端口)│ TCP连接 │ 随机端口 │ 用完即关 │ │ └─────────┘ └─────────┘ │ │ ▲ ▲ │ │ │ 实际的文件内容 │ │ │ │ 010101010101... │ │ │ │ └─────────────────────────────────────────────────────────────┘控制连接Control Connection端口服务器端固定使用21端口客户端使用随机高位端口通常是1024-65535之间生命周期从登录到注销始终保持连接传输内容FTP命令和响应纯文本类似HTTP请求头数据连接Data Connection端口服务器端可能是20端口主动模式或随机端口被动模式客户端使用随机高位端口生命周期每次传输就建立传完就断开传输内容实际的文件数据、目录列表等 为什么用两条连接这是FTP的设计哲学把控制和数据分离。好处是控制命令不会被大数据流阻塞服务器可以随时中断传输、查询状态。坏处嘛…就是NAT和防火墙看到FTP都头疼。二、主动模式PORT——卖家送货上门主动模式是FTP的原始形态。想象一下网购你告诉卖家你的地址卖家主动把货送到你家门口。这就是PORT模式的工作逻辑。2.1 主动模式工作流程┌─────────────────────────────────────────────────────────────┐ │ FTP 主动模式 (PORT) 流程图 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Step 1: 建立控制连接 │ │ ─────────────────── │ │ 客户端:50001 ─────────────► 服务器:21 │ │ Hi我要连接 │ │ │ │ Step 2: 客户端告诉服务器自己的数据端口 │ │ ──────────────────────────────── │ │ 客户端 ───────────────────► 服务器:21 │ │ PORT 192,168,1,100,195,80 ← 50000 195*256 80 │ │ │ │ Step 3: 服务器主动连接客户端的数据端口 │ │ ──────────────────────────────── │ │ 服务器:20 ────────────────► 客户端:50000 ◄── 服务器主动 │ │ 我来送货了 │ │ │ │ Step 4: 传输数据 │ │ ─────────────────── │ │ 服务器:20 ◄══════════════► 客户端:50000 │ │ [文件内容传输中...] │ │ │ └─────────────────────────────────────────────────────────────┘2.2 PORT命令详解PORT命令的格式看起来有点奇怪# 客户端发送给服务器的PORT命令示例 PORT 192,168,1,100,195,80 # 含义解析 # 192,168,1,100 → IP地址192.168.1.100 # 195,80 → 端口计算195 * 256 80 50000 # 所以客户端说请连接 192.168.1.100:50000FTP使用这种逗号分隔的格式是因为协议设计得太早那时候还没JSON这种东西呢2.3 主动模式的优缺点优点缺点服务器端配置简单只需开放20和21端口客户端必须有公网IP否则服务器连不上服务器安全性可控连接由服务器发起客户端防火墙通常会阻止入站连接符合传统客户端-服务器模型NAT环境下几乎无法工作⚠️ 主动模式的致命伤在NAT环境下客户端告诉服务器的IP是内网IP如192.168.x.x服务器根本连不上。就算客户端有公网IP客户端的防火墙看到外部服务器主动连进来通常也会直接拒绝。三、被动模式PASV——买家去指定地点取货既然主动模式在NAT和防火墙面前处处碰壁PASVPassive Mode被动模式应运而生。这次换种思路你告诉卖家我到你店里取货然后你主动去找卖家。3.1 被动模式工作流程┌─────────────────────────────────────────────────────────────┐ │ FTP 被动模式 (PASV) 流程图 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Step 1: 建立控制连接 │ │ ─────────────────── │ │ 客户端:50001 ─────────────► 服务器:21 │ │ Hi我要连接用被动模式 │ │ │ │ Step 2: 服务器告诉客户端数据端口 │ │ ──────────────────────────────── │ │ 客户端:50001 ─────────────► 服务器:21 │ │ PASV │ │ │ │ 服务器 ◄──────────────────── 客户端:50001 │ │ 227 Entering Passive Mode (203,0,113,10,195,80) │ │ ← 请连接 203.0.113.10:50000 │ │ │ │ Step 3: 客户端主动连接服务器的数据端口 │ │ ──────────────────────────────── │ │ 客户端:50002 ─────────────► 服务器:50000 ◄── 客户端主动 │ │ 我来取货了 │ │ │ │ Step 4: 传输数据 │ │ ─────────────────── │ │ 客户端:50002 ◄════════════► 服务器:50000 │ │ [文件内容传输中...] │ │ │ └─────────────────────────────────────────────────────────────┘3.2 PASV命令与227响应# 客户端发送PASV命令 PASV # 服务器响应227状态码表示进入被动模式 227 Entering Passive Mode (203,0,113,10,195,80) # 含义解析 # 203,0,113,10 → 服务器IP203.0.113.10 # 195,80 → 端口计算195 * 256 80 50000 # 所以服务器说请连接 203.0.113.10:50000 来取数据3.3 被动模式的优缺点优点缺点完美支持NAT环境客户端在内网也能工作服务器端需要开放大量高位端口客户端防火墙友好出站连接通常被允许服务器防火墙配置更复杂现代FTP客户端的默认选择每个传输都要动态分配端口 为什么PASV能解决NAT问题因为数据连接是由客户端发起的出站方向而NAT设备对出站连接非常友好——它会自动记录映射关系让返回的数据包能找到内网的客户端。四、两种模式的防火墙配置对比配置FTP服务器的防火墙就像给两条高速公路设置收费站——你得知道车从哪来、到哪去。4.1 主动模式防火墙规则# 服务器端防火墙iptables示例 # 允许入站连接21端口控制连接 iptables -A INPUT -p tcp --dport 21 -j ACCEPT # 允许出站连接20端口数据连接源端口 iptables -A OUTPUT -p tcp --sport 20 -j ACCEPT # 允许已建立连接的数据传输 iptables -A INPUT -p tcp --sport 20 -m state --state ESTABLISHED -j ACCEPT # ⚠️ 注意服务器需要能主动连接客户端的任意高位端口 # 这在有严格出站规则的环境中是个大麻烦4.2 被动模式防火墙规则# 服务器端防火墙iptables示例 # 允许入站连接21端口控制连接 iptables -A INPUT -p tcp --dport 21 -j ACCEPT # 允许入站连接被动模式端口范围需要预先配置 # 假设FTP服务器配置被动端口范围为 50000-50100 iptables -A INPUT -p tcp --dport 50000:50100 -j ACCEPT # 允许已建立连接的响应 iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # vsftpd配置文件中设置被动端口范围 # /etc/vsftpd.conf pasv_min_port50000 pasv_max_port50100 pasv_address203.0.113.10 # 服务器的公网IP4.3 防火墙配置对比表配置项主动模式 (PORT)被动模式 (PASV)服务器入站端口2121 被动端口范围服务器出站限制需允许连任意高位端口标准出站规则即可客户端防火墙需允许入站数据连接无需特殊配置NAT兼容性差好云服务器适用性不推荐推荐五、NAT环境下的FTP穿透——一场猫鼠游戏NAT网络地址转换是FTP的天敌。当FTP遇到NAT就像两个说不同语言的人试图沟通——需要翻译。5.1 为什么NAT会让FTP崩溃问题在于FTP在控制连接中传输的是明文IP地址和端口。当NAT设备修改了IP包头部的地址信息时它并不知道要同时修改FTP协议数据里面的地址┌─────────────────────────────────────────────────────────────┐ │ NAT环境下FTP的问题示意 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 主动模式在NAT下的悲剧 │ │ │ │ 内网客户端(192.168.1.100) │ │ │ │ │ │ PORT 192,168,1,100,195,80 ← 告诉服务器连内网IP │ │ ▼ │ │ NAT路由器(203.0.113.5) ← 公网IP │ │ │ │ │ │ [NAT只改IP包头不改FTP数据里的地址] │ │ ▼ │ │ FTP服务器(203.0.113.10) │ │ │ │ │ │ 我要连 192.168.1.100:50000 │ │ ▼ │ │ ╳ 连接失败192.168.1.100是内网地址服务器连不上 │ │ │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 被动模式在NAT下的相对幸运 │ │ │ │ 内网客户端 ──► NAT ──► 服务器 │ │ │ │ │ │ │ PASV │ 227 (服务器公网IP,端口) │ │ │◄────────────────────┘ │ │ │ │ │ │ 客户端主动连接服务器公网IP ✓ │ │ ▼ │ │ 连接成功NAT自动处理出站连接 │ │ │ └─────────────────────────────────────────────────────────────┘5.2 FTP ALG——NAT的救星ALGApplication Layer Gateway应用层网关是一种特殊的NAT处理机制。它能读懂FTP协议自动修改FTP数据中的IP地址和端口信息。# 在Linux上启用FTP ALG通过nf_conntrack_ftp模块 modprobe nf_conntrack_ftp modprobe nf_nat_ftp # 查看是否加载成功 lsmod | grep ftp # 输出示例 # nf_nat_ftp 16384 0 # nf_conntrack_ftp 20480 1 nf_nat_ftp # nf_nat 45056 2 nf_nat_ftp,nf_nat_ipv45.3 企业级解决方案对于复杂的企业网络环境有几种更可靠的解决方案FTP代理服务器在DMZ区部署专门的FTP代理统一处理内外网的FTP连接SFTP/FTPS放弃传统FTP改用基于SSH的SFTP或基于SSL的FTPS它们只用单个连接VPN隧道让客户端通过VPN接入内网消除NAT问题 现代建议如果你的环境有NAT直接放弃主动模式使用被动模式ALG模块。如果还不行考虑迁移到SFTP——它只用22端口一个连接NAT友好得多。六、FTP传输类型——ASCII vs 二进制FTP不是简单的原样传输它提供了两种传输模式这源于那个Windows和Unix还在打架的年代。6.1 ASCII模式文本模式ASCII模式会执行换行符转换# 不同系统的换行符表示 Unix/Linux: LF (\n, 0x0A) Windows: CRLF (\r\n, 0x0D 0x0A) Mac (旧): CR (\r, 0x0D) # ASCII模式会自动转换 # 从Unix下载到WindowsLF → CRLF # 从Windows上传到UnixCRLF → LF适用场景纯文本文件.txt, .html, .css, .js, .py等⚠️ 千万不要用ASCII模式传二进制文件如果你用ASCII模式传输图片、ZIP压缩包、可执行文件FTP会好心地把文件中的0x0A字节转换成0x0D 0x0A结果文件就毁了。这是新手最常犯的FTP错误之一。6.2 二进制模式Image模式二进制模式是逐字节原样传输不做任何转换。适用场景所有非文本文件图片、视频、压缩包、可执行文件、PDF等6.3 如何切换传输模式# 在FTP命令行中切换模式 ftp ascii # 切换到ASCII模式 200 Type set to A ftp binary # 切换到二进制模式简写bin 200 Type set to I ftp type # 查看当前模式 Using binary mode to transfer files.6.4 现代FTP客户端的自动识别大多数现代FTP客户端如FileZilla、WinSCP会根据文件扩展名自动选择传输模式# FileZilla的默认ASCII文件扩展名列表 .txt, .htm, .html, .php, .css, .js, .json, .xml .ini, .conf, .sh, .bat, .cmd, .py, .pl, .cgi但自动识别不是100%可靠关键文件建议手动确认传输模式。七、FTP命令详解——和服务器对话的语法FTP协议定义了一套完整的命令集。即使你用图形化客户端了解这些命令也能帮你排查问题。7.1 连接与认证命令命令说明示例响应USER发送用户名331 Please specify the password.PASS发送密码230 Login successful.QUIT断开连接221 Goodbye.SYST查询服务器系统类型215 UNIX Type: L87.2 目录操作命令命令说明类比Linux命令PWD显示当前目录pwdCWD切换目录cdCDUP返回上级目录cd …MKD创建目录mkdirRMD删除目录rmdirLIST列出目录内容详细ls -lNLST列出文件名简略ls7.3 文件传输命令命令说明使用场景RETR下载文件Retrieve从服务器获取文件到本地STOR上传文件Store将本地文件发送到服务器DELE删除文件删除服务器上的文件RNFR重命名从Rename From指定原文件名RNTO重命名到Rename To指定新文件名SIZE获取文件大小断点续传前查询7.4 传输模式命令命令说明TYPE A设置为ASCII模式TYPE I设置为二进制模式ImagePORT主动模式告诉服务器连哪个IP:端口PASV被动模式请求服务器告知数据端口REST设置断点续传的起始位置7.5 实战完整的FTP会话示例# 使用telnet模拟FTP控制连接端口21 $ telnet ftp.example.com 21 Trying 203.0.113.10... Connected to ftp.example.com. 220 Welcome to Example FTP Server # 认证 USER alice 331 Please specify the password. PASS secret123 230 Login successful. # 查看当前目录 PWD 257 /home/alice is the current directory # 切换到被动模式并列出文件 PASV 227 Entering Passive Mode (203,0,113,10,195,80) # 计算端口195*2568050000现在连接203.0.113.10:50000 LIST 150 Here comes the directory listing. # [在另一个连接上接收目录数据] 226 Directory send OK. # 下载文件 TYPE I 200 Switching to Binary mode. PASV 227 Entering Passive Mode (203,0,113,10,195,81) RETR document.pdf 150 Opening BINARY mode data connection for document.pdf (1024000 bytes). # [在数据连接上接收文件内容] 226 Transfer complete. # 退出 QUIT 221 Goodbye.八、用Python实现一个简单的FTP客户端理论讲完了来点实战代码。Python的ftplib模块提供了完整的FTP客户端功能。from ftplib import FTP import os class SimpleFTPClient: def __init__(self, host, username, password, port21): self.ftp FTP() self.host host self.port port self.username username self.password password def connect(self): 建立连接并登录 print(fConnecting to {self.host}:{self.port}...) self.ftp.connect(self.host, self.port) self.ftp.login(self.username, self.password) print(fServer response: {self.ftp.getwelcome()}) # 设置为被动模式推荐 self.ftp.set_pasv(True) print(Passive mode enabled) def list_files(self, path.): 列出目录内容 print(f\nListing directory: {path}) print(- * 50) files [] self.ftp.dir(path, files.append) for f in files: print(f) return files def download(self, remote_file, local_fileNone): 下载文件二进制模式 if local_file is None: local_file os.path.basename(remote_file) print(f\nDownloading: {remote_file} - {local_file}) with open(local_file, wb) as f: self.ftp.retrbinary(fRETR {remote_file}, f.write) print(fDownload complete: {local_file}) def upload(self, local_file, remote_fileNone): 上传文件二进制模式 if remote_file is None: remote_file os.path.basename(local_file) print(f\nUploading: {local_file} - {remote_file}) with open(local_file, rb) as f: self.ftp.storbinary(fSTOR {remote_file}, f) print(fUpload complete: {remote_file}) def disconnect(self): 断开连接 self.ftp.quit() print(\nDisconnected from server) # 使用示例 if __name__ __main__: # 配置FTP服务器信息 FTP_HOST ftp.example.com FTP_USER your_username FTP_PASS your_password client SimpleFTPClient(FTP_HOST, FTP_USER, FTP_PASS) try: client.connect() client.list_files() # client.download(remote_file.txt) # client.upload(local_file.txt) except Exception as e: print(fError: {e}) finally: client.disconnect()8.1 断点续传实现def download_with_resume(self, remote_file, local_file): 支持断点续传的下载 # 获取远程文件大小 remote_size self.ftp.size(remote_file) # 检查本地文件已下载的大小 local_size 0 if os.path.exists(local_file): local_size os.path.getsize(local_file) if local_size remote_size: print(File already downloaded) return print(fResuming download from byte {local_size}/{remote_size}) # 设置续传起点 self.ftp.sendcmd(fREST {local_size}) # 以追加模式打开本地文件 with open(local_file, ab) as f: self.ftp.retrbinary(fRETR {remote_file}, f.write) print(Download complete)九、总结——FTP的现在与未来核心要点回顾FTP使用双连接控制连接端口21 数据连接主动模式服务器主动连客户端NAT环境下基本不可用被动模式客户端主动连服务器现代环境的默认选择传输模式ASCII用于文本会转换换行符二进制用于所有其他文件NAT环境下需要ALG模块或改用SFTP/FTPSFTP虽然古老但它并没有消失。在以下场景FTP依然是最佳选择共享主机/虚拟主机的文件管理cPanel等控制面板底层就是FTP批量文件同步lftp、ncftp等工具依然高效与老旧系统的集成很多工业设备只支持FTP匿名文件分发公共软件镜像站当然如果你从零开始设计系统优先考虑SFTPSSH File Transfer Protocol或基于HTTPS的传输——它们更安全对NAT更友好而且只需要一个端口。但了解FTP的工作原理依然有价值。当你遇到能连上但传不了文件、目录列表出不来这类诡异问题时理解PORT和PASV的区别可能就是解决问题的关键。 源码获取本文所有代码示例已整理到GitHub仓库包含完整Python FTP客户端实现支持断点续传、进度显示iptables防火墙配置脚本vsftpd服务器配置模板GitHub地址https://github.com/yourname/ftp-protocol-demos如果对你有帮助欢迎点个⭐Star支持一下 思考题为什么FTP的被动模式能解决NAT问题而主动模式不行试着画出数据包的流向图。如果你在企业内网部署FTP服务器外网用户需要访问你会选择哪种模式需要做哪些防火墙配置假设你用ASCII模式传输了一个PNG图片文件会发生什么变化为什么FTP的断点续传是如何实现的REST命令的作用是什么欢迎在评论区分享你的答案和见解 系列文章预告网络协议系列持续更新中下一篇预告《SSH协议深度解析——安全远程登录的瑞士军刀》——从握手到隧道彻底搞懂SSH的工作原理《DNS协议实战——从递归查询到DNSSEC》——为什么刷新DNS缓存能解决90%的网络问题《HTTP/3与QUIC——下一代Web传输协议》——基于UDP的HTTP到底解决了什么问题点击关注第一时间获取更新通知本文首发于CSDN转载请注明出处标签FTP协议文件传输网络协议防火墙配置服务器配置