I have been trying to create a non-blocking server using Crystal and Kemal which will (a) listen for a stream of UDP messages being sent to it, and (b) then forwarding that message to a WebSocket to any browsers who have started a ws connection.
So far, the best I can manage is:
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
This all seems a little inelegant, and indeed, doesn't work as expected because:
I was hoping for a server.on_message type handling which would enable me to run code only when UDP packets were received, rather than continual polling which blocks the server. Is there another way to achieve this using Crystal/Kemal?
Thanks!
There are several problems with your approach:
First, socket.on_close
can't work because this line is never reached. The while loop will run as long as udp_working == true
and it would only be set to false
in the on_close
hook.
If you don't want UDP datagrams to pile up, you need to receive them from the beginning and do whatever you want (perhaps dispose?) if there is no websocket connected. There is no on_message
hook for UDPServer
but receive
is already non-blocking. So you can just run it in a loop (in it's own fiber) and act whenever the method returns. See Crystal Concurrency for details; there is also an example using a TCPSocket, but UDP should be similar in this regard.