文章目录
概述
frp(Fast Reverse Proxy)是一个高性能的反向代理应用,专注于内网穿透。本文档详细介绍了frp核心功能的底层处理逻辑,帮助第一次接触该项目的开发者深入理解其技术架构和实现原理。
技术架构
整体架构模式
frp采用典型的客户端-服务器(C/S)架构模式,包含以下核心组件:
外部用户 frps服务端 内网客户端frpc 本地服务 ControlManager ProxyManager PluginManager ProxyManager VisitorManager Control连接
核心组件关系
1. 服务端核心组件
- Service: 服务端主服务,负责管理所有连接和组件
- ControlManager: 管理所有客户端控制连接
- ProxyManager: 管理所有代理实例
- MessageTransporter: 消息传输层,处理客户端-服务端通信
2. 客户端核心组件
- Service: 客户端主服务,管理与服务端的连接
- Control: 控制连接管理器,维护与服务端的控制通道
- ProxyManager: 管理客户端代理实例
- VisitorManager: 管理访问者连接(用于P2P)
核心处理流程
1. 连接建立流程
客户端登录过程
frpc客户端 frps服务端 ControlManager 发送Login消息 验证认证信息 创建Control实例 返回LoginResp消息 建立控制连接 发送ReqWorkConn请求 创建工作连接 frpc客户端 frps服务端 ControlManager
核心代码逻辑:
- 
客户端登录: - 客户端发送Login消息包含版本、主机名、认证信息
- 服务端验证认证信息(Token或OIDC)
- 创建唯一的RunID标识客户端会话
 
- 客户端发送
- 
控制连接建立: go// 服务端创建Control实例 ctl, err := NewControl(ctx, rc, pxyManager, pluginManager, authVerifier, ctlConn, !internal, loginMsg, serverCfg) // 客户端创建Control实例 ctl := &Control{ sessionCtx: sessionCtx, msgDispatcher: msg.NewDispatcher(conn), msgTransporter: transport.NewMessageTransporter(dispatcher.SendChannel()), }
2. 消息处理机制
消息分发架构
frp使用了一套完善的消息处理系统,类似于HTTP2的多路复用机制:
网络连接 MessageDispatcher 消息解析 类型路由 Handler处理 MessageTransporter 消息发送 响应等待 响应分发
核心实现:
- 
消息分发器(Dispatcher): gotype Dispatcher struct { rw io.ReadWriter sendCh chan Message msgHandlers map[reflect.Type]func(Message) defaultHandler func(Message) } // 注册消息处理器 dispatcher.RegisterHandler(&msg.NewProxy{}, handleNewProxy) dispatcher.RegisterHandler(&msg.ReqWorkConn{}, handleReqWorkConn)
- 
消息传输器(MessageTransporter): gotype MessageTransporter interface { Send(msg.Message) error Do(ctx context.Context, req msg.Message, laneKey, recvMsgType string) (msg.Message, error) Dispatch(m msg.Message, laneKey string) bool }
- 
消息类型系统: - 支持多种消息类型:Login、NewProxy、ReqWorkConn、StartWorkConn等
- 每种消息都有对应的处理逻辑和响应机制
 
3. 代理创建与管理
代理生命周期管理
发送NewProxy 服务端确认 启动失败 关闭代理 重试 健康检查失败 重新启动 ProxyPhaseNew ProxyPhaseWaitStart ProxyPhaseRunning ProxyPhaseStartErr ProxyPhaseClosed ProxyPhaseCheckFailed
核心处理逻辑:
- 
代理工厂模式: govar proxyFactoryRegistry = map[reflect.Type]func(*BaseProxy, v1.ProxyConfigurer) Proxy{} func RegisterProxyFactory(proxyConfType reflect.Type, factory func(*BaseProxy, v1.ProxyConfigurer) Proxy) { proxyFactoryRegistry[proxyConfType] = factory }
- 
代理包装器(Wrapper): - 管理代理状态和生命周期
- 实现健康检查机制
- 处理重连和错误恢复
 
- 
工作连接处理: gofunc (pm *Manager) HandleWorkConn(name string, workConn net.Conn, m *msg.StartWorkConn) { pw, ok := pm.proxies[name] if ok { pw.InWorkConn(workConn, m) // 将工作连接分发给对应代理 } }
4. 心跳与连接保活
心跳机制
客户端 服务端 Ping消息 Pong响应 检查Pong超时 重连或关闭 alt [超时检测] 检查Ping超时 关闭连接 alt [服务端检测] loop [心跳循环] 客户端 服务端
实现细节:
- 
心跳发送: gofunc (ctl *Control) heartbeatWorker() { sendHeartBeat := func() (bool, error) { pingMsg := &msg.Ping{} ctl.sessionCtx.AuthSetter.SetPing(pingMsg) return false, ctl.msgDispatcher.Send(pingMsg) } go wait.BackoffUntil(sendHeartBeat, backoffManager, true, ctl.doneCh) }
- 
超时检测: - 客户端检测Pong消息超时
- 服务端检测Ping消息超时
- 超时后自动重连或关闭连接
 
高级功能实现
1. P2P模式与NAT穿透
NAT类型分类
frp实现了智能的NAT类型检测和穿透算法:
NAT类型检测 STUN服务器探测 地址映射分析 NAT类型 EasyNAT: 端口固定 HardNAT: 端口变化 行为分析 端口变化规律 BehaviorNoChange BehaviorPortChanged BehaviorIPChanged BehaviorBothChanged
穿透模式选择
            
            
              go
              
              
            
          
          type NatFeature struct {
    NatType            string  // EasyNAT/HardNAT
    Behavior           string  // 端口变化行为
    PortsDifference    int     // 端口差值
    RegularPortsChange bool    // 是否规律变化
    PublicNetwork      bool    // 是否公网
}穿透算法:
- 
模式0: 双方都是EasyNAT或一方在公网 - 简单的双向探测
- 低TTL消息探测
 
- 
模式1: HardNAT与EasyNAT,端口变化规律 - 多端口范围探测
- 基于端口预测算法
 
- 
模式2: HardNAT与EasyNAT,端口变化不规律 - 随机端口探测
- 多监听端口策略
 
- 
模式3/4: 双HardNAT场景 - 复合探测策略
- 适应性算法选择
 
穿透实现流程
Visitor frps服务端 Client NatHoleVisitor消息 NatHoleClient消息 分析NAT特征 选择穿透策略 NatHoleResp(策略A) NatHoleResp(策略B) UDP探测包 UDP探测包 par [并发穿透] 建立P2P连接 Visitor frps服务端 Client
2. 多协议支持架构
代理类型工厂
            
            
              go
              
              
            
          
          // 通用TCP代理
type GeneralTCPProxy struct {
    *BaseProxy
}
// UDP代理
type SUDPProxy struct {
    *BaseProxy
    cfg *v1.SUDPProxyConfig
    localAddr *net.UDPAddr
}
// HTTP代理(继承TCP)
func init() {
    RegisterProxyFactory(reflect.TypeOf(&v1.TCPProxyConfig{}), NewGeneralTCPProxy)
    RegisterProxyFactory(reflect.TypeOf(&v1.HTTPProxyConfig{}), NewGeneralTCPProxy)
    RegisterProxyFactory(reflect.TypeOf(&v1.SUDPProxyConfig{}), NewSUDPProxy)
}协议处理差异
- 
TCP类协议 (TCP/HTTP/HTTPS): - 使用GeneralTCPProxy统一处理
- 支持连接池和多路复用
- HTTP协议额外支持虚拟主机路由
 
- 
UDP协议 (UDP/SUDP): - 无连接状态管理
- 数据包级别的转发
- 支持NAT穿透
 
- 
特殊协议 (STCP/XTCP): - STCP:安全的内网直连
- XTCP:P2P模式的大数据传输
 
3. 连接池与多路复用
工作连接池设计
控制连接 工作连接池 WorkConn1 WorkConn2 WorkConn3 用户请求 代理分发
实现机制:
            
            
              go
              
              
            
          
          type Control struct {
    workConnCh chan net.Conn  // 工作连接池
    poolCount  int             // 池大小
}
func (ctl *Control) GetWorkConn() (net.Conn, error) {
    select {
    case workConn := <-ctl.workConnCh:
        // 获取到连接后,请求服务端补充新连接
        ctl.msgDispatcher.Send(&msg.ReqWorkConn{})
        return workConn, nil
    case <-time.After(timeout):
        return nil, fmt.Errorf("获取工作连接超时")
    }
}安全机制
1. 传输安全
- TLS加密:控制和数据连接加密
- Token验证:防止未授权访问
- IP白名单:访问控制
2. 访问控制
- 用户权限:基于用户的代理访问控制
- 域名限制:HTTP代理的域名白名单
- 端口限制:服务端端口使用限制
项目地址
作者:https://afdian.com/a/fatedier