frp 源码学习
字数 2129 2025-08-22 12:23:24
FRP 源码分析与高级功能开发指南
1. FRP 概述
FRP 是一个专注于内网穿透的高性能反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议,且支持 P2P 通信。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。
1.1 核心特性
- 多协议支持:TCP、UDP、HTTP、HTTPS
- P2P 通信能力
- 内网穿透能力
- 高性能反向代理
2. FRP 核心架构分析
2.1 客户端(FRPC)工作流程
2.1.1 初始化与登录
- 服务启动:
loopLoginUntilSuccess函数负责持续尝试登录 - 退避机制:使用指数退避算法避免连续冲突
- 每次间隔时间指数上升
- 加入随机延迟(抖动)防止同步重试
wait.BackoffUntil(loginFunc, ...)
-
连接建立:
- 使用
github.com/fatedier/golib/net建立连接 - 多路复用使用
github.com/hashicorp/yamux库 - Yamux 提供 session 管理机制,保存 Yamux session 和 Agent 对应关系
- 使用
-
认证机制:
- Token 认证:基于用户设置的 Token 和时间戳生成加密私钥
- OIDC 认证
2.1.2 消息处理
- 使用
github.com/fatedier/golib/msg/json库处理 JSON 消息 - 未加密时流量可见特征(默认 TLS 可避免)
2.1.3 控制中心
NewControl创建消息调度器- 处理发送和响应
- 心跳处理
2.1.4 代理工作流程
svr.login()客户端发送登录消息ctl.worker()启动消息调度器sendLoop()向服务端发送消息readLoop()读取消息并使用各种 handler 处理
- 代理状态管理:
msg.NewProxyResp新建代理响应msg.ReqWorkConn请求工作连接
- 代理启动:
- 检查加密、压缩配置
- 插件处理
- TCP 端口转发
2.2 服务端(FRPS)工作流程
2.2.1 初始化
NewService创建服务结构体- 初始化操作并监听服务端端口
2.2.2 连接处理
HandleListener接收客户端连接svr.handleConnection处理连接msg.Login处理登录请求svr.RegisterControl注册控制器
2.2.3 控制器工作
- 响应客户端登录成功
- 发送
msg.ReqWorkConn消息 - 启动心跳和消息调度
2.2.4 代理管理
msg.NewProxy处理新建代理请求ctl.RegisterProxy注册代理pxy.Run()启动代理:- 监听转发端口
- 处理用户连接与客户端工作连接交换
3. 高级功能开发
3.1 免杀技术实现
3.1.1 流量免杀
- 默认开启 TLS 传输
- 默认开启流量加密和压缩
- 默认关闭 TLS 首字节特征
3.1.2 静态免杀
- 精简客户端代码:
- 只保留 TCP 相关功能
- 移除不必要的依赖
- 修改调用结构
- 同步修改 client 和 server 包中的默认密钥
3.2 功能增强
3.2.1 SOCKS5 代理增强
- 随机用户名密码
- 端口设置为 0(服务端自动寻找空闲端口)
- 代理描述信息包含:
- 主机名
- 网络信息
- 上线时间
- SOCKS 用户名密码
- 代理名称生成规则:
- 主机名 + socks5 + MD5(主机名) + 网络信息字符串部分
3.2.2 运行模式
- 正常模式:
- 启动后子进程运行
- 成功运行后删除自身
- 服务模式:
- Linux 系统服务
- 进程退出后 2 分钟自动重启
- 开机自启动
3.2.3 登录失败处理
- 登录失败超过 500 次自动退出
3.3 服务端控制功能
3.3.1 FRPS 控制 FRPC 退出
- 消息定义:
// pkg/msg/msg.go
type ExitMessage struct {
ProxyName string
}
- 服务端 API 添加:
// server/dashboard_api.go
func (api *DashboardApi) ExitProxy(c *gin.Context) {
// 实现退出逻辑
}
- 客户端处理:
// client/control.go
func (ctl *Control) handleExitMessage() {
// 处理退出消息
}
3.3.2 Web 控制台集成
- 修改 Vue 组件:
// web/frps/src/components/ProxyView.vue
methods: {
exitProxy() {
// 调用退出API
}
}
- 构建前端并集成到 assets/frpc/static
4. 关键实现细节
4.1 连接复用机制
- 使用 Yamux 实现多路复用
- 每个内网可运行多个 Agent
- 新建连接时从已有 Agent session 列表中随机选择
- 通过创建新的 Yamux Stream 复用连接
4.2 数据传输优化
- 使用
io.Copy和io.CopyBuffer高效传输数据 - 缓冲写入和读取:
- 减少频繁文件操作
- 控制内存使用
- 提高性能
4.3 代理状态管理
ProxyPhaseNew: 新建状态ProxyPhaseWaitStart: 等待启动ProxyPhaseRunning: 正常运行
5. 开发注意事项
- 客户端和服务端版本兼容性
- 密钥同步问题
- 错误处理和重试机制
- 资源清理(连接关闭、文件删除等)
- 性能监控和优化
6. 扩展思路
- 增加代理协议支持(如 QUIC)
- 实现动态端口分配策略
- 添加流量统计和分析功能
- 开发集群模式支持
- 实现更细粒度的访问控制
通过深入理解 FRP 的核心架构和工作原理,开发者可以灵活地进行二次开发,定制符合特定需求的内网穿透解决方案。