一句话定义
长连接是指 TCP 连接建立后不立即关闭,后续多次通信复用同一条连接的机制。
对应的短连接是:每次通信都新建 TCP 连接,用完就关。
短连接的问题
HTTP/1.0 时代默认就是短连接。一次请求的完整流程是:
三次握手 → 发请求 → 收响应 → 四次挥手 → 连接关闭
下次再请求,重复一遍。问题很明显:
- 握手开销大:三次握手 + 四次挥手,至少 7 个包,还没传数据
- 慢启动:TCP 每次新建连接都要从慢启动开始,拥塞窗口从小往大爬
- TLS 开销:如果是 HTTPS,还要加一轮 TLS 握手,多好几个 RTT
一个页面要加载几十个资源,如果每个都新建连接,光握手就浪费大量时间。
长连接怎么解决
连接建立一次,之后一直复用:
三次握手 → [请求1 → 响应1] → [请求2 → 响应2] → ... → 四次挥手
省掉了重复的握手和 TLS 开销,也避开了反复慢启动。
长连接的几种实现层次
1. HTTP/1.1 的 keep-alive
在 HTTP 头里加 Connection: keep-alive,告诉对方"这条连接别关,我还要用"。但 HTTP/1.1 是串行的:同一个连接上,请求必须一个接一个,前一个响应没回来,后一个请求不能发(队头阻塞)。浏览器为了并发,通常对同一个域名开 6 条左右的连接。
2. HTTP/2 的多路复用
HTTP/2 在一条 TCP 连接上跑多个请求,每个请求是一个 Stream,有独立 ID。解决了 HTTP/1.1 的队头阻塞,一条连接就能并发。gRPC 就是基于这个。
3. WebSocket
握手阶段借用 HTTP,升级后变成全双工通道。双方可以随时互相发消息,不再有"请求-响应"的配对关系。
4. 其他协议
MQTT、AMQP、数据库连接池、SSH、Redis 客户端------这些都是长连接,只是上层协议不同。
长连接的代价
长连接不是白来的,它带来几个必须处理的问题:
保活:连接空闲太久,中间设备(NAT、防火墙、负载均衡器)可能悄悄把连接表项删掉,双方却不知道。所以要定期发心跳包(HTTP/2 的 PING 帧、WebSocket 的 ping/pong、应用层自定义心跳)。
资源占用:每条连接在服务端都占内存和文件描述符。成千上万条连接,服务端压力不小。
粘性 :连接一旦建立,就绑定了某个后端实例。服务端扩容时,新实例分不到流量,因为老连接还粘在旧实例上。这就是你前面问 gRPC 时聊到的 MaxConnectionAge 要解决的问题。
断线重连:网络抖动、服务端重启都会断连。客户端需要有重连策略,还要处理重连期间的数据补发和幂等。
回到我当前业务的场景
设备配的 Telemetry 里,设备通过 gRPC 向 SDN 控制器上报数据。这里的"长连接"具体是:
- 底层:一条 TCP 连接(可能带 TLS)
- 协议层:HTTP/2
- 应用层:gRPC 双向流
- 保活:gRPC 的 keepalive 机制
- 生命周期:由
MaxConnectionAge等策略控制
所以当你说"gRPC 是长连接"时,准确的理解是:gRPC 复用了 HTTP/2 的长连接特性,在一条 TCP 连接上多路复用多个 RPC 流。
和 WebSocket 的关系
再澄清一下你前面问的:
- 长连接是描述"连接不关、反复用"这个特征
- WebSocket是满足这个特征的一种具体协议
- gRPC也是满足这个特征的一种具体实现
它们不是并列关系,而是"概念"和"实现"的关系。就像"交通工具"和"汽车"------汽车是交通工具的一种,但交通工具不只有汽车。
查看长连接,本质上就是查看处于 ESTABLISHED(已建立)状态的 TCP 连接。
基础命令:查看当前连接
最常用的是 ss 命令,它是 netstat 的现代替代品,速度更快。
查看所有已建立的 TCP 连接:
bash
ss -tn state established
参数含义:-t 只显示 TCP,-n 不解析域名(直接显示 IP,更快),state established 过滤出已建立的连接。
只看某台设备的连接(结合你 SDN 场景,比如查控制器和某设备的 gRPC 连接):
bash
ss -tn state established '( dport = :24009 or sport = :24009 )'

统计连接数量:
bash
ss -tn state established | wc -l
如果只想快速看汇总,用 ss -s 会显示各类状态的连接数汇总,包括 estab(已建立)、timewait 等。