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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 00:15:55