PHP搭建Websocket服务握手失败 运行数分钟报500错误如何排查
Windows 10 + PHP 5.6.3 环境Websocket服务端故障排查方案
已操作项有效性校验
先排查无效操作,避免浪费时间:
- Windows可选功能中的Websocket协议是IIS服务的专属模块,仅对IIS托管的Websocket应用生效。如果你是通过PHP原生Socket套接字独立启动Websocket服务,该功能是否开启和服务运行状态无关,不需要在这个配置上反复校验。
- 执行
php --ini查看CMD运行脚本时实际加载的php.ini路径,绝大多数Windows环境下PHP会分CLI、Web服务两套独立的ini配置,很多人修改的是Apache/Nginx目录下的Web环境配置,CLI模式根本不会加载。确认实际加载的ini文件中extension=php_sockets.dll没有被分号注释,同时PHP安装目录下ext文件夹内存在和当前PHP版本匹配的php_sockets.dll文件,版本不匹配会导致扩展静默加载失败。 - 调试阶段暂时关闭Xdebug断点拦截:WebSocket握手默认超时时间仅3-5秒,Xdebug触发断点后会暂停脚本执行,超时后客户端会直接断开连接,判定握手失败。
握手失败核心遗漏配置
针对无法完成握手的问题,按以下顺序排查:
- 校验握手响应头格式合规性,这是PHP5.6环境下最常见的问题:
- 从客户端请求头中提取
Sec-WebSocket-Key字段后,拼接固定字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11,取SHA1哈希后做base64编码,注意哈希计算要传第二个参数true返回原始二进制数据,编码结果不能带多余换行、空格 - 所有响应头分隔符必须使用
\r\n,不能用单\n,且响应头末尾必须额外加一组\r\n空行标记头结束,少这个空行会被所有客户端判定为握手响应非法
合规的握手响应代码参考:
// 从客户端请求中匹配提取$clientKey逻辑省略 $acceptValue = base64_encode(sha1($clientKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); $response = "HTTP/1.1 101 Switching Protocols\r\n"; $response .= "Upgrade: websocket\r\n"; $response .= "Connection: Upgrade\r\n"; $response .= "Sec-WebSocket-Accept: {$acceptValue}\r\n\r\n"; socket_write($clientSocket, $response, strlen($response)); - 从客户端请求头中提取
- 端口连通性校验:执行
netstat -ano | findstr "你配置的Websocket监听端口",确认端口仅被你的PHP进程独占,没有被IIS、Apache等其他服务占用,同时在Windows防火墙中给对应端口添加入站、出站放行规则,防火墙拦截会导致握手包被中途丢弃。 - 加密连接额外校验:如果测试的是
wss://加密连接,要确认CLI模式的php.ini中已经加载extension=php_openssl.dll扩展,缺少该扩展会直接导致加密握手失败。
运行数分钟返回500错误排查
- 在服务端脚本最开头添加两行配置关闭执行时限、开启全量错误输出:
PHP5.6 CLI模式默认脚本最大执行时间为30秒,部分环境配置为120秒,不手动设置set_time_limit(0); error_reporting(E_ALL); ini_set('display_errors', 1);set_time_limit(0)的话,脚本到时限会被强制终止,刚好对应运行数分钟后报错的现象。 - 校验你使用的GitHub示例代码版本兼容性:PHP5.6不支持PHP7+新增的标量类型声明、null合并运算符、匿名类等语法,很多新提交的示例代码没有做低版本兼容,运行数分钟触发对应逻辑时就会抛出致命错误,返回500状态。
- 检查内存占用:PHP5.6处理多客户端连接的数组循环逻辑存在已知的内存泄漏问题,如果代码没有做无用连接回收、内存及时释放的逻辑,运行数分钟内存占满后进程会崩溃返回500,可以在脚本中加内存定期打印逻辑确认是否存在该问题。
内容的提问来源于stack exchange,提问作者Rafael Bitencourt
相关产品推荐
相关产品推荐

