【问题标题】:Laravel + Ratchet: Pushing notifications to specific clientsLaravel + Ratchet:向特定客户端推送通知
【发布时间】:2021-07-26 14:19:25
【问题描述】:

我是 websockets 的新手,我想在我的 Laravel 应用程序中实现这样的服务。 我已经阅读了有关此主题的几篇文章/页面,但没有一个解释我需要做什么。他们都展示了如何创建一个“Echo”websocket 服务器,服务器只响应从客户端收到的消息,这不是我的情况。

作为起始基础,我使用了以下提供的代码:

https://medium.com/@errohitdhiman/real-time-one-to-one-and-group-chat-with-php-laravel-ratchet-websocket-library-javascript-and-c64ba20621ed

从命令行或其他控制台运行 websocket 服务器的位置。服务器有自己的类来定义它并导入 WebSocketController 类(MessageComponentInterface),其中包含经典的 WebSocket 服务器事件(onOpen、onMessage、onClose、onError)。

一切正常,但是,我如何“告诉”WebSocket 服务器从另一个类(也属于另一个命名空间)向特定连接(客户端)发送消息?这是通知或事件的情况,其中必须将新的 Web 内容发送到该特定客户端。客户端没有订阅或发布。

正如@Alias 在他的帖子Ratchet PHP - Push messaging service 中所问的那样,我显然无法创建 Websocket 服务器或其事件管理类的新实例,那么向客户端发送内容或消息的最佳方法是什么?

正如大家所见,通信只有一种方式:从 WebSocket 服务器到客户端,而不是相反。 我已经为此准备了一个通知类和一个侦听器类,但是我仍然不知道如何通过 handle() 方法解决与客户端的通信:

namespace App\Listeners;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Events\NotificationSent;
use Illuminate\Queue\InteractsWithQueue;

class LogNotification
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
       //
    }

    /**
     * Handle the event.
     *
     * @param  NotificationSent  $event
     * @return void
     */
    public function handle(NotificationSent $event)
    {
       // Can content or message be sent to the client from here? how?
    }
}

【问题讨论】:

  • 好吧,我一直在使用 RatchetPHP/Pawl,它作为 PHP 客户端工作,但它也阻止了我的 javascript websocket 客户端。一旦 Pawl 断开连接,javascript websocket 客户端就会连接。看起来 Pawl 创建的循环阻止了 js 客户端。我需要 PHP 中的一些东西来向 JS websocket 客户端发送通知,而不会阻止它们。
  • 通过安装 amphp/websocket-client 我可以有一个 PHP 客户端连接,向 websocket 服务器发送一条消息并告诉它发送回复到另一个连接。但是,如果用户浏览应用程序菜单/链接,随后的 HTML 请求会断开 javascript websocket 客户端。稍微阅读一下,我需要将 JS websocket 保留在 web worker 中,以保持连接处于活动状态。如果没有,则会在下一页建立一个新连接,这会使事情更难维护,并产生更多的网络流量来更新连接表/列表。

标签: php laravel notifications pusher ratchet


【解决方案1】:

好的,经过大量研究和研究,我可以在以下位置发布类似问题的答案:

How to send a message to specific websocket clients with symfony ratchet?

这是我找到的解决方案,根据我在此线程的第二条评论中所写的内容。

我使用 composer 为 websocket 服务器安装了 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() 事件,在 webSocket 服务器的处理程序类中,如下所示:

   /**
    * @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 客户端 (wsClient) 的实例,以向任何用户发送任何数据:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多