Chat application

Getting started with WebSockets in Tornado

In this article, we’re going to build a simple chat application with WebSockets, using the Tornado framework in Python. You can clone the GitHub repository and try it out.

1. Getting started

First, follow the “Installation” steps on Tornado’s website.

Specifically, run pip install tornado .

Next, go ahead and create a file named chat.py with this starter code:

import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html") def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()

You can run this server with python3 chat.py . If you navigate to http://localhost:8888 , it should give an internal server error, since we haven’t written the index.html page yet.

2. Writing the index.html page

Let’s define a WebSocket with the following specification. It will be located at the route /websocket (that is, http://localhost:8888/websocket ), and it takes a JSON dictionary with two key-value pairs: one key will be message , and the other key will be user . It sends a JSON dictionary to clients with the same format.

For example, we might make a request by sending the following dictionary to /websocket :

(Obviously, in practice, we would require some sort of authentication.)

Then we’ll make the page prompt the user to give a username on load. When a user sends a message, we’ll send the name that they enter as the value for “user”, and their message as the value for “message.” When the user receives a message, we’ll append it to a div on the page. Here’s some quick-and-dirty sample HTML (which we’ll save as index.html ) that gets the job done:

      

3. Writing the WebSocket

Now, we’ll implement the /websocket route, by modifying the chat.py file accordingly:

import tornado.ioloop import tornado.web import tornado.websocket class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html") class SimpleWebSocket(tornado.websocket.WebSocketHandler): connections = set() def open(self): self.connections.add(self) def on_message(self, message): [client.write_message(message) for client in self.connections] def on_close(self): self.connections.remove(self) def make_app(): return tornado.web.Application([ (r"/", MainHandler), (r"/websocket", SimpleWebSocket) ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()

And we’re done! You can run the server with python3 chat.py , and try out the chat box by opening http://localhost:8888 in multiple tabs. Let me know if you have any questions or other ideas in the comments.

About the Author

I’m a quantitative researcher in San Francisco. I’m interested in computer science, math, philosophy and entrepreneurship. You can follow me on LinkedIn and Twitter.

Источник

tornado.websocket — Bidirectional communication to the browser¶

WebSockets allow for bidirectional communication between the browser and server.

WebSockets are supported in the current versions of all major browsers, although older versions that do not support WebSockets are still in use (refer to http://caniuse.com/websockets for details).

This module implements the final version of the WebSocket protocol as defined in RFC 6455. Certain browser versions (notably Safari 5.x) implemented an earlier draft of the protocol (known as “draft 76”) and are not compatible with this module.

Changed in version 4.0: Removed support for the draft 76 protocol version.

Subclass this class to create a basic WebSocket handler.

Override on_message to handle incoming messages, and use write_message to send messages to the client. You can also override open and on_close to handle opened and closed connections.

See http://dev.w3.org/html5/websockets/ for details on the JavaScript interface. The protocol is specified at http://tools.ietf.org/html/rfc6455.

Here is an example WebSocket handler that echos back all received messages back to the client:

class EchoWebSocket(tornado.websocket.WebSocketHandler): def open(self): print("WebSocket opened") def on_message(self, message): self.write_message(u"You said: " + message) def on_close(self): print("WebSocket closed") 

WebSockets are not standard HTTP connections. The “handshake” is HTTP, but after the handshake, the protocol is message-based. Consequently, most of the Tornado HTTP facilities are not available in handlers of this type. The only communication methods available to you are write_message() , ping() , and close() . Likewise, your request handler class should implement open() method rather than get() or post() .

If you map the handler above to /websocket in your application, you can invoke it in JavaScript with:

var ws = new WebSocket("ws://localhost:8888/websocket"); ws.onopen = function() < ws.send("Hello, world"); >; ws.onmessage = function (evt) < alert(evt.data); >;

This script pops up an alert box that says “You said: Hello, world”.

Web browsers allow any site to open a websocket connection to any other, instead of using the same-origin policy that governs other network access from javascript. This can be surprising and is a potential security hole, so since Tornado 4.0 WebSocketHandler requires applications that wish to receive cross-origin websockets to opt in by overriding the check_origin method (see that method’s docs for details). Failure to do so is the most likely cause of 403 errors when making a websocket connection.

When using a secure websocket connection ( wss:// ) with a self-signed certificate, the connection from a browser may fail because it wants to show the “accept this certificate” dialog but has nowhere to show it. You must first visit a regular HTML page using the same certificate to accept it before the websocket connection will succeed.

Event handlers¶

Invoked when a new WebSocket is opened.

The arguments to open are extracted from the tornado.web.URLSpec regular expression, just like the arguments to tornado.web.RequestHandler.get .

WebSocketHandler. on_message ( message ) [source] ¶

Handle incoming messages on the WebSocket

This method must be overridden.

Invoked when the WebSocket is closed.

If the connection was closed cleanly and a status code or reason phrase was supplied, these values will be available as the attributes self.close_code and self.close_reason .

Changed in version 4.0: Added close_code and close_reason attributes.

Invoked when a new WebSocket requests specific subprotocols.

subprotocols is a list of strings identifying the subprotocols proposed by the client. This method may be overridden to return one of those strings to select it, or None to not select a subprotocol. Failure to select a subprotocol does not automatically abort the connection, although clients may close the connection if none of their proposed subprotocols was selected.

Output¶

Sends the given message to the client of this Web Socket.

The message may be either a string or a dict (which will be encoded as json). If the binary argument is false, the message will be sent as utf8; in binary mode any byte string is allowed.

If the connection is already closed, raises WebSocketClosedError .

Changed in version 3.2: WebSocketClosedError was added (previously a closed connection would raise an AttributeError )

Changed in version 4.3: Returns a Future which can be used for flow control.

Once the close handshake is successful the socket will be closed.

code may be a numeric status code, taken from the values defined in RFC 6455 section 7.4.1. reason may be a textual message about why the connection is closing. These values are made available to the client, but are not otherwise interpreted by the websocket protocol.

Changed in version 4.0: Added the code and reason arguments.

Configuration¶

Override to enable support for allowing alternate origins.

The origin argument is the value of the Origin HTTP header, the url responsible for initiating this request. This method is not called for clients that do not send this header; such requests are always allowed (because all browsers that implement WebSockets support this header, and non-browser clients do not have the same cross-site security concerns).

Should return True to accept the request or False to reject it. By default, rejects all requests with an origin on a host other than this one.

This is a security protection against cross site scripting attacks on browsers, since WebSockets are allowed to bypass the usual same-origin policies and don’t use CORS headers.

This is an important security measure; don’t disable it without understanding the security implications. In particular, if your authenticatino is cookie-based, you must either restrict the origins allowed by check_origin() or implement your own XSRF-like protection for websocket connections. See these articles for more.

To accept all cross-origin traffic (which was the default prior to Tornado 4.0), simply override this method to always return true:

def check_origin(self, origin): return True 

To allow connections from any subdomain of your site, you might do something like:

def check_origin(self, origin): parsed_origin = urllib.parse.urlparse(origin) return parsed_origin.netloc.endswith(".mydomain.com") 

Override to return compression options for the connection.

If this method returns None (the default), compression will be disabled. If it returns a dict (even an empty one), it will be enabled. The contents of the dict may be used to control the memory and CPU usage of the compression, but no such options are currently implemented.

Set the no-delay flag for this stream.

By default, small messages may be delayed and/or combined to minimize the number of packets sent. This can sometimes cause 200-500ms delays due to the interaction between Nagle’s algorithm and TCP delayed ACKs. To reduce this delay (at the expense of possibly increasing bandwidth usage), call self.set_nodelay(True) once the websocket connection is established.

Other¶

Send ping frame to the remote end.

WebSocketHandler. on_pong ( data ) [source] ¶

Invoked when the response to a ping frame is received.

exception tornado.websocket. WebSocketClosedError [source] ¶

Raised by operations on a closed connection.

Client-side support¶

tornado.websocket. websocket_connect ( url, io_loop=None, callback=None, connect_timeout=None, on_message_callback=None, compression_options=None ) [source] ¶

Client-side websocket support.

Takes a url and returns a Future whose result is a WebSocketClientConnection .

compression_options is interpreted in the same way as the return value of WebSocketHandler.get_compression_options .

The connection supports two styles of operation. In the coroutine style, the application typically calls read_message in a loop:

conn = yield websocket_connect(url) while True: msg = yield conn.read_message() if msg is None: break # Do something with msg 

In the callback style, pass an on_message_callback to websocket_connect . In both styles, a message of None indicates that the connection has been closed.

Changed in version 3.2: Also accepts HTTPRequest objects in place of urls.

Changed in version 4.1: Added compression_options and on_message_callback . The io_loop argument is deprecated.

class tornado.websocket. WebSocketClientConnection ( io_loop, request, on_message_callback=None, compression_options=None ) [source] ¶

WebSocket client connection.

This class should not be instantiated directly; use the websocket_connect function instead.

Closes the websocket connection.

code and reason are documented under WebSocketHandler.close .

Changed in version 4.0: Added the code and reason arguments.

Sends a message to the WebSocket server.

Reads a message from the WebSocket server.

If on_message_callback was specified at WebSocket initialization, this function will never return messages

Returns a future whose result is the message, or None if the connection is closed. If a callback argument is given it will be called with the future when it is ready.

© Copyright 2009-2016, The Tornado Authors. Revision 4ecc31a5 .

Versions latest Downloads On Read the Docs Project Home Builds Free document hosting provided by Read the Docs.

Источник

Читайте также:  What is command line argument in cpp
Оцените статью