【问题标题】:How to pass message to websocket?如何将消息传递给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?

标签: php node.js websocket symfony4


【解决方案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); })
猜你喜欢
  • 1970-01-01
  • 2017-01-17
  • 2019-07-18
  • 2015-10-04
  • 1970-01-01
  • 2019-08-27
  • 2019-12-28
  • 2020-09-12
  • 2014-04-29
相关资源
最近更新 更多