使用Crystal / Kemal侦听UDP数据包

时间:2017-10-18 08:18:26

标签: udp crystal-lang kemal

我一直在尝试使用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实现这一目标吗?

谢谢!

2 个答案:

答案 0 :(得分:3)

您的方法存在一些问题:

首先,socket.on_close无效,因为永远不会到达此行。 while循环的运行时间与udp_working == true一样长,并且只会在false挂钩中设置为on_close

如果你不希望UDP数据报堆积起来,你需要从头开始接收它们,如果没有连接websocket,你可以做任何你想做的事情(可能会处理?)。 on_message没有UDPServer个钩子,但receive已经无阻塞。因此,您可以在循环中(在它自己的光纤中)运行它,并在方法返回时执行操作。有关详细信息,请参阅Crystal Concurrency;还有一个使用TCPSocket的例子,但UDP在这方面应该是相似的。

答案 1 :(得分:3)

Crystal为您处理非阻塞,您需要在单独的光纤中编写阻塞代码并使用通道进行通信。 Crystal将在幕后使用非阻塞代码和select()调用。

此外,您需要一个框架或一些自己的代码来将收到的消息复制到每个监听websocket。这通常称为发布/订阅或发布/订阅。

  

...

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
  

即。 socket.on_close未被触发,

您希望首先运行socket.on_close,否则事件处理程序将不会被添加,因此在循环之后才会运行,但循环实际上是无限的。此外,如果在检查socket.send message var和调用udp_working

之间关闭套接字,则#send因封闭套接字而错误的可能性很小