ardavis
ardavis

Reputation: 9895

Ruby - Send message to a Websocket

How can I send data to a WebSocket using Ruby in a Background Process?

Background

I already have a separate ruby file running a Websocket server using the websocket-eventmachine-server gem. However, within my Rails application, I want to send data to the websocket in a background task.

Here is my WebSocket server:

EM.run do
  trap('TERM') { stop }
  trap('INT') { stop }

  WebSocket::EventMachine::Server.start(host: options[:host], port: options[:port]) do |ws|

    ws.onopen do
      puts 'Client connected'
    end

    ws.onmessage do |msg, type|
      ws.send msg, type: type
    end

    ws.onclose do
      puts 'Client disconnected'
    end

  end

  def stop
    puts 'Terminating WebSocket Server'
    EventMachine.stop
  end
end

However, in my background process (I'm using Sidekiq), I'm not sure how to connect to the WebSocket and send data to it.

Here's my Sidekiq worker:

class MyWorker
  include Sidekiq::Worker

  def perform(command)
    100.times do |i|
      # Send 'I am on #{i}' to the Websocket
    end  
  end

end

I was hoping to be able to do something like EventMachine::WebSocket.send 'My message!' but I don't see an API for that or something similar. What is the correct way to send data to a WebSocket in Ruby?

Upvotes: 4

Views: 4181

Answers (2)

Myst
Myst

Reputation: 19221

Accepted Answer:

Af your keeping your current websocket server:

You can use Iodine as a simple websocket client for testing. It runs background tasks using it's own reactor pattern based code and has a websocket client (I'm biased, I'm the author).

You could do something like this:

require 'iodine/http'
Iodine.protocol = :timers
# force Iodine to start immediately
Iodine.force_start!

options = {}
options[:on_message] = Proc.new {|data| puts data}

100.times do |i|
    options[:on_open] = Proc.new {write "I am number #{i}"}
    Iodine.run do
        Iodine::Http.ws_connect('ws://localhost:3000', options) 
    end
end

P.S.

I would recommend using a framework, such as Plezi, for your websockets (I'm the author). Some frameworks let you run their code within a Rails/Sinatra app (Plezi does that and I think Faye, although not strictly a framework, does that too).

Using EM directly is quite hardcore and there are a lot of things to manage when dealing with Websockets, which a good framework helps you manage.

EDIT 3:

Iodine WebSocket client connections are (re)supported starting with Iodine 0.7.17, including TLS connections when OpenSSL >= 1.1.0.

The following code is an updated version of the original answer:

require 'iodine'

class MyClient
  def on_open connection
    connection.subscribe :updates
    puts "Connected"
  end
  def on_message connection, data
    puts data
  end
  def on_close connection
    # auto-reconnect after 250ms.
    puts "Connection lost, re-connecting in 250ms"
    Iodine.run_after(250) { MyClient.connect }
  end

  def self.connect
    Iodine.connect(url: "ws://localhost:3000/path", handler: MyClient.new)
  end
end


Iodine.threads = 1
Iodine.defer { MyClient.connect if Iodine.master? }
Thread.new { Iodine.start }

100.times {|i| Iodine.publish :updates, "I am number #{i}" }

EDIT 2:

This answer in now outdated, since Iodine 0.2.x doesn't include a client any longer. Use Iodine 0.1.x or a different gem for websocket clients.

Upvotes: 3

fotanus
fotanus

Reputation: 20116

websocket-eventmachine-server is a websockets server.

If you want to connect to a websocket server using ruby, you can do it with some gems, like

Upvotes: 1

Related Questions