WebSocket连接失败主因是端口未监听或被占、Nginx未透传Upgrade头、防火墙/WAF拦截;需依次验证ss -tuln端口状态、lsof查占用、Nginx三行配置、tcpdump抓包定位拦截点。

WebSocket 连接在 Linux 上失败,尤其是卡在“升级握手”阶段(比如报 ERR_CONNECTION_REFUSED、502 Bad Gateway 或控制台显示 unexpected response code),往往不是代码写错了,而是端口层面出了问题——服务根本没起来、被占了、没通过去,或者被中间件拦住了。排查要从“连接能否建立”开始,而不是直接看业务逻辑。
先确认 WebSocket 服务进程是否真在监听目标端口
很多情况下,你以为服务启动了,其实它压根没成功绑定端口。别只信日志里的“Started”,要亲眼看见端口在 LISTEN 状态:
- 用
sudo ss -tuln | grep :9100(把9100换成你实际用的端口)——有输出且状态是LISTEN才算真正监听中 - 如果没结果,说明服务没起来,或启动时抛异常退出了(查服务日志,比如
journalctl -u your-websocket-service) - 如果看到的是
127.0.0.1:9100而不是*:9100,表示只监听本地回环,外部请求(包括 Nginx 反代)连不上,需改服务配置绑定0.0.0.0
检查端口是否被其他进程悄悄占用了
常见冲突源:另一个测试实例、旧进程残留、Docker 容器、代理工具(如 Proxifier)、甚至某些安全软件的后台服务。不能只靠“我没开别的服务”来判断:
Linux 性能分析与调优专家,覆盖 CPU、内存、磁盘 I/O、网络、内核参数、编译优化、容器/K8s。适用场景:系统卡顿/高负载、内存不足/OOM/Swap 高、CPU 异常/iowait 高。
- 运行
sudo lsof -i :9100或sudo fuser 9100/tcp,直接看到 PID 和进程名 - 若发现占用进程不是你的服务,用
sudo kill -9 PID杀掉,再重启你的 WebSocket 服务 - 特别注意:有些 Java 进程即使已关闭,端口仍可能处于
TIME_WAIT状态(持续几十秒),可等一会再试,或临时调小内核参数net.ipv4.tcp_fin_timeout
验证 Nginx 是否正确透传 WebSocket 升级请求
如果用了 Nginx 做反向代理,80% 的“升级失败”都出在这里。Nginx 默认不处理 Upgrade 头,必须显式配置,否则它就把 WebSocket 握手当成普通 HTTP 请求转发,后端收不到 Sec-WebSocket-Key,只能返回 502 或 400:
- 确认 Nginx 配置里有这三行(位置在
location /ws/ { ... }块内):proxy_http_version 1.1;<br>proxy_set_header Upgrade $http_upgrade;<br>proxy_set_header Connection "upgrade";
- 检查
proxy_pass后地址是否指向正确的后端 IP 和端口(比如http://127.0.0.1:9100),别写成ws://(Nginx 不支持) - 修改后执行
sudo nginx -t && sudo systemctl reload nginx,别忘了重载
抓包确认握手请求到底发到哪、被谁拦下了
当上面都看似正常但还是连不上,就该用原始手段看真实流量:
- 在服务器上运行
sudo tcpdump -i any port 9100 -w ws.pcap,然后前端触发一次连接 - 用 Wireshark 打开
ws.pcap,过滤http.request.uri contains "upgrade",看有没有带Upgrade: websocket和Sec-WebSocket-Key的 GET 请求到达服务器 - 如果没有,说明请求根本没到服务器——检查防火墙(
ufw status)、云厂商安全组、或客户端网络策略 - 如果有,但没收到 101 响应,说明服务进程收到请求但没正确响应——回到服务日志和握手逻辑检查

















