OpenTok Sessions using Node.js

Problem

When developing an OpenTok application, one choice developers must make is to decide how to generate new sessions. Should all publishers get their own session? Should only X number of people be allowed in a given session? How do you decide what person goes in to what session?

Since there is no direct way of querying how many clients are in a given session (and who those clients are), it is difficult to implement session management logic. One solution is to track session/client information in a database. Before joining a session, you could query a database that stores client information, however constantly maintaining this database can become slow and complicated, and dealing with client disconnects is non-trivial.

Solution

The solution that I decided on was to use Node.js to create a simple HTTP and socket server that collects information about connected clients (who they are and how many are connected). With that information I can then generate sessions based on whatever logic I would like.

The server works by accepting an incoming client, processing the logic to determine which session the client should be placed in, and then sending all the relevant session data back to the client. This is a fast, scalable, and lightweight solution that broadcasts in real-time, doesn’t require a database, and is created entirely in JavaScript.

Tutorial Overview

In this tutorial I will discuss how I implemented the server using Node.js and the OpenTok Node.js SDK created by Brian Stoner.

Assumptions

This tutorial assumes that you are familiar with the OpenTok API. If you are not please first read through the Getting Started Guide.

This tutorial also assumes that you have installed Node.js and the node package manager. I will try to walk through each part without any assumed knowledge beyond the Hello World tutorial from Node.js. If you would like to learn more about using Node.js, I recommend reading this outstanding tutorial.

Use Case

The particular use case that this tutorial will solve is the following:

  • Each session can have a maximum number of 3 users
  • When a new user connects, the user is put in to the first session that is not full
  • If all sessions are full, a new session will be generated

While this tutorial uses the above logic to solve this particular use case, the techniques discussed here can be applied to several other cases.

Part One: Download Required Modules

The server uses three Node modules:

  • opentok: OpenTok Server-Side SDK used for generating sessions and tokens.
  • socket.io: Enables easy two-way, real-time connection between server and client.
  • connect: Middleware framework for adding features to HTTP server.

Install these modules using Node Package Manager with the following commands:

npm install opentok
npm install socket.io
npm install connect

Once you have modules installed, download the project code from the github repo with this command:

git clone https://github.com/jonmumm/OpenTok-Node-Session.git

Once you have the repository downloaded, cd to that directory and run this command to start the server:

node server.js

This will start the server on port 3000. You should now be able to view the application at http://localhost:3000.

Notice that our node server is actually serving the HTTP content, it contains its own HTTP server. This is done in only a few lines of code. This is pretty cool.

I will go in to more detail of how this works, however, you should know that while node can serve our HTTP content, it does not have to. For our purposes, we could just as easily use an existing HTTP server and use the node server for doing other things (like for sockets, which is what we use it for).

The project consists of two files:

  • server.js: the node server-side code.
  • index.html: the client that connects to the node server.

I will go through both files and discuss how each works.

Part Two: Server-Side Code

The server-side code can be separated into two parts. The first part initializes the HTTP server, socket server, and event handlers and the second part implements the OpenTok session generation logic for my particular use case.

Here is the first part of the server:

// Import the Node modules we need
var connect = require('connect'),
	io = require('socket.io'),
	opentok = require('opentok');

// Set up the HTTP server for serving our static files
var server = connect(
	connect.static(__dirname + "/web")
);
server.listen(3000); // Start server on port 3000

// Set up Socket listener and initialize event handlers
var socket = io.listen(server);
socket.on('connection', function(client) {

	// When a client connects, figure out which session to join
	getSession(client);

	client.on('disconnect', function() {
		// When a client disconnects, drop that client from the session
		leaveSession(client);
	});
});

First we import the required modules we need. Then we initialize the HTTP server. For that I am using the Connect middleware framework which allows you to quickly specify middleware modules to run on your HTTP server. In this case I am simply specifying what directory to serve the static content out of (i.e. my html and javascript files). For our purposes, using the HTTP server to serve your content is optional; you could serve your files elsewhere and just use the node server for sockets.

Once our HTTP server is created and listening on port 3000, we create the socket server. The socket server is what will allow us to send messages back and forth between client and server in real-time.

Notice that we pass the HTTP server as a parameter when initializing our socket. This makes it so that the HTTP and socket servers are both listening on the same port. However neither server takes over the other. What will happen is, any socket request will be automatically intercepted by the socket server, and all other requests will go to the HTTP server.

After our socket server is initialized, we set up event handlers for when a client connects and disconnects. Essentially what we are doing here is saying “when a client connects, get that client a session to connect to” and “when a client disconnects, remove that client from the session”.

Here we only set up event handlers for the “connection” and “disconnect” events from the client, however you could also set up a listener for the “message” event for when the client sends a message to the server, however we have no need for that here.

That is all the code you need for setting up the basics of the server. Now we will dive in to the logic for generating OpenTok sessions.

// OpenTok Variables
var OPENTOK_API_KEY = '413302',		// Replace with your API key
	OPENTOK_API_SECRET = 'fc512f1f3c13e3ec3f590386c986842f92efa7e7',		// Replace with your API secret

	// OpenTok SDK
	ot = new opentok.OpenTokSDK(OPENTOK_API_KEY, OPENTOK_API_SECRET),

	// NOTE: Uncomment for production, defaults to "staging.tokbox.com"
	// ot.setEnvironment("api.tokbox.com"),

	// Variables for managing OpenTok Sessions
	MAX_SESSION_CONNECTIONS = 3,	// Maximum number of client connections we want in a given session
 	session_map = {},				// Hash for getting the session of a given client
	ot_sessions = new Array();		// Array for holding all sessions we have generated

// Finds an available session for the client to connect to
function getSession(client) {

	var session;
	// Look through all sessions to find a session that has less than the max number of sessions
	// NOTE: We start searching from the top of the array since it is more likely a non-full session is there
	for (var i = ot_sessions.length - 1; i >= 0; i--) {
		var tmp_session = ot_sessions[i];
		if (tmp_session.clients.length < MAX_SESSION_CONNECTIONS) {
			session = tmp_session;
			break;
		}
	}

	if (!session) {
		// If we didn't find a session, generate one and enter it
		ot.createSession('localhost',{},function(session) {
			ot_sessions.push(session);
			enterSession(session,client);
		})
	} else {
		// Otherwise enter the session we found
		enterSession(session, client);
	}
}

// Sends the session info back to the client for the client to join
function enterSession(session, client) {
	// Construct info object to pass back to client then send it
	var opentok_info = {
		sessionId: session.sessionId,
		apiKey: OPENTOK_API_KEY,
		token: ot.generateToken()
	}
	client.send(opentok_info);

	// Create array to hold all the clients in the session
	if (!session.clients) {
		session.clients = new Array();
	}

	// Add the client to the session
	session.clients.push(client.sessionId);
	session_map[client.sessionId] = session;	// Use map later to identify what session client was in
}

// Finds which session the client was in and removes the client from that session.
function leaveSession(client) {
	// Find the session that the client was in
	var session = session_map[client.sessionId];

	// Find the position of the client in the session
	var index = session.clients.indexOf(client.sessionId);

	// Remove the client from the session
	session.clients.splice(index, 1);
}

All we are doing here is implementing the logic from the use case I described above. We first look to see if there are any sessions that have less than 3 people in it, if so we use the first session that does. Otherwise we generate a new session. Once we have the sessionId, we package it into a JSON object along with a token and the apiKey and send it through the socket to the client.

Part Three: Client-Side Code

Since the node server takes care of all the session logic, it makes our client implementation very simple.

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
	<title>OpenTok Node.js</title>
	
	<script src="http://cdn.socket.io/stable/socket.io.js" type="text/javascript"></script>
	<script src="http://jonmumm.github.com/OpenTok-JS-LayoutContainer/OT_LayoutContainer.js" type="text/javascript"></script>
	<script src="http://staging.tokbox.com/v0.91/js/TB.min.js" type="text/javascript" ></script>
	<script src="OT_Widget.js" type="text/javascript" ></script>
	
	<script>
		// Initialize the socket, set up event handler, and connect
		var socket = new io.Socket(null, {port: 3000, rememberTransport: false});
		socket.on("message",messageHandler);
		socket.connect();
		
		function messageHandler(message) {	
			var opentok_info = message;
			
			OT_Widget.startWidget("widgetContainer", 700, 400, 
				opentok_info.apiKey, opentok_info.sessionId, opentok_info.token);
		}
	</script>
</head>

<body>
	<div id="widgetContainer"></div>
</body>
</html>

Here we connect to the socket server, listen for messages from it, and then use the message we receive to set up our OpenTok session. Very simple.

Notice here I use a class called OT_Widget which is just a wrapper for the example that uses the LayoutContainer class from my previous blog post on layout management.

Conclusion

Using Node.js with socket.io gives us a flexible option for implementing session generation logic. If you have other ideas on how to use Node.js with OpenTok, post your thoughts here or tweet them @tokboxdev.

Tutorial GitHub Repo
OpenTok Node.js SDK created by Brian Stoner.

  • Coldangelfire311

    Hi, I get many many “unhandled socket.io url” errors when I tried to run your code from github

    • http://twitter.com/JonMumm Jonathan Mumm

      The socket.io libraries have changed, so in order for this to work you have to use a socket.io version before 0.70.

  • George

    Extremely helpful, but socketIO is unneccassary.