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

如何解决如何使用symfony ratchet向特定的websocket客户发送消息?

有一个symfony应用程序,它使用带有棘轮(http://socketo.me/docs/sessions)的php websockets。创建一个Websocket应用程序似乎很普遍,该应用程序可以将收到的消息广播到所有连接的客户端(Web浏览器)。但是我很难将消息仅发送到特定客户端(例如,使用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())。如上面的链接所述,可以使用包含标题和cookie数据的Symfony2 Session对象或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

现在有解决这个问题的现代方法吗?还是我可以阅读的任何教程或参考书?

解决方法

我正在为 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 服务器时,它的连接号或资源 ID 都存储在一个数组中。这样,用户名存储在一个数组 ($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 服务器(也称为回显服务器)向一个特定客户端或一组客户端发送消息的任何人。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-