You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

部署必做事项

  1. 端口开放:确保服务器防火墙/安全组开放了8090端口的TCP入站权限。
  2. SSL证书:必须使用有效的SSL证书(比如Let's Encrypt免费证书),证书路径要配置正确。
  3. PHP扩展:确认PHP启用了sockets扩展,可通过php -m | grep sockets检查。
  4. 运行方式:WebSocket服务需要后台持续运行,通过命令行启动:php ws.php,或用nohup php ws.php &让它在后台常驻。

内容的提问来源于stack exchange,提问作者que1326

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:47:04