Websocket - a simple example

Websocket is bi-directional communication protocol which emerged recently, with the introduction of HTML5. It enables full-duplex message based communication between client and server. After connection is established, messages can be transmitted, either client or server initiated. This means that you can make a dynamic web page where changes occur in real time. In that way Websockest communication presents a suitable protocol for IoT world where changes are usually asynchronously occuring and number of clients can be very large. Ok, sounds cool but what do I need to develop such web pages? To implement Websocket communication in your app there are some prerequisites on your server that has to be done. Apart from that, clients have to run browsers that support Webosckets. In this article, a simple example which includes Websocket is presented. The example is tested on Raspbian (kernel ver. 3.12.21) and Debian (kernel ver. 2.6.39). In both cases Apache web server was used to deliver html pages to the clients.

Image taken from: http://www.websocket.org/aboutwebsocket.html

 

Server side

First thing to do is to choose webserver implementation with Websocket protocol support. Among different solutions such as Node.js (Socket.IO, WebSocket-Node, ws), Java (Jetty), Ruby (EventMachine), Python (pywebsocket, Tornado), C++ (libwebsockets), .NET (SuperWebSocket) we have decided to pick Tornado, an asynchronous webserver for python which is capable to simultaneously handle more than 10k connections. To install Tornado we recommend to use some package manager like pip or EasyInstall. We used pip, so get it:

sudo apt-get install pip

After successful installation of pip package manager you can execute the following command to install Tornado:

pip install tornado

Alternatively, you can install Tornado manually (see here). Now your system is ready to use Websocketprotocol.

The next step is to write a simple Tornado web server app. Run your favorite text editor (we like nano):

sudo nano ws.py

and write some code:

import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web

class WSHandler(tornado.websocket.WebSocketHandler):

  def open(self):
    print 'user is connected.\n'

  def on_message(self, message):
    print 'received message: %s\n' %message
    self.write_message(message + ' OK')

  def on_close(self):
    print 'connection closed\n'

application = tornado.web.Application([(r'/ws', WSHandler),])

if __name__ == "__main__":
  http_server = tornado.httpserver.HTTPServer(application)
  http_server.listen(8888)
  tornado.ioloop.IOLoop.instance().start()

As you can see, code consists of four parts:

  • import of necessary modules,
  • Websocket handler class,
  • Initialization of Tornado app – web app configuration (websocket request handler) and
  • Main program – setting up Tornado server, port definition and service start.

In WSHandler class we have defined three event handlers for basic communication:

  • open – occurs when the connection is established,
  • on_message – executed on every incomming message and
  • on_close – handler triggered on the connection close event.

Here we are connecting appropriate event handler (WSHandler) with the URI to listen to (/ws’):

application = tornado.web.Application([(r'/ws', WSHandler),])

In the main, after defining the port number, we will call the start method of the Tornado server:

if __name__ == "__main__":
  http_server = tornado.httpserver.HTTPServer(application)
  http_server.listen(8888)
  tornado.ioloop.IOLoop.instance().start()

You can finally start the server in the background:

python ws.py &

Now, your server is up and running, or more precisely waiting for websocket connection. If you are connecting over SSH, the server will be killed once you exit the session. To avoid that, use nohup command to ignore hang up signal at the end of SSH connection:

nohup python ws.py &

(Note: Make sure that port 8888 is opened on your machine. Additionally, if you are running Tornado on a machine in your local network which is behind the router, you have to make port forwarding for websockets (port 8888). Otherwise this example won't work for clients that connect from outside).

Client app

To implement websocket protocol all you need is s a simple HTML web page with some JavaScript code. Well, we used JQuery to enable websockets on the client side. Body of HTML document has <label> element for connection status text, <textbox> to input text and <button> for sending messages:

<!doctype html>
<html>
<head>
<title>Websockets</title>
<script src="http://code.jquery.com/jquery-2.0.0.js"></script>
</head>
<body>
<h1>Websockets</h1>
<label id="conn_text"></label><br />
<input type="text" id="input_text"/>
<input type="submit" id="button" value="Send" />
<div id="messages_txt" />

Javascript code should be executed after the page loads, i.e. after loading of JQuery script so it will be placed inside anonymous function which is called when document.ready event occurs:

<script>
  $(document).ready(function () {

  //script goes here

});
</script>
</body>
</html>

We will create an instance of WebSocket class to handle all requests for our app:

var ws = new WebSocket("ws://example.com:8888/ws");  //change example.com with your host

We will define three event handlers: onopen, onmessage and onclose and they are invoked respectively: when connection is opened, when new message arrives from server and when the websocket connection is closed. onopen notifies and onclose alerts client about the connection status while onmessage writes message from the server to the client’s web page:

ws.onopen = function(evt) {
  var conn_status = document.getElementById('conn_text');
  conn_status.innerHTML = "Connection status: Connected!"
};

ws.onmessage = function(evt) {
  var newMessage = document.createElement('p');
  newMessage.textContent = "Server: " + evt.data;
  document.getElementById('messages_txt').appendChild(newMessage);
};

ws.onclose = function(evt) {
  alert ("Connection closed");
};

and message sending is performed when user clicks the button:

$("#button").click(function(evt) {
  evt.preventDefault();
  var message = $("#input_text").val();
  ws.send(message);
  var newMessage = document.createElement('p');
  newMessage.textContent = "Client: " + message;
  document.getElementById('messages_txt').appendChild(newMessage);
});

If you save the whole document on your server you are ready to test websockets. Write address of your webpage into the web browser’s address bar and you will be informed if connecting to server was successful with the message inside the web page: “Connection status: Connected!". Similar information, ”Connection closed”, is alerted after closing the connection (for example if you kill ws.py). On every message sent to the server, the server echoes back  that message but with “OK” in the end. You can see every message sent from client or server in your web page:

The whole code of the web page should look like this:

<!doctype html> 
<html>
<head>
<title>Websocket</title>
<script src="http://code.jquery.com/jquery-2.0.0.js"></script>
</head>
<body>
<h1>Websocket</h1>
<label id="conn_text"></label><br />
<input type="text" id="input_text"/>
<input type="submit" id="button" value="Send" /><br />
<div id="messages_txt" />
<script>
  $(document).ready(function () {
    //change example.com with your IP or your host
    var ws = new WebSocket("ws://example.com:8888/ws");
    ws.onopen = function(evt) {
      var conn_status = document.getElementById('conn_text');
      conn_status.innerHTML = "Connection status: Connected!"
    };
    ws.onmessage = function(evt) {
      var newMessage = document.createElement('p');
      newMessage.textContent = "Server: " + evt.data;
      document.getElementById('messages_txt').appendChild(newMessage);
    };
    ws.onclose = function(evt) {
      alert ("Connection closed");
    };
    $("#button").click(function(evt) {
      evt.preventDefault();
      var message = $("#input_text").val();
      ws.send(message);
      var newMessage = document.createElement('p');
      newMessage.textContent = "Client: " + message;
      document.getElementById('messages_txt').appendChild(newMessage);
    });
  });
</script>
</body></html>
Share:  Add to Facebook Tweet This Add to Delicious Submit to Digg Stumble This