隧道把一种通信封装到另一种通信中。选型先回答“究竟需要传送什么”:一台不能改造设备的 Ethernet 帧、一整个网段的 IP 包,还是某个服务的一条 TCP 连接。这些需求对应的广播范围、路由行为和权限边界不同,不能只按产品性能排序。
先确定要搬运的对象,再选择封装和保护方式
二层帧
需要 MAC、广播域等语义
VXLAN / Geneve
三层包
连接地址或网段
IPIP / GRE / WireGuard
服务连接
只开放指定 TCP/UDP 服务
rathole / frp
隧道层次与加密是不同维度;VXLAN 可叠加加密,端口转发也可承载端到端 TLS。
需求 先考虑的能力 容易额外引入的责任
两个可信网络间搬运 IP 包 IPIP、GRE 等封装 可达性、过滤、MTU,必要时另加加密
通过不可信网络连接网段 WireGuard、IPsec 等安全隧道 密钥、身份、路由与回程
自动管理设备身份和多变网络 Tailscale 一类覆盖网络平台 控制面、策略传播、中继依赖
在三层网络中构建二层网络 VXLAN、Geneve MAC/邻居学习、BUM、控制面
从外部访问少量内网服务 反向代理或端口转发 公网入口权限、代理信任、服务恢复
“可部署”还要考虑现有设备是否支持、运维能否管理密钥与路由、出口是否允许所需传输。设备支持 IPsec 卸载并不证明指定算法、封装和流量已经走上卸载路径。
IPIP 与 GRE:最简单的 IP 封装不是同一个协议
IPv4-in-IPv4 直接在原 IPv4 包前增加新的 IPv4 头,外层 Protocol 为 4;无选项时新增 20 B。GRE 使用协议号 47,另有基础 4 B GRE 头,其中 Protocol Type 指示被封装的协议。因此 GRE 更通用,但不能称为“没有额外头的 IPIP”。IP-in-IP RFC 2003 、GRE RFC 2784
GRE 可按相应扩展携带 Key、序号或校验等字段,长度不应固定为 4 B。Key 可用于区分逻辑隧道,不能作为身份认证凭据;攻击者能构造相同 Key,并不需要知道任何加密私钥。基础 IPIP 与 GRE 都不提供业务内容机密性。
假设内层是 1400 B IPv4 包:无选项的 IPv4-in-IPv4 为 1420 B,基础 GRE over IPv4 为 1424 B。这里不含外层 Ethernet 头,也未增加加密。把这个数字与“线上占用字节”比较时,需要补齐同一层的口径。
VXLAN 与 Geneve:隧道本身不是完整虚拟网络控制面
VXLAN 把内层 Ethernet 帧封装到 UDP,基础 VXLAN 头为 8 B,VNI 为 24 位。VNI 表示逻辑网络,不是加密密钥;相同 VNI 的误接入仍需要底层隔离和入口验证防范。UDP 源端口可以为外层 ECMP 提供流量散列信息,不等于每个包都应随机换路径。VXLAN RFC 7348
Geneve 的基础头也是 8 B,支持可变长选项,适合携带网络虚拟化元数据;计算开销时必须加上实际选项长度。遇到无法理解的关键选项时,处理规则会影响能否接收,不能把选项当作对所有端点都透明的装饰。Geneve RFC 8926
封装只说明包怎么搬运,不说明远端 MAC 在哪里、未知单播应该发给谁、广播怎样复制。学习、静态配置或 EVPN 等控制方式承担这些问题。二层扩展把广播与故障影响带到更多位置;若应用只需单播 IP 互访,应先判断是否真的依赖同一广播域。
为什么 VXLAN 不要求 MTU 一定是 9000
以无 VLAN、内层帧不携带 FCS、外层 IPv4 无选项为例:一个 1500 B 内层 IP 包加内层 Ethernet 14 B、VXLAN 8 B、UDP 8 B、外层 IP 20 B,共形成 1550 B 外层 IP 包 。需要的是路径支持这个尺寸,9000 只是可能的网络配置之一;也可以降低内层 MTU,不能把任意分片当作无代价补救。
inner_ip = 1500
encapsulation = {
' IPIP/IPv4' : 20 ,
' GRE-basic/IPv4' : 20 + 4 ,
' VXLAN/IPv4' : 20 + 8 + 8 + 14 ,
' VXLAN/IPv6' : 40 + 8 + 8 + 14 ,
' Geneve/IPv4+16B-options' : 20 + 8 + 8 + 16 + 14 ,
}
assert inner_ip + encapsulation [ ' VXLAN/IPv4' ] == 1550
assert 1500 - encapsulation [ ' VXLAN/IPv4' ] == 1450
assert 1500 - encapsulation [ ' VXLAN/IPv6' ] == 1430
assert 1500 - encapsulation [ ' Geneve/IPv4+16B-options' ] == 1434
assert 1550 + 14 + 4 == 1568
print ( ' 统一按外层 IP 长度计算的封装预算通过' )
内层 VLAN、额外加密或第二层隧道都继续占用预算。TCP MSS 只影响 TCP 数据段;降低 MSS 不能自动解决大 UDP 数据报,也不能替代所有路径的 PMTU 处理。
1500字节内层包,外面到底装不装得下?
先比较VXLAN的IPv4与IPv6外层,内层上限相差20 B。再选ESP,把内层长度从1438增加到1439,外层不是只增加1 B,而是因对齐从1500跳到1504。预算必须包含这种离散变化。
正在呈现知识画面 重新加载
1500字节内层包,外面到底装不装得下? 各层头部必须从同一预算中逐层扣除。VXLAN还带内层Ethernet头;ESP的对齐会让总长按台阶增长。
{"id":"tunnel-layer-budget","title":"1500字节内层包,外面到底装不装得下?","summary":"各层头部必须从同一预算中逐层扣除。VXLAN还带内层Ethernet头;ESP的对齐会让总长按台阶增长。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"presets\" role=\"group\" data-i18n-label=\"mode\"><button data-mode=\"gre\" data-i18n=\"gre\"></button><button data-mode=\"vx\" data-i18n=\"vx\"></button><button data-mode=\"esp\" data-i18n=\"esp\"></button></div><label class=\"form-field\"><span data-i18n=\"ip\"></span><select id=\"ip\"><option value=\"20\">IPv4 (20 B)</option><option value=\"40\">IPv6 (40 B)</option></select></label><div class=\"control\"><label for=\"inner\" data-i18n=\"inner\"></label><output for=\"inner\" id=\"inner-value\"></output><input id=\"inner\" type=\"range\" min=\"40\" max=\"1600\" value=\"1500\" step=\"1\"></div><div class=\"control\"><label for=\"mtu\" data-i18n=\"mtu\"></label><output for=\"mtu\" id=\"mtu-value\"></output><input id=\"mtu\" type=\"range\" min=\"1280\" max=\"1600\" value=\"1500\" step=\"1\"></div><div id=\"packet\" class=\"budget\"></div><div class=\"metrics\"><div><span data-i18n=\"total\"></span><strong id=\"total\"></strong></div><div><span data-i18n=\"ceiling\"></span><strong id=\"ceiling\"></strong></div></div><p id=\"equation\" class=\"formula\"></p><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px var(--viz-font-ui);padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 var(--viz-font-ui);overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}let mode='vx';function size(n){const ip=+$('#ip').value;return mode==='gre'?n+ip+4:mode==='vx'?n+ip+30:n+ip+8+8+8+2+16+((4-(n+2)%4)%4)}function draw(){const n=+$('#inner').value,mtu=+$('#mtu').value,total=size(n);let ceiling=0;for(let i=0;i<=mtu;i++)if(size(i)<=mtu)ceiling=i;set('inner-value',n);set('mtu-value',mtu);band('packet',[{name:'inner IP',value:n},{name:'encapsulation',value:total-n}],Math.max(total,mtu));set('total',total+(total<=mtu?' ✓':' ✗'));set('ceiling',ceiling);set('equation','inner '+n+' + overhead '+(total-n)+' = '+total+'\\n'+total+' ≤ MTU '+mtu+' : '+String(total<=mtu));$('#total').dataset.value=total;$('#ceiling').dataset.value=ceiling;pressed(mode);}document.querySelectorAll('input,select').forEach(e=>e.oninput=draw);document.querySelectorAll('[data-mode]').forEach(b=>b.onclick=()=>{mode=b.dataset.mode;draw()});draw();","audio":false,"strings":{"ceiling":"最大可装内层 IP(B)","esp":"ESP-GCM NAT-T","gre":"GRE","heading":"1500字节内层包,外面到底装不装得下?","inner":"内层 IP 总长(B)","intro":"选择封装、外层IP版本与路径MTU,图中统一以外层IP总长为口径。","ip":"外层 IP 版本","mode":"封装","mtu":"路径 MTU(B)","reset":"重置","scope":"无VLAN/FCS/IP选项;GRE基础4 B;VXLAN8+UDP8+内Ethernet14;ESP AES-GCM/UDP含8 B IV、16 B tag、四字节对齐。按单包预算,不模拟分片。","scopeLabel":"模型范围与约定","total":"外层 IP 总长(B)","vx":"VXLAN"}}
WireGuard、IPsec 与平台能力应分别比较
WireGuard 将 peer 公钥、AllowedIPs 与 UDP 数据平面结合,协议本身没有完整用户目录或通用自动穿透服务。IPsec/IKEv2 通过策略与 SA 保护 IP 流量,认证、算法协商和设备互操作有更丰富的配置空间。性能由实现、算法加速、包长、核数与链路共同决定,没有跨环境固定冠军。
Tailscale 在 WireGuard 之上增加身份、策略分发、发现和中继。它与裸 WireGuard 的差异不只是某次吞吐测试;比较部署成本时要算上手工维护 peer、公钥和路由的工作量,以及平台控制面的依赖。
“端到端加密”的端点必须写清:两台业务主机各自终止隧道,与两台网关之间的隧道不同。后者离开网关后可继续走明文网络;应用 TLS 可以提供更远一层的保护。
反向隧道:内网客户端主动出站,公网端接收访问
普通公网端口转发方案中,内网代理 C 主动连到公网代理 S,S 把外部连接转交给 C,再由 C 连接本地服务。它避开了直接入站访问内网的要求,但公网 S 仍需可达,C 的出站也必须被允许。这里不必成功建立访问者到内网的 UDP 打洞。
反向连接解决入口可达性,应用加密决定谁能看内容
访问者
连接公网入口
公网代理 S
监听服务端口
内网代理 C
连接本地服务
C 主动建立出站隧道到 S
转发
TLS 透传:应用 TLS 可跨 S、C,一直终止在源站
TLS 在 S 终止:S 可读应用明文,S—C 需另行保护
隧道注册 token 认证的是代理客户端;公开服务仍需自己的访问认证与授权。
rathole 与 frp 的常见部署都有 server/client 角色,不能把前者写成天然对称去中心化、后者才是集中结构。rathole 当前仓库配置支持 TCP 与 UDP 服务,并提供 TLS、Noise 等传输选择,“只支持 TCP、加密只能靠 Caddy”均不准确。rathole 项目 、传输配置
比较项 rathole frp
常见部署 公网 server 与内网 client frps 与 frpc
服务能力 按服务配置 TCP/UDP 转发 TCP/UDP、HTTP/HTTPS 等多种代理
传输保护 可配置 TLS 或 Noise 支持 TLS 等传输配置
连接复用/池 按所用版本和实现检查 文档明确支持 TCP 复用与连接池
资源评估 用自己的并发、包长、缓冲测量 使用相同负载与版本口径测量
产品支持 UDP 服务与“代理间必定原生 UDP 传输”是两回事。若 UDP 数据通过可靠流承载,丢包和队头阻塞行为会变化;若多个业务连接共享一个 TCP 传输,外层丢包恢复可能影响其他复用连接。frp 网络配置
连接池为什么不能消除所有握手
池把一部分连接准备工作移到业务请求到达之前,命中时减少等待;复用则允许多个逻辑连接共用已有传输。frp 同时支持这些机制,因此不能用“rathole 预建、frp 永远按请求新建 TCP”作为两者的根本区别。
即使 S—C 通道已准备,访问者到 S 的连接、C 到本地服务的连接,以及应用 TLS/登录步骤仍可能存在。池中连接失效时还需要检测和补充;不能把 early eof 唯一归因于 NAT 空闲超时,它也可能来自对端关闭、进程退出或协议不匹配。
例如请求需要按需建立通道时额外等待 40 ms,预备通道命中率为 90%,仅计算这一项的平均等待是 4 ms。剩余 10% 的请求仍有 40 ms 等待,不能用平均值说明尾延迟已消失。池越大还会占用更多 FD、内存和网关状态。
入口 TLS 与隧道加密:两个不同的终止位置
如果 Caddy 在 S 终止浏览器 TLS,S 能看到 HTTP 明文;启用 S—C 的 TLS/Noise 可以继续保护传输,但不会把 S 重新变成看不见业务内容的中继。如果从访问者到源站透传应用 TLS,S 只转发对应密文,源站负责证书与应用认证。
代理注册 token 用于证明 C 有权注册某个转发服务,并不会自动验证访问公网监听端口的每位用户。公开管理页面、数据库端口或文件服务时,访问控制需要在实际入口或应用处落地;只给隧道双方配一个 token 不等于该服务已私有化。
用一个完整场景做选择
假设需要让三位同事访问家庭 NAS 的管理页面,访问者可以安装客户端,且无需公开访问。首先比较设备覆盖网络方案的成员授权、客户端支持与恢复成本;无需先把管理页面开放到公网再叠加额外入口认证。
若目标变为无需安装客户端的公开演示站点,反向 HTTP 代理更符合入口形式,接着确定 TLS 终点、域名、源站身份与请求大小/超时。若目标变为连接两个有大量 IP 服务的网段,再比较网关 VPN、路由与回程。若应用必须依赖二层发现或特定 Ethernet 语义,才继续评估二层覆盖网络及广播边界。
这些判断不是产品永久排名。评估报告至少给出:数据实际经过谁、谁能解密、允许访问什么、最大有效包长、满载时的内存与尾延迟,以及控制连接中断后新旧业务各会怎样。只写“轻量、低延迟、企业级”无法据此复现或作出取舍。