我一直在尝试使用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实现这一目标吗?
谢谢!
答案 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
因封闭套接字而错误的可能性很小