Tinyftp 源码学习笔记:3000 行 C 实现的 FTP 服务器

学完 socket 网络编程之后,只会写一些小demo,往往还缺一个更接近真实服务的练习项目:既能用标准客户端对接,代码量又不会太大。整理了一个网络编程的项目 Tinyftp——一个用 C 写的简易 FTP 服务器,大约三千行代码,这个项目参考 vsftpd,十分具备学习意义。

1. 项目概览

Tinyftp 适合作为网络编程练手项目,主要因为:

  • 兼容常见 FTP 命令,可用 FileZilla 等标准客户端
  • 功能较完整:登录、PORT/PASV、上传下载、目录操作、限速、断点续传、连接数限制
  • 架构清晰:多进程 + nobody 特权分离,接近精简版 vsftpd

一句话总结:代码量不是太大,符合RFC协议规范,框架还不错。

2. FTP 基础

FTP 使用两根连接:

控制连接
持续存在,传输命令与应答,例如 USERPASSRETR,以及 220230226 等状态码。

数据连接
临时建立,用于传文件、列目录;传输结束后关闭。
在这里插入图片描述
数据连接有两种模式:

  • PORT(主动):服务器连接客户端
  • PASV(被动):客户端连接服务器
    在这里插入图片描述
    Tinyftp 两种模式均已实现。主动模式需绑定 20 端口,相关特权操作由 nobody 进程完成,这是源码阅读的重点之一。

3. 源码阅读顺序与模块分工

  1. tinyftpd.conf —— 配置项
  2. main.c —— 监听与 accept
  3. session.c —— 会话内二次 fork
  4. ftpproto.c —— 命令表与协议主循环
  5. privparent.cprivsock.* —— nobody 与 FD 传递

src/ 模块大致如下:

文件作用
main.c监听、连接数限制、fork 会话
session.c会话启动:服务进程 / nobody
ftpproto.c命令分发、上传下载、LIST 等
privparent.cnobody:绑 20 端口、建立数据连接
privsock.*父子进程通信、传递文件描述符
hash.*按 IP 统计连接数
ftpcodes.hFTP 状态码
parseconf / tunable配置加载

常见配置:listen_port(示例多为 5188)、PASV/PORT 开关、最大连接数、上下行限速、空闲超时等。

4. 核心结构:三层进程模型

在这里插入图片描述

第一层:主进程

以 root 启动,读取配置并 listen。
客户端接入后:accept → 统计该 IP 连接数 → fork:

  • 子进程:持有 connfd,进入 begin_session
  • 父进程:关闭 connfd,继续监听;在 SIGCHLD 中回收子进程并更新连接计数

超过限制时返回 421
这一层把「接客」与「处理会话」分开。

第二层:会话内再 fork

begin_session() 中再次 fork:

  • 子进程 handle_child:解析命令、传输数据;登录成功后切换为普通用户权限
  • 父进程 handle_parent:降权为 nobody,负责特权相关操作

可概括为:

协议处理运行在较低权限;需要特权(如绑定 20 端口)时,由 nobody 代为执行。

第三层:socketpair 与传 FD

父子进程通过内部通道交换命令,例如:

  • 按 PORT 信息连接客户端
  • 开启 PASV 监听 / accept
  • 成功后将 data_fd 传回服务进程

5. 协议主循环:命令表驱动

ftpproto.c 采用表驱动方式组织命令,例如:

USER → do_user
PASS → do_pass
PORT → do_port
PASV → do_pasv
RETR → do_retr
STOR → do_stor
LIST → do_list
REST → do_rest
(另有 CWD / MKD / DELE / ABOR 等)

handle_child 主循环可记为四步:

  1. 发送欢迎应答 220
  2. readline 读取一行命令
  3. 拆分命令与参数,命令转大写
  4. 查表调用处理函数;未知命令返回 500

应答格式统一为:

状态码 + 空格 + 说明文字 + \r\n

多数应用层协议服务器的骨架与此类似:读入 → 解析 → 分发 → 回写

6. 登录流程

未使用自建用户表,而是对接系统账号:

  1. USERgetpwnam,存在则返回 331
  2. PASS → 读取 shadow,经 crypt 校验,成功返回 230
  3. seteuid / setegid,并 chdir 到用户家目录

因此需要 root 启动(读取 shadow 后再降权)。
作为学习项目结构清晰;若部署到公网,需考虑网络安全问题。

7. PORT / PASV 流程笔记

PORT(主动)

客户端告知服务器目标 IP 与端口。
服务器先返回 200;真正进行 LIST / RETR / STOR 时,由 nobody 从 20 端口发起 connect,再将数据套接字 FD 交回服务进程。

PASV(被动)

服务进程请求 nobody 监听随机端口,返回类似:

227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)

客户端连入后,accept 得到的 FD 同样回传给服务进程用于传数。

两种模式最终都会进入 get_transfer_fd()
若尚未执行 PORT 或 PASV,则返回 425 Use PORT or PASV first

阅读建议:可先沿 PASV → LIST/RETR 跟完整条路径,再对照 PORT 实现。

8. 其他值得记录的点

  • REST:断点续传偏移
  • limit_rate:根据当前速率 sleep,实现限速
  • ABOR:传输过程中中断(与 SIGURG 相关)
  • 连接数限制:总连接数 / 每 IP 连接数,配合哈希表维护

这些点使项目更接近可用的 FTP 服务实现,而不只是最小 demo。

9. 编译与运行备忘

git clone 仓库后进入目录,按 README 编译(部分版本脚本名为 bulid.sh)。

注意:

  • 需以 root 运行
  • 监听端口以 tinyftpd.conf 为准
  • 可用 FileZilla 连接本机验证
  • 建议仅用于学习,勿直接作为公网 FTP 服务暴露

10. 小结

通过阅读 Tinyftp,可将以下内容串起来:

  1. 应用层协议如何用「状态码 + 命令表」组织
  2. FTP 双连接模型,以及 PORT / PASV 的连接方向
  3. 特权分离思想,nobody 进程的职责
  4. 进程间协作与文件描述符传递

项目链接

  • Gitee:https://gitee.com/vbirds/Tinyftp
  • GitHub:https://github.com/vbirds/Tinyftp
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值