docs(kb): 流量记账口径知识库 — 连接页实时(含直连) vs 统计页累计(仅代理)

记录两处上下行数字口径差异:连接页来自内核 Clash API 全局吞吐含直连,
统计页来自节点 v2ray_api per-user 计数仅代理;差额=直连,设计使然。
含对账口径警示。登记进 docs/index.html 知识库分类。决策:保持现状不改连接页口径。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-29 21:04:50 +08:00
parent 2b20a611d7
commit 23bf3077c8
2 changed files with 121 additions and 0 deletions
+5
View File
@@ -108,6 +108,11 @@
<div class="d">逐功能盘点验证五端是否正确:A 共享逻辑+后端(控制面 21 包/数据面渲染+记账/Flutter 共享),每行「现有能力→已覆盖+怎么覆盖→欠缺→只能人工」;B 平台隧道层五端矩阵 + 每端真连通验收清单(出口IP变化/DNS劫持/分流/记账对账/KillSwitch/切节点)。配套 test-architecture.html 的「逐功能」视角。</div>
<div class="path">docs/feature-test-coverage-checklist.html</div>
</a>
<a class="doc" href="traffic-accounting-scopes.html">
<div class="t">流量记账口径 · 连接页实时 vs 统计页累计 <span class="tag html">HTML</span></div>
<div class="d">客户端两处"上传/下载"为何对不上:连接页实时来自内核 Clash API 全局吞吐(/traffic+/connections)<b>含直连</b>;统计页累计来自服务端节点 v2ray_api per-user 计数<b>仅代理</b>。差额=直连流量(国内站/分流),设计使然非 bug;附对账口径警示(不能拿连接页含直连的数去对节点计数)。</div>
<div class="path">docs/traffic-accounting-scopes.html</div>
</a>
<a class="doc" href="killswitch-design.html">
<div class="t">KillSwitch 设计与跨平台能力矩阵 <span class="tag html">HTML</span></div>
<div class="d">断网保护 L0–L3 分级模型 + 各平台能力天花板 / 当前实现矩阵。KillSwitch 决策依据。</div>
+116
View File
@@ -0,0 +1,116 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pangolin 流量记账口径 · 连接页实时 vs 统计页累计</title>
<style>
:root{
--bg:#0f1117; --panel:#171a22; --panel2:#1d2129; --fg:#e6e8ee; --fg2:#a8afbd;
--accent:#e0884f; --accent2:#5fb0c9; --ok:#5ec27a; --bad:#e06a6a; --warn:#e0b84f;
--border:#272c36; --mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
--sans:-apple-system,"PingFang SC","Helvetica Neue",Arial,sans-serif;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.7;font-size:15px}
.wrap{max-width:920px;margin:0 auto;padding:48px 24px 96px}
h1{font-size:30px;line-height:1.3;margin:0 0 8px;letter-spacing:-.01em}
.sub{color:var(--fg2);font-size:15px;margin:0 0 28px}
h2{font-size:21px;margin:44px 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border)}
h3{font-size:16.5px;margin:24px 0 8px;color:var(--accent)}
p{margin:10px 0}
code{font-family:var(--mono);font-size:.88em;background:var(--panel2);padding:1px 6px;border-radius:5px;color:#f0d9c4}
a{color:var(--accent2);text-decoration:none}
a:hover{text-decoration:underline}
ul{margin:8px 0;padding-left:22px}
li{margin:5px 0}
.lead{background:linear-gradient(180deg,rgba(224,136,79,.10),transparent);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:0 0 8px}
.small{color:var(--fg2);font-size:13px}
.note{color:var(--fg2);font-size:13px;border-left:3px solid var(--border);padding-left:12px;margin:10px 0}
table{width:100%;border-collapse:collapse;margin:14px 0;font-size:13.5px}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left;vertical-align:top}
th{background:var(--panel2);color:var(--fg);font-weight:600}
td{color:var(--fg2)}
td b,td strong{color:var(--fg)}
.tag{display:inline-block;font-size:11px;font-weight:700;padding:1px 8px;border-radius:999px;white-space:nowrap}
.t-ok{background:rgba(94,194,122,.16);color:var(--ok)}
.t-warn{background:rgba(224,184,79,.16);color:var(--warn)}
.t-bad{background:rgba(224,106,106,.16);color:var(--bad)}
.yes{color:var(--ok);font-weight:700}
.no{color:var(--bad);font-weight:700}
.xref{display:inline-block;margin-top:6px;font-size:13px}
</style>
</head>
<body>
<div class="wrap">
<h1>流量记账口径 · 连接页实时 vs 统计页累计</h1>
<p class="sub">两个上下行数字来自不同源、统计的是不同范围——连接页含直连,统计页只算代理</p>
<div class="lead">
<strong>一句话:</strong>客户端有两处"上传/下载"数字,<b>口径不同、天然对不上</b>
<b>连接页实时速率</b>来自本地内核的全局吞吐,<b>含直连</b><b>统计页累计用量</b>来自服务端按用户的节点计数,<b>只含真正经过节点的代理流量</b>。两者差额 = 直连流量(国内站 / 分流 direct / LAN)。这是<b>设计使然</b>,非 bug:计费/配额按代理流量算。
</div>
<h2>两个数据源</h2>
<table>
<tr><th>维度</th><th>连接页 · 实时上下行</th><th>统计页 · 累计用量</th></tr>
<tr>
<td><b>取数源</b></td>
<td>客户端<b>内核 Clash API</b><br><code>GET /traffic</code>SSE 推 <code>{up,down}</code> bytes/s+ <code>GET /connections</code><code>downloadTotal/uploadTotal</code> 差分算瞬时速率)</td>
<td><b>服务端</b> <code>GET /v1/usage</code> ← 节点 sing-box <b>v2ray_api StatsService</b> 的 per-user 计数器</td>
</tr>
<tr>
<td><b>计数粒度</b></td>
<td>sing-box <b>引擎全局总量</b>(不分出站,所有连接合计)</td>
<td>每用户独立计数器 <code>user&gt;&gt;&gt;{dp_uuid}&gt;&gt;&gt;traffic&gt;&gt;&gt;uplink|downlink</code></td>
</tr>
<tr>
<td><b>测量位置</b></td>
<td>客户端本机(TUN 入口处的总吞吐)</td>
<td>节点上(只有流量真正到达节点才被计)</td>
</tr>
<tr>
<td><b>含直连流量?</b></td>
<td class="yes">含 ✅</td>
<td class="no">不含 ❌</td>
</tr>
<tr>
<td><b>语义</b></td>
<td>"这台设备此刻的总网速"(代理 + 直连)</td>
<td>"通过节点代理出去、计费口径的用量"</td>
</tr>
</table>
<h2>为什么统计页不含直连</h2>
<p>全局 TUN 模式下,客户端 sing-box 按路由规则把流量分成两类:</p>
<ul>
<li><b>代理(proxy 出站)</b>:经 REALITY 数据口送到节点 → 节点 sing-box 处理 → 节点的 v2ray_api per-user 计数器<b>能看到、会计数</b></li>
<li><b>直连(direct 出站)</b>:走本地物理网卡直接出网(国内站 split-CN、LAN、被判 direct 的域名)→ <b>根本不碰节点</b> → 节点计数器无从得知。</li>
</ul>
<p>所以服务端记账(统计页)<b>物理上只能看到代理流量</b>。直连那部分对节点是不可见的。</p>
<h2>结论与含义</h2>
<ul>
<li>恒有 <b>连接页实时(含直连)≥ 统计页累计(仅代理)</b></li>
<li>对国内用户尤其明显:刷国内站 / 看视频走直连时,连接页速率飙升,统计页却不动(没走节点、不计费)。</li>
<li><b>合理且符合预期</b>:配额 / 计费就该按代理流量算,直连不该计费。统计页口径正确。</li>
<li><b>已知 UX 副作用</b>:连接页实时速率含直连,可能让用户误解"下了这么多怎么没扣量"。</li>
</ul>
<h2>现状决策(2026-06-29</h2>
<p><b>保持现状,不改连接页口径。</b>连接页继续显示"总吞吐"语义(含直连)——与多数客户端一致;统计页继续"仅代理"。两者口径差异由本文档说明,不视为缺陷。</p>
<div class="note">
若未来要让两者口径一致:连接页可改为<b>仅累加走 <code>proxy</code> 出站的连接字节</b>——<code>/connections</code> 每条连接带 <code>chains</code>/outbound 信息可分类求和;全局 SSE <code>/traffic</code> 给不了出站维度,需改用 <code>/connections</code> 轮询差分。当前未做。
</div>
<h2>相关代码 / 文档</h2>
<ul>
<li>连接页速率:<code>client/lib/screens/connect_page.dart</code><code>stats.downloadSpeed/uploadSpeed</code>)← <code>client/lib/bridge/kernel_process.dart</code><code>/traffic</code><code>/connections</code></li>
<li>统计页用量:<code>client/lib/screens/stats_page.dart</code><code>GET /v1/usage</code><code>server/internal/usage/*</code>)← agent 读 v2ray_api<code>server/internal/agentd/render.go</code> · <code>parseUserStat</code></li>
<li>对账验收:<a href="feature-test-coverage-checklist.html">功能 × 测试覆盖清单</a> §A.2「记账对账」——对账时须按本文口径,<b>客户端统计页 vs 节点计数对的是"代理流量",不能拿连接页实时(含直连)去对</b></li>
</ul>
</div>
</body>
</html>