切换深浅色
第20章 VLESS的传输承载方式
20.1 RAW:减少一层适配,不是绕过TCP
第19章的解析器需要一条按序交付字节的输入。最直接的办法,是让代理客户端与服务器建立一条TCP连接,在适当的安全层之上放入VLESS头和正文。不再用WebSocket消息或HTTP请求包裹这条逻辑流,就是本章所说的RAW承载。
Xray固定提交0888beb38a9ba45440557175e71ce21cf8ba3b13的配置构建器把raw和tcp映射到内部传输名tcp,rawSettings也映射到TCPConfig。因此看到内部路径transport/internet/tcp,不代表配置中的RAW是另一套未被实现的协议。反过来,也不能从名字raw推断它使用操作系统的Raw Socket。
Raw Socket是一类允许程序处理较底层报文的Socket接口,常涉及IP报文和额外权限;这里的RAW承载通常仍使用普通TCP Socket。TCP负责序号、确认、重传、接收窗口与拥塞控制,应用仍读取字节流,操作系统仍可能合并或拆分写入。它没有提供“VLESS一个头恰好占一个TCP包”的保证。
在不启用额外头部适配和其他可选包装的普通路径,几种层次可以写成:
text
RAW + TLS:
IP → TCP → TLS记录 → VLESS头与正文
RAW + REALITY:
IP → TCP → REALITY建立和管理的安全连接 → VLESS头与正文
RAW + VLESS Encryption:
IP → TCP → Encryption握手及记录 → VLESS头与正文箭头表示从外层向里解释,并非说三个安全机制的格式相同。TLS与REALITY是streamSettings安全选项中的不同路径;Encryption在VLESS处理器中建立,可以位于传输提供的连接之上。第22章再给它的精确格式。没有安全保护时,基本VLESS字节可以直接暴露;而本提交对非私有目标的VLESS出站还实施第18章已说明的安全组合检查,不能把这个层次图当成任意公网明文配置都能被接受的承诺。
源码中的RAW客户端先调用系统拨号或配置的下层拨号包装,再按设置建立TLS或REALITY,之后才返回连接给上层代理处理。普通TLS客户端路径会主动执行握手;服务端则可先把TCP连接包装成TLS连接,在后续读取时触发握手。两者函数返回时机不完全一样,但VLESS正常读取看到的是TLS解密后的应用字节。
若应用准备发往网站的第一批内容是TLS ClientHello,这份网站握手在代理层看来只是正文。RAW+TLS会产生两份不同TLS会话:外层由代理客户端和代理服务器维护,内层由浏览器和网站维护。两份会话不共享记录序号或流量密钥;RAW减少的是HTTP/消息适配层,不能自动减少这两份会话到一份。
RAW的一项工程优势是数据路径直接:无需把每批字节转换成WebSocket消息或gRPC对象;对于后面介绍的Vision,还可能满足底层连接直接复制的条件。但直接并不保证最快。不同安全处理、内核路径、流量大小与链路质量都会改变成本;一次短连接的握手时延也不能只从正文封装层数推导。
代价同样明确。普通HTTP反向代理收到解密后的RAW字节,首先看见的是VLESS版本与ID,而不是一条GET或POST,它无法按正常HTTP路由规则转发。需要支持这一传输的直连入口,或明确的四层转发机制。把端口改成443,或者让TLS协商名字看起来像网站,都不会让后续字节自动具备HTTP语法。
本节的名称映射见配置构建器,连接建立与包装顺序见RAW客户端和服务端。以下各承载继续使用同一固定提交,不由某个配置名称推断实现能力。
20.2 WebSocket:消息边界在适配层被消去
WebSocket提供双向消息,VLESS需要双向字节流。两者能够组合,是因为传输适配器规定如何把消息内容按顺序交给上层,而不是因为两种协议天然具有相同的边界。
普通路径先完成HTTP/1.1 Upgrade。开启TLS时,外层先建立TLS,Xray该WebSocket拨号器选择http/1.1;成功得到101响应后,后续字节按照WebSocket帧处理。客户端将VLESS头和后续正文作为二进制消息的内容发送。客户端帧需要按第10章规则掩码,服务器帧不掩码;掩码本身不提供保密性。
固定connection.Write调用WebSocket的WriteMessage,并指定BinaryMessage。connection.Read则取得当前消息的Reader,持续读内容,消息结束后换到下一条。它不会把消息内的EOF直接当作整条VLESS连接EOF。于是上层看到的是多条消息内容首尾拼接的字节序列。
例如一条34字节VLESS头被拆成两条二进制消息,第一条20字节,第二条14字节;第二条之后另有5字节hello消息。适配器按顺序交付后,VLESS仍看到39字节。它不会把20字节那条消息解释成一个不完整而必须立即报错的请求,也不会把第二条消息首字节重新解释为版本。实际消息如何拆分由写入、缓冲及库实现决定,这个例子用于说明可接受的映射,不承诺Xray每次都按20/14/5划分。
WebSocket自己还可能将一条消息拆成多个帧,控制帧也可插入其间。对应库负责帧级处理,传输适配器取得消息负载,VLESS解析器再恢复自己的字段。这是三个不同边界:TCP段、WebSocket帧/消息、VLESS头与正文。把某个抓包中的一次Read直接标成“一条VLESS消息”,就会跳过其中两层。
服务端先匹配配置的Host和路径,再执行Upgrade。路径是HTTP入口的路由条件,既不是VLESS目标地址,也不能代替UUID认证。/tunnel可以承载发往许多网站的连接;目标example.com:443仍在VLESS头内。HTTP Host或TLS SNI通常标识代理入口,不能直接填成每次浏览的网站名称,并期待服务端自动知道这条逻辑连接的目标。
固定实现还支持一种常被称为early data的扩展:延迟拨号器在首批Write到来时,如果整批字节不超过配置阈值,将其以无填充Base64URL放入Sec-WebSocket-Protocol请求头。服务端解码后用extraReader把这些字节放在后续WebSocket消息之前。若首批超过阈值,所读代码走普通消息发送,不是自动把前半截塞进头、后半截另发。
这里的early data是把上层首批字节随HTTP Upgrade请求携带,区别于第16章使用PSK的TLS 0-RTT;它没有因为名字相近就获得相同密钥派生或重放保证。Base64URL也只是表示转换。如果TLS在CDN终止,CDN能读取这份请求头,可能连VLESS ID和目标前缀都在其中;不能以为“它在HTTP头里所以被另外加密了”。实际是否重放、记录或过滤,还要检查中间节点和上层保护。
Upgrade通过只表示WebSocket承载建立。之后UUID无效、目标连接失败,依然可以使VLESS会话失败;101不是代理认证结果。WebSocket Close、底层TLS关闭和某个目标应用的完成条件也不相同。传输适配器可以把关闭转换成流错误,却不能凭空保留所有TCP半关闭能力。
固定提交构建配置时,对WebSocket打印建议改用XHTTP的弃用提示,但仍注册并实现该承载。这个软件维护状态与RFC6455的协议机制是两回事,不应将日志建议改写成“WebSocket规范不再支持代理”,也不能仅凭建议产生性能数字。
数据映射见connection.go,握手与首批数据见dialer.go,路径、头与extraReader见hub.go。
20.3 gRPC:一条代理连接映射到一次双向流式调用
gRPC已经建立了HTTP/2流、消息定界和服务分派。Xray在其中定义双向流式调用,把代理字节装成消息,再在对端还原为读写接口。承载选择gRPC,并不把VLESS改造成“一次访问网站对应一个普通RPC返回值”;一个调用中可以连续发送和接收许多批字节,直到逻辑连接结束。
固定stream.proto的普通消息Hunk只有字段1,类型bytes,名为data。Tun是双向消息流,另一个TunMulti使用MultiHunk,其中data为repeated bytes。因此封装层次为:
text
TCP / 外层安全连接
HTTP/2连接中的某条流
gRPC消息:压缩标志1字节 + 消息长度4字节 + Protobuf消息
Hunk.data中的VLESS字节不要把VLESS自己的Addons Protobuf与这里的Hunk Protobuf混淆。Addons只在基本头的指定位置解释Flow等字段;Hunk可以装载从版本字节开始的任意一批VLESS流数据,也可以只装正文的一小段。它是外部承载的容器。
若某次普通Hunk发送五字节hello,不启用消息压缩,则Protobuf内容为七字节:
text
0a 05 68 65 6c 6c 6f加上gRPC五字节前缀,HTTP/2 DATA负载中对应十二字节为:
text
00 00 00 00 07 | 0a 05 68 65 6c 6c 6f
标志与消息长7 Hunk消息这里四字节长度是大端,值7;不是VLESS正文长5,也不包含自身的五字节。若恰好放入一帧无Padding的DATA,该帧再加9字节头成为21字节。HTTP/2不要求这个gRPC消息必须独占一帧;拆成两帧或与其他消息同帧时,帧开销会变化。
HunkReaderWriter.Write调用Send发送Hunk,Read在当前data耗尽后再Recv下一条消息。消息边界因此对VLESS透明:一份请求头可跨多条Hunk,某条Hunk也可同时包含请求尾部和正文。TunMulti进一步把一组Buffer放入一条MultiHunk的多个data字段;接收方按顺序恢复这些内容。MultiMode的“多”在这里指消息中多段数据,不表示一条TunMulti调用无须会话编号就能混合多个独立网站连接。
独立代理逻辑连接通常各自建立一次双向调用,而这些调用可以成为同一HTTP/2连接中的不同流。固定拨号器维护gRPC ClientConn缓存,以目标和内存传输配置组成键,在状态未Shutdown时复用。ClientConn是库管理的通道对象,内部连接可以重建;缓存命中不保证此刻已有一条健康TCP连接,也不意味着任意不同配置必然共享一个底层连接。
这种复用省去了每条逻辑连接都新建外层TCP和TLS的必要,却引入共享范围。HTTP/2连接窗口、TCP重传和拥塞状态由多个流共同承受;某条流的RST可以局部结束,底层TCP失效却会影响承载其上的多个调用。流数增加还受到对端SETTINGS及实现资源策略约束,不能无限复用。
服务名称与流方法名组合成HTTP路径,固定自定义服务函数使用/服务名/方法名发起流。这个路径标识承载入口,与VLESS头中的网站目标无关。两端服务/方法不匹配,可以在进入VLESS认证前就失败。HTTP状态200也不是业务完成保证,gRPC还有自己的状态和Trailers;VLESS仍有自己的响应前缀与后续错误。
阅读拨号器时还有一处典型误判:代码使用gRPC的insecure传输凭据,并不能单独证明网络没有TLS。该自定义拨号函数可能已经返回TLS或REALITY包装后的连接,gRPC层因而不再重复加一层TLS。安全结论要沿实际返回连接追踪,不能只凭一个函数名。若自定义拨号路径确实没有建立安全层,才需要按相应部署分析明文暴露。
固定配置对gRPC也会打印改用XHTTP的提示;它描述项目维护取向,不能取代上面的字节与状态分析。实际格式与连接映射见stream.proto、hunkconn.go、multiconn.go与dial.go。
20.4 XHTTP:用HTTP请求重建双向逻辑连接
WebSocket要求中间节点支持Upgrade后的消息转发;gRPC要求能够转发相应HTTP/2双向流。现实网络中的HTTP服务,对长请求体、流式响应、HTTP版本和超时策略支持并不一致。XHTTP在HTTP语义之内提供多种上行组织方式,并将得到的上行与下行组合成一条逻辑连接。
固定配置把xhttp映射到内部传输名splithttp。这个历史名称帮助定位源码,却不能据此断言所有模式都把两方向放进两个请求;stream-one正是使用一个请求及其响应来承载两方向。基本接口仍然是读和写,只是两者背后的HTTP对象可能不同。
| 模式 | 上行组织 | 下行组织 | 基本关联方式 |
|---|---|---|---|
| packet-up | 多次分批上传请求,按会话与序号组织 | 持续读取一个下载响应 | 会话标识将上传和下载关联 |
| stream-up | 一条持续上传的请求体 | 另一个下载响应 | 会话标识关联两方向 |
| stream-one | 一条持续上传的请求体 | 同一请求的响应体 | 同一个HTTP请求/响应上下文 |
这里packet表示XHTTP对上行字节流所作的分批,不是“原始UDP数据报”,也不是一份IP包。一批可以包含多次应用写入,VLESS请求头也可能跨批;服务端按协议规则恢复有序上行。原始目标若是UDP,仍要在VLESS或其他上层格式中表达数据报边界,不能借packet-up名称省掉它。
默认语义下,独立下载使用GET,上行使用配置归一化后的上传方法。新版本还可以调整元数据和负载在HTTP中的放置方式,不能把某个旧版固定路径当成永远不变的完整规范。第24章会按本提交逐项展开会话、序号、请求字段和上传内容;本章先确立它们与VLESS的接口关系。
例如客户端将第19章39字节送入packet-up。传输可以将前24字节放入序号0的上传批,余下15字节放入序号1。服务端即使先接到序号1,也需要按规定组织到有序输入中,才能把连续39字节交给VLESS;不能按HTTP请求到达顺序随意拼接。下行中出现的VLESS两字节响应头,则属于下载响应中的内容,不是上传请求返回的HTTP 200状态。
stream-up省去反复建立上传请求的消息开销,但要求中间层持续转发上传数据,而不是先收完整请求体再转发。stream-one进一步把上下行放在同一HTTP流,其成立条件包括服务端能在请求体尚未结束时返回数据,中间层也要允许相应双向进展。只看“支持HTTP/2”这一个产品标签,并不足以证明某条具体转发路径具备这些行为。
HTTP版本与XHTTP模式是两个维度:模式描述逻辑会话如何使用请求,HTTP/1.1、HTTP/2、HTTP/3描述这些请求如何在连接上承载。HTTP/3又会使底层使用QUIC/UDP,而交给VLESS的仍可以是一条有序字节流。不能将“外层有UDP”直接解释成“正文不可靠”。
固定实现还允许独立DownloadSettings,让上行和下行选择不同的传输入口。它需要两方向最终能够关联到同一份服务端会话状态;把它们随意发到两台没有共享或路由关联的服务器,不会仅凭一个相同标识自动工作。XMUX则管理HTTP客户端与其承载资源的复用和生命周期,不能先把它等同于VLESS的Mux命令。
XHTTP提供了适应HTTP中间节点的空间,也让状态与资源路径更复杂:上传被接受、下载已建立、上行字节已交付、VLESS认证通过,是不同事件。详细模式及默认分支留到第24章,不能在这里由概览表推出所有CDN兼容或性能更优的结论。
以上层次和模式来自dialer.go、client.go、hub.go与splitConn。所举24/15字节分批是格式映射例,并非默认分批尺寸或生产观测。
20.5 建立时间:先定义计时终点,再数往返
“几RTT协议”必须说明从哪里计时、何时算完成,以及哪些连接已经存在。TCP握手完成、承载建立、VLESS头抵达服务器、目标TCP建立、浏览器收到网页首字节,是不同终点。只报一个RTT整数,很容易把缓存连接与冷启动放在同一张比较表中。
构造一个无丢包、路径对称、两端处理时间忽略的客户端C到代理P模型,往返时延为r。DNS已完成,不使用TCP Fast Open、TLS恢复或0-RTT,外层为普通TLS 1.3。计时从新建外层TCP的SYN发出开始;终点仅为P拿到可供VLESS解析的首批字节,不计P到网站的连接。
TCP三次握手使C在t=r收到SYN-ACK并发出ACK。TLS ClientHello可随这次发送到达P,即t=1.5r;服务器握手飞行在t=2r抵达C。C验证后可以把Finished与首批应用数据发出,它们在t=2.5r到达P。RAW+TLS在这一模型下即可交付VLESS字节,不需要再先等一个基本VLESS成功状态。
普通WebSocket路径还要等Upgrade成功。C在t=2r发送Finished和Upgrade请求,P在t=2.5r收到并回复101;C在t=3r得到101,随后发送WebSocket数据,P在t=3.5r得到VLESS首批字节。多出的r来自等待Upgrade回应后才发消息这一依赖,而不是WebSocket每帧都要确认一次。
| 模型内的路径 | C可以发送普通VLESS首批字节的时刻 | P得到相应字节的时刻 |
|---|---|---|
| 新TCP、新TLS,RAW | 2r | 2.5r |
| 同样冷启动,普通WebSocket | 3r | 3.5r |
| 已有可用外层TLS连接,允许立即承载新流 | 相对本次新流起点可为0 | 约0.5r加调度与封装等待 |
最后一行没有包含此前建立连接的成本,也不声称每种承载都能在同一已用连接里新开代理流。普通WebSocket升级后的连接已经属于一个WebSocket会话,不能直接再在其中发一份新的HTTP Upgrade来创造独立隧道。HTTP/2则可以在可用流额度内新建流,复用原TCP/TLS;gRPC新调用利用的是这一性质。
HTTP/2前言和SETTINGS本身也不总意味着“在TLS之外再加一RTT才允许发送请求”。遵守初始参数与协议规则的客户端可以发送前言、SETTINGS及请求帧,未必先等待对端SETTINGS的ACK;是否需要额外等待要看实际依赖。消息和帧多一层会增加字节、处理和可能的缓冲,却不能仅凭层数机械加RTT。
WebSocket首批数据扩展会改变前述普通路径:VLESS前缀可以随Upgrade请求到达P,因而首字节到达不必等到t=3.5r。但客户端确认承载建立仍需收到101,完整连接仍可能在随后认证或目标连接阶段失败。减少首批上行等待与确认成功,是两个指标。
QUIC把传输建立与TLS集成,冷启动时序与TCP再TLS不同;恢复0-RTT又是另一个模型。XHTTP可能复用HTTP连接、分离两方向,还会受上传合批、HTTP流量窗口和下载建立影响。若没有明确模式及调用路径,不能给它填一个固定数字与上表作伪精确排名。
实际首屏还要经过P到目标的DNS/TCP、浏览器到网站的内层TLS,以及应用服务器处理。外层连接复用能减少C—P的重复建立,不能把P—网站的必要握手也一并减掉。第25章将沿完整浏览器路径标出这些阶段;第30章再讨论丢包、排队与不同路径RTT如何改变判断。这里的数值都是依赖图推导,未作为软件实测时延。
20.6 CDN与反向代理:HTTP可转发,不代表字节不可见
在入口前放置HTTP反向代理或CDN,意味着新增一个需要理解HTTP的端点。设C为代理客户端,E为HTTP边缘,P为运行VLESS的源站。常见结构是C与E建立TLS,E解密HTTP,再另行与P建立HTTP或HTTPS连接。E要根据路径、Host或其他HTTP字段做路由,就必须能读取对应字段。
正在绘制图表…
查看图表原文
sequenceDiagram
participant C as 代理客户端C
participant E as HTTP边缘E
participant P as VLESS源站P
C->>E: 建立第一份TLS,发送HTTP承载
E->>E: 解析路径、字段及可见的承载内容
E->>P: 用另一条连接转发承载
P->>P: 还原VLESS流,检查用户与目标
P-->>E: 承载内的返回字节
E-->>C: 第一份TLS中的返回字节
即使E—P也使用TLS,仍是两份会话。它保护边缘到源站这段网络,不让边缘失去解密第一份会话的能力。如果VLESS没有再加独立端点保护,E理论上能够恢复二进制消息或HTTP正文中的VLESS头,读取ID及目标;边缘选择不解析它,不等于密码学上无法解析。
目标HTTPS的内层TLS又不同:浏览器与网站的密钥不因E终止代理外层TLS就自动交给E。E可能看到VLESS目标、内层ClientHello的可见部分、长度和时序,却不能据此直接解密正常验证下的网站应用正文。目标若为明文HTTP,且没有另一层覆盖这些字节的端点加密,那么解开承载的节点可见明文请求和响应。要讨论隐私,必须逐层列出密钥由谁掌握。
四层转发的职责较窄。它可把TCP字节交给P,让TLS仍由C与P直接终止;普通四层转发不同时获得按解密HTTP路径路由的能力。它也可能读取未加密ClientHello中的SNI作分流,但这与读取TLS内部的HTTP路径是两个能力。是否还支持UDP转发,要单独看服务,不可从TCP四层能力推出QUIC可达。
承载兼容性也应逐项检查:WebSocket需要支持升级与持续双向转发;gRPC需要正确处理HTTP/2、流式消息和结束状态;XHTTP则依赖所选模式是否允许持续上传、持续下载或多次分批上传。普通HTTP缓存功能不是这些能力的替代品。
一个具体机制例子是Nginx:其官方说明指出Upgrade和Connection是逐跳字段,WebSocket反向代理需要相应处理,不能指望普通逐跳转发规则自然保留它们。响应缓冲也会改变流式数据何时交付;Nginx支持用X-Accel-Buffering响应字段控制相关行为,但配置可以忽略这个字段。这说明源站发出了“不要缓冲”的意图,不等于路径上的每个节点都已遵守。WebSocket转发说明、响应缓冲说明
XHTTP固定服务端正会发送X-Accel-Buffering: no及Cache-Control: no-store,并在相应配置下发送text/event-stream类型,帮助中间节点按流式响应处理。它们没有给任意正文自动生成一份标准SSE事件语法,也不是让所有CDN无条件实时转发的魔法开关。请求体缓冲、总时长限制、空闲超时和请求尺寸限制还可以独立影响连接。
此外,边缘和源站可能使用不同HTTP版本:C—E是HTTP/3,不代表E—P也为HTTP/3;某一段用HTTP/2,也不代表整条路径共用一套流编号、流量窗口或拥塞控制。边缘重新发起请求时可以重分片、重编码字段,语义正确的情况下仍保留同一上层内容。
因此“支持CDN”是特定承载模式、服务功能与源站路径共同满足的部署判断。它不是基本VLESS格式的一个位,也不能保证源站地址永不泄露、没有流量关联或所有供应商都接受同样请求。REALITY对端认证能否穿过终止TLS的普通HTTP边缘,要回到第21章的握手端点分析;不能仅因它也有ClientHello就当成普通CDN证书终止模式。
XHTTP上述响应行为可在固定hub.go核对。本节只使用明确的角色模型和官方中间节点机制,没有声称已对某家CDN完成兼容性测试。
20.7 同样39字节,为什么线上长度不同
取第19章的完整基本请求:头34字节,正文hello为5字节,共39字节。Flow为空、不启用VLESS Encryption。以下仅比较这批上行字节在已经可用的承载中的数据封装,不包含各自握手、HTTP请求字段、响应字段、确认、IP/TCP或重传。
还固定三项边界:WebSocket装成一帧二进制消息;gRPC装成一条普通Hunk且不开消息压缩;HTTP/2路径中相应消息或正文装成一帧无Padding的DATA。外层都是TLS 1.3,使用16字节AEAD标签,所列承载数据恰好独占一个无Padding的TLS记录。它们都是计数条件,不是软件保证的默认分片。
| 承载 | 这批数据在TLS内部占用的字节 | 再加TLS记录22字节后 |
|---|---|---|
| RAW | 39 | 61 |
| WebSocket客户端帧 | 39+2基本头+4掩码=45 | 67 |
| gRPC普通Hunk,单DATA帧 | 39+2 Protobuf+5 gRPC+9 HTTP/2=55 | 77 |
| XHTTP流式请求体,单H2 DATA帧 | 39+9 HTTP/2=48 | 70 |
gRPC这一行的Protobuf是0a 27加39字节,消息总长41;五字节gRPC前缀为00 00 00 00 29。29是41的十六进制,不是39。HTTP/2 DATA负载长46,帧长55;若再把这55字节独立装入TLS,才得到77。
XHTTP行只是流式请求体的这一个片段:没有计入请求HEADERS、路径、会话信息、XHTTP填充或另一方向的下载请求。它不等于“完整XHTTP请求只比RAW多9字节”。packet-up还会按上传批次增加请求元数据,HTTP/1.1文本与HTTP/3变长帧也不能代入这份H2表直接计算。
同样,WebSocket首帧还依赖先前Upgrade,gRPC依赖已有HTTP/2状态;表里把它们排除,是为了比较同一批内容的封装,不是让读者忽略冷启动费用。若只有这一批39字节就关闭连接,整个会话开销占比可能由握手主导。若后续连续传输大量正文,初始头与握手费用会被摊薄。
分片一变,结果立刻不同。把39字节放入两条短WebSocket消息,各一帧,客户端帧头合计12字节,TLS内部变成51;若两帧仍同在一个TLS记录,合计73。若两帧又各占一个TLS记录,TLS部分总开销是44而非22,总计95。两种情况都保留同样39字节VLESS输入。
若普通gRPC改用两条Hunk,分别装20和19字节,则两条Protobuf各多2字节、gRPC包络各多5字节,合计53字节。它们若同在一帧DATA中,帧为62字节、单TLS记录为84;若跨两帧或两记录,还要进一步加对应边界。这里只因长度很小,Protobuf内部长度各占一字节;更大消息跨过可变长整数阈值后不能继续固定加2。
由此可以区分三个优化方向:减少额外语法字段,合并消息或帧,合并TLS记录。它们可能减少字节与调用,却也可能增加等待和缓存。为了凑满较大批次而延迟发送,可能对大吞吐有利,却损害短交互时延。字节更少也不必然意味着CPU更少;掩码、对象编码、锁、系统调用和内存处理都需要另外分析。
这里所有整数来自已解释的协议结构和固定适配模式,没有网络测量或性能分数。公开比较至少应同时给出是否复用连接、批次大小、HTTP版本、安全算法、方向和统计层;否则“开销低百分之多少”无法复核。
20.8 三条网站连接,究竟共享了什么
设本地代理同时处理三条TCP目标连接A、B、C,基本VLESS命令均为TCP,不启用VLESS Mux,也不启用Vision。只考虑普通直连代理入口,不额外加入客户端代理链。每条逻辑连接都有自己的目标、基本请求头、响应解析和生命周期。
使用普通RAW+TLS时,这三条逻辑连接通常分别占用三条C—P的TCP/TLS连接;使用本章普通WebSocket适配时,通常是三条WebSocket会话,各自经过其升级后的外层连接。它们可以去相同服务器端口,甚至请求相同网站,仍不等于共享一条TCP字节序列。端口号是服务入口,不能拿来区分同一流内没有显式会话标记的三份正文。
使用gRPC时,可以由同一可复用HTTP/2连接中的三次双向流式调用承载A、B、C。HTTP/2流编号区分调用,Hunk包络恢复各自字节;VLESS本身仍分别解析三次基本请求。这与把基本Command改成Mux的设计处于不同层次:前者的会话分隔由HTTP/2完成,后者需要Mux正文自己的逻辑会话结构。
XHTTP则先区分三份逻辑会话,再由XMUX和HTTP客户端决定哪些请求共享承载资源。packet-up的同一会话还可以有多条上传请求及一个下载请求;HTTP请求数量不等于VLESS连接数量。stream-one可以让一个HTTP请求/响应上下文对应一份逻辑连接,但HTTP/2或HTTP/3底层又可能承载其他会话。不能用“看到十个POST”直接推断浏览器开了十条目标TCP连接。
这种分层决定故障范围。A的目标网站关闭,可以只使A正文结束;A所在HTTP/2流被复位,也可能只影响这一调用。若承载A、B、C的同一外层TCP连接断开,则三条流都失去传输。HTTP/3可以降低一个流的丢失对另一个流交付的直接阻塞,却仍共享QUIC连接、路径拥塞和连接级错误;它不是给每条流购买一条独立网络。
“复用能省握手”和“故障会集中”都来自资源共享,不能只保留其中一个结论。反过来,多建连接也有成本:握手计算、Socket状态、内存和拥塞竞争会增加。合理并发取决于路径与目标,不能在没有负载条件的情况下给出一个永远最优的连接数。
关闭语义还需从上层向下核对。TCP允许半关闭,一方向完成发送后仍可接收;WebSocket协议使用连接关闭握手,普通适配不应被假定能完整表达任意TCP半关闭。HTTP/2和QUIC有流方向结束的表达,但具体gRPC或XHTTP适配如何把它映射为Read、Write和Close,还要看实现。一个类型提供net.Conn形状的接口,不保证它背后具备TCP的所有可观察行为。
这一点在截止时间上尤其具体:固定XHTTP splitConn的SetDeadline、SetReadDeadline和SetWriteDeadline方法返回nil,却没有实现对应超时动作。因此调用成功本身不能证明底层HTTP会按这一时间中断;取消、HTTP请求、队列和上层计时器仍可能控制生命周期,必须分别追踪。接口兼容只是函数可被调用,行为兼容需要验证契约。
带着这份资源与端点图,安全问题也更清楚:究竟哪一层认证代理服务器,谁持有解密密钥,VLESS身份检查发生在谁的连接之上?下一章沿TLS与REALITY回答这些问题,再进入独立的VLESS Encryption,避免用一个泛化的“加密节点”掩盖不同的认证边界。
共享与关闭行为的实现入口为gRPC拨号与缓存、XHTTP拨号与XMUX关联及splitConn接口。本章没有对所有关闭组合进行运行时互操作验证,详细现代机制将在对应章节继续逐路径审查。