Skip to content

Commit 555b69e

Browse files
Tornado 5 support
1 parent 58c8a6f commit 555b69e

File tree

11 files changed

+328
-3
lines changed

11 files changed

+328
-3
lines changed

README.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ Features
2020
- Compatible with Python 2.7 and Python 3.3+.
2121
- Supports large number of clients even on modest hardware when used with an
2222
asynchronous server based on `asyncio <https://docs.python.org/3/library/asyncio.html>`_
23-
(`sanic <http://sanic.readthedocs.io/>`_ and `aiohttp <http://aiohttp.readthedocs.io/>`_),
23+
(`sanic <http://sanic.readthedocs.io/>`_, `aiohttp <http://aiohttp.readthedocs.io/>`_
24+
or `tornado <http://www.tornadoweb.org/>`_),
2425
`eventlet <http://eventlet.net/>`_ or `gevent <http://gevent.org/>`_. For
2526
development and testing, any WSGI compliant multi-threaded server can also be
2627
used.

examples/README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,13 @@ aiohttp
1313
-------
1414

1515
Examples that are compatible with the aiohttp framework for asyncio.
16+
17+
sanic
18+
-----
19+
20+
Examples that are compatible with the sanic framework for asyncio.
21+
22+
tornado
23+
-------
24+
25+
Examples that are compatible with the tornado framework.

examples/tornado/app.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import os
2+
3+
import tornado.ioloop
4+
from tornado.options import define, options, parse_command_line
5+
import tornado.web
6+
7+
import socketio
8+
9+
define("port", default=8888, help="run on the given port", type=int)
10+
define("debug", default=False, help="run in debug mode")
11+
12+
sio = socketio.AsyncServer(async_mode='tornado')
13+
14+
15+
async def background_task():
16+
"""Example of how to send server generated events to clients."""
17+
count = 0
18+
while True:
19+
await sio.sleep(10)
20+
count += 1
21+
await sio.emit('my response', {'data': 'Server generated event'},
22+
namespace='/test')
23+
24+
25+
class MainHandler(tornado.web.RequestHandler):
26+
def get(self):
27+
self.render("app.html")
28+
29+
30+
@sio.on('my event', namespace='/test')
31+
async def test_message(sid, message):
32+
await sio.emit('my response', {'data': message['data']}, room=sid,
33+
namespace='/test')
34+
35+
36+
@sio.on('my broadcast event', namespace='/test')
37+
async def test_broadcast_message(sid, message):
38+
await sio.emit('my response', {'data': message['data']}, namespace='/test')
39+
40+
41+
@sio.on('join', namespace='/test')
42+
async def join(sid, message):
43+
sio.enter_room(sid, message['room'], namespace='/test')
44+
await sio.emit('my response', {'data': 'Entered room: ' + message['room']},
45+
room=sid, namespace='/test')
46+
47+
48+
@sio.on('leave', namespace='/test')
49+
async def leave(sid, message):
50+
sio.leave_room(sid, message['room'], namespace='/test')
51+
await sio.emit('my response', {'data': 'Left room: ' + message['room']},
52+
room=sid, namespace='/test')
53+
54+
55+
@sio.on('close room', namespace='/test')
56+
async def close(sid, message):
57+
await sio.emit('my response',
58+
{'data': 'Room ' + message['room'] + ' is closing.'},
59+
room=message['room'], namespace='/test')
60+
await sio.close_room(message['room'], namespace='/test')
61+
62+
63+
@sio.on('my room event', namespace='/test')
64+
async def send_room_message(sid, message):
65+
await sio.emit('my response', {'data': message['data']},
66+
room=message['room'], namespace='/test')
67+
68+
69+
@sio.on('disconnect request', namespace='/test')
70+
async def disconnect_request(sid):
71+
await sio.disconnect(sid, namespace='/test')
72+
73+
74+
@sio.on('connect', namespace='/test')
75+
async def test_connect(sid, environ):
76+
await sio.emit('my response', {'data': 'Connected', 'count': 0}, room=sid,
77+
namespace='/test')
78+
79+
80+
@sio.on('disconnect', namespace='/test')
81+
def test_disconnect(sid):
82+
print('Client disconnected')
83+
84+
85+
def main():
86+
parse_command_line()
87+
app = tornado.web.Application(
88+
[
89+
(r"/", MainHandler),
90+
(r"/socket.io/", socketio.get_tornado_handler(sio)),
91+
],
92+
template_path=os.path.join(os.path.dirname(__file__), "templates"),
93+
static_path=os.path.join(os.path.dirname(__file__), "static"),
94+
debug=options.debug,
95+
)
96+
app.listen(options.port)
97+
tornado.ioloop.IOLoop.current().start()
98+
99+
100+
if __name__ == "__main__":
101+
main()

examples/tornado/latency.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import os
2+
3+
import tornado.ioloop
4+
from tornado.options import define, options, parse_command_line
5+
import tornado.web
6+
7+
import socketio
8+
9+
define("port", default=8888, help="run on the given port", type=int)
10+
define("debug", default=False, help="run in debug mode")
11+
12+
sio = socketio.AsyncServer(async_mode='tornado')
13+
14+
15+
class MainHandler(tornado.web.RequestHandler):
16+
def get(self):
17+
self.render("latency.html")
18+
19+
20+
@sio.on('ping_from_client')
21+
async def ping(sid):
22+
await sio.emit('pong_from_server', room=sid)
23+
24+
25+
def main():
26+
parse_command_line()
27+
app = tornado.web.Application(
28+
[
29+
(r"/", MainHandler),
30+
(r"/socket.io/", socketio.get_tornado_handler(sio)),
31+
],
32+
template_path=os.path.join(os.path.dirname(__file__), "templates"),
33+
static_path=os.path.join(os.path.dirname(__file__), "static"),
34+
debug=options.debug,
35+
)
36+
app.listen(options.port)
37+
tornado.ioloop.IOLoop.current().start()
38+
39+
40+
if __name__ == "__main__":
41+
main()

examples/tornado/requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
tornado==5.0.2
2+
python-engineio
3+
python_socketio
4+
six==1.10.0

examples/tornado/static/style.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
body { margin: 0; padding: 0; font-family: Helvetica Neue; }
2+
h1 { margin: 100px 100px 10px; }
3+
h2 { color: #999; margin: 0 100px 30px; font-weight: normal; }
4+
#latency { color: red; }
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<title>python-socketio test</title>
5+
<script type="text/javascript" src="//code.jquery.com/jquery-2.1.4.min.js"></script>
6+
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.slim.js"></script>
7+
<script type="text/javascript" charset="utf-8">
8+
$(document).ready(function(){
9+
namespace = '/test';
10+
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
11+
12+
socket.on('connect', function() {
13+
socket.emit('my event', {data: 'I\'m connected!'});
14+
});
15+
socket.on('disconnect', function() {
16+
$('#log').append('<br>Disconnected');
17+
});
18+
socket.on('my response', function(msg) {
19+
$('#log').append('<br>Received: ' + msg.data);
20+
});
21+
22+
// event handler for server sent data
23+
// the data is displayed in the "Received" section of the page
24+
// handlers for the different forms in the page
25+
// these send data to the server in a variety of ways
26+
$('form#emit').submit(function(event) {
27+
socket.emit('my event', {data: $('#emit_data').val()});
28+
return false;
29+
});
30+
$('form#broadcast').submit(function(event) {
31+
socket.emit('my broadcast event', {data: $('#broadcast_data').val()});
32+
return false;
33+
});
34+
$('form#join').submit(function(event) {
35+
socket.emit('join', {room: $('#join_room').val()});
36+
return false;
37+
});
38+
$('form#leave').submit(function(event) {
39+
socket.emit('leave', {room: $('#leave_room').val()});
40+
return false;
41+
});
42+
$('form#send_room').submit(function(event) {
43+
socket.emit('my room event', {room: $('#room_name').val(), data: $('#room_data').val()});
44+
return false;
45+
});
46+
$('form#close').submit(function(event) {
47+
socket.emit('close room', {room: $('#close_room').val()});
48+
return false;
49+
});
50+
$('form#disconnect').submit(function(event) {
51+
socket.emit('disconnect request');
52+
return false;
53+
});
54+
});
55+
</script>
56+
</head>
57+
<body>
58+
<h1>python-socketio test</h1>
59+
<h2>Send:</h2>
60+
<form id="emit" method="POST" action='#'>
61+
<input type="text" name="emit_data" id="emit_data" placeholder="Message">
62+
<input type="submit" value="Echo">
63+
</form>
64+
<form id="broadcast" method="POST" action='#'>
65+
<input type="text" name="broadcast_data" id="broadcast_data" placeholder="Message">
66+
<input type="submit" value="Broadcast">
67+
</form>
68+
<form id="join" method="POST" action='#'>
69+
<input type="text" name="join_room" id="join_room" placeholder="Room Name">
70+
<input type="submit" value="Join Room">
71+
</form>
72+
<form id="leave" method="POST" action='#'>
73+
<input type="text" name="leave_room" id="leave_room" placeholder="Room Name">
74+
<input type="submit" value="Leave Room">
75+
</form>
76+
<form id="send_room" method="POST" action='#'>
77+
<input type="text" name="room_name" id="room_name" placeholder="Room Name">
78+
<input type="text" name="room_data" id="room_data" placeholder="Message">
79+
<input type="submit" value="Send to Room">
80+
</form>
81+
<form id="close" method="POST" action="#">
82+
<input type="text" name="close_room" id="close_room" placeholder="Room Name">
83+
<input type="submit" value="Close Room">
84+
</form>
85+
<form id="disconnect" method="POST" action="#">
86+
<input type="submit" value="Disconnect">
87+
</form>
88+
<h2>Receive:</h2>
89+
<div><p id="log"></p></div>
90+
</body>
91+
</html>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>Socket.IO Latency</title>
5+
<link rel="stylesheet" href="/static/style.css" />
6+
</head>
7+
<body>
8+
<h1>Socket.IO Latency <span id="latency"></span></h1>
9+
<h2 id="transport">(connecting)</h2>
10+
<canvas id="chart" height="200"></canvas>
11+
12+
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js"></script>
13+
<script src="//cdnjs.cloudflare.com/ajax/libs/smoothie/1.27.0/smoothie.js"></script>
14+
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.slim.js"></script>
15+
<script>
16+
// socket
17+
var socket = io.connect('http://' + document.domain + ':' + location.port);
18+
var char = $('chart').get(0);
19+
socket.on('connect', function() {
20+
if (chart.getContext) {
21+
render();
22+
window.onresize = render;
23+
}
24+
send();
25+
});
26+
socket.on('pong_from_server', function() {
27+
var latency = new Date - last;
28+
$('#latency').text(latency + 'ms');
29+
if (time)
30+
time.append(+new Date, latency);
31+
setTimeout(send, 100);
32+
});
33+
socket.on('disconnect', function() {
34+
if (smoothie)
35+
smoothie.stop();
36+
$('#transport').text('(disconnected)');
37+
});
38+
39+
var last;
40+
function send() {
41+
last = new Date;
42+
socket.emit('ping_from_client');
43+
$('#transport').text(socket.io.engine.transport.name);
44+
}
45+
46+
// chart
47+
var smoothie;
48+
var time;
49+
function render() {
50+
if (smoothie)
51+
smoothie.stop();
52+
chart.width = document.body.clientWidth;
53+
smoothie = new SmoothieChart();
54+
smoothie.streamTo(chart, 1000);
55+
time = new TimeSeries();
56+
smoothie.addTimeSeries(time, {
57+
strokeStyle: 'rgb(255, 0, 0)',
58+
fillStyle: 'rgba(255, 0, 0, 0.4)',
59+
lineWidth: 2
60+
});
61+
}
62+
</script>
63+
</body>
64+
</html>

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
platforms='any',
3030
install_requires=[
3131
'six>=1.9.0',
32-
'python-engineio>=1.2.1'
32+
'python-engineio>=2.2.0'
3333
],
3434
tests_require=[
3535
'mock',

socketio/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .zmq_manager import ZmqManager
99
from .server import Server
1010
from .namespace import Namespace
11+
from .tornado import get_tornado_handler
1112
if sys.version_info >= (3, 5): # pragma: no cover
1213
from .asyncio_server import AsyncServer
1314
from .asyncio_manager import AsyncManager
@@ -26,4 +27,4 @@
2627
'Namespace']
2728
if AsyncServer is not None: # pragma: no cover
2829
__all__ += ['AsyncServer', 'AsyncNamespace', 'AsyncManager',
29-
'AsyncRedisManager']
30+
'AsyncRedisManager', 'get_tornado_handler']

0 commit comments

Comments
 (0)