且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何使用 symfony 棘轮向特定的 websocket 客户端发送消息?

更新时间:2022-06-09 20:06:02

我正在为 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.