rubywebsocketem-websocket

is Ruby em-websocket blocking?


I'm writing a ruby program that has 2 threads. One that listens on an incoming UDP connection and another that broadcasts on a websocket from which browsers on the client side read.I'm using the em-websocket gem. However, My UDP listener thread never gets called and it looks like the code stays within the websocket initialization code. I'm guessing because em-websocket is blocking, but I haven't been able to find any info online that suggests that. Is it an error on my side? I'm kinda new to ruby so I'm not able to figure out what I'm doing wrong.

require 'json'
require 'em-websocket'
require 'socket'

socket=nil
text="default"
$x=0

EventMachine.run do
EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
  ws.onopen    { 
            ws.send "Hello Client!"
            socket=ws
            $x=1
        }

  ws.onmessage { |msg| socket.send "Pong: #{msg}" }
  ws.onclose   { puts "WebSocket closed" }
end
end
def listen()
puts "listening..."
s = UDPSocket.new
s.bind(nil, 3000)
while 1<2 do
  text, sender = s.recvfrom(1024)     
  puts text
  if $x==1 then
    socket.send text      
  end   
end
end

t2=Thread.new{listen()}
t2.join

Solution

  • em-websocket is non-blocking, however UDPSocket#recv_from is. Might be better to just use EventMachine's open_datagram_socket instead.

    Another thing to note: you should not expose socket as a "global" variable. Every time somebody connects the reference to the previously connected client will be lost. Maybe make some sort of repository for socket connections, or use an observer pattern to broadcast messages when something comes in. What I would do is have a dummy object act as an observer, and whenever a socket is connected/disconnect you register/unregister from the observer:

    require 'observer'
    
    class Dummy
      include Observable
    
      def receive_data data
        changed true
        notify_observers data
      end
    end
    
    # ... later on ...
    
    $broadcaster = Dummy.new
    
    class UDPHandler < EventMachine::Connection
      def receive_data data
        $broadcaster.receive_data data
      end
    end
    
    EventMachine.run do
      EM.open_datagram_socket "0.0.0.0", 3000, UDPHandler
    
      EM::WebSocket.start :host => "0.0.0.0", :port => 8080 do |ws|
        ws.onopen do
          $broadcaster.add_observer ws
        end
    
        ws.onclose do
          $broadcaster.delete_observer ws
        end
    
        # ...
      end
    end
    

    The whole point of EventMachine is to abstract away from the basic socket and threading structure, and handle all the asynchronous bits internally. It's best not to mix the classical libraries like UDPSocket or Thread with EventMachine stuff.