如何将消息传递给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 - “私人”消息传递
如何使用 websocket 检查消息是不是真的在 Netty 中传递?