首页
/ frp 弃用特性跟踪指南:Wire 协议 v1、INI 配置与无 runID Visitor 连接的下线计划与源码解读

frp 弃用特性跟踪指南:Wire 协议 v1、INI 配置与无 runID Visitor 连接的下线计划与源码解读

2026-09-04 12:38:20作者:廉彬冶Miranda

本文基于仓库中的 deprecations.md 展开,完整解读 frp 当前仍在随版本发布、但已明确计划移除的三项特性——Wire 协议 v1、INI 配置格式、不带 runID 的 Visitor 连接。读完本篇,你将了解每一项弃用特性的版本时间线、替代方案、对应的源码位置与底层实现,以及在实际升级 frp 时如何安全迁移、如何评估这些弃用项对自身部署的影响。

一、deprecations.md 是什么:frp 的弃用特性总账

frp 仓库在 deprecations.md 中专门维护了一份"弃用账本",其开头明确了这份文档的定位:

This document tracks deprecated features and APIs that are still shipped but scheduled for removal. Maintainers should review this list before each release to decide whether any items are due for removal.

即:该文档跟踪仍然随版本发布、但已列入移除计划的特性与 API。维护者在每次发版前应复核这份清单,判断哪些条目已到期、可以真正移除。文档同时指出,约束这些支持窗口的版本兼容策略见仓库根目录的 Release.md

文档采用固定的两条目结构:

  • Active:当前仍可用但已弃用、有明确或待定移除计划的特性;
  • Removed:已经完成移除的特性(当前仓库版本中该节内容尚为空,即还没有条目真正走完下线流程)。

每个 Active 条目都遵循统一的描述格式:Deprecated since(自哪个版本弃用)、Removal target(计划移除版本)、Replacement(替代方案)、Code references(相关代码位置)、Notes(移除后果等注意事项)。当前仓库中 Active 区域共有三个条目:Wire 协议 v1、INI 配置格式、不带 runID 的 Visitor 连接。下面逐一展开,并结合仓库源码印证每一项的实际状态。

二、Active 条目一:Wire 协议 v1

2.1 文档给出的弃用信息

deprecations.md 中对 Wire 协议 v1 的记录如下:

  • Deprecated since: v0.70.0(计划中,届时 v2 将成为默认协议);
  • Removal target: v0.78.0 或更晚。文档特别说明:v0.69.0 是 v1 作为默认协议的最后版本,该版本需支持到 v0.78.0 发布为止,因此 v0.77.0 是必须保留 v1 支持的最后版本
  • Replacement: Wire 协议 v2,即在 frpc 中配置 transport.wireProtocol = "v2"
  • Code references: v1 的消息类型与编解码器位于 pkg/msg/ 目录,协议协商路径分布在 client/server/
  • Notes: 移除 v1 将同时失去与"不会协商 v2"的 frpc/frps 的兼容性。

2.2 源码印证:v1 与 v2 如何共存

配置入口。 frpc 的客户端配置结构体中包含 WireProtocol 字段,定义于 client.go

WireProtocol string `json:"wireProtocol,omitempty"`

配置校验逻辑位于 validation/client.go,非法取值会直接报错:

errs = AppendError(errs, fmt.Errorf("invalid transport.wireProtocol, optional values are %v", SupportedWireProtocols))

因此迁移动作很直接:在 frpc 配置(如基于 conf/frpc.toml 的模板)中为 transport 配置 wireProtocol = "v2",即可显式走 v2 协议。

v2 的编码实现。 v2 消息编解码在 wire_v2.go 中实现。该文件以 uint16 类型的消息 ID(V2TypeLoginV2TypeNatHoleReport,另加二进制 UDP 包类型 V2TypeUDPPacketBinary)配合 JSON 载荷编码消息帧:EncodeV2MessageFrame 负责将 Go 消息对象序列化为"2 字节 typeID + JSON 内容"的帧载荷,DecodeV2MessageFrame / DecodeV2MessageFrameInto 负责反向解析与类型校验(见 wire_v2.go)。

协议协商与读写器选择。 客户端建立控制/工作连接时,handler.go 中的 NewReadWriter 会根据传入的 wireProtocol 字符串选择 v1 或 v2 读写器;connector.go 中的 messageConnector 在连接建立后,对 v2 协议还会写入协议 magic(wire.WriteMagicIfV2),使对端能在读取第一条消息前就识别协议版本。

测试覆盖两种协议。 仓库测试明确以 v1/v2 双协议为测试矩阵,例如 xtcp_test.go 中针对 NAT 穿透消息的读取同时覆盖 wire.ProtocolV2wire.ProtocolV1 与默认值;udp_binary_test.go 则验证 UDP 包编解码器对 v1、v2 及未知协议值的行为。E2E 层同样保留了针对两种协议的用例(如 test/e2e/v1/basic/wire.go)。从测试结构看,v1 支持目前是"仍在被持续验证的活跃代码",与文档中"removal target 尚未到来"的状态一致。

2.3 对使用者的意义

  • 若你的 frpc 与 frps 版本较旧、未显式配置 wireProtocol,它们将沿用默认的 v1 协商路径;按文档规划,v1 将在 v0.78.0 及之后的版本中被移除,届时混用旧版本会出现"无法协商 v2"导致的连接失败。
  • 提前在 frpc 中设置 transport.wireProtocol = "v2" 是推荐的迁移动作,且 Release.md 已说明 v2 下 UDP 包可使用更紧凑的二进制编解码(在 v2 协商成功后生效;v1 保持 JSON 表示),提前切换还能受益于此优化。

三、Active 条目二:INI 配置格式

3.1 文档给出的弃用信息

  • Deprecated since: 早于该弃用文档的建立时间,启动警告已持续存在于多个发布版本中;
  • Removal target: TBD(待定);
  • Replacement: YAML / JSON / TOML;
  • Code references:
    • cmd/frpc/sub/root.go — frpc 启动警告;
    • cmd/frps/root.go — frps 启动警告;
    • pkg/config/legacy/ — 遗留 INI 解析器,需与警告一并移除;
  • 文档未给出移除后果的额外 Notes,但从 Replacement 一项可以直接读出迁移方向:改用 YAML、JSON 或 TOML 配置格式。

3.2 源码印证:启动警告与遗留解析器

frpc 侧的警告。root.gorunClient 中,配置加载完成后会检查加载结果是否为遗留格式,若是则打印警告:

result, err := config.LoadClientConfigResult(cfgFilePath, strictConfigMode)
if err != nil {
    return err
}
if result.IsLegacyFormat {
    fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " +
        "please use yaml/json/toml format instead!\n")
}

frps 侧的警告。 服务端入口 root.goLoadServerConfig 返回 isLegacyFormat 标记,同样触发一模一样的警告:

svrCfg, isLegacyFormat, err = config.LoadServerConfig(cfgFile, strictConfigMode)
...
if isLegacyFormat {
    fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " +
        "please use yaml/json/toml format instead!\n")
}

也就是说,只要你的 frpc 或 frps 还在用 .ini 配置启动,每次启动都会看到这条警告——这正是文档所说"startup warning has been in place for several releases"的实证。

遗留解析器实现。 INI 解析位于 pkg/config/legacy/(含 parse.goclient.goserver.goconversion.go 等文件)。legacy/README.md 说明了其实现思路:frp 选用了 go-ini 库完成基础键值匹配,再叠加自定义逻辑处理 maparray 等标准库不直接支持的结构,整个 Unmarshal 分两步完成;且自定义 tag 关键字(如 inline、extends 等)与 json/protobuf 等标准库并不一致。仓库内也保留了完整的 INI 示例配置(如 conf/legacy/frpc_legacy_full.iniconf/legacy/frps_legacy_full.ini),可作为迁移到 TOML/YAML/JSON 时的对照参考;新版完整示例见 conf/frpc_full_example.tomlconf/frps_full_example.toml

3.3 迁移建议

尽管 Removal target 尚为 TBD,INI 路径是"最确定会消失"的条目之一(文档明确"legacy INI parser 将与警告一并移除")。迁移动作是纯配置层的:将 .ini 中的代理定义改写为 TOML/YAML/JSON 格式即可,运行方式与命令参数不变。由于遗留格式存在自定义 tag 语义(见上述 legacy README),迁移时建议对照新版 full example 逐项核对,而不是机械做格式转换。

四、Active 条目三:不带 runID 的 Visitor 连接

4.1 文档给出的弃用信息

  • Deprecated since: v0.50.0(runID 引入的版本);
  • Removal target: TBD;
  • Replacement: 要求每一条 Visitor 连接都必须携带 runID
  • Code references: server/service.go 中的 RegisterVisitorConn 目前仍接受空 runID,以维持向后兼容;
  • Notes: 移除后将导致 v0.50.0 之前发布的 frpc 客户端无法工作;应安排在一个"可以接受丢弃 pre-v0.50.0 frpc 兼容"的版本中执行。

4.2 源码印证:RegisterVisitorConn 的兼容分支

文档指向的实现正是 server/service.go 中的 RegisterVisitorConn,其关键注释与逻辑为:

// TODO(deprecation): Compatible with old versions, can be without runID, user is empty.
// In later versions, it will be mandatory to include runID.
// If runID is required, it is not compatible with versions prior to v0.50.0.
if newMsg.RunID != "" {
    admitted, err := svr.ctlManager.admitVisitorByRunID(newMsg.RunID, func(...) error {
        if wireProtocol != controlWireProtocol {
            return fmt.Errorf("visitor connection wire protocol mismatch: got %s want %s", ...)
        }
        return admit(visitorUser, controlWireProtocol, controlUDPPacketCodec)
    })
    ...
}
return admit("", wireProtocol, "")

可以清晰地看到文档所述的两条路径:

  1. 新路径(携带 runID): 服务端通过 admitVisitorByRunID 按 runID 匹配客户端控制连接,并顺带校验 Visitor 连接与控制连接的 wire 协议一致(不匹配则直接报错);
  2. 兼容回退路径(空 runID): 最后的 return admit("", wireProtocol, "") 即为"用户为空"的旧版本兜底分支,这正是移除目标——一旦该分支被删除,pre-v0.50.0 的 frpc 将无法再建立 Visitor 连接(如 stcp/xtcp/sudp 类访问)。

测试层面,service_test.go 针对 RegisterVisitorConn 同时覆盖了 v1/v2 协议下的注册、runID 匹配失败与 wire 协议不匹配等场景,说明该兼容分支目前是受测试约束的活跃行为,尚未处于"即将删除"的冻结状态(与 Removal target: TBD 相符)。

4.3 对使用者的意义

对绝大多数使用当前版本 frpc 的用户,这一条几乎没有感知——现代 frpc 发起的 Visitor 连接天然携带 runID。它主要影响两种场景:

  • 部署中存在 v0.50.0 之前发布的旧 frpc 作为 stcp/xtcp/sudp 的 visitor 端;
  • 第三方程序自行构造 frp 协议报文接入服务端的场景。

这类环境应尽快升级客户端;而服务端运维在规划版本节奏时,可以把"移除空 runID 分支"作为未来某个版本的兼容性决策点提前纳入评估。

五、综合视角:如何跟随这份弃用清单做版本规划

把三个 Active 条目放在一起看,可以得到一张清晰的迁移优先级表:

弃用项 弃用时间 移除目标 替代方案 移除后果 主要代码位置
Wire 协议 v1 v0.70.0(计划) v0.78.0+(v0.77.0 为最后需保留的版本) transport.wireProtocol = "v2" 失去与不协商 v2 的 frpc/frps 的兼容 pkg/msg/client/connector.goserver/service.go
INI 配置格式 早于本文档 TBD YAML / JSON / TOML 无法再用 .ini 启动 frpc/frps cmd/frpc/sub/root.gocmd/frps/root.gopkg/config/legacy/
无 runID 的 Visitor 连接 v0.50.0 TBD Visitor 连接强制携带 runID 无法兼容 pre-v0.50.0 的 frpc server/service.go

三个条目的共性是:功能现在仍完全可用,都有明确的替代方案,且文档标注了移除将造成的兼容性后果。因此对使用者的实践建议是:

  1. 有明确时间线的优先处理。 Wire 协议 v1 是唯一给出具体移除版本窗口的条目(v0.77.0 为最后需保留 v1 的版本),跨版本混用 frpc/frps 的环境应尽早统一升级到支持 v2 的版本,并在 frpc 中显式设置 transport.wireProtocol = "v2"
  2. 用启动行为验证状态。 INI 弃用项的"心跳"就是每次启动的 WARNING: ini format is deprecated...,只要还看到这条警告,就说明仍在弃用窗口内;
  3. 关注兼容性边界而非单点配置。 两个 TBD 条目的移除都以"能否接受丢弃旧版本客户端"为决策前提,若你的环境存在旧版本 frpc(尤其是 pre-v0.50.0),升级客户端比调整任何服务端配置都更根本;
  4. 周期性复核文档。 按文档自身的约定,维护者在每次发版前复核该清单;使用者在升级大版本前,也建议对照 deprecations.md 检查自己是否还依赖 Active 区条目,以及原本 Active 的条目是否已移入 Removed 区(当前版本中 Removed 区尚为空,说明还没有条目真正走完下线流程)。

六、结语

deprecations.md 以极短的篇幅给出了 frp 向前演进过程中"新旧共存窗口"的完整账目:Wire 协议 v1 有明确的 v0.78.0+ 移除目标与 v2 替代路径(实现见 pkg/msg/wire_v2.goclient/connector.go 的协商逻辑);INI 配置格式自多个版本起通过 frpcfrps 启动警告持续提示迁移到 YAML/JSON/TOML(遗留解析器位于 pkg/config/legacy/);无 runID 的 Visitor 连接则保留着 server/service.go 中的兼容回退分支,等待一个可接受丢弃 pre-v0.50.0 客户端的版本再行移除。理解并跟进行动这三条时间线,就能在 frp 的版本演进中平滑过渡、避免兼容性断裂。

登录后查看全文
热门项目推荐
相关项目推荐