How can I create several websocket chats in Tornado?

90 views
Skip to first unread message

Philipp Konrad

unread,
Feb 24, 2014, 10:29:44 AM2/24/14
to python-...@googlegroups.com
Hello,
(FYI: I added the same question to Stackoverflow: http://stackoverflow.com/questions/21991348/how-can-i-create-several-websocket-chats-in-tornado)


I am trying to create a Tornado application with several chats. The chats should be based on HTML5 websocket. The Websockets communicate nicely, but I always run into the problem that each message is posted twice.
In the tornado demos section the websocket is created and managed through classmethods and a single WebSocketHandler.
However, for my application I need several chats so I tried to create another abstraction layer with Chats and a Singleton ChatPool.
I expected that it does not make any difference if the cache and waiters would be organized through a class (like in the demo) or through
an simple object. In my version, however, each websocket is called twice.

The application uses four classes to handle the chat:
  •   `Chat` contains all written messages so far and a list with all `waiters` which should be notified
  •   `ChatPool` serves as a lookup for new Websockets - it creates a new chat when there is no one with the required `scratch_id` or returns an existing chat instance.
  •   `ScratchHandler` is the entry point for all HTTP requests - it parses the base template and returns all details of client side.
  •  `ScratchWebSocket` queries the database for user information, sets up the connection and notifies the chat instance if a new message has to be sprea?
How can I prevent that the messages are posted several times?
How can I build a multi chat application with tornado?



My code

    import uuid
    import tornado.websocket
    import tornado.web
    import tornado.template
   
    from site import models
    from site.handler import auth_handler
   
    class ChatPool(object):
   
        # contains all chats
   
        chats = {}
   
        @classmethod
        def get_or_create(cls, scratch_id):
   
            if scratch_id in cls.chats:
                return cls.chats[scratch_id]
            else:
                chat = Chat(scratch_id)
                cls.chats[scratch_id] = chat
                return chat
   
   
        @classmethod
        def remove_chat(cls, chat_id):
   
            if chat_id not in cls.chats: return
            del(cls.chats[chat_id])
   
   
    class Chat(object):
   
        def __init__(self, scratch_id):
   
            self.scratch_id = scratch_id
            self.messages = []
            self.waiters = []
   
        def add_websocket(self, websocket):
            self.waiters.append(websocket)
   
        def send_updates(self, messages, sending_websocket):
            print "WAITERS", self.waiters  
            for waiter in self.waiters:
                waiter.write_message(messages)
            self.messages.append(messages)
   
   
    class ScratchHandler(auth_handler.BaseHandler):
   
        @tornado.web.authenticated
        def get(self, scratch_id):
   
            chat = ChatPool.get_or_create(scratch_id)
            return self.render('scratch.html', messages=chat.messages,
                                               scratch_id=scratch_id)
   
   
    class ScratchWebSocket(tornado.websocket.WebSocketHandler):
   
        def allow_draft76(self):
            # for iOS 5.0 Safari
            return True
   
        def open(self, scratch_id):
   
            self.scratch_id = scratch_id
            scratch = models.Scratch.objects.get(scratch_id=scratch_id)
   
            if not scratch:
                self.set_status(404)
                return
   
            self.scratch_id = scratch.scratch_id
   
            self.title = scratch.title
            self.description = scratch.description
            self.user = scratch.user
   
            self.chat = ChatPool.get_or_create(scratch_id)
            self.chat.add_websocket(self)       
   
        def on_close(self):
            # this is buggy - only remove the websocket from the chat.
            ChatPool.remove_chat(self.scratch_id)
   
        def on_message(self, message):
            print 'I got a message'
            parsed = tornado.escape.json_decode(message)
            chat = {
                "id": str(uuid.uuid4()),
                "body": parsed["body"],
                "from": self.user,
                }
   
            chat["html"] = tornado.escape.to_basestring(self.render_string("chat-message.html", message=chat))
            self.chat.send_updates(chat, self)

David Nellessen

unread,
Feb 26, 2014, 11:19:37 AM2/26/14
to python-...@googlegroups.com
Have a look at this chat example application based on Tornado and Redis Pub/Sub. It uses websockets and supports mutliple chat rooms. So it might be exactly what you are looking for:

https://github.com/nellessen/Tornado-Redis-Chat
http://tornado-redis-chat.herokuapp.com/ (Live Demo)

Cheers
David
--
You received this message because you are subscribed to the Google Groups "Tornado Web Server" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-tornad...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply all
Reply to author
Forward
0 new messages