使用 Crystal/Kemal 监听 UDP 数据包
Using Crystal/Kemal to listen for UDP packets
我一直在尝试使用 Crystal 和 Kemal 创建一个非阻塞服务器,它将 (a) 侦听发送给它的 UDP 消息流,以及 (b) 然后将该消息转发给一个 WebSocket 到任何已经启动 ws 连接的浏览器。
到目前为止,我能做到的最好的是:
require "kemal"
require "socket"
server = UDPSocket.new
server.bind "localhost", 1234
puts "Started..."
ws "/" do |socket|
udp_working = true
while udp_working
message, client_addr = server.receive
socket.send message
end
socket.on_close do
puts "Goodbye..."
udp_working = false
end
end
这一切看起来有点不雅,而且确实没有按预期工作,因为:
- 在正在启动的 Crystal 服务器和连接到 Crystal 服务器的第一个 Web 浏览器之间发送的所有 UDP 数据包都被缓存并在一个巨大的积压中发送
- 浏览器与 WebSockets 断开连接没有得到正确处理,即 socket.on_close 没有被触发,循环一直持续到我终止 Crystal 服务器
我希望有一个 server.on_message 类型的处理,这样我就可以 运行 仅在收到 UDP 数据包时进行编码,而不是持续轮询而阻塞服务器。是否有另一种方法可以使用 Crystal/Kemal?
实现此目的
谢谢!
你的方法有几个问题:
首先,socket.on_close
无法工作,因为从未达到此行。 while 循环将 运行 只要 udp_working == true
并且它只会在 on_close
挂钩中设置为 false
。
如果你不想让 UDP 数据报堆积起来,你需要从头开始接收它们,并且在没有连接 websocket 的情况下做任何你想做的事情(也许是处理?)。 UDPServer
没有 on_message
挂钩,但 receive
已经是非阻塞的。所以你可以 运行 它在一个循环中(在它自己的纤程中)并在方法 returns 时采取行动。详见Crystal Concurrency;还有一个使用TCPSocket的例子,但是UDP在这方面应该是类似的。
Crystal 为您处理非阻塞,您需要在单独的纤程中编写阻塞代码并使用通道进行通信。 Crystal 将在幕后使用非阻塞代码和 select() 调用。
此外,您需要一个框架或您自己的一些代码来将收到的消息复制到每个侦听的 websocket。这通常称为 publish/subscribe 或 pub/sub。
…
ws "/" do |socket|
udp_working = true
while udp_working
message, client_addr = server.receive
socket.send message
end
socket.on_close do
puts "Goodbye..."
udp_working = false
end
end
i.e. socket.on_close is not being triggered,
您要先 运行 socket.on_close
,否则事件处理程序将不会被添加,因此直到循环之后才会 运行,但循环实际上是无限的因为那个。此外,如果套接字在检查 udp_working
var 和调用 #send
之间关闭,则 socket.send message
有可能会因套接字关闭而出错
我一直在尝试使用 Crystal 和 Kemal 创建一个非阻塞服务器,它将 (a) 侦听发送给它的 UDP 消息流,以及 (b) 然后将该消息转发给一个 WebSocket 到任何已经启动 ws 连接的浏览器。
到目前为止,我能做到的最好的是:
require "kemal"
require "socket"
server = UDPSocket.new
server.bind "localhost", 1234
puts "Started..."
ws "/" do |socket|
udp_working = true
while udp_working
message, client_addr = server.receive
socket.send message
end
socket.on_close do
puts "Goodbye..."
udp_working = false
end
end
这一切看起来有点不雅,而且确实没有按预期工作,因为:
- 在正在启动的 Crystal 服务器和连接到 Crystal 服务器的第一个 Web 浏览器之间发送的所有 UDP 数据包都被缓存并在一个巨大的积压中发送
- 浏览器与 WebSockets 断开连接没有得到正确处理,即 socket.on_close 没有被触发,循环一直持续到我终止 Crystal 服务器
我希望有一个 server.on_message 类型的处理,这样我就可以 运行 仅在收到 UDP 数据包时进行编码,而不是持续轮询而阻塞服务器。是否有另一种方法可以使用 Crystal/Kemal?
实现此目的谢谢!
你的方法有几个问题:
首先,socket.on_close
无法工作,因为从未达到此行。 while 循环将 运行 只要 udp_working == true
并且它只会在 on_close
挂钩中设置为 false
。
如果你不想让 UDP 数据报堆积起来,你需要从头开始接收它们,并且在没有连接 websocket 的情况下做任何你想做的事情(也许是处理?)。 UDPServer
没有 on_message
挂钩,但 receive
已经是非阻塞的。所以你可以 运行 它在一个循环中(在它自己的纤程中)并在方法 returns 时采取行动。详见Crystal Concurrency;还有一个使用TCPSocket的例子,但是UDP在这方面应该是类似的。
Crystal 为您处理非阻塞,您需要在单独的纤程中编写阻塞代码并使用通道进行通信。 Crystal 将在幕后使用非阻塞代码和 select() 调用。
此外,您需要一个框架或您自己的一些代码来将收到的消息复制到每个侦听的 websocket。这通常称为 publish/subscribe 或 pub/sub。
…
ws "/" do |socket| udp_working = true while udp_working message, client_addr = server.receive socket.send message end socket.on_close do puts "Goodbye..." udp_working = false end end
i.e. socket.on_close is not being triggered,
您要先 运行 socket.on_close
,否则事件处理程序将不会被添加,因此直到循环之后才会 运行,但循环实际上是无限的因为那个。此外,如果套接字在检查 udp_working
var 和调用 #send
socket.send message
有可能会因套接字关闭而出错