PHP Ratchet WebSocket请求无响应及连接问题求助
Ratchet WebSocket服务连接问题排查与修复
我依照官方文档搭建了基础PHP WebSocket服务,但测试时遇到一系列问题:
- 调用测试文件时请求一直无法完成,Postman卡在“发送响应”状态
- 执行
netstat -anp | grep LISTEN确认端口处于监听状态,但请求始终等待onOpen回复 - Postman的WebSocket功能连接时返回
Unexpected server response: 200错误 - 终端用telnet能正常通信,但浏览器连接时触发Content Security Policy限制
测试调用代码
<?php // API for privacy setting of user for blocking/Message Limit ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); //file_put_contents('zee.log',print_r($_POST,true)); // Function to send the SDP and user IDs to the server function sendSDP($sdp, $userIdA, $userIdB) { // Create a new cURL resource $ch = curl_init(); // Set the cURL options curl_setopt($ch, CURLOPT_URL, "callSocketChat_2008.php"); // callSocketChat_2008.php file curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( 'sdp' => $sdp, 'userIdA' => $userIdA, 'userIdB' => $userIdB ))); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Execute the cURL request $response = curl_exec($ch); // Check for errors if ($response === false) { echo "cURL Error: " . curl_error($ch); } else { // Request successful, display the response echo $response; } // Close the cURL resource curl_close($ch); } // Test the sendSDP function $sdp = "SDP valuexx YY"; // Replace with your actual SDP value $userIdA = "4"; // Replace with your actual User ID A value $userIdB = "8"; // Replace with your actual User ID B value sendSDP($sdp, $userIdA, $userIdB); ?>
Ratchet服务代码
<?php // Enable error reporting and display errors for debugging purposes error_reporting(E_ALL); ini_set('display_errors', 1); require_once("/home/bitnami/vendor/autoload.php"); use Ratchet\MessageComponentInterface;; use Ratchet\ConnectionInterface; class WebSocketHandler implements MessageComponentInterface { protected $clients; public function __construct() { $this->clients = new \SplObjectStorage(); print_r("Print Value "); } public function onOpen(ConnectionInterface $conn) { // Handle new WebSocket connection // You can add the connection to $this->clients for future reference //$request = $conn->httpRequest; //$userId = $_POST['userId']; // Assuming the user ID is received in the POST request // Save the user ID in the resourceId property of the connection $conn->resourceId = $userIdA; // Add the connection to the clients list $this->clients->attach($conn); echo "New connection! ({$conn->resourceId})\n"; } public function onMessage(ConnectionInterface $from, $msg) { // Handle incoming WebSocket message // You can process the message and send a response back to the client } public function onClose(ConnectionInterface $conn) { // Handle WebSocket connection close // You can remove the connection from $this->clients if necessary $this->clients->detach($conn); } public function onError(ConnectionInterface $conn, \Exception $e) { // Handle WebSocket error // You can log or handle the error as needed } } // Get the SDP and user IDs from the request $sdp = $_POST['sdp']; $userIdA = $_POST['userIdA']; $userIdB = $_POST['userIdB']; // Fetch user details from the elaxer database using the user IDs $userDetailsA = fetchUserDetails($userIdA); $userDetailsB = fetchUserDetails($userIdB); // Send push notification to wake up User B's device //sendPushNotification($userDetailsB['fcmToken']); // Wait for User B's device to establish a WebSocket connection $waitForConnectionTimeout = 30; // Timeout in seconds $startTime = time(); while (time() - $startTime < $waitForConnectionTimeout) { // Check if User B's device has connected to the WebSocket if (userDeviceConnected($userIdB)) { // User B's device connected, retrieve SDP from the elaxer database $sdpB = fetchSDP($userIdB); // Save User A's SDP in the elaxer database saveSDP($userIdA, $sdp); // Return User B's SDP to User A echo $sdpB; exit; } // Wait for a short duration before checking again sleep(1); } // Instantiate WebSocketHandler //$server = \Ratchet\Server\IoServer::factory( // new \Ratchet\Http\HttpServer( // new \Ratchet\WebSocket\WsServer($webSocketHandler) //), //8080 // Change the port number if needed //); // Set up the WebSocket server using Ratchet $server = Ratchet\Server\IoServer::factory(new WebSocketHandler(),20081); // Change the port number if needed $server->run(); // Timeout reached, handle the case when User B's device didn't connect handleTimeout(); // Fetch user details from the elaxer database using the user ID function fetchUserDetails($userId) { // Implement the logic to fetch user details from the elaxer database // Return an array containing the user details } // Send push notification to wake up User B's device function sendPushNotification($fcmToken) { // Implement the logic to send a push notification using FCM to the specified FCM token } // Check if User B's device has connected to the WebSocket function userDeviceConnected($userId) { // Implement the logic to check if User B's device has connected to the WebSocket // Return true if the device is connected, false otherwise } // Fetch SDP from the elaxer database using the user ID function fetchSDP($userId) { // Implement the logic to fetch SDP from the elaxer database using the user ID // Return the retrieved SDP } // Save SDP in the elaxer database using the user ID function saveSDP($userId, $sdp) { // Implement the logic to save the SDP in the elaxer database using the user ID } // Handle the case when User B's device didn't connect within the timeout function handleTimeout() { // Implement the logic to handle the timeout case // For example, you can return an error response or perform a fallback action } ?>
补充错误信息
Postman WebSocket连接错误
Could not connect to ws://www.example.com/callSocketChat_2008.php 01:58:47 Error: Unexpected server response: 200 Handshake Details Request URL: ws://www.example.com/callSocketChat_2008.php Request Method: GET Status Code: 200 OK Request Headers Sec-WebSocket-Version: 13 Sec-WebSocket-Key: ansqUU0N7J4Q6S8PyR6XEg== Connection: Upgrade Upgrade: websocket Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits Host: www.example.com/callSocketChat_2008 Response Headers Date: Tue, 30 May 2023 20:28:15 GMT Server: Apache Vary: Accept-Encoding X-UA-Compatible: IE=Edge Transfer-Encoding: chunked Content-Type: text/html; charset=UTF-8
浏览器连接错误
Content Security Policy: The page’s settings blocked the loading of a resource at ws://example.in:8080/ (“connect-src”). debugger eval code:1:11
问题排查与修复方案
1. 分离HTTP请求处理与WebSocket服务逻辑
你的服务代码把POST请求循环和WebSocket启动放在同一个文件里,导致服务永远无法启动(循环会先执行并阻塞)。必须将两者拆分:
- 单独创建WebSocket服务启动文件,仅负责启动Ratchet服务
- 创建独立的HTTP接口处理SDP提交和用户逻辑
2. 修正WebSocket服务初始化代码
直接用IoServer实例化处理器缺少协议层包装,无法处理标准WebSocket握手,正确写法:
$webSocketHandler = new WebSocketHandler(); $server = \Ratchet\Server\IoServer::factory( new \Ratchet\Http\HttpServer( new \Ratchet\WebSocket\WsServer($webSocketHandler) ), 20081 // 指定监听端口 ); $server->run();
3. 修复onOpen方法中的未定义变量
onOpen中直接使用$userIdA会报错,应从WebSocket握手请求的URL参数获取用户标识:
public function onOpen(ConnectionInterface $conn) { $queryParams = $conn->httpRequest->getUri()->getQuery(); parse_str($queryParams, $params); $userId = $params['userId'] ?? $conn->resourceId; // 优先取URL参数,默认用系统分配的resourceId $conn->userId = $userId; $this->clients->attach($conn); echo "New connection! ({$conn->userId})\n"; }
4. 正确连接WebSocket服务
不要通过Apache解析PHP文件连接WebSocket,正确地址是ws://你的域名:20081,带用户参数的示例:
var conn = new WebSocket('ws://example.com:20081?userId=4'); conn.onopen = function(e) { console.log("Connection established!"); }; conn.onmessage = function(e) { console.log(e.data); };
5. 解决Content Security Policy限制
- 在页面的CSP配置中添加
ws://example.in:20081到connect-src规则 - 生产环境建议使用wss(WebSocket over SSL),需要给Ratchet配置SSL证书:
$server = \Ratchet\Server\IoServer::factory( new \Ratchet\Http\HttpServer( new \Ratchet\WebSocket\WsServer($webSocketHandler) ), 20081, '0.0.0.0', [ 'local_cert' => '/path/to/your/certificate.pem', 'local_pk' => '/path/to/your/private-key.pem', 'verify_peer' => false ] );
6. 修正测试流程
- WebSocket服务需要在终端独立启动:
php your-websocket-server.php - 用浏览器或专门的WebSocket测试工具连接,不要用cURL调用服务文件
内容的提问来源于stack exchange,提问作者Ritu
相关产品推荐
相关产品推荐

