业务模型:后台服务借助 TCP 与集团的行情网关建立连接。每次连接时,需先行发送一个授权请求,随后持续发送心跳包以维持连接状态。 然而,某一天,收到了服务断开连接的告警信息。通过仔细排查日志后发现,后台服务一直在持续发送心跳包,但对方却毫无回应,可连接却始终未断开。
现场简述
原本正在公司加班推进项目进度,工作群里突然弹出告警信息。乍一看,我还以为是老毛病,大概率是网络超时致使心跳发送失败,进而导致服务断开连接。可在仔细排查日志后,却发现实际情况并非如此。后台已发送了授权登录消息,然而一直未收到应答,与此同时,心跳包仍在持续不断地发送,对方却始终未回复任何心跳数据。经过对日志的深入分析,暴露出了以下几个关键问题:
- 授权消息无应答:极有可能是对方系统正在重启,使得授权消息未能得到及时处理。
- 未成功授权却发送心跳数据:经排查,发现这是程序逻辑上的漏洞。心跳发送函数的判断逻辑存在缺陷,仅仅校验了连接状态,却遗漏了对授权状态的校验。
- 服务未断开连接:若服务能够断开连接,便可以触发重连机制,从而重新发送授权消息。
目前,还剩下最后一个亟待解决的问题——为何服务没有断开连接。这一问题的解决需要开展更为深入细致的排查工作。
分析网络数据包
tcpdump
是一个非常强大的网络抓包工具,可以用来捕获网络数据包。通过分析网络数据包,我们可以更加直观地了解网络通信的细节。在这里,我们可以使用 tcpdump
来捕获网络数据包,以便进一步分析。
分析图中的数据,我可以看到心跳一直在正常发送,对方服务器并没有回复任何数据,但是给了 ACK
,这就导致连接不会主动断开了。
常见标志位说明
在 TCP 协议中,PSH
(Push)和 ACK
(Acknowledgment)是两个重要的标志位,分别用于控制数据传输和流量确认。它们的作用如下:
1. PSH(Push Flag)
-
功能:
PSH
标志位的作用是请求接收方立即将缓冲区中的数据推送给上层应用(而不是等待缓冲区填满)。这意味着一旦收到带有PSH
标志的数据段,接收方会尽可能快地处理并传递给应用程序,而非暂存于操作系统缓冲区中。 -
典型场景:
- HTTP/HTTPS 请求:客户端发送请求时(如
GET /index.html
)会设置PSH
,希望服务器立即响应。 - SSH 协议:每次键盘输入都会触发
PSH
,确保输入字符实时传输。 - 实时通信:视频流、在线游戏等低延迟场景可能使用
PSH
减少延迟。
- HTTP/HTTPS 请求:客户端发送请求时(如
-
注意:
PSH
并非强制要求,接收方可以选择忽略该标志位(但仍需正常处理数据)。- 发送方可能不设置
PSH
,此时接收方会根据自身缓冲策略决定何时推送数据。
2. ACK(Acknowledgment Flag)
-
功能:
ACK
标志位表示确认已正确接收前序数据段。每个ACK
包含一个确认号(Acknowledgment Number
),表示期望接收的下一个字节序号。它是 TCP 可靠传输的核心机制。 -
工作原理:
- 发送方发送数据段时,会携带期望接收方的
ACK
值(例如ACK = 序列号 + 数据长度
)。 - 接收方收到数据后,会生成一个
ACK
报文段,确认已接收的数据序号。 - 发送方只有在收到对应的
ACK
后,才会重传未被确认的数据。
- 发送方发送数据段时,会携带期望接收方的
-
示例:
- 若发送方发送了序号为
100~199
的数据段,则期望接收方的ACK
应为200
。 - 若接收方未收到
100~199
中的某些数据,会通过ACK=150
告知发送方重传。
- 若发送方发送了序号为
3. PSH 和 ACK 的组合
在 TCP 报文中,PSH
和 ACK
可以同时出现,常见于以下场景:
-
HTTP 请求响应:
客户端发送POST
请求时(含数据),会设置PSH
和ACK
(确认之前的响应)。Client → Server: SYN, ACK=1 → 建立连接 Client → Server: PSH, ACK=1, 数据 → 发送请求数据 Server → Client: PSH, ACK=数据长度+1 → 返回响应
-
SSH 握手后传输命令:
客户端输入命令后,发送带有PSH
和ACK
的数据段,确保命令立即传输并被服务器处理。
4. 其他标志位的关联
标志位 | 名称 | 简要说明 |
---|---|---|
SYN | 同步 | 初始化连接(三次握手) |
FIN | 结束 | 优雅关闭连接 |
RST | 复位 | 强制终止连接(异常情况) |
URG | 紧急 | 标记紧急指针(极少使用) |
总结
- PSH 关注的是数据尽快到达应用层,降低延迟。
- ACK 关注的是数据的可靠传输,避免丢包或乱序。
两者协同工作,平衡了 TCP 协议的效率和可靠性。