前言
在日常测试工作中,我们经常接触 HTTP 接口、WebSocket 长连接、MQTT 消息推送等各种通信场景。这些协议看似独立,实则都建立在 TCP/IP 协议栈之上。搞清楚 TCP/IP 和 WebSocket 的关系,以及它们各自的测试重点,能帮我们在遇到网络超时、连接中断、消息丢失等问题时,更快地定位和推进解决。
本文基于真实项目经验,从测试视角出发,梳理 TCP/IP 和 WebSocket 的测试方法论。不堆砌理论,直接讲清楚"测什么"和"怎么测"。
一、TCP/IP:互联网通信的"地基"
1.1 白话理解
TCP/IP 是互联网通信的基石,核心就两个字:可靠。
可以把 TCP 通信理解成一次快递运输:
-
寄件前要先确认收件地址有效(三次握手)
-
包裹太大要拆成多个小包分开发,到了目的地再拼装(拆包/粘包)
-
每个小包都要回执确认收到了,没收到就重发(超时重传)
-
全部签收后正式结束(四次挥手)
TCP/IP 保证的是:数据不丢、不乱序、不重复。但代价是有额外的开销(握手、确认、重传),速度相对 HTTP 的长连接轮询要慢一些。
1.2 TCP/IP 测试四维度
在实际项目中,我从四个维度来保障 TCP/IP 层面的通信质量:
维度一:通不通 ------ 连接建立
测试目标:验证 IP/端口可达,三次握手能正常完成。
测试方法:
bash
# 基础连通性验证
telnet <目标IP> <目标端口>
# 查看当前已建立的连接状态
netstat -ant | grep ESTABLISHED
合格标准 :telnet 能成功连接,连接状态为 ESTABLISHED。
维度二:扛不扛得住 ------ 并发与连接资源
测试目标:高并发场景下,端口资源不耗尽,握手队列不溢出。
核心关注状态:
| 连接状态 | 含义 | 风险点 |
|---|---|---|
SYN_RECV |
服务端收到SYN,等待客户端ACK | 半连接队列堆积 → SYN Flood攻击或性能不足 |
TIME_WAIT |
主动关闭方等待2MSL后释放端口 | 堆积过多 → 本地端口耗尽 |
CLOSE_WAIT |
被动关闭方等待应用层调用close() | 持续增长 → 代码存在连接泄漏 |
测试方法:
-
使用 JMeter 发起高并发短连接请求(如 5000 并发,总请求数 10 万)
-
压测结束后立即执行以下命令:
bash
# 检查半连接队列是否溢出
netstat -ant | grep SYN_RECV | wc -l
# 检查服务端是否有连接泄漏
netstat -ant | grep CLOSE_WAIT | wc -l
# 检查客户端端口回收情况
netstat -ant | grep TIME_WAIT | wc -l
合格标准:
-
SYN_RECV在压测结束后快速归零 -
CLOSE_WAIT数量稳定,不随时间持续上涨 -
TIME_WAIT数量允许偏高,但应随时间逐渐下降
真实案例 :在成飞中台项目的亿级数据压测中,JMeter 高并发时频繁报错 Cannot assign requested address。通过 netstat 排查发现 TIME_WAIT 堆积超过 1.5 万个,导致本地端口耗尽。解决方案是将 HTTP 请求改为长连接复用(Keep-Alive),TPS 从 500 提升至 2000。
维度三:丢包了咋办 ------ 传输可靠性
测试目标:网络不稳定时,TCP 能否通过超时重传保证数据最终送达。
测试方法:
- Fiddler 模拟随机丢包 :在
CustomRules.js的OnBeforeRequest中增加脚本:
javascript
// 随机丢弃 10% 的请求包
if (Math.random() < 0.1) {
oSession.oRequestFlags = SessionFlags.Abort;
}
-
Wireshark 抓包验证重传 :观察抓包结果中的
TCP Retransmission标记(通常显示为黑色或高亮),确认 TCP 层确实在自动重传。 -
业务层验证:在丢包 10%~20% 的情况下,执行核心业务流程(如订单提交、文件上传),观察最终结果是成功还是卡死。
合格标准:
-
丢包率 ≤ 10%:核心业务应最终成功(依赖 TCP 重传兜底)
-
丢包率 ≥ 30%:系统应在合理超时后明确报错,不能无限等待
维度四:断线了咋整 ------ 保活与异常恢复
测试目标:空闲连接不被中间设备断开;服务异常崩溃后,客户端能自动恢复。
测试方法:
-
Keep-Alive 空闲保活测试 :客户端建立连接后不做任何操作,静置 1 小时 ,用
netstat确认连接仍处于ESTABLISHED状态。如果连接被 NAT 或防火墙断开,说明需要调整系统 Keep-Alive 参数或在应用层增加心跳。 -
RST 异常断开测试 :压测过程中直接在服务器上
kill -9杀掉后端服务进程,观察客户端:-
能否触发
onerror或onclose事件 -
是否在 1~3 秒内启动重连(推荐指数退避策略:1s → 2s → 4s → 8s...)
-
重连后用户是否需要重新登录
-
二、WebSocket:在 TCP 之上建立"实时对讲"
2.1 白话理解
WebSocket 是基于 TCP 协议 之上的一种持久双向通信协议 ,核心就两个字:实时。
类比一下 HTTP 和 WebSocket 的区别:
-
HTTP 是"写信" :客户端写一封(请求),服务器回一封(响应)。每次都要重新拿信封写地址(携带请求头),而且只能客户端主动问,服务器不能主动说话。
-
WebSocket 是"打电话" :先拨号(HTTP 握手升级),接通后双方随时可以说话。服务器有新数据能立刻"推"给客户端,不需要等客户端来问。后续消息不再带冗长的 HTTP 头,省流量、延迟低。
2.2 WebSocket 的连接流程
text
客户端 服务端
| |
|-------- HTTP GET (Upgrade) ------->| ← ① 握手请求
| | (带上 Upgrade: websocket)
|<------- 101 Switching Protocols ---| ← ② 协议升级成功
| |
|======= WebSocket 全双工通道 ========| ← ③ 双向通信
|-------- 数据帧 (客户端发) --------->|
|<-------- 数据帧 (服务端推送) -------|
|-------- Ping (心跳) --------------->|
|<-------- Pong (心跳响应) -----------|
|-------- Close (关闭帧) ------------>|
|<-------- Close (关闭帧响应) --------|
2.3 WebSocket 与 TCP/IP 的关系
WebSocket 并不替代 TCP/IP,而是跑在 TCP/IP 之上的一层协议:
| 层级 | 协议 | 角色 |
|---|---|---|
| 应用层 | WebSocket / HTTP | 业务数据格式、消息语义 |
| 传输层 | TCP | 可靠传输、拥塞控制、流量控制 |
| 网络层 | IP | 寻址和路由 |
换句话说:TCP/IP 保证"数据包能可靠到达",WebSocket 在之上规定了"怎么握手升级、消息长什么样、怎么保持心跳"。
三、WebSocket 测试五维度
基于 TCP/IP 的底层测试能力,再加上 WebSocket 独有的上层校验,形成以下五维测试体系:
维度一:握手通不通
测试目标:验证 HTTP 升级请求能否成功切换为 WebSocket 协议。
测试方法:
-
使用 Postman / Apifox 发起 WebSocket 连接请求
-
检查服务端返回的状态码必须是 101(Switching Protocols)
-
验证鉴权场景:Token 过期或错误时,应返回 401 或拒绝连接,而不是 101
关键 Headers:
text
GET /ws/chat HTTP/1.1
Host: example.com
Upgrade: websocket ← 关键:要求升级
Connection: Upgrade ← 关键:连接管理
Sec-WebSocket-Key: xxx ← 握手密钥
Sec-WebSocket-Version: 13 ← 协议版本
维度二:消息发不收得
测试目标:验证 WebSocket 通道建立后,双向消息能否正常收发。
测试要点:
| 测试类型 | 验证内容 |
|---|---|
| 文本消息 | 普通字符串、JSON 格式数据 |
| 二进制消息 | 图片、文件等二进制数据 |
| 消息顺序 | 连续发送多条,是否按顺序到达(不出现乱序) |
| 消息分片 | 超大消息是否被拆分成多个帧,接收端能否正确拼装 |
| 消息幂等 | 网络重传是否导致服务端收到重复消息(需业务层去重) |
注意:WebSocket 底层基于 TCP 流传输,也会遇到 TCP 粘包/拆包问题。测试时需要验证应用层是否能正确区分消息边界。
维度三:长连接扛不扛得住
测试目标:验证服务器支持的最大并发长连接数。
测试方法:
-
使用 JMeter(需安装 WebSocket Sampler 插件)同时建立 1000、5000、10000 个长连接
-
观察指标:
-
服务器内存是否线性增长(每个连接约占用几 KB 内存)
-
文件句柄数是否接近系统上限(
ulimit -n) -
CPU 在空闲连接下的消耗是否正常
-
与 HTTP 压测的区别:
| HTTP 压测 | WebSocket 压测 | |
|---|---|---|
| 关注指标 | QPS、响应时间 | 最大并发连接数、连接内存占用 |
| 资源瓶颈 | CPU、数据库连接池 | 文件句柄、内存 |
| 连接特征 | 短连接,用完即关 | 长连接,持续占用资源 |
维度四:空闲保活 ------ Ping/Pong 心跳
测试目标:连接闲置时,业务层心跳能否防止被中间设备踢掉。
测试方法:
-
建立 WebSocket 连接后不做任何操作,静置 30~60 分钟
-
通过抓包观察是否有 WebSocket 协议帧(Opcode: 0x9 Ping / 0xA Pong)在定时交互
-
确认连接没有被运营商 NAT 或防火墙断开
关键区别:
| 保活机制 | 层级 | 默认间隔 | 测试重点 |
|---|---|---|---|
| TCP Keep-Alive | 操作系统 TCP 层 | 通常 7200 秒(2 小时) | 系统级配置是否生效 |
| WebSocket Ping/Pong | 应用协议层 | 通常 30~60 秒 | 业务层是否主动发送心跳帧 |
即使 TCP Keep-Alive 没开,只要 WebSocket 的 Ping/Pong 正常工作,连接也能保持存活。
维度五:异常断开与自动重连 ------ 续命能力
测试目标:服务端异常崩溃或网络中断时,客户端能否自动恢复连接和业务状态。
测试方法:
-
服务端崩溃(RST 复位) :直接
kill -9杀掉 WebSocket 服务进程 -
网络中断:拔网线或 Fiddler 模拟断网
-
重连验证:
-
客户端能否在 1~3 秒内触发
onerror/onclose事件 -
是否按指数退避策略尝试重连(1s → 2s → 4s → 8s,避免风暴)
-
重连成功后,业务状态是否恢复:
-
用户需要重新登录吗?(理想情况是复用原有 token)
-
需要重新订阅业务频道/房间吗?(理想情况是自动恢复订阅)
-
重连期间错过的消息是否有补偿机制(断线重连补偿队列)
-
-
真实案例:在圈层查控系统中,我专门模拟了大运会期间检查站 4G 信号闪断的场景,验证了预警推送在 WebSocket 重连后不会丢失,且客户端能自动重新订阅实时布控频道。
四、TCP/IP vs WebSocket 测试对比总结
| 测试点 | TCP/IP 测试 | WebSocket 测试 | 说明 |
|---|---|---|---|
| 连接建立 | 三次握手,关注 ESTABLISHED |
握手升级,关注 101 状态码 | WebSocket 多一层 Upgrade 校验 |
| 并发压测 | 短连接压测,关注 TIME_WAIT 端口回收 |
长连接压测,关注 内存 + 句柄数 | 压测模型完全不同 |
| 丢包/重传 | ✅ 完全一致 | ✅ 完全一致 | WebSocket 底层走 TCP,重传机制一样 |
| 网络中断 | ✅ 完全一致 | ✅ 完全一致 | 抓包看 Retransmission 或 RST |
| 空闲保活 | TCP Keep-Alive(操作系统层,默认 2h) | Ping/Pong 帧(应用层,通常 30s~60s) | 两层独立,WebSocket 必须单独测 Ping/Pong |
| 服务端崩溃 | ✅ 完全一致(测 RST 后的重连) | ✅ 一致,但多了业务恢复验证(重新登录?重新订阅?) | WebSocket 需验证业务上下文恢复 |
五、常用命令与工具速查
5.1 连接状态查看(Linux / Windows)
bash
# 查看所有 TCP 连接状态统计
netstat -ant | awk '{print $6}' | sort | uniq -c
# 查看特定状态的连接数量
netstat -ant | grep CLOSE_WAIT | wc -l
netstat -ant | grep TIME_WAIT | wc -l
netstat -ant | grep SYN_RECV | wc -l
# 查看进程占用的文件句柄数(排查连接泄漏)
lsof -p <PID> | wc -l
5.2 Fiddler 丢包脚本
javascript
// CustomRules.js - OnBeforeRequest 中添加
// 随机丢弃 10% 的请求包,模拟网络丢包
if (Math.random() < 0.1) {
oSession.oRequestFlags = SessionFlags.Abort;
}
5.3 JMeter WebSocket 压测
需要的插件:WebSocket Sampler by Peter Doornbosch(通过 JMeter Plugins Manager 安装)
关键参数设置:
-
Server Name or IP:目标服务器地址 -
Port Number:WebSocket 端口 -
Protocol:ws / wss(加密) -
Connection Timeout:连接超时时间 -
Read Timeout:读取超时时间 -
Streaming Connection:保持连接持续监听
六、总结
TCP/IP 和 WebSocket 不是二选一的关系,而是地基与建筑的关系:
-
TCP/IP 测的是"水管漏不漏水、水压够不够"------关注连接建立、端口资源、丢包重传、断线恢复
-
WebSocket 在 TCP 之上加了三层独有的保障------握手升级(101)、业务心跳(Ping/Pong)、重连后的业务续命
在测试实践中,TCP/IP 的那套工具链(Fiddler 丢包、netstat 看状态、JMeter 压测、Wireshark 抓包)可以直接复用到 WebSocket 的底层测试中,只需要额外补充 WebSocket 特有的握手、心跳、订阅恢复等校验点即可。
掌握这套方法论,无论是测 HTTP 接口、WebSocket 推送,还是 MQTT 物联网通信,都能做到心中有数、手中有招。