Michel Chatmajian
Michel Chatmajian

Reputation: 53

How can I send messages to specific client using Faye Websockets?

I've been working on a web application which is essentially a web messenger using sinatra. My goal is to have all messages encrypted using pgp and to have full duplex communication between clients using faye websocket.

My main problem is being able to send messages to a specific client using faye. To add to this all my messages in a single chatroom are saved twice for each person since it is pgp encrypted.

So far I've thought of starting up a new socket object for every client and storing them in a hash. I do not know if this approach is the most efficient one. I have seen that socket.io for example allows you to emit to a specific client but not with faye websockets it seems ? I am also considering maybe using a pub sub model but once again I am not sure.

Any advice is appreciated thanks !

Upvotes: 3

Views: 776

Answers (1)

Myst
Myst

Reputation: 19221

I am iodine's author, so I might be biased in my approach.

I would consider naming a channel by the used ID (i.e. user1...user201983 and sending the message to the user's channel.

I think Faye will support this. I know that when using the iodine native websockets and builtin pub/sub, this is quite effective.

So far I've thought of starting up a new socket object for every client and storing them in a hash...

This is a very common mistake, often seen in simple examples.

It works only in single process environments and than you will have to recode the whole logic in order to scale your application.

The channel approach allows you to scale using Redis or any other Pub/Sub service without recoding your application's logic.

Here's a quick example you can run from the Ruby terminal (irb). I'm using plezi.io just to make it a bit shorter to code:

require 'plezi'

class Example
  def index
    "Use Websockets to connect."
  end
  def pre_connect
    if(!params[:id])
      puts "an attempt to connect without credentials was made."
      return false
    end
    return true
  end
  def on_open
    subscribe channel: params[:id]
  end
  def on_message data
    begin
      msg = JSON.parse(data)
      if(!msg["to"] || !msg["data"])
        puts "JSON message error", data
        return
      end
      msg["from"] = params[:id]
      publish channel: msg["to"].to_s, message: msg.to_json
    rescue => e
      puts "JSON parsing failed!", e.message
    end

  end
end

Plezi.route "/" ,Example
Iodine.threads = 1
exit

To test this example, use a Javascript client, maybe something like this:

// in browser tab 1
var id = 1
ws = new WebSocket("ws://localhost:3000/" + id)
ws.onopen = function(e) {console.log("opened connection");}
ws.onclose = function(e) {console.log("closed connection");}
ws.onmessage = function(e) {console.log(e.data);}
ws.send_to = function(to, data) {
    this.send(JSON.stringify({to: to, data: data}));
}.bind(ws);

// in browser tab 2
var id = 2
ws = new WebSocket("ws://localhost:3000/" + id)
ws.onopen = function(e) {console.log("opened connection");}
ws.onclose = function(e) {console.log("closed connection");}
ws.onmessage = function(e) {console.log(e.data);}
ws.send_to = function(to, data) {
    this.send(JSON.stringify({to: to, data: data}));
}.bind(ws);

ws.send_to(1, "hello!")

Upvotes: 1

Related Questions