返回项目列表
PROJECT / 发布 2026-01-07 12:02 / 更新 2026-01-07 12:02

Pierce

基于 Netty 的 TCP 内网穿透学习项目,支持 Server/Client 控制通道、多端口映射、心跳与断线重连。

JavaNettyTCP内网穿透
Pierce 服务端与客户端连接演示

Pierce

Pierce 是一个基于 Netty 实现的 TCP 内网穿透学习项目,用来把内网服务通过公网服务器暴露出去。它不是一个完整生产级代理系统,而是围绕长连接管理、端口映射、心跳保活和数据转发链路做的一次工程练习。

GitHub README 更适合放安装、配置和启动命令,这里主要记录项目定位、核心链路和实现取舍。

运行效果

服务端和客户端建立连接后,客户端可以向服务端注册本地服务映射。外部请求访问服务端暴露的端口时,流量会被转发回内网客户端,再由客户端转发到本地服务。

Pierce 服务端与客户端连接演示

项目定位

这个项目的重点不是替代 frp、ngrok 这类成熟工具,而是拆开内网穿透背后的基础问题:

  • 公网服务端如何同时维护外部用户连接和内网客户端控制通道。
  • 客户端断线后如何重连,并恢复可用状态。
  • 多个代理端口如何注册、校验和分发。
  • 数据转发时如何把一组外部连接和一组内网连接绑定起来。
  • 长连接系统里心跳、断开、异常关闭应该如何处理。

项目拆成 pierce-serverpierce-clientpierce-common 三部分。Server 负责公网入口和连接调度,Client 负责连接 Server 并访问本地服务,Common 放共享消息体和协议常量。

核心转发链路

Pierce 的主流程可以理解成两条通道协作:

  1. Client 启动后,先和 Server 建立控制通道。
  2. Client 通过控制通道向 Server 注册一个或多个远程端口。
  3. 外部用户连接 Server 的远程端口时,Server 生成一次连接上下文。
  4. Server 通知 Client 为这次外部连接建立对应的本地连接。
  5. Client 连接本地服务后,两端通过连接标识转发字节数据。
  6. 任意一侧断开时,另一侧收到断开消息并清理连接资源。

这种设计把“控制消息”和“真实数据转发”放在同一个 Netty 事件模型里处理,代码更容易观察完整链路,也方便在日志里复盘连接生命周期。

实现重点

控制通道

Client 和 Server 之间保持一条长连接,用于认证、端口注册、心跳、连接建立和连接关闭通知。这个通道是整个系统的调度入口,如果它断开,Server 侧对应的代理端口就不应该继续被视为稳定可用。

连接映射

当外部用户访问远程端口时,Server 需要知道这个端口归哪个 Client 负责,并为这次访问生成连接标识。后续数据包都依赖这个标识找到对应的 Channel,避免多个用户连接互相串流。

心跳与重连

长连接项目不能只处理“正常请求”。Pierce 加入了心跳保活和 Client 断线重连,用来处理网络抖动、服务重启和连接半开等常见情况。这里的实现偏学习性质,但已经能覆盖基本的断线恢复流程。

端口冲突处理

远程端口是公网入口,不能被多个 Client 随意抢占。Server 在注册代理端口时会做冲突判断,避免后注册的 Client 覆盖已有映射。

技术取舍

当前版本为了把链路跑通,协议层使用 Java 对象序列化承载控制消息和转发数据。这让早期开发更直接,但也带来安全、性能和跨语言扩展问题。如果继续演进,应该替换为 Protobuf 或自定义二进制协议。

传输层目前仍是明文 TCP,没有引入 TLS 或应用层加密。对于学习 Netty 和连接模型来说这足够清晰,但真实环境需要补齐鉴权、加密、限流、审计和更完整的异常处理。

项目也没有做复杂的控制台和持久化配置,主要通过服务端、客户端进程和配置文件完成演示。这让代码更轻,但不适合直接当作生产工具维护大量节点。

当前边界

  • 只支持 TCP 转发,暂不支持 UDP。
  • 鉴权机制仍然很轻,不能抵御恶意 Client 接入。
  • 协议格式不适合跨语言客户端。
  • 缺少流量统计、连接观测和管理后台。
  • 断线恢复能覆盖基础场景,但还没有做到完整的会话续传。

后续方向

后续如果继续完善,我会优先处理这些点:

  • 用 Protobuf 或自定义二进制协议替换 Java 序列化。
  • 增加 Token 鉴权和传输加密。
  • 补充连接指标、端口状态和流量统计。
  • 改进异常关闭、半开连接和资源回收策略。
  • 探索 UDP 转发和更灵活的多客户端管理。