首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使用symfony ratchet向特定的websocket客户端发送消息?

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

Stack Overflow用户
提问于 2020-10-10 19:25:07
回答 1查看 610关注 0票数 0

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

这是我的设置:

代码语言:javascript
运行
复制
// WebsocketServer.php
$server = IoServer::factory(
    new HttpServer(
        new SessionProvider(
            new WsServer(
                new WebSocketCommunicator()
            ),
            new MySessionHandler())

    ),
    $this->_websocketPort
);

$server->run();
代码语言:javascript
运行
复制
// 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数据的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

现在有没有现代的解决方案呢?或者任何我可以读到的教程或参考资料?

EN

回答 1

Stack Overflow用户

发布于 2021-06-03 13:06:11

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

我所做的是:

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

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

代码语言:javascript
运行
复制
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()事件,如下所示:

代码语言:javascript
运行
复制
   /**
    * @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-> message )。这两件事都是JSON编码的,这样参数$msg就可以被视为对象。

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

代码语言:javascript
运行
复制
    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()方法如下所示:

代码语言:javascript
运行
复制
   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)最后,我可以在项目中的任何地方创建project客户端的实例:

代码语言:javascript
运行
复制
public function handle(NotificationSent $event) {
    $clientSocket = new wsClient();
    $clientSocket->connect(array('to'=>'Anatoly,Joachim,Caralampio', 'message'=>$event->notification->data));
}

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

好吧,这篇文章是为那些想知道如何从PHP websocket服务器(AKA echo server)向一个特定的客户端或一组客户端发送消息的人准备的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64292868

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档