
简介PCMans FTP Server 是一款面向初学者的开源 FTP 服务端软件定位为轻量、易上手的入门级工具帮助不熟悉网络配置的用户快速搭建可用的 FTP 服务。它不追求复杂功能与高强度安全策略而是把「简单」放在首位适合个人学习、局域网文件共享或教学演示等场景。资源包共 143 个文件以 34 个 cpp 源文件、39 个 h 头文件为核心配合 43 个 ini 配置、4 个 dsp 工程文件、4 个 rc 资源脚本及 ico、bmp 等界面素材构成一套完整的 Windows 工程源码压缩包约 154KB体积小巧便于携带与二次编译。目前已有 202 人学习下载。通过阅读源码读者可理解 FTP 命令解析、用户管理、主框架与对话框交互等模块的实现思路并借助工程文件直接编译运行适合作为网络编程入门与 MFC 项目练手的参考素材。1. 老 FTP 服务端跑不动了聊聊 PCMans FTP Server 这个开源轻量方案手里有台低配 Windows 机器想临时搭个文件交换点装 FileZilla Server 嫌重、IIS 的 FTP 又懒得配这种场景我遇到过不止一次。PCMans FTP Server 就是冲着这个空档来的——一个体积只有几百 KB 的开源 FTP 服务端绿色免安装双击就能起服务支持多用户、权限目录、上传下载限速这些基础但够用的功能。它由 PCMan就是写出 PCMan File Manager 的那位作者开发代码开源用 Delphi 写的原生跑在 Windows 上。适合谁做嵌入式开发要给板子传固件、做运维临时开个内网共享、教学演示 FTP 协议流程或者你只是想要一个不占内存、不写注册表、删掉就干净的小工具。它不适合高并发生产环境也不适合需要 FTPS/SFTP 加密的合规场景这一点先摆清楚后面选型章节会展开。2. 拆开看PCMans FTP Server 的架构与选型逻辑2.1 为什么是 Delphi 写的单文件服务端很多人第一次看到 PCMans FTP Server 会愣一下——这年头还有用 Delphi 写的开源项目但恰恰是这个技术选型决定了它的核心优势。Delphi 编译出来的是原生 Windows 可执行文件不依赖 .NET Runtime、不依赖 Java 虚拟机、不依赖 Python 解释器一个 exe 丢过去就能跑。对比一下FileZilla Server 需要安装服务、写注册表、带一堆依赖库Python 的 pyftpdlib 得先装 Python 环境Node 的 ftp-srv 要 npm install 一堆包。PCMans FTP Server 把这些全省了。从源码结构看项目主体就是几个 Pascal 单元文件核心是 FTP 协议命令的解析与响应循环。它实现了 RFC 959 里最常用的那批命令USER、PASS、PWD、CWD、LIST、RETR、STOR、DELE、MKD、RMD、PORT、PASV、TYPE、SIZE、QUIT 等。没有实现的是那些偏门或已废弃的命令比如 ACCT、SMNT、REIN这些在实际使用中几乎遇不到。提示Delphi 项目编译需要 RAD Studio 或较老版本的 Delphi 环境如果你只是想用而不想改直接拿编译好的 exe 就行不必折腾编译链。2.2 用户模型与权限控制的实现方式PCMans FTP Server 的用户管理走的是「配置文件 内存加载」的路子。它不像 FileZilla Server 那样把用户存进 XML 或数据库而是用一个简单的文本配置来定义用户、密码、根目录和权限位。启动时读入内存运行中不热加载——改了配置得重启服务。这个设计在临时场景下完全够用但如果你需要动态增删用户就得自己改代码或者接受重启。权限控制粒度是「目录级」的每个用户可以指定一个根目录Home Directory然后通过权限标志控制是否允许上传、下载、删除、重命名、建目录。没有更细的文件级 ACL也没有虚拟目录映射。对于「给同事开个只读下载账号」或者「给外包开个只能上传不能看的账号」这类需求够用。我一般会这样规划用户一个 admin 账号指向完整共享目录权限全开一个 guest 账号指向同一个目录但只给读权限如果是对外临时收集文件再开一个 upload-only 账号根目录设成单独的 incoming 文件夹只给写权限不给列目录权限。这样即使账号泄露损失也可控。2.3 主动模式与被动模式的配置差异FTP 最让人头疼的就是数据连接的双通道设计。PCMans FTP Server 同时支持 PORT主动和 PASV被动两种模式但默认配置下被动模式需要你手动指定端口范围否则客户端可能连不上数据通道。主动模式下服务端用 20 端口主动连客户端指定的端口这在客户端有防火墙时经常翻车。被动模式下服务端开一个随机端口等客户端来连问题转移到服务端防火墙。我的经验是内网环境用主动模式省事跨网段或客户端在 NAT 后面就用被动模式并且把被动端口范围固定下来方便在防火墙上开规则。配置被动模式时你需要在设置里指定一个端口区间比如 50000-50100然后在 Windows 防火墙里对这几个端口放行。同时如果服务端本身在 NAT 后面还需要在设置里填上外部 IP 地址否则服务端在 PASV 响应里返回的是内网 IP客户端拿到后连不上。2.4 从源码到可运行服务的完整步骤假设你拿到的是源码包想自己编译一份或者改点东西流程大致如下。如果你只用现成 exe可以跳过编译部分直接看配置。# 第一步确认目录结构典型源码包长这样 # pcman-ftp-server/ # ├── src/ # Pascal 源码 # │ ├── Main.pas # 主窗体与事件循环 # │ ├── FTPServer.pas # FTP 协议核心逻辑 # │ └── UserMgr.pas # 用户与权限管理 # ├── bin/ # 编译输出目录 # └── README.md ls -la pcman-ftp-server/src/// 第二步如果你要改默认端口在源码里找到监听端口常量 // 常见做法是在 Main.pas 或 FTPServer.pas 的初始化段 const DEFAULT_FTP_PORT 21; // 控制连接端口 DEFAULT_PASV_START 50000; // 被动模式起始端口 DEFAULT_PASV_END 50100; // 被动模式结束端口 // 改完重新编译即可注意 21 端口在 Windows 上可能需要管理员权限# 第三步编译需要 Delphi/RAD Studio 命令行 # 如果装了 RAD Studio用 dcc32 编译 dcc32 -B src/Main.pas -Ebin/ # 编译成功后 bin/ 下会生成可执行文件; 第四步配置文件示例具体文件名以项目实际为准 ; 用户定义段 [User:admin] Passwordyour_strong_password HomeDirD:\ftp_share PermissionsREAD|WRITE|DELETE|MKDIR|RENAME [User:guest] Passwordguest_readonly HomeDirD:\ftp_share\public PermissionsREAD [User:uploader] Passwordupload_only_2024 HomeDirD:\ftp_share\incoming PermissionsWRITE|MKDIR上面四段分别对应目录确认、端口参数修改、编译命令、用户配置。参数上重点说三个——HomeDir必须是服务端本地存在的绝对路径权限标志用竖线分隔WRITE只给上传权限但不给列目录的话客户端上传时可能因为无法 LIST 而报错所以 upload-only 场景建议至少给WRITE|MKDIR有些客户端还需要LIST才能正常上传这个坑后面会细说。2.5 启动服务与客户端连接验证配置写好后启动 exe界面上一般有个「Start」按钮或者自动监听。验证分两步先在本机用命令行 ftp 客户端连一下确认控制通道通再用 FileZilla 这类图形客户端测数据通道。# 本机验证控制连接 ftp 127.0.0.1 21 # 输入用户名密码后执行 ftp pwd ftp ls ftp passive # 切换到被动模式再试一次 ls ftp quit# 用 curl 验证下载主动模式 curl -u admin:your_strong_password ftp://127.0.0.1/pub/test.txt -o test.txt # 被动模式验证 curl -u admin:your_strong_password --ftp-pasv ftp://127.0.0.1/pub/test.txt -o test.txt如果pwd和ls都正常但传输文件卡住九成是数据通道端口没放行。主动模式检查客户端防火墙被动模式检查服务端防火墙的 50000-50100 区间。这个排查思路在下一章会展开成具体的避坑条目。3. 避坑指南PCMans FTP Server 部署中的五个真实翻车点3.1 现象客户端能登录但 LIST 命令超时原因这是被动模式端口没放行的典型症状。控制连接走 21 端口通了但 LIST 需要开数据连接服务端在 PASV 响应里返回了一个端口客户端去连却被防火墙挡了。解决在 Windows 防火墙入站规则里放行你配置的被动端口区间比如 50000-50100。同时确认服务端设置里的 PASV 端口范围和防火墙规则一致。如果服务端在 NAT 后面还要在设置里填外部 IP否则 PASV 响应返回内网地址外网客户端拿到也没用。3.2 现象中文文件名上传后变成乱码原因FTP 协议本身对文件名编码没有强制规定PCMans FTP Server 默认走的是系统本地编码GBK而现代客户端如 FileZilla 默认用 UTF-8。两边编码不一致就乱码。解决在 FileZilla 站点管理器里把字符集从「自动」改成「强制 UTF-8」试试如果还乱就改成「使用自定义字符集」填 GBK。反过来如果服务端支持 UTF-8 开关部分版本有打开它让服务端也用 UTF-8。最稳妥的办法是文件名全用英文数字这是血泪经验。3.3 现象upload-only 账号上传文件时报 550 权限拒绝原因很多 FTP 客户端在上传前会先执行 LIST 或 MLSD 来确认目录存在如果账号没有列目录权限客户端直接报错退出根本走不到 STOR 命令。解决给 upload-only 账号至少加上 LIST 权限但可以通过目录权限设置让它只能看到 incoming 目录本身看不到其他内容。如果项目不支持这种细粒度控制就单独建一个只包含 incoming 的根目录给这个账号。3.4 现象服务运行一段时间后新连接连不上原因PCMans FTP Server 是单线程事件循环模型每个连接占用一个句柄。如果客户端异常断开没有正常发送 QUIT服务端可能没有及时回收句柄积累到一定程度就拒绝新连接。解决在服务端设置里找连接超时时间如果有设短一点比如 300 秒。没有的话就定期重启服务或者写个脚本监控连接数。生产环境不建议用这个方案临时用的话设个定时任务每天重启一次。3.5 现象Windows 10/11 上 21 端口被系统占用原因Windows 自带的 IIS FTP 服务或者某些系统组件可能已经监听了 21 端口导致 PCMans FTP Server 启动失败或监听不到。解决先用netstat -ano | findstr :21找到占用进程的 PID再用tasklist | findstr PID确认是什么程序。如果是 IIS去「启用或关闭 Windows 功能」里把 FTP 服务关掉。如果不想动系统组件就把 PCMans FTP Server 的监听端口改成 2121 之类的非标准端口客户端连接时指定端口即可。4. 进阶技巧用脚本批量管理用户与自动化验证4.1 批量生成用户配置临时项目经常需要一次开几十个账号手动改配置文件不现实。我一般用 Python 脚本生成配置段再贴进配置文件重启服务。# gen_ftp_users.py # 批量生成 PCMans FTP Server 用户配置段 import hashlib def gen_user(username, password, home_dir, perms): 生成单个用户的配置段 username: 用户名 password: 明文密码项目若支持明文则直接写 home_dir: 用户根目录绝对路径 perms: 权限列表如 [READ, WRITE] perm_str |.join(perms) return f[User:{username}] Password{password} HomeDir{home_dir} Permissions{perm_str} # 批量生成 10 个只读账号 users [] for i in range(1, 11): uname freader{i:02d} pwd fRead{i:02d}2024 users.append(gen_user(uname, pwd, rD:\ftp_share\public, [READ])) # 写入配置文件追加模式注意先备份原配置 with open(ftp_users.ini, a, encodinggbk) as f: f.write(\n.join(users)) print(f已生成 {len(users)} 个用户配置)这段脚本的关键参数是home_dir和perms。home_dir用原始字符串避免反斜杠转义问题perms列表里 READ 和 WRITE 是最常用的组合。注意写入编码用 gbk 是因为 PCMans FTP Server 在中文 Windows 上默认读 GBK 编码的配置文件用 UTF-8 写进去可能读出来是乱码。生成后记得重启服务让配置生效。4.2 自动化验证脚本部署完不验证等于没部署。我习惯用一个 Python 脚本跑完整的连接、列目录、上传、下载、删除流程确认所有权限位都按预期工作。# verify_ftp.py # 自动化验证 FTP 服务各项功能 from ftplib import FTP import io def verify(host, user, passwd, test_uploadTrue): 验证 FTP 服务核心功能 host: 服务端地址 user/passwd: 测试账号 test_upload: 是否测试上传只读账号设为 False results {} try: ftp FTP() ftp.connect(host, 21, timeout10) ftp.login(user, passwd) results[login] OK # 测试列目录 files [] ftp.retrlines(LIST, files.append) results[list] fOK ({len(files)} items) # 测试当前目录 pwd ftp.pwd() results[pwd] fOK ({pwd}) if test_upload: # 测试上传 content io.BytesIO(bverification test file) ftp.storbinary(STOR _verify_test.txt, content) results[upload] OK # 测试下载 download io.BytesIO() ftp.retrbinary(RETR _verify_test.txt, download.write) results[download] OK # 清理 ftp.delete(_verify_test.txt) results[delete] OK ftp.quit() except Exception as e: results[error] str(e) return results if __name__ __main__: r verify(127.0.0.1, admin, your_strong_password) for k, v in r.items(): print(f{k}: {v})这个脚本覆盖了登录、LIST、PWD、STOR、RETR、DELE 六个核心操作。timeout10防止连接卡死io.BytesIO在内存里构造测试文件避免落盘。跑一遍如果全 OK说明服务端配置和防火墙都没问题。如果 upload 那步报 550回去检查权限位如果 list 超时回去检查被动端口。4.3 用 curl 做快速回归测试不想写 Python 的时候几条 curl 命令也能覆盖主要场景。我一般把这几条存成一个 bat 脚本每次改完配置跑一遍。# 测试登录与列目录 curl -u admin:your_strong_password ftp://127.0.0.1/ --list-only # 测试上传 echo test content _test.txt curl -u admin:your_strong_password -T _test.txt ftp://127.0.0.1/incoming/ # 测试下载 curl -u admin:your_strong_password ftp://127.0.0.1/incoming/_test.txt -o _downloaded.txt # 测试删除 curl -u admin:your_strong_password ftp://127.0.0.1/incoming/ -Q DELE _test.txt--list-only只列目录不下载-T是上传-Q是在连接后发送指定命令。这几条命令跑通基本功能就没问题了。注意 curl 默认用主动模式如果要测被动模式加--ftp-pasv。4.4 日志与连接数监控PCMans FTP Server 的日志功能比较简陋界面上一般只显示当前连接数和简单的传输记录。如果需要更详细的日志常见做法是在服务端前面加一层抓包或者用 Windows 自带的性能监视器看网络吞吐。我自己的习惯是开一个netstat -an | findstr :21的定时任务每分钟记录一次连接数超过阈值就告警。这个方案糙但有效毕竟用这个工具的场景本身就不追求企业级监控。从那以后我每次部署 PCMans FTP Server都强制走一遍「改端口 → 配用户 → 开防火墙 → 跑验证脚本」这四步少一步都可能在上线后翻车。希望帮到你。本文还有配套的精品资源点击获取