MonoShiro
MonoShiro

Reputation: 83

Websockets with tornado

I'm relatively new to the tornado framework and so far things have been pretty confusing, especially when dealing with web sockets. So far my code looks like this:

import tornado.options
import tornado.web
import tornado.websocket
import tornado.ioloop

from tornado.options import options, define

define(name='port', default=8000, help='.', type=int)

class WSHandler(tornado.websocket.WebSocketHandler):
    clients = []

    def open(self):
        WSHandler.clients.append(self)
        self.write('client connected')

    def on_close(self):
        WSHandler.clients.remove(self)
        self.write('client removed')

    def on_message(self, message):
        for client in WSHandler.clients:
            WSHandler.client.write_message(message)

    def check_origin(self, origin):
        return True

if __name__ == '__main__':
    tornado.options.parse_command_line()
    application = tornado.web.Application(
        handlers=[
            (r'/webSocket', WSHandler),
        ],
    )
    application.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

and the javascript file looks like this

var ws = new WebSocket("ws://localhost:8000/webSocket")
ws.onopen = function() {
  ws.setData('Hello World');
};
ws.onmessage = function(evt) {
  ws.send(evt.data);
};

I'm currently reading the book 'Introduction to Tornado' and realized that certain things are now deprecated. I did download the demos provided on git-hub and tried understanding the websocket demo.

I tried creating a simple websocket application but all it does is give me this error:

Can "Upgrade" only to "WebSocket"

Also, I have another question on tornado
what does _() mean? the example given in the documentation is

_("A person liked this", "%(num)d people liked this", len(people)) % {"num": len(people)}

and this is supposed to return "A person liked this" if there are only one person in people. I don't quite understand how this works

I apologize if I made an error that is stupidly obvious

Upvotes: 1

Views: 3741

Answers (1)

TanLingxiao
TanLingxiao

Reputation: 412

I think you can see tornado source,and you can find following:

# Upgrade header should be present and should be equal to WebSocket
    if self.request.headers.get("Upgrade", "").lower() != 'websocket':
        self.set_status(400)
        log_msg = "Can \"Upgrade\" only to \"WebSocket\"."
        self.finish(log_msg)
        gen_log.debug(log_msg)
        return

Obviously,HTML5 WebSocket protocol is a new protocol. It implements the browser and the server full-duplex communication (full-duplex).But use http can only make single communite.So I advice you try another demo.

Server:

    #!/usr/bin/python
#coding:utf-8
import os.path

import tornado.httpserver
import tornado.web
import tornado.ioloop
import tornado.options
import tornado.httpclient
import tornado.websocket

import json
class IndexHandler(tornado.web.RequestHandler):
  def get(self):
    self.render("index.html")

class SocketHandler(tornado.websocket.WebSocketHandler):
  """docstring for SocketHandler"""
  clients = set()

  @staticmethod
  def send_to_all(message):
      for c in SocketHandler.clients:
          c.write_message(json.dumps(message))

  def open(self):
      self.write_message(json.dumps({
          'type': 'sys',
          'message': 'Welcome to WebSocket',
      }))
      SocketHandler.send_to_all({
          'type': 'sys',
          'message': str(id(self)) + ' has joined',
      })
      SocketHandler.clients.add(self)

  def on_close(self):
      SocketHandler.clients.remove(self)
      SocketHandler.send_to_all({
          'type': 'sys',
          'message': str(id(self)) + ' has left',
      })

  def on_message(self, message):
    SocketHandler.send_to_all({
  'type': 'user',
  'id': id(self),
  'message': message,
        })

##MAIN
if __name__ == '__main__':
  app = tornado.web.Application(
    handlers=[
      (r"/", IndexHandler),
      (r"/chat", SocketHandler)
    ],
    debug = True,
    template_path = os.path.join(os.path.dirname(__file__), "templates"),
        static_path = os.path.join(os.path.dirname(__file__), "static")
  )
  app.listen(8000)
  tornado.ioloop.IOLoop.instance().start()

Client:

    <html>
<head>
<script type="text/javascript">
var ws = new WebSocket("ws://localhost:8000/chat");
ws.onmessage = function(event) {
  console.log(event);
}
function send() {
  ws.send(document.getElementById('chat').value );
}
</script>
</head>

<body>
  <div>
    hello
    <input id="chat">
    <button  onclick="send()">send</button>
  </div>    
</body>
</html>

Upvotes: 2

Related Questions