应优先选用Boost.Beast等成熟库而非手写socket,因其提供可靠TCP能力并可配合状态机实现USER/PASS/LIST/RETR等核心命令,避免PASV/PORT切换、权限校验等复杂问题。

用 libftp 还是自己手写 socket?先别急着编码
直接手写一个符合 RFC 959 的完整 FTP 服务器,在 C++ 里不现实——光是 PASV/PORT 模式切换、ASCII/BINARY 模式处理、用户认证状态机、目录遍历权限校验,就足够踩满一周坑。除非你明确要练底层网络和协议解析,否则第一反应应该是:有没有轻量、可嵌入、MIT/BSD 协议的现成库?libftp 并不存在(常见误搜),真正可用的是 cpp-ftp-server(GitHub 上维护较活跃的头文件库)或更稳妥的 Boost.Beast + 自定义 FTP 命令解析。
Boost.Beast 搭建最小可行 FTP 服务的关键三步
Beast 本身不内置 FTP,但提供了可靠的 TCP accept / async_read / async_write 能力,配合状态机就能支撑基础命令交互。重点不是“实现全部”,而是让 USER、PASS、LIST、RETR 四个命令跑通:
- 每个连接启动独立
tcp::socket,用beast::flat_buffer接收命令行(注意 FTP 命令以\r\n结尾,不是\n) - 手动解析命令:用
std::string_view分割空格,区分USER anonymous和RETR /readme.txt;路径必须做../过滤,否则直接std::ifstream打开会越权 -
LIST响应不能只写文件名,得模拟 Unixls -l格式(哪怕硬编码时间/权限),否则 FileZilla 之类客户端会卡在“等待目录列表”
为什么 bind() 到 21 端口失败?检查这三件事
新手最常卡在这一步,错误通常是 bind: Permission denied 或 Address already in use:
- Linux/macOS 下端口
1024以下需 root 权限,开发时建议先绑定到8021测试,别硬刚21 - 确认没其他 FTP 进程占着端口:
lsof -i :8021或netstat -tuln | grep 8021 - 如果用了
SO_REUSEADDR但还是报Address already in use,大概率是上次进程崩溃没释放 socket,等TIME_WAIT超时(通常 60 秒),或者换端口更快
被动模式(PASV)响应怎么构造?别信网上随手抄的 IP 字符串
FTP 客户端发 PASV 后,服务器必须返回形如 227 Entering Passive Mode (192,168,1,100,14,21) 的响应,括号内是 IP 四段 + 端口高字节、低字节。问题在于:
立即学习“C++免费学习笔记(深入)”;
- 不能直接填
127.0.0.1——外网客户端连不到;也不能填0.0.0.0——RFC 不允许 - 真实部署时得从网卡取本机局域网 IP(
getifaddrs()),或由配置项指定(比如--public-ip=203.0.113.45) - 端口必须是随机且未被占用的,且要确保防火墙放行该端口范围(比如
50000-50100),否则客户端connect()超时
复杂点在于,这个“被动端口”得和主控 socket 分开监听,每个数据连接都要新 accept 一次,容易漏掉 async_accept 的错误分支处理。


















