Ratchet PHP库消息推送异常:聊天应用无法接收他人消息
问题:Ratchet聊天应用无法接收其他用户消息
我正在使用Ratchet PHP库开发一款简易聊天应用,用户发送的消息会存储至数据库并展示,但用户无法接收其他用户发送的消息。
相关代码
server.php(核心函数为onMessage)
<?php namespace MyApp; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; class Chat implements MessageComponentInterface { protected $clients; protected $userConnections; public function __construct() { $this->clients = new \SplObjectStorage; $this->userConnections = []; } public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); echo "New connection! ({$conn->resourceId})\n"; } public function onMessage(ConnectionInterface $from, $msg) { list($recipient_id, $message) = explode(':', $msg, 2); if (isset($this->userConnections[$recipient_id])) { echo "Received message for User ID {$recipient_id}: {$message}\n"; $recipientConn = $this->userConnections[$recipient_id]; $recipientConn->send($message); } } public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); echo "Connection {$conn->resourceId} has disconnected\n"; $userId = array_search($conn, $this->userConnections); if ($userId !== false) { unset($this->userConnections[$userId]); } } public function onError(ConnectionInterface $conn, \Exception $e) { echo "An error has occurred: {$e->getMessage()}\n"; $conn->close(); } } ?>
chat.php
<?php include("connect.php"); include("auth_check.php"); $crpyted_userId = @$_GET['user_id']; $crypted_recipient_id = @$_GET['recipient_id']; $recipient_id = base64_decode($crypted_recipient_id); if ( empty($recipient_id)) { echo("Could not validate your request. Recipient id empty"); } else { ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="chatroom.css"> <title>Chat Room</title> </head> <body> <div class="chat-container" id="chat"> <div class="input-container"> <textarea type="text" id="messageInput" oninput="validateMessage()" placeholder="Type your message..." maxlength="600"></textarea> <button id="send" onclick="sendMessage()"><img id="message_icon" src="uploads/message.png"></button> </div> </div> <script> function validateMessage() { var sendButton = document.getElementById('send'); var message = document.getElementById('messageInput').value.trim(); var isValid = message.length > 0 && message.length <= 600; sendButton.disabled = !isValid; return isValid; } validateMessage(); const socket = new WebSocket('ws://localhost:8080'); const chatContainer = document.getElementById('chat'); chatContainer.scrollTop = 0; function updateScroll() { chatContainer.scrollTop = chatContainer.scrollHeight; } socket.onmessage = function (event) { console.log("testing:"); chatContainer.innerHTML += '<div class="message recipient"><div class="message-content"><p>' + event.data + '</p></div></div>'; updateScroll(); }; function sendMessage() { if (validateMessage()) { const messageInput = document.getElementById('messageInput'); const message = messageInput.value; var messageData = new FormData(); messageData.append('message', message); messageData.append('messages', 'one'); messageData.append('recipient_id', <?php echo $recipient_id ?>); var xhr = new XMLHttpRequest(); xhr.open('POST', 'messageHandling.php', true); xhr.onload = function () { if (xhr.readyState === 4 && xhr.status === 200) { const jsonResponse = JSON.parse(xhr.responseText); const message = jsonResponse.message; //important when cannot process json response socket.send(<?php echo json_encode($recipient_id); ?> + ':' + message); chatContainer.innerHTML += '<div class="message sender"><div class="message-content"><p>' + message + '</p></div></div>'; updateScroll(); } }; xhr.send(messageData); } } document.addEventListener("DOMContentLoaded", function() { var xhr = new XMLHttpRequest(); var messageData = new FormData(); messageData.append('recipient_id', <?php echo $recipient_id?>); messageData.append('messages', 'all'); // Include 'messages' in FormData xhr.open('POST','messageHandling.php', true); xhr.onload = function(){ if(xhr.readyState === 4 && xhr.status === 200){ var messages = JSON.parse(xhr.responseText); socket.onopen = function(){ for (const msg of messages) { socket.send(<?php echo json_encode($recipient_id); ?> + ':' + msg); chatContainer.innerHTML += '<div class="message sender"><div class="message-content"><p>' + msg + '</p></div></div>'; updateScroll(); } } } } xhr.send(messageData); }); </script> </body> </html> <?php } ?>
问题原因
核心问题是用户建立WebSocket连接后,没有将当前用户ID与连接实例绑定到服务器的$userConnections数组中。服务器的onMessage方法尝试从$userConnections中查找接收者的连接,但该数组始终为空,导致无法推送消息。
解决方案
1. 前端修改:连接建立后发送用户ID绑定请求
首先在chat.php中解码当前用户ID,然后在WebSocket连接打开时,向服务器发送绑定请求:
// 在chat.php的PHP部分新增 $user_id = base64_decode($crpyted_userId);
// 修改chat.php的socket.onopen逻辑 document.addEventListener("DOMContentLoaded", function() { socket.onopen = function(){ // 发送当前用户ID到服务器完成绑定 socket.send('bind:<?php echo $user_id; ?>'); // 原有加载历史消息逻辑 var xhr = new XMLHttpRequest(); var messageData = new FormData(); messageData.append('recipient_id', <?php echo $recipient_id?>); messageData.append('messages', 'all'); xhr.open('POST','messageHandling.php', true); xhr.onload = function(){ if(xhr.readyState === 4 && xhr.status === 200){ var messages = JSON.parse(xhr.responseText); for (const msg of messages) { chatContainer.innerHTML += '<div class="message sender"><div class="message-content"><p>' + msg + '</p></div></div>'; updateScroll(); } } } xhr.send(messageData); }; });
2. 后端修改:处理绑定请求并完善消息转发逻辑
修改server.php的onMessage方法,先处理用户绑定请求,再处理消息转发:
public function onMessage(ConnectionInterface $from, $msg) { // 处理用户绑定请求 if (str_starts_with($msg, 'bind:')) { $userId = substr($msg, 5); $this->userConnections[$userId] = $from; echo "User {$userId} bound to connection {$from->resourceId}\n"; return; } // 处理普通消息转发 list($recipient_id, $message) = explode(':', $msg, 2); if (isset($this->userConnections[$recipient_id])) { echo "Received message for User ID {$recipient_id}: {$message}\n"; $recipientConn = $this->userConnections[$recipient_id]; $recipientConn->send($message); } else { echo "Recipient {$recipient_id} is not online or not bound\n"; } }
额外检查点
- 确认
chat.php中$user_id解码正确,否则绑定的用户ID会错误 - 查看服务器控制台输出,确认用户绑定成功、消息接收日志正常
- 检查浏览器控制台,确认WebSocket连接无报错
内容的提问来源于stack exchange,提问作者Mahdi
相关产品推荐
相关产品推荐

