Change website

From Jan 16 2015,


All post content will be move to we's offical website with many content...

Can access website here: http://justox.com

Thanks for your visit!

Wednesday, 18 December 2013

Python Tornado Web Server With WebSockets – Part I

Today i will show you and give you some input how to make a web and websocket server with Tornado, which is currently my favorite when i need “mockup” service so i can show it to someone. Anyway this article will mostly cover websockets then “standard” web.

Tornado is a scalable, non-blocking web server and web application framework written in Python. It was developed for use by FriendFeed; the company was acquired by Facebook in 2009 and Tornado was open-sourced soon after.
For installing Tornado on our machine we need Python first, anyhow, Python is installed on most Linux distros possibly because most Gnome and KDE apps using Python 2.5+ interpreters. For installing Python on Windows machine you can check it out here.
After we have Python in place we should continue installing Tornado with easy_install tornado command which will install latest stable version. For running easy_install from Windows command prompt you can google a bit because this is covered on a lot of blogs. Also, i’m using Eclipse for Python development but you can use whatever you like.
Now let’s get to the real, simple example. Let’s create a file called server.py :
import tornado.ioloop
import tornado.web

from tornado.options import define, options, parse_command_line

define("port", default=8888, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.write("This is your response")
        self.finish()

app = tornado.web.Application([
    (r'/', IndexHandler),
])

if __name__ == '__main__':
    parse_command_line()
    app.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()
We can see our example for simple response without rendering any html (this will be part II). Also, we can start this example with additional option port which is by default on 8888. For changing port we can start it by ./server.py --port=9999.
Notice that we put decorator @tornado.web.asynchronous before get method, and this will prevent the RequestHandler from automatically calling self.finish() eg. it means that server will hold connection until we execute finish.
Now, let’s go to our browser and write in address bar http://localhost:8888/ or eventually click on this link.
Because this is mostly websocket oriented post we should continue with modifying/extending our simple example:
import tornado.ioloop
import tornado.web
import tornado.websocket

from tornado.options import define, options, parse_command_line

define("port", default=8888, help="run on the given port", type=int)

# we gonna store clients in dictionary..
clients = dict()

class IndexHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.write("This is your response")
        self.finish()

class WebSocketHandler(tornado.websocket.WebSocketHandler):
    def open(self, *args):
        self.id = self.get_argument("Id")
        self.stream.set_nodelay(True)
        clients[self.id] = {"id": self.id, "object": self}

    def on_message(self, message):        
        """
        when we receive some message we want some message handler..
        for this example i will just print message to console
        """
        print "Client %s received a message : %s" % (self.id, message)
        
    def on_close(self):
        if self.id in clients:
            del clients[self.id]

app = tornado.web.Application([
    (r'/', IndexHandler),
    (r'/', WebSocketHandler),
])

if __name__ == '__main__':
    parse_command_line()
    app.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()
With upper extended simple example, we done nothing yet because we actually need some client to connect with. If we go again and refresh localhost link in our browser we should get same message as last time. Also we can see that we didn’t change route to websocket handler, they can both work on same route, but what is different is that when we want to connect to websocket there is ws:// insted http:// and Tornado knows how to handle those routes.
For simple client we can use index handler with html rendering so let’s change server.py a bit:
class IndexHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        #self.write("This is your response")
        self.render("index.html")
        #we don't need self.finish() because self.render() is fallowed by self.finish() inside tornado
        #self.finish()
Now we need index.html, so let’s create one..
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
 <script type="text/javascript">
        var messageContainer = document.getElementById("messages");
        function WebSocketTest() {
            if ("WebSocket" in window) {
                messageContainer.innerHTML = "WebSocket is supported by your Browser!";
                var ws = new WebSocket("ws://localhost:8888/?Id=123456789");
                ws.onopen = function() {
                    ws.send("Message to send");
                };
                ws.onmessage = function (evt) { 
                    var received_msg = evt.data;
                    messageContainer.innerHTML = "Message is received...";
                };
                ws.onclose = function() { 
                    messageContainer.innerHTML = "Connection is closed...";
                };
            } else {
                messageContainer.innerHTML = "WebSocket NOT supported by your Browser!";
            }
        }
        </script>
    </head>
    <body>
        <a href="javascript:WebSocketTest()">Run WebSocket</a>
        <div id="messages" style="height:200px;background:black;color:white;"></div>
    </body>
</html>
And this is it.. we can now run our Tornado server and ho to http://localhost:8888/ we will see index.html rendered in browser, also if we click on link “Run WebSocket” it should start connecting on our websocket and we should see messages in container.
This is it for now, my next few post will hold few modification on server side and i will make small and simple client side library for handling websocket connections and messages.
Happy Hacking!

No comments:

Post a Comment