文章目录
概述
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