Using signals to pass real-time data

Problem

You are developing a multi-user OpenTok application and you need a way to pass data between user in real-time.

Solution

Use Session.signal() to notify user when there is new data on the server to be retrieved.

Tutorial Overview

In this tutorial I will show you how to implement this solution by creating a simple chat app that uses OpenTok signals to pass data between users in an OpenTok session. The app will allow connected users to communicate through both video chat and text chat.

I will use Rails for the backend portion of the app, however this technique can be applied to any server-side technology. I also use jQuery for DOM manipulation and AJAX and the OT_LayoutContainer class created and explained in this blog post for managing the layout of the video streams.

Part One: Server set up

The first thing we are going to do is set up our server. This involves 1) creating our app, 2) creating the model to hold chat entries, and 3) creating the controller that we are going to make AJAX calls to. We can do this all with the following rails commands

[code]
rails new ChatTok
rails generate model ChatEntry body:text connectionId:string
rake db:migrate
rails generate controller ChatEntries
[/code]

We create a ChatEntry model that holds the body of the chat message and the connectionId of the user that submitted the message.

Next we are going to define two methods for our controller. The add method will simply create a new chat_entry by parsing for the body and connectionId from a POST request. The latest method will get the latest chat entry from a given connectionId.

[ruby]
class ChatEntriesController < ApplicationController

# GET /chat_entries/latest/:connectionId
def latest
# Get the last chat entry from this connectionId
@chat_entry = ChatEntry.where("connectionId = ?", params[:connectionId]).last

render :json => @chat_entry
end

#POST /chat_entries/add
def add
# Create a new chat entry
@chat_entry = ChatEntry.new
@chat_entry.connectionId = params[:connectionId]
@chat_entry.body = params[:body]

@chat_entry.save

render :json => @chat_entry
end

end
[/ruby]

Finally we add the routes to these methods to our routes.rb file.

[ruby]
get ‘/chat_entries/latest/:connectionId’ , :to => ‘chat_entries#latest’

post ‘/chat_entries/add’ , :to => ‘chat_entries#add’
[/ruby]

Part Two: Client set up

First we do standard OpenTok initialization stuff. I use the OT_Widget class I created previously which basically takes care of all the stream publishing and subscribing for us.

The important thing to notice here is that we have set up an event handler for the signalReceived event.

[javascript]
// Public vars
var session;

// Initialize OpenTok stuff
(function() {
var apiKey = 1127;
var sessionId = ‘287a9e4ad0aa8e501309df11fe53831452fa1167’;
var token = ‘devtoken’;

session = TB.initSession(sessionId);

// This class does all the video publishing and subscribing for us
OT_Widget.init(session, "streamContainer", 800, 300);

// Set up event handler to listen for signals
session.addEventListener("signalReceived", signalReceivedHandler);

session.connect(apiKey, token);
})();
[/javascript]

Now anytime a connection in the session calls Session.signal(), signalReceivedHandler is going to execute and is passed a SignalEvent where we can get the connectionId of the connection that sent the signal.

[javascript]
// Event handler for receiving signals called
// by session.signal() from other clients in session
function signalReceivedHandler(event) {
getChatEntry(event.fromConnection.connectionId);
}
[/javascript]

So whenever we receive a signal, we are going to take the connectionId of the user that sent the signal, and make an AJAX call to the latest method we defined on the server to get the latest chat entry from the connection that sent the signal. Then once we receive the entry, we add it to the page.

[javascript]
// Gets the latest chat entry from the database
// given somebody’s connectionId
function getChatEntry(connectionId) {
$.ajax({
url: ‘/chat_entries/latest/’ + connectionId,
success: function(data) {
$("#chat").append("<li><strong>"
+ data.chat_entry.connectionId
+ ":</strong> " + data.chat_entry.body + "</li>")
}
})
}
[/javascript]

The last thing we need to do is define how we send new chat entries to the server. We make an AJAX call and pass our add method the entry body and connectionId.

The most important thing to notice here is that we call session.signal() after we get a successful result from the server. This is what tells the other connections in the session that new data is available.

[javascript]
// Posts a new chat entry in to the database
function postChatEntry(body) {
var data = {
connectionId: session.connection.connectionId,
body: body
}

$.ajax({
url: ‘/chat_entries/add’,
data: data,
type: ‘POST’,
success: function(data) {
// Signal to other clients that we have inserted new data
session.signal();
}
})
}
[/javascript]

Conclusion

To use signals to pass data between connections in a session, follow these four general steps:

  1. Post data to the server and associate it with a connectionId.
  2. After the data is stored, call session.signal() to notify all connections in the session that there is new data.
  3. Listen for the signalReceived event.
  4. When a client receives a signalReceived event, use the connectionId from the event to get the latest entry on the server

You can see the source code for this tutorial and the complete chat application on the github repo.