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)