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 初始化与登录

  1. 服务启动loopLoginUntilSuccess 函数负责持续尝试登录
  2. 退避机制:使用指数退避算法避免连续冲突
    • 每次间隔时间指数上升
    • 加入随机延迟(抖动)防止同步重试
wait.BackoffUntil(loginFunc, ...)
  1. 连接建立

    • 使用 github.com/fatedier/golib/net 建立连接
    • 多路复用使用 github.com/hashicorp/yamux
    • Yamux 提供 session 管理机制,保存 Yamux session 和 Agent 对应关系
  2. 认证机制

    • Token 认证:基于用户设置的 Token 和时间戳生成加密私钥
    • OIDC 认证

2.1.2 消息处理

  • 使用 github.com/fatedier/golib/msg/json 库处理 JSON 消息
  • 未加密时流量可见特征(默认 TLS 可避免)

2.1.3 控制中心

  • NewControl 创建消息调度器
  • 处理发送和响应
  • 心跳处理

2.1.4 代理工作流程

  1. svr.login() 客户端发送登录消息
  2. ctl.worker() 启动消息调度器
    • sendLoop() 向服务端发送消息
    • readLoop() 读取消息并使用各种 handler 处理
  3. 代理状态管理:
    • msg.NewProxyResp 新建代理响应
    • msg.ReqWorkConn 请求工作连接
  4. 代理启动:
    • 检查加密、压缩配置
    • 插件处理
    • TCP 端口转发

2.2 服务端(FRPS)工作流程

2.2.1 初始化

  • NewService 创建服务结构体
  • 初始化操作并监听服务端端口

2.2.2 连接处理

  1. HandleListener 接收客户端连接
  2. svr.handleConnection 处理连接
  3. msg.Login 处理登录请求
  4. svr.RegisterControl 注册控制器

2.2.3 控制器工作

  1. 响应客户端登录成功
  2. 发送 msg.ReqWorkConn 消息
  3. 启动心跳和消息调度

2.2.4 代理管理

  1. msg.NewProxy 处理新建代理请求
  2. ctl.RegisterProxy 注册代理
  3. pxy.Run() 启动代理:
    • 监听转发端口
    • 处理用户连接与客户端工作连接交换

3. 高级功能开发

3.1 免杀技术实现

3.1.1 流量免杀

  • 默认开启 TLS 传输
  • 默认开启流量加密和压缩
  • 默认关闭 TLS 首字节特征

3.1.2 静态免杀

  1. 精简客户端代码:
    • 只保留 TCP 相关功能
    • 移除不必要的依赖
  2. 修改调用结构
  3. 同步修改 client 和 server 包中的默认密钥

3.2 功能增强

3.2.1 SOCKS5 代理增强

  • 随机用户名密码
  • 端口设置为 0(服务端自动寻找空闲端口)
  • 代理描述信息包含:
    • 主机名
    • 网络信息
    • 上线时间
    • SOCKS 用户名密码
  • 代理名称生成规则:
    • 主机名 + socks5 + MD5(主机名) + 网络信息字符串部分

3.2.2 运行模式

  1. 正常模式:
    • 启动后子进程运行
    • 成功运行后删除自身
  2. 服务模式:
    • Linux 系统服务
    • 进程退出后 2 分钟自动重启
    • 开机自启动

3.2.3 登录失败处理

  • 登录失败超过 500 次自动退出

3.3 服务端控制功能

3.3.1 FRPS 控制 FRPC 退出

  1. 消息定义:
// pkg/msg/msg.go
type ExitMessage struct {
    ProxyName string
}
  1. 服务端 API 添加:
// server/dashboard_api.go
func (api *DashboardApi) ExitProxy(c *gin.Context) {
    // 实现退出逻辑
}
  1. 客户端处理:
// client/control.go
func (ctl *Control) handleExitMessage() {
    // 处理退出消息
}

3.3.2 Web 控制台集成

  1. 修改 Vue 组件:
// web/frps/src/components/ProxyView.vue
methods: {
    exitProxy() {
        // 调用退出API
    }
}
  1. 构建前端并集成到 assets/frpc/static

4. 关键实现细节

4.1 连接复用机制

  • 使用 Yamux 实现多路复用
  • 每个内网可运行多个 Agent
  • 新建连接时从已有 Agent session 列表中随机选择
  • 通过创建新的 Yamux Stream 复用连接

4.2 数据传输优化

  • 使用 io.Copyio.CopyBuffer 高效传输数据
  • 缓冲写入和读取:
    • 减少频繁文件操作
    • 控制内存使用
    • 提高性能

4.3 代理状态管理

  • ProxyPhaseNew: 新建状态
  • ProxyPhaseWaitStart: 等待启动
  • ProxyPhaseRunning: 正常运行

5. 开发注意事项

  1. 客户端和服务端版本兼容性
  2. 密钥同步问题
  3. 错误处理和重试机制
  4. 资源清理(连接关闭、文件删除等)
  5. 性能监控和优化

6. 扩展思路

  1. 增加代理协议支持(如 QUIC)
  2. 实现动态端口分配策略
  3. 添加流量统计和分析功能
  4. 开发集群模式支持
  5. 实现更细粒度的访问控制

通过深入理解 FRP 的核心架构和工作原理,开发者可以灵活地进行二次开发,定制符合特定需求的内网穿透解决方案。

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 函数负责持续尝试登录 退避机制 :使用指数退避算法避免连续冲突 每次间隔时间指数上升 加入随机延迟(抖动)防止同步重试 连接建立 : 使用 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 退出 消息定义: 服务端 API 添加: 客户端处理: 3.3.2 Web 控制台集成 修改 Vue 组件: 构建前端并集成到 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 的核心架构和工作原理,开发者可以灵活地进行二次开发,定制符合特定需求的内网穿透解决方案。