连接关到一半,算断开还是没断开?很多人以为网络连接只有”开”和”关”两种状态,但 TCP 里还有一种中间态:发送方向已经关闭,接收方向却还开着。这种”关一半”的状态叫半关闭,在代理链路里遇到它时,转发是否正常直接决定上层应用能不能跑完最后一步。
半关闭到底关了什么
先说正常关闭。一次完整关闭要走四次挥手:一端先发送关闭信号,另一端回应确认,再反向重复一轮,双方都确认不再收发数据,连接才算结束。整个过程里,两个方向是先后关闭的,而不是同时切断。
半关闭就是把”两个方向”拆开处理:应用只关闭自己的发送方向,接收方向保持打开。此时对端会收到一个文件结束信号,明白”你不能再往这边发数据了”,但它依然可以继续发送,直到它自己也关闭写方向,连接才彻底收尾。
这个状态不是理论概念。很多真实协议都依赖它:客户端发完请求后关闭写方向,告诉服务器”请求完毕”,但保留读方向等待完整响应;服务器返回数据后再关闭自己的写方向,连接优雅结束。
在直连环境下,这个流程由两端系统直接配合完成;一旦中间插了代理,转发方能不能把”半关闭”原样传递过去,就成了决定成败的细节。
| 状态 | 发送方向 | 接收方向 | 对端感知 |
|---|---|---|---|
| 正常使用 | 开放 | 开放 | 持续收到数据 |
| 半关闭 | 已关闭 | 仍开放 | 收到结束信号 |
| 完全关闭 | 已关闭 | 已关闭 | 连接回收 |
半关闭的价值在于:它让”我发完了”和”你还能发”两个信息同时成立,服务器可以据此判断请求边界,又不会提前掐断响应通道。
哪些场景会真实触发半关闭
最常见的是 HTTP 交互里的”请求结束但响应未完”。客户端发送完请求体后主动关闭写方向,服务器据此知道不用再等更多请求数据,专心生成响应即可。文件上传、长表单提交这类场景里,半关闭是协议顺利完成的隐形前提。
另一类场景在流式传输中:客户端已经发完所有指令,等待服务端持续推送内容。这时关闭写方向并不会中断接收,反而是一种明确的”话已说完,请继续”的信号。
代理转发半关闭的两种命运
规范且完整的代理会把半关闭状态原样透传:客户端关写方向,代理也向目标端关闭自己的写方向,同时保持到客户端的读方向畅通,两端语义完全一致。这是大多数主流代理的默认行为。
另一种情况是代理实现不完整:它只看到”连接关闭”信号,就把整条连接当作结束来处理,直接掐断反向数据通道。结果就是客户端发完请求后收不到完整响应,明明服务端数据还在路上,连接却先断了。
半关闭与完全关闭不是一回事。前者只是单方向告别,后者才是连接终结;把两者混为一谈的代理,会在半关闭场景里提前切断响应,制造出”莫名其妙收不到数据”的故障。
排查时先看连接是”半关”还是”全关”。遇到响应中断,先确认是发送方向被关、还是整条连接被回收,再判断是应用行为还是转发层的问题,方向对了排查才不白费。
稳定的代理会把半关闭处理成细节透明的事。好的转发实现不需要应用感知这一层存在,该透传的透传,该保持的保持——用户只看到请求顺利、响应完整。
半关闭是 TCP 给应用留的一扇侧门:话可以说一半,听却不能停。把这一细节处理稳妥的代理链路,才能让每一句”我说完了”之后,都稳稳接住对方的回应。
