The Building of Yeww: A WebRTC ProjectChat as a Service

Posted on September 23, 2016 at 4:36 PM by Lynn Walker

The chat application is the most common example used for demonstrating the use of socket.io. Virtually every example you find serves the site from node.js, along with the chat features. That is, node.js is acting as both web server and chat server.

Real world application of chat

Only a small percentage of existing web sites are served by node.js. For individuals and small entities, with hosting accounts, it may not be possible to use node.js as a web server.

Existing examples of chat with socket.io use node.js as the webserver for simplicity of the tutorial, only.

Service Oriented Architecture

A popular modern design pattern for non-trivial web applications is “Service Oriented Architecture”, or SOA. SOA involves the separating of tasks into distinct modules that operate as self-contained units contributing a specific “service” to the whole application via network communication.

In the application we are building, Yeww, the chat, or IM feature is a perfect example of a unit of functionality that can be modularized as a service.

Reusability

By deploying the chat feature as a service that will be used, or consumed by the Yeww application we will be creating something that can also be used by other, existing websites. We create something reusable.

Testability

By separating the chat features into its own service, we can test it more easily and we are more likely to produce quality functionality.

Replacing the web server

In order to change our web server from node.js to apache we need to make a few minor changes to the server file, “app”.js and the web page, “index.html”, will be stored in a new location.

Changes to node.js server file

Remove the lines of code that serve up the file, “index.html”, on a request for the root of the application. This web page will be served by apache instead:

app.get('/', function(req, res){
    res.sendFile(__dirname + '/index.html');
});
                    

The new file looks like this:

App.js

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(8000);
var nicknames = [];

io.sockets.on('connection', function(socket){
	socket.on('new user', function(data, callback){
            callback(true);
            socket.nickname = data;
            nicknames.push(socket.nickname);
            updateNicknames();
        });
	
	function updateNicknames(){
            io.sockets.emit('usernames', nicknames);
	}

	socket.on('send message', function(data){
            io.sockets.emit('new message', {msg: data, name: socket.nickname});
	});
	
	socket.on('disconnect', function(data){
            if(!socket.nickname) return;
            nicknames.splice(nicknames.indexOf(socket.nickname), 1);
            updateNicknames();
	});
});

Now the node.js chat server listens for “connection” events on port 8000, listens for “new user”, “send message” and “disconnect” messages, and sends the appropriate responses to each.

Apache Server

We previously installed apache and tested that it was running. The page displayed by apache, by default, is index.html located in /var/www/html. The node server is listening on port 8000, but the apache web server will be listening on port 80, which is typical for http.

Our existing file, “index.html”, will render correctly without any changes. Only the location of the file needs to change, to /var/www/html. Copy the file to the apache content folder.

sudo cp /dev/chat/index.html /var/www/html

index.html

<!doctype html>
<html lang="en">
<head>
	<title>Socket.io Username Example</title>
	<style>
		#messages{ height:300px; }
		#contentWrap{ display: none; }
		#messageWrap{ float: left; border: 1px #000 solid; }
	</style>
</head>
<body>
	<div id="nickWrap">
		<p>Enter a username:</p>
		<p id="nickError"></p>
		<form id="setNick">
			<input size="35" id="nickname"></input>
			<input type="submit"></input>
		</form>
	</div>

	<div id="contentWrap">
		<div id="messageWrap">
			<div id="messages"></div>
			<form id="send-message">
				<input size="35" id="message"></input>
				<input type="submit"></input>
			</form>
		</div>
		<div id="users"></div>
	</div>
	
	<script src="http://code.jquery.com/jquery-latest.min.js"></script>
	<script src="/socket.io/socket.io.js"></script>
	<script>
		jQuery(function($){
            var socket = io('http://localhost:8000');
			var $nickForm = $('#setNick');
			var $nickError = $('#nickError');
			var $nickBox = $('#nickname');
			var $users = $('#users');
			var $messageForm = $('#send-message');
			var $messageBox = $('#message');
			var $messages = $('#messages');
			
			$nickForm.submit(function(e){
				e.preventDefault();
				socket.emit('new user', $nickBox.val(), function(data){
					if(data){
						$('#nickWrap').hide();
						$('#contentWrap').show();
					} else{
						$nickError.html('That username is already taken!  Try again.');
					}
				});
				$nickBox.val('');
			});
			
			socket.on('usernames', function(data){
				var html = '';
				for(i=0; i < data.length; i++){
					html += data[i] + '<br/>'
				}
				$users.html(html);
			});
			
			$messageForm.submit(function(e){
				e.preventDefault();
				socket.emit('send message', $messageBox.val());
				$messageBox.val('');
			});
			
			socket.on('new message', function(data){
				$messages.append('<b>' + data.nick + ': </b>' + data.msg + "<br/>");
			});
		});
	</script>
</body>
</html>

How it works

It works basically like it did before, with one exception. Instead of hosting the web page, “index.html”, on node.js and having our site visitors access it on port 8000, we are serving it with apache on port 80, which means our visitors don’t have to specify a port, 80 is the default for all browsers serving .html pages over HTTP.

In previous examples the node server was doing double duty, serving the web page and handling the WebSocket traffic. In this example we simplified node’s task list, and handed serving the web page off to apache.

The results are the same:

Logging in:

Chatting:

The same fundamentals are taking place. The web page references the socket.io client library presented by the node server and makes a web socket connection. The page and server send messages to each other over the open socket connection. How the page is served is not important.

Chat server for existing websites

The node.js chat server we have created can provide chat features to an existing web site. Integrating this socket.io based chat with an existing web site requires that it:

  1. Include the jquery library.
  2. Include the socket.io client at the address and port number of the node.js chat server.
  3. Include these html elements:
    • A form with the id “send-message”.
    • An input with the id “message”.
    • A div with the id “messages”.
    • A div with the id “users”.
  4. Include this javascript for client-side connection and event handling:
    • Create a socket variable that stores the return value of opening a socket at the ip address and port of the nodejs chat server.
    • var socket = io('http://[CHAT_SERVER_IP]:8000');

    • Create an event listener with the socket variable listening on a “usernames” event, which updates the “users” div with the username list passed in the event callback.
    • socket.on('usernames', function(data){
          var html = '';
          for(i=0; i < data.length; i++){
              html += data[i] + '<br/>'
          }
          $users.html(html);
      });
      

    • Creates an event listener with the socket variable listening on a “new message” event, which updates the “messages” div with the message received in the event callback.
    • socket.on('new message', function(data){
          $messages.append('<b>' + data.nick + ': </b>' + data.msg + "<br/>");
      });
      

    • Create an event handler for the “send-message” form’s submit event. The handler cancels the standard form submission action, then it sends the contents of the “message” input to the node.js chat server as a “send message” event. Finally, it empties the contents of the “message” input to imply the message has been sent and prepare the field for another.
    • $messageForm.submit(function(e){
          e.preventDefault();
          socket.emit('send message', $messageBox.val());
          $messageBox.val('');
      });
      

    • Optionally, some styling to improve the display. At the minimum, I give the “messages” div a height of 300-500 pixels.

One more thing

To complete the system you also need a server where you can run node.js applications, to host your chat server.

Wrap up

If you’ve searched the web for socket.io based chat examples you’ve probably found dozens. Not a single example until this one has demonstrated how to integrate socket.io based chat into an existing website, even though we’ve seen that it is no more difficult than adding other javascript features like a carousel or image slider.


Previous lesson

Future lessons

In future lessons we will show how to easily add features to the chat service, both on the server and the client.

Previous lesson