如何将消息传递给websocket?

Posted

技术标签:

【中文标题】如何将消息传递给websocket?【英文标题】:How to pass message to websocket? 【发布时间】:2021-09-01 20:59:54 【问题描述】:

我有一个 twig 模板,我正在尝试在 url 中发送具有相同令牌的消息意味着从 http://url/token1 发送的消息只能由具有相同令牌的另一个 url 接收。

但我无法将任何消息从 twig 传递到 node 到 symfony。

index.html.twig

<div id="chat">

</div>
<div>
    <div class="form-group">
        <label for="name">Name:</label> <input type="text" id="name">
    </div>
    <div class="form-group">
        <label for="message">Message:</label> <input type="text" id="message">
    </div>
    <button type="button" id="sendBtn" class="btn-primary">Send</button>
</div>

<script src="/bin/ws-broker.js"></script>
<script>
    const socket = new WebSocket("ws://localhost:8080");
    document.getElementById("sendBtn").addEventListener("click", function() 
        const message = 
            name: document.getElementById("name").value,
            message: document.getElementById("message").value
        ;
        socket.send(JSON.stringify(message));
    );
</script>

/bin/ws-broker.js

const WebSocket = require('ws');
const qs = require('querystring');
const wss = new WebSocket.Server( port: 8080 );
const http = require('http');

    wss.on('connection', function connection(ws, req)
    
        console.log('Connection Received from IP: ' + req.socket.remoteAddress);
        ws.on('message', function incoming(message) 
            if ('' === message) 
                //empty message
                return;
            
            try 
                //process message as JSON object
                message = JSON.parse(message);
             catch (e) 
                //failed parsing the message as JSON
                return;
            
    
        );
    );
    
    console.log('Listening on port 8080');

控制器

    /**
     * @Route("/token", name="home")
     */
    public function index(): Response
    
        return $this->render('home/index.html.twig', [
            'controller_name' => 'HomeController',
        ]);
    

【问题讨论】:

我提供的/bin/ws-broker.js 根本不打算放在前端。它必须通过后端的 nodejs 作为服务器运行。例如:node bin/ws-broker.js 替换 symfony bin/console --env=prod app:command 有很多东西可能会阻止 websockets。仅此页面就列出了十几个:support.grammarly.com/hc/en-us/articles/…。建议:1)首先关注您的开发环境。编写一对简单的“hello world”应用程序只是为了验证您可以将消息从客户端发送到服务器。 2)然后从那里扩大你的范围。 3) 一旦您对 end::end 通信在您的开发环境中工作感到满意,请在更“现实”的环境中进行测试。 @Will B. 是的,我已经启动了节点 token 来自哪里?它需要从客户端 javascript 发送到节点 websocket 侦听器bin/ws-broker.js。客户应该如何处理响应? 这是一个相同的问题,不是后续问题:How to arguments into the Websocket handler? 【参考方案1】:

NodeJS WebSocket 代理服务和浏览器 WebSocket 客户端似乎有些混淆。

为了澄清,WebSocket 代理在后端作为 Apache 旁边的服务器运行,以侦听从浏览器 WebSocket 客户端发送的消息。与 Apache 侦听来自浏览器客户端的 HTTP 请求的方式相同。

WebSocket 代理服务器接收到一条消息,然后将数据作为 HTTP 请求转发,以便 Symfony 可以处理该请求并将代理返回给 WebSocket 客户端的响应发送回。

该过程与 Ratchet 的侦听方式相同,但没有使用 php bin/console app:command 在永久运行状态下加载 Symfony 和 Doctrine 的开销。除了已经在 Symfony 环境中之外,bin/ws-broker.js 会创建一个 HTTP 请求以发送到 Symfony。

bin/ws-broker.js 进程分解是这样的。

Browser HTTP Request -> /path/to/index -> 
  Symfony AppController::index() -> 
  return Response(Twig::render('index.html.twig')) -> 
    WebSocket Client - socket.send(message) -> 
      [node bin/ws-broker.js WebSocket Server - ws.on('message')] ->
      [node bin/ws-broker.js http.request()] -> /path/to/score-handler -> 
        Symfony AppController::scoreHandler() -> 
        return JsonResponse(data) ->  
      [node bin/ws-broker.js WebSocket Server - ws.send(response) to WebSocket Client] ->
    WebSocket Client - socket.onmessage()

bin/console app:command Ratchet 进程分解如下。

Browser HTTP Request -> /path/to/index -> 
  Symfony AppController::index() -> 
  return Response(Twig::render('index.html.twig')) -> 
    WebSocket Client - socket.send(message) -> 
      [php bin/console app:command - Ratchet\MessageComponentInterface::onMessage()] ->
      [php bin/console app:command - $client->send(response) to WebSocket Client] ->
    WebSocket Client - socket.onmessage()

将 Symfony HTTP 请求和响应处理程序添加到 NodeJS WebSocket 侦听器服务

// bin/ws-broker.js

const WebSocket = require('ws');
const qs = require('querystring');
const http = require('http');
const wss = new WebSocket.Server( port: 8080 );

wss.on('connection', function connection(ws, req) 
    console.log('Connection Received from IP: ' + req.socket.remoteAddress);
    ws.on('message', function incoming(message) 
        if ('' === message) 
            //empty message
            return;
        

        try 
            //process message as JSON object
            message = JSON.parse(message);
         catch (e) 
            //failed parsing the message as JSON
            return;
        

        //convert the WS message to a query string for the Symfony Request object
        let postData = qs.stringify(
            "name": message.name, 
            "message": message.message
        );
        let http_options = 
            host: 'localhost',
            path: '/' + message.token,
            port: 8000,
            method: 'POST',
            headers: 
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': postData.length
            
        ;

        //forward the message to Symfony using an HTTP Request
        let req = http.request(http_options, function(res) 
            res.setEncoding('utf8');
            //process the Symfony response data
            let data = '';
            res.on('data', (chunk) => 
                data += chunk
            );

            //send the Symfony Response back to the WebSocket client
            res.on('end', function() 
                ws.send(data);
            );
        );

        //send the requested message to Symfony
        req.write(postData);
        req.end();
    );
);

console.log('Listening on port 8080');

运行 WebSocket 代理服务器

node bin/ws-broker.js &

添加一个路由来处理代理请求并将响应发送回代理。将令牌添加到 home/index.html.twig 上下文中。

class AppController extends AbstractController

    /**
     * @Route("/token", name="score_handler", requirements= "token":"\w+"  methods= "POST" )
     */
    public function scoreHandler(string $token): JsonResponse
    
        //called by bin/ws-broker.js

        //do things here...

        return $this->json([
            'data' => 'Message Received!'
        ]);
    

    /**
     * @Route("/token", name="home", requirements= "token":"\w+"  methods= "GET" )
     */
    public function index(string $token): Response
    
        //called by Apache/Browser

        return $this->render('home/index.html.twig', [
            'controller_name' => 'HomeController',
            'token' => $token
        ]);
    

从前端移除 bin/ws-broker.js,将令牌发送到 Broker 并添加响应处理程序

<!-- home/index.html.twig -->

<div id="chat"></div>
<div>
    <div class="form-group">
        <label for="name">Name:</label> <input type="text" id="name">
    </div>
    <div class="form-group">
        <label for="message">Message:</label> <input type="text" id="message">
    </div>
    <button type="button" id="sendBtn" class="btn-primary">Send</button>
</div>

<script type="text/javascript">
    const socket = new WebSocket("ws://localhost:8080");
    socket.onmessage = function(evt) 
        window.console.log(evt.data);
        //handle WebSocket Server Response...
    ;
    document.getElementById("sendBtn").addEventListener("click", function() 
        const message = 
            name: document.getElementById("name").value,
            message: document.getElementById("message").value,
            token: " token " //<------ SEND TOKEN to WebSocket Server
        ;
        socket.send(JSON.stringify(message));
    );
</script>

【讨论】:

非常感谢您的解释。令牌来自 URL。例如。 http://localhost:8000/unique_token 使用此 URL,然后我的 index.html.twig 页面被呈现并在发送按钮上单击具有相同令牌 url 的下一个人应该收到消息 更新了home 路由以在 GET 请求期间支持 URL 中的令牌。 我打开了 2 个具有相同 URL 和令牌的选项卡。但是另一个选项卡没有收到来自一个选项卡的消息。 如果您想将消息广播给所有客户端,您必须更改ws.send() 以遍历所有客户端。 Server Broadcast 下一个问题是,您是否尝试根据token 为每条消息创建频道?如果是这样,这会显着改变您的新问题的范围,因为它需要设置发送哪些客户端的条件。 这里有一个很好的例子来说明如何实现一个令牌/用户ID通道系统。 Sending message to a specific connected users using webSocket? 在我的示例中使用 Symfony 进程调整代码 res.on('end', function() toUserWebSocket.send(data); )

以上是关于如何将消息传递给websocket?的主要内容,如果未能解决你的问题,请参考以下文章

Ratchet PHP Websockets:私人消息传递(控制消息发送给谁)

Spring STOMP over Websocket - “私人”消息传递

如何保证 websockets 的可靠消息传递?

如何使用 websocket 检查消息是不是真的在 Netty 中传递?

如何将 websocket 实例传递给 vue.js 组件,然后在其上调用 send()?

Spring消息传递+安全如何通过websockets登录?