PHP实现带时间响应的简易WebSocket遇连接超时问题求助
解决PHP WebSocket连接超时(ERR_CONNECTION_TIMED_OUT)的问题
看你遇到的net::ERR_CONNECTION_TIMED_OUT错误,核心是服务端代码和客户端配置的几个关键不匹配导致的,咱们一步步来修复:
核心问题拆解
你的代码存在三个直接导致连接失败的问题:
1. Socket绑定地址错误
服务端里$address = 'https://myWebsite.com/';是完全错误的用法——socket_bind()需要的是IP地址(比如0.0.0.0监听所有网卡,或者服务器公网IP),而不是带协议的URL。用URL绑定的话,Socket根本无法关联到正确的网络接口,自然无法接受客户端连接。
2. WSS协议需要SSL加密,但服务端未配置
客户端用的是wss://(加密WebSocket),这要求服务端必须支持SSL/TLS握手。但你当前的代码是普通TCP Socket,没有处理加密逻辑,客户端发起加密连接请求时,服务端无法响应,最终超时断开。
3. 代码缺少基础的错误处理与多连接支持
当前代码只能接受一个客户端连接,且没有捕获Socket操作的错误,一旦出现异常直接退出,也没有日志帮助排查问题。
修复后的服务端代码
下面是修正后的代码,解决了上述问题,同时加入SSL支持(需要你准备好有效的SSL证书文件):
<?php error_reporting(E_ALL); ini_set('display_errors', 1); // 替换为服务器公网IP,或用0.0.0.0监听所有网卡 $address = '0.0.0.0'; $port = 8090; // 替换为你的SSL证书和私钥路径(比如Let's Encrypt证书) $certFile = '/path/to/your/certificate.crt'; $keyFile = '/path/to/your/private.key'; try { // 创建TCP Socket $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if (!$server) throw new Exception("Socket创建失败: " . socket_strerror(socket_last_error())); socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1); // 绑定到正确的地址和端口 if (!socket_bind($server, $address, $port)) { throw new Exception("绑定失败: " . socket_strerror(socket_last_error($server))); } // 开始监听连接 if (!socket_listen($server)) { throw new Exception("监听失败: " . socket_strerror(socket_last_error($server))); } echo "WebSocket服务已启动,监听 {$address}:{$port}\n"; // 循环接受客户端连接 while (true) { $client = socket_accept($server); if (!$client) { echo "接受连接失败: " . socket_strerror(socket_last_error($server)) . "\n"; continue; } // 启用SSL加密(必须在接受连接后立即执行) if (!socket_set_option($client, SOL_SOCKET, SO_SSL_ENABLE, true)) { echo "SSL启用失败: " . socket_strerror(socket_last_error($client)) . "\n"; socket_close($client); continue; } // 加载SSL证书与私钥 if (!socket_set_option($client, SOL_SOCKET, SO_SSL_CERT_FILE, $certFile)) { echo "加载证书失败: " . socket_strerror(socket_last_error($client)) . "\n"; socket_close($client); continue; } if (!socket_set_option($client, SOL_SOCKET, SO_SSL_KEY_FILE, $keyFile)) { echo "加载私钥失败: " . socket_strerror(socket_last_error($client)) . "\n"; socket_close($client); continue; } // 处理WebSocket握手 $request = socket_read($client, 5000); if (!$request) { socket_close($client); continue; } preg_match('#Sec-WebSocket-Key: (.*)\r\n#', $request, $matches); if (!isset($matches[1])) { echo "握手失败: 未找到Sec-WebSocket-Key\n"; socket_close($client); continue; } $key = base64_encode(pack('H*', sha1(trim($matches[1]) . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); $headers = "HTTP/1.1 101 Switching Protocols\r\n"; $headers .= "Upgrade: websocket\r\n"; $headers .= "Connection: Upgrade\r\n"; $headers .= "Sec-WebSocket-Version: 13\r\n"; $headers .= "Sec-WebSocket-Accept: {$key}\r\n\r\n"; socket_write($client, $headers, strlen($headers)); // 维持客户端连接,定时发送时间消息 while (true) { // 检测客户端是否断开 $buffer = socket_read($client, 1024); if ($buffer === false || $buffer === '') { break; } // 发送可读性更好的时间格式 $content = 'Now: ' . date('Y-m-d H:i:s'); // 按照WebSocket帧格式打包文本消息 $frame = chr(129) . chr(strlen($content)) . $content; socket_write($client, $frame, strlen($frame)); sleep(1); } socket_close($client); echo "客户端已断开连接\n"; } socket_close($server); } catch (Exception $e) { echo "服务端错误: " . $e->getMessage() . "\n"; if (isset($server)) socket_close($server); } ?>
客户端代码的小调整
客户端URL有个小笔误,且可以简化(WebSocket服务端直接监听端口,不需要路径),同时补充连接状态的日志:
<html> <body> <div id="root"></div> <script> var host = 'wss://myWebsite.com:8090/'; var socket = new WebSocket(host); socket.onmessage = function(e) { document.getElementById('root').innerHTML = e.data; }; socket.onerror = function(e) { console.warn('Error FE', e); }; socket.onopen = function() { console.log('WebSocket连接成功!'); }; socket.onclose = function() { console.log('WebSocket连接关闭'); }; </script> </body> </html>
部署必做事项
- 端口开放:确保服务器防火墙/安全组开放了8090端口的TCP入站权限。
- SSL证书:必须使用有效的SSL证书(比如Let's Encrypt免费证书),证书路径要配置正确。
- PHP扩展:确认PHP启用了
sockets扩展,可通过php -m | grep sockets检查。 - 运行方式:WebSocket服务需要后台持续运行,通过命令行启动:
php ws.php,或用nohup php ws.php &让它在后台常驻。
内容的提问来源于stack exchange,提问作者que1326
相关产品推荐
相关产品推荐

