使用 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

这一切看起来有点不雅,而且确实没有按预期工作,因为:

我希望有一个 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 有可能会因套接字关闭而出错