如何使用 symfony 棘轮向特定的 websocket 客户端发送消息? [英] How to send a message to specific websocket clients with symfony ratchet?

查看:55
本文介绍了如何使用 symfony 棘轮向特定的 websocket 客户端发送消息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

There is a symfony application that uses php websockets with Ratchet (http://socketo.me/docs/sessions). It seems to be very common to create a websocket application that can broadcast a received messages to all connected clients (web browsers). But I have big issues to send a message only to a specific client (e.g. load user with getUser() and find its belonging websocket client object).

这是我的设置:

// 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?

When using websockets there is no regular session or security object where I can load the logged in user entity (->getUser()). As described in the link above there is the possibility to use the Symfony2 Session object or the httpRequest object that contains header and cookie data. Is there a way to retrieve the user object or even the user id in on of there instances?

我能找到的只有以下示例和解决方法:

All I could find are examples and workarounds like these:

将用户 ID 从浏览器发送到 websocket服务器同时打开连接

如何您是否使用棘轮网络套接字将数据发送到服务器上?

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

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

Is there a modern solution to this now? Or any kind of a tutorial or reference where I could read about?

推荐答案

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

I am developing a Laravel 8.0 application, PHP 7.4 and cboden/Ratchet package for the websocket server. I needed to send notifications to a user/group of users, or update the UI, when an event is fired in the backend.

我所做的是:

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

1) Installed amphp/websocket-client package using composer.

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

2) Created a separated class in order to instantiate an object that could get connected to the websocket server, send the desired message and disconnect:

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() 事件如下所示:

3) The onMessage() event, in the handler class, looks like this:

   /**
    * @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 可以被视为一个对象.

As you can see the user(s), you want the websocket server to send the message to, are specified as a string ($data->to) in the $msg parameter along with the message itself ($data->message). Those two things are JSON encoded so that the parameter $msg can be treated as an object.

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

4) On the client side (javascript in a layout blade file) I send the user name to the websocket server when the client connects (just like your first link suggests to)

    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 服务器中.

So, the user name and its connection number are saved in the websocket server.

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

5) The onOpen() method in the handler class looks like this:

   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 服务器时,它的连接号或资源 ID 都存储在一个数组中.这样,用户名存储在一个数组 ($names) 中,而键存储在另一个数组 ($clients) 中.

Every time a client gets connected to the websocket server, its connection number or resourceId is stored in a array. So that, the user names are stored in an array ($names), and the keys are stored in another array ($clients).

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

6) Finally, I can create an instance of the PHP websocket client anywhere in my project:

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

在本例中,我使用的是通知事件侦听器的 handle() 方法.

In this case I am using the handle() method of a notification event Listener.

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

Alright, this is for anyone wondering how to send messages from the PHP websocket server (AKA echo server) to one specific client or to a group of clients.

这篇关于如何使用 symfony 棘轮向特定的 websocket 客户端发送消息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆