【问题标题】:How to send a message to specific websocket clients with symfony ratchet?如何使用 symfony 棘轮向特定的 websocket 客户端发送消息?
【发布时间】:2021-01-25 07:00:35
【问题描述】:

有一个 symfony 应用程序使用带有 Ratchet 的 php websockets (http://socketo.me/docs/sessions)。创建一个可以将接收到的消息广播到所有连接的客户端(Web 浏览器)的 websocket 应用程序似乎很常见。但是我有一个大问题是只向特定客户端发送消息(例如,使用 getUser() 加载用户并找到其所属的 websocket 客户端对象)。

这是我的设置:

// WebsocketServer.php
$server = IoServer::factory(
    new HttpServer(
        new SessionProvider(
            new WsServer(
                new WebSocketCommunicator()
            ),
            new MySessionHandler())

    ),
    $this->_websocketPort
);

$server->run();
// Handler for communication with clients
class WebSocketCommunicator implements HttpServerInterface
{

    protected $clients;

    public function __construct()
    {
        $this->clients = new \SplObjectStorage();
    }

    public function onOpen(ConnectionInterface $conn, RequestInterface $request = null)
    {
        echo "URI: " . $conn->httpRequest->getUri()->getHost();
        echo "KEY: " . $conn->Session->get('key');
        
        // ==> I need somethink like:
        $user = $conn->Session->get('user_from_session')->getId();

        // Attach new client
        $this->clients->attach($conn);
    }

    public function onMessage(ConnectionInterface $pClient, $msg)
    {
        $msgObj = json_decode($msg);
        echo "WebSocket-Request msg: " . $msg->msg;
    }
}

使用 websockets 时,没有常规会话或安全对象,我可以在其中加载登录的用户实体 (->getUser())。如上面链接中所述,可以使用 Symfony2 Session 对象或包含标头和 cookie 数据的 httpRequest 对象。有没有办法在其中一个实例中检索用户对象甚至用户 ID?

我能找到的只是这样的示例和解决方法:

Send user ID from browser to websocket server while opening connection

How do you send data to the server onload using ratchet websockets?

https://medium.com/@nihon_rafy/create-a-websocket-server-with-symfony-and-ratchet-973a59e2df94

现在有解决这个问题的现代解决方案吗?或者我可以阅读的任何类型的教程或参考资料?

【问题讨论】:

    标签: php symfony websocket session-cookies ratchet


    【解决方案1】:

    我正在为 websocket 服务器开发一个 Laravel 8.0 应用程序、PHP 7.4 和 cboden/Ratchet 包。 当后端触发事件时,我需要向用户/用户组发送通知,或更新 UI。

    我所做的是:

    1) 使用 composer 安装 amphp/websocket-client 包。

    2) 创建了一个单独的类,以便实例化一个可以连接到 websocket 服务器、发送所需消息并断开连接的对象:

    namespace App;
    use Amp\Websocket\Client;
    
    class wsClient {
    
       public function __construct() {
          //
       }
    
    
       // Creates a temporary connection to the WebSocket Server
       // The parameter $to is the user name the server should reply to.
       public function connect($msg) {
          global $x;
          $x = $msg;
          \Amp\Loop::run(
             function() {
                global $x;
                $connection = yield Client\connect('ws://ssa:8090');
                yield $connection->send(json_encode($x));
                yield $connection->close();
                \Amp\Loop::stop();
             }
          );
       }
    }
    

    3) 处理程序类中的 onMessage() 事件如下所示:

       /**
        * @method onMessage
        * @param  ConnectionInterface $conn
        * @param  string              $msg
        */   
       public function onMessage(ConnectionInterface $from, $msg) {
          $data = json_decode($msg);
          // The following line is for debugging purposes only
          echo "   Incoming message: " . $msg . PHP_EOL;
          if (isset($data->username)) {
             // Register the name of the just connected user.
             if ($data->username != '') {
                $this->names[$from->resourceId] = $data->username;
             }
          }
          else {
             if (isset($data->to)) {
                // The "to" field contains the name of the users the message should be sent to.
                if (str_contains($data->to, ',')) {
                   // It is a comma separated list of names.
                   $arrayUsers = explode(",", $data->to);
                   foreach($arrayUsers as $name) {
                      $key = array_search($name, $this->names);
                      if ($key !== false) {
                         $this->clients[$key]->send($data->message);
                      }
                   }
                }
                else {
                   // Find a single user name in the $names array to get the key.
                   $key = array_search($data->to, $this->names);
                   if ($key !== false) {
                      $this->clients[$key]->send($data->message);
                   }
                   else {
                      echo "   User: " . $data->to . " not found";
                   }
                }
             } 
          }
    
          echo "  Connected users:\n";
          foreach($this->names as $key => $data) {
             echo "   " . $key . '->' . $data . PHP_EOL;
          }
       }
    

    如您所见,您希望 websocket 服务器将消息发送到的用户在 $msg 参数中指定为字符串 ($data->to) 以及消息本身 ($data- >消息)。这两件事是 JSON 编码的,因此参数 $msg 可以被视为一个对象。

    4) 在客户端(布局刀片文件中的 javascript)我在客户端连接时将用户名发送到 websocket 服务器(就像您的第一个链接一样建议)

        var currentUser = "{{ Auth::user()->name }}";
        socket = new WebSocket("ws://ssa:8090");
        
        socket.onopen = function(e) {
           console.log(currentUser + " has connected to websocket server");
           socket.send(JSON.stringify({ username: currentUser }));
        };
        
        socket.onmessage = function(event) {
           console.log('Data received from server: ' + event.data);
        };
    

    所以,用户名和它的连接数保存在 websocket 服务器中。

    5) 处理程序类中的 onOpen() 方法如下所示:

       public function onOpen(ConnectionInterface $conn) {
          // Store the new connection to send messages to later
          $this->clients[$conn->resourceId] = $conn;
          echo " \n";
          echo "  New connection ({$conn->resourceId}) " . date('Y/m/d h:i:sa') . "\n";
       }
    

    每次客户端连接到 websocket 服务器时,它的连接号或 resourceId 都会存储在一个数组中。因此,用户名存储在一个数组($names)中,而密钥存储在另一个数组($clients)中。

    6) 最后,我可以在项目中的任何位置创建 PHP websocket 客户端的实例:

    public function handle(NotificationSent $event) {
        $clientSocket = new wsClient();
        $clientSocket->connect(array('to'=>'Anatoly,Joachim,Caralampio', 'message'=>$event->notification->data));
    }
    

    在这种情况下,我使用的是通知事件侦听器的 handle() 方法。

    好的,这适用于想知道如何从 PHP websocket 服务器(AKA 回显服务器)向一个特定客户端或一组客户端发送消息的人。

    【讨论】:

    • 我现在正在处理即使在 Web 请求或浏览器窗口中的不同选项卡之间保持 websocket 客户端连接持久性。
    猜你喜欢
    • 2018-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多