“Connection reset by peer”是TCP层对端主动发RST中断连接的正常反映,并非Workerman自身缺陷;常见原因包括客户端断连、Nginx反代超时、四层负载均衡健康检查及防火墙强制断连等。

Workerman 报错 “Connection reset by peer” 通常不是 Workerman 本身的问题,而是连接另一端(客户端或中间设备)主动发 RST 包中断了 TCP 连接。
为什么 Workerman 日志里总出现这个 IOException?
Workerman 基于 PHP 的 socket 或 ReactPHP/EventLoop 实现,底层依赖操作系统 TCP 栈。当它调用 recv() 或 read() 时,若对端已关闭连接且未走正常 FIN 流程(比如直接 kill 进程、断网、防火墙拦截、负载均衡健康检查),内核会返回 ECONNRESET 错误,PHP 就抛出 IOException: Connection reset by peer。
这不是 Workerman 没处理好,而是它如实反映了 TCP 层的异常状态。
- 常见触发点:微信小程序断开 WebSocket、App 切后台被系统回收连接、Nginx 反代超时后发 RST、四层 LB 健康检查完成即断连
- Workerman 默认不会静默吞掉这个错误——所以你会在
onMessage、onClose或onError回调里看到它 - 如果没做
try/catch,可能直接导致 worker 进程 crash(尤其在非守护模式下)
如何区分是客户端问题还是服务端配置问题?
先看错误发生时的上下文:
- 只在特定客户端(如某版本 iOS App)出现 → 检查该客户端网络行为:是否频繁切后台、是否用 NSURLSession 默认 timeout、是否未正确 close socket
- 集中在固定时间点(如每 60s 一次)→ 很可能是 Nginx 的
proxy_read_timeout或 LB 的 idle timeout 触发断连 - 伴随大量
FIN_WAIT2或TIME_WAIT连接堆积 → 检查 Workerman 的$worker->count是否过小,或客户端连接复用率低 - 错误 IP 段固定为云厂商 LB 内网地址(如 100.64.x.x)→ 基本可确认是四层健康检查所致,可过滤日志
用 ss -tan | grep :your_port 观察连接状态分布,比单纯看错误日志更有效。
Workerman 中怎么安全捕获并忽略这类错误?
不要在 onMessage 里裸写逻辑,必须包裹异常处理:
public function onMessage($connection, $data)
{
try {
// 你的业务逻辑
$connection->send('ok');
} catch (\Exception $e) {
if (strpos($e->getMessage(), 'Connection reset by peer') !== false) {
// 客户端已断,无需处理,也不用 log 全栈
return;
}
// 其他异常照常处理或记录
\Workerman\Worker::log($e);
}
}
- 注意:
onClose回调通常已由 Workerman 自动触发,不必再手动 close 连接 - 若用的是
WebServer+HttpConnection,还要留意$_SERVER['REMOTE_ADDR']是否被反代污染,影响健康检查 IP 判断 - 高频报错但无业务影响?建议加简单计数器,超过阈值才告警,避免日志刷屏
最容易被忽略的点:Workerman 的 heartbeat 心跳不等于 TCP keepalive
Workerman 的 $worker->pingInterval 和 onWebSocketConnect 里的心跳是应用层机制,只对 WebSocket 有效;而 Linux 的 net.ipv4.tcp_keepalive_time 是内核级保活,对所有 socket 生效。两者不等价。
- 如果你依赖心跳维持长连接,但客户端没响应 ping,Workerman 会主动 close —— 这不会报 “Connection reset by peer”,而是走正常
onClose - 但若中间防火墙在 30s 后强制清空空闲连接,而你的 TCP keepalive 设置是 7200s,那就会在第 31s 收到 RST
- 验证方式:在服务器执行
sysctl net.ipv4.tcp_keepalive_time,默认 7200,生产环境建议调成 600~1200

















