“Bad file descriptor”错误源于Webbench 2.5及更早版本不遵循TCP状态机,高并发下向已失效fd发送数据触发内核EBADF;换用ab或wrk可验证此为Webbench缺陷,非Workerman问题。

Webbench 压测 Workerman 时出现 “Bad file descriptor” 错误,基本可以确定是 Webbench 自身 bug 导致的无效连接请求被 Workerman 处理时触发了系统级 fd 检查失败——不是 Workerman 代码问题,也不是你的配置错误。
为什么 Webbench 会触发 EBADF?
Webbench 在高并发下存在已知缺陷:它不严格遵循 TCP 状态机,会在连接未真正建立(如 SYN_SENT 阶段)或已断开(如 RST 后)时,继续向服务端发送 HTTP 数据包。Workerman 的 event-loop 收到这些数据后,尝试对对应 socket fd 调用 recv() 或 read(),但此时内核已将该 fd 标记为无效(例如因超时、RST、或未完成三次握手),导致返回 errno=9(EBADF)。
这不是 Workerman 没处理好连接,而是 Webbench 发出了“非法上下文”的数据,内核直接拒绝操作。
- Webbench 2.5 及更早版本无重传/状态校验逻辑,压测中大量
connect()返回 -1 但后续仍发send() - Workerman 默认启用
SO_REUSEADDR,能复用 TIME_WAIT 端口,但无法绕过内核对已失效 fd 的拦截 - 错误通常出现在
Worker::onMessage或底层stream_socket_recvfrom()调用处,而非你写的业务代码
怎么快速验证是不是 Webbench 的锅?
换一个合规的压测工具,结果立刻不同:
- 用
ab -n 1000 -c 100 http://127.0.0.1:2345/,几乎不会报 EBADF - 用
wrk -t4 -c100 -d10s http://127.0.0.1:2345/,同样稳定 - 如果换工具后错误消失,且 Workerman 日志里
onClose回调没被频繁触发,就坐实是 Webbench 问题
注意:strace -e trace=recv,read,close -p $(pgrep -f 'php start.php') 可看到大量 recv() = -1 EBADF (Bad file descriptor),但对应 fd 在前序 accept() 后从未被正常 close() ——说明是“幽灵连接”。
Workerman 侧要不要加防护?
可以加,但只建议用于日志降噪,不解决根本问题:
- 在
Worker::onMessage入口加if ($fd ,避免业务逻辑被干扰 - 用
stream_get_meta_data($connection)检查timed_out或eof状态,提前丢弃异常连接 - 不要在
onClose里再对 $fd 调用fclose()或stream_socket_shutdown()——Workerman 已自动清理,重复操作反而可能引发二次 EBADF
Workerman 本身对无效 fd 有基础防御(比如 EventLoop 中检测 epoll_wait() 返回的事件是否合法),但 Webbench 的畸形流量会绕过部分检查路径。
真正要注意的是:别在生产环境用 Webbench 做验收压测。它的报告数值(如 req/s)不可信,EBADF 只是表象,背后是连接状态混乱。用 wrk 或 hey 才能得到真实吞吐与错误率。











