Skip to content

refactor(tcp-forward): 重构 TCP 转发代码,让转发工作更加稳健 - #3593

Merged
tangge233 merged 4 commits into
devfrom
refactor/tcp-forward
Sep 3, 2026
Merged

refactor(tcp-forward): 重构 TCP 转发代码,让转发工作更加稳健#3593
tangge233 merged 4 commits into
devfrom
refactor/tcp-forward

Conversation

@tangge233

@tangge233 tangge233 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Sourcery 摘要

围绕可配置的工作器重构 TCP 转发,以改进连接管理、关闭可靠性和大厅端口转发集成。

新功能:

  • 引入可配置的 TCP 转发构建器和工作器,支持生命周期控制、连接数限制、缓冲区大小配置和端点报告。

错误修复:

  • 改进 TCP 套接字关闭处理,并确保动态分配的本地端口能够可靠地提供给调用方。

增强功能:

  • 使用更健壮的异步转发模型替换之前的 TCP 转发实现,并更新大厅集成以使用该模型。
  • 更新网络组件,以一致地使用优雅的套接字关闭方式。

维护工作:

  • 移除已废弃的 TcpForward 实现。
Original summary in English

Sourcery 总结

以可配置且具备生命周期管理能力的 TCP 转发工作器替换旧实现,提升连接管理、关闭可靠性及大厅端口转发集成。

新功能:

  • 引入可配置的 TCP 转发构建器和工作器,支持连接生命周期管理、并发连接限制、缓冲区配置及本地端点报告。

错误修复:

  • 确保动态分配的监听端口能够可靠地提供给调用方,并改进套接字关闭处理。

增强功能:

  • 将大厅端口转发集成迁移到新的 TCP 转发工作器,并统一网络组件的优雅关闭方式。

杂项:

  • 移除已废弃的 TcpForward 实现。
Original summary in English

Sourcery 摘要

用可配置、受生命周期管理的工作线程替换旧版 TCP 转发实现,使转发和大厅集成更加可靠。

新功能:

  • 引入可配置的 TCP 转发构建器和工作线程,支持生命周期控制、连接数限制、缓冲区大小配置以及本地端点报告。

错误修复:

  • 改进转发连接的关闭处理,并可靠地公开动态分配的监听端口。

增强功能:

  • 将大厅端口转发迁移到基于新工作线程的实现,并统一网络组件中套接字的优雅关闭方式。

日常维护:

  • 移除已弃用的 TcpForward 实现。
Original summary in English

Summary by Sourcery

Replace the legacy TCP forwarding implementation with a configurable, lifecycle-managed worker to make forwarding and lobby integration more reliable.

New Features:

  • Introduce a configurable TCP forwarding builder and worker with lifecycle control, connection limits, buffer sizing, and local endpoint reporting.

Bug Fixes:

  • Improve forwarded connection shutdown handling and reliably expose dynamically assigned listening ports.

Enhancements:

  • Migrate lobby port forwarding to the new worker-based implementation and standardize graceful socket closure across network components.

Chores:

  • Remove the deprecated TcpForward implementation.
Original summary in English

@chatgpt-codex-connector

This comment was marked as low quality.

@pcl-ce-automation pcl-ce-automation Bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 size: XL PR 大小评估:超大型 labels Sep 2, 2026
@sourcery-ai

sourcery-ai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

审查者指南

该 PR 删除旧的 TcpForward 实现,改用基于 Builder/Config/Worker 的异步 TCP 转发架构,增加并发限制、缓冲区配置和生命周期管理,同时统一 socket 关闭逻辑并迁移 Lobby 相关调用方。

有界双向 TCP 转发的时序图

sequenceDiagram
    participant Client
    participant Worker as TcpForwardWorker
    participant Target as RemoteServer

    Client->>Worker: AcceptAsync
    Worker->>Worker: WaitAsync(0)
    alt connection slot available
        Worker->>Target: ConnectAsync
        par client to target
            Worker->>Target: ReceiveAsync / SendAsync
        and target to client
            Target-->>Worker: ReceiveAsync / SendAsync
        end
        Worker->>Worker: CancelAsync
        Worker->>Client: CloseGracefully
        Worker->>Target: CloseGracefully
    else maximum connections reached
        Worker->>Client: CloseGracefully
    end
Loading

TCP 转发器生命周期与 Lobby 集成的时序图

sequenceDiagram
    participant LobbyController
    participant NetworkHelper
    participant Builder as TcpForwardBuilder
    participant Worker as TcpForwardWorker
    participant Broadcast as BroadcastLocal

    LobbyController->>NetworkHelper: NewTcpPort()
    NetworkHelper-->>LobbyController: available port
    LobbyController->>Builder: BindLocal(port)
    LobbyController->>Builder: SetRemote(IPAddress.Loopback, port)
    LobbyController->>Builder: Build()
    Builder-->>LobbyController: TcpForwardWorker
    LobbyController->>Worker: Start()
    LobbyController->>Broadcast: Start()
Loading

文件级变更

变更 详情 文件
重构 TCP 转发为可配置、可生命周期管理的 Builder/Worker 架构。
  • 新增配置对象和链式 Builder,支持本地端口、远端地址、缓冲区大小及最大连接数配置。
  • Worker 负责监听、接受连接、限制并发连接数,并为每条连接建立目标 socket。
  • 使用两个异步数据转发任务实现双向 relay,在任一方向结束后取消另一方向并统一关闭 socket。
  • 支持 Start/Stop/Dispose 生命周期控制、动态分配本地端口和活动连接数查询。
PCL.Core/IO/Net/SocketForward/TcpForwardBuilder.cs
PCL.Core/IO/Net/SocketForward/TcpForwardConfig.cs
PCL.Core/IO/Net/SocketForward/TcpForwardWorker.cs
PCL.Core/IO/Net/TcpForward.cs
统一并强化 socket 关闭和临时 TCP 端口分配逻辑。
  • 以 Socket 绑定回环地址和端口 0 获取临时端口,并通过 using 自动释放。
  • 将 SafeClose 重命名为 CloseGracefully,分别处理未连接 socket、Shutdown 和 Close 异常。
PCL.Core/IO/Net/NetworkHelper.cs
PCL.Core/IO/Net/SocketExtension.cs
迁移现有调用方到新的 TCP 转发 API。
  • LobbyController 改用 TcpForwardBuilder 创建并启动转发 worker。
  • LobbyInfoProvider 更新转发实例类型。
  • BroadcastLocal 改用新的 socket 关闭扩展。
PCL.Core/Link/Lobby/LobbyController.cs
PCL.Core/Link/Lobby/LobbyInfoProvider.cs
PCL.Core/Link/BroadcastLocal.cs

可能关联的问题

  • #重构/占位: PR 直接重构 Network 模块中的 TCP 转发及 Socket 相关实现,属于该占位 issue 范围。

提示和命令

与 Sourcery 交互

  • 触发新的审查: 在拉取请求中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 回复审查评论,请 Sourcery 根据该评论创建 issue。你也可以回复审查评论并使用 @sourcery-ai issue,以根据该评论创建 issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在拉取请求中评论 @sourcery-ai title,以随时生成或重新生成标题。
  • 生成拉取请求摘要: 在拉取请求正文的任意位置写入 @sourcery-ai summary,即可在指定位置随时生成 PR 摘要。你也可以在拉取请求中评论 @sourcery-ai summary,以随时生成或重新生成摘要。
  • 生成审查者指南: 在拉取请求中评论 @sourcery-ai guide,即可随时生成或重新生成审查者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理完所有评论并且不想再看到它们,这一功能会很有用。
  • 忽略所有 Sourcery 审查: 在拉取请求中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。如果你想从新的审查开始,这一功能尤其有用——别忘了评论 @sourcery-ai review 以触发新的审查!

自定义使用体验

访问你的控制面板即可:

  • 启用或禁用审查功能,例如 Sourcery 生成的拉取请求摘要、审查者指南等。
  • 更改审查语言。
  • 添加、删除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

该 PR 删除旧的 TcpForward 实现,改用基于 Builder/Config/Worker 的异步 TCP 转发架构,增加并发限制、缓冲区配置和生命周期管理,同时统一 socket 关闭逻辑并迁移 Lobby 相关调用方。

Sequence diagram for bounded bidirectional TCP forwarding

sequenceDiagram
    participant Client
    participant Worker as TcpForwardWorker
    participant Target as RemoteServer

    Client->>Worker: AcceptAsync
    Worker->>Worker: WaitAsync(0)
    alt connection slot available
        Worker->>Target: ConnectAsync
        par client to target
            Worker->>Target: ReceiveAsync / SendAsync
        and target to client
            Target-->>Worker: ReceiveAsync / SendAsync
        end
        Worker->>Worker: CancelAsync
        Worker->>Client: CloseGracefully
        Worker->>Target: CloseGracefully
    else maximum connections reached
        Worker->>Client: CloseGracefully
    end
Loading

Sequence diagram for TCP forwarder lifecycle and lobby integration

sequenceDiagram
    participant LobbyController
    participant NetworkHelper
    participant Builder as TcpForwardBuilder
    participant Worker as TcpForwardWorker
    participant Broadcast as BroadcastLocal

    LobbyController->>NetworkHelper: NewTcpPort()
    NetworkHelper-->>LobbyController: available port
    LobbyController->>Builder: BindLocal(port)
    LobbyController->>Builder: SetRemote(IPAddress.Loopback, port)
    LobbyController->>Builder: Build()
    Builder-->>LobbyController: TcpForwardWorker
    LobbyController->>Worker: Start()
    LobbyController->>Broadcast: Start()
Loading

File-Level Changes

Change Details Files
重构 TCP 转发为可配置、可生命周期管理的 Builder/Worker 架构。
  • 新增配置对象和链式 Builder,支持本地端口、远端地址、缓冲区大小及最大连接数配置。
  • Worker 负责监听、接受连接、限制并发连接数,并为每条连接建立目标 socket。
  • 使用两个异步数据转发任务实现双向 relay,在任一方向结束后取消另一方向并统一关闭 socket。
  • 支持 Start/Stop/Dispose 生命周期控制、动态分配本地端口和活动连接数查询。
PCL.Core/IO/Net/SocketForward/TcpForwardBuilder.cs
PCL.Core/IO/Net/SocketForward/TcpForwardConfig.cs
PCL.Core/IO/Net/SocketForward/TcpForwardWorker.cs
PCL.Core/IO/Net/TcpForward.cs
统一并强化 socket 关闭和临时 TCP 端口分配逻辑。
  • 以 Socket 绑定回环地址和端口 0 获取临时端口,并通过 using 自动释放。
  • 将 SafeClose 重命名为 CloseGracefully,分别处理未连接 socket、Shutdown 和 Close 异常。
PCL.Core/IO/Net/NetworkHelper.cs
PCL.Core/IO/Net/SocketExtension.cs
迁移现有调用方到新的 TCP 转发 API。
  • LobbyController 改用 TcpForwardBuilder 创建并启动转发 worker。
  • LobbyInfoProvider 更新转发实例类型。
  • BroadcastLocal 改用新的 socket 关闭扩展。
PCL.Core/Link/Lobby/LobbyController.cs
PCL.Core/Link/Lobby/LobbyInfoProvider.cs
PCL.Core/Link/BroadcastLocal.cs

Possibly linked issues

  • #重构/占位: PR 直接重构 Network 模块中的 TCP 转发及 Socket 相关实现,属于该占位 issue 范围。

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嘿——我发现了 3 个问题

面向 AI 代理的提示
请处理本次代码审查中的评论:

## 个别评论

### 评论 1
<location path="PCL.Core/Link/Lobby/LobbyController.cs" line_range="99" />
<code_context>
+            var tcpPortForForward = NetworkHelper.NewTcpPort();
+            McForward = new TcpForwardBuilder()
+                .BindLocal((ushort)tcpPortForForward)
+                .SetRemote(IPAddress.Loopback, (ushort)tcpPortForForward)
+                .Build();
             McForward.Start();
</code_context>
<issue_to_address>
**issue (bug_risk):** 转发工作器被配置为连接到 `127.0.0.1:tcpPortForForward`,也就是工作器自身的监听端口,而不是 EasyTier 转发的 Minecraft 端口 `localPort`。因此,每个被接受的连接都会回环到转发监听器,直到达到连接数限制,而 Minecraft 流量永远无法到达目标。

**触发条件:** 启动大厅并且客户端通过公布的本地转发端口连接时。

**建议修复:**`localPort` 传递给 `SetRemote`,同时仅将 `tcpPortForForward` 保留用于本地绑定和广播端口。
</issue_to_address>

### 评论 2
<location path="PCL.Core/Link/Lobby/LobbyInfoProvider.cs" line_range="21" />
<code_context>
     public static int ProtocolVersion { get; set; } = 6;

     public static BroadcastLocal? McBroadcast { get; internal set; }
-    public static TcpForward? McForward { get; internal set; }
+    public static TcpForwardWorker? McForward { get; internal set; }

</code_context>
<issue_to_address>
**issue (bug_risk):**`McForward` 更改为 `TcpForwardWorker` 会破坏 `PageToolsGameLink.xaml.cs` 中现有的调用方,因为该调用方访问 `LobbyInfoProvider.McForward.LocalPort``TcpForwardWorker` 提供了 `LocalEndPoint`,但没有 `LocalPort` 属性,因此项目将无法编译。

**建议修复:**`TcpForwardWorker` 上公开兼容的 `LocalPort` 属性,或者更新调用方,使其使用 `LocalEndPoint?.Port` 并进行适当的空值处理。
</issue_to_address>

### 评论 3
<location path="PCL.Core/IO/Net/SocketForward/TcpForwardWorker.cs" line_range="177" />
<code_context>
+                var bytesRead = await source.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false);
+                if (bytesRead == 0) break; // 连接已关闭
+
+                await destination.SendAsync(buffer[..bytesRead], SocketFlags.None, cancellationToken).ConfigureAwait(false);
+            }
+        }
</code_context>
<issue_to_address>
**issue (bug_risk):** 转发循环假设一次 `SendAsync` 调用就能发送接收到的整个缓冲区。套接字发送可能只完成部分操作,因此未发送的后缀会被丢弃,TCP 流会被无提示地截断或损坏。

**触发条件:** 目标套接字在一次发送操作中接收的字节数少于请求发送的字节数时。

**建议修复:** 循环发送,直到全部 `bytesRead` 个字节都已发送;每次根据 `SendAsync` 返回的字节数推进缓冲区切片。
</issue_to_address>

Sourcery 对开源项目免费——如果您喜欢我们的审查结果,请考虑分享给他人 ✨
帮助我变得更有用!请对每条评论点击 👍 或 👎,我会利用反馈来改进审查结果。
Original comment in English

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="PCL.Core/Link/Lobby/LobbyController.cs" line_range="99" />
<code_context>
+            var tcpPortForForward = NetworkHelper.NewTcpPort();
+            McForward = new TcpForwardBuilder()
+                .BindLocal((ushort)tcpPortForForward)
+                .SetRemote(IPAddress.Loopback, (ushort)tcpPortForForward)
+                .Build();
             McForward.Start();
</code_context>
<issue_to_address>
**issue (bug_risk):** The forwarding worker is configured to connect to `127.0.0.1:tcpPortForForward`, which is the worker's own listening port, instead of the EasyTier-forwarded Minecraft port `localPort`. Every accepted connection therefore loops back into the forwarding listener until the connection limit is reached, and Minecraft traffic is never reached.

**Triggers:** When a lobby is launched and a client connects through the advertised local forwarding port.

**Suggested fix:** Pass `localPort` to `SetRemote` while retaining `tcpPortForForward` only as the local bind and broadcast port.
</issue_to_address>

### Comment 2
<location path="PCL.Core/Link/Lobby/LobbyInfoProvider.cs" line_range="21" />
<code_context>
     public static int ProtocolVersion { get; set; } = 6;

     public static BroadcastLocal? McBroadcast { get; internal set; }
-    public static TcpForward? McForward { get; internal set; }
+    public static TcpForwardWorker? McForward { get; internal set; }

</code_context>
<issue_to_address>
**issue (bug_risk):** Changing `McForward` to `TcpForwardWorker` breaks the existing caller in `PageToolsGameLink.xaml.cs`, which accesses `LobbyInfoProvider.McForward.LocalPort`; `TcpForwardWorker` exposes `LocalEndPoint` but has no `LocalPort` property, so the project fails to compile.

**Suggested fix:** Expose a compatible `LocalPort` property on `TcpForwardWorker`, or update the caller to use `LocalEndPoint?.Port` with appropriate null handling.
</issue_to_address>

### Comment 3
<location path="PCL.Core/IO/Net/SocketForward/TcpForwardWorker.cs" line_range="177" />
<code_context>
+                var bytesRead = await source.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false);
+                if (bytesRead == 0) break; // 连接已关闭
+
+                await destination.SendAsync(buffer[..bytesRead], SocketFlags.None, cancellationToken).ConfigureAwait(false);
+            }
+        }
</code_context>
<issue_to_address>
**issue (bug_risk):** The forwarding loop assumes one `SendAsync` call transmits the entire received buffer. Socket sends are allowed to complete partially, so the unsent suffix is discarded and the TCP stream is silently truncated or corrupted.

**Triggers:** When the destination socket accepts fewer bytes than requested in a send operation.

**Suggested fix:** Loop until all `bytesRead` bytes have been sent, advancing the buffer slice by the number of bytes returned from each `SendAsync` call.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread PCL.Core/Link/Lobby/LobbyController.cs Outdated
Comment thread PCL.Core/Link/Lobby/LobbyInfoProvider.cs
Comment thread PCL.Core/IO/Net/SocketForward/TcpForwardWorker.cs Outdated
@pcl-ce-automation pcl-ce-automation Bot added 🕑 等待合并 已处理完毕,正在等待代码合并入主分支 and removed 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 labels Sep 3, 2026
@tangge233
tangge233 merged commit a89aabe into dev Sep 3, 2026
3 checks passed
@pcl-ce-automation pcl-ce-automation Bot added 👌 完成 相关问题已修复或功能已实现,计划在下次版本更新时正式上线 and removed 🕑 等待合并 已处理完毕,正在等待代码合并入主分支 labels Sep 3, 2026
@tangge233
tangge233 deleted the refactor/tcp-forward branch September 3, 2026 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: XL PR 大小评估:超大型 👌 完成 相关问题已修复或功能已实现,计划在下次版本更新时正式上线

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants