diff --git a/README.rst b/README.rst index 40d8cc45..cf3c0c4e 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,8 @@ Features - Compatible with Python 2.7 and Python 3.3+. - Supports large number of clients even on modest hardware when used with an asynchronous server based on `asyncio `_ - (`sanic `_ and `aiohttp `_), + (`sanic `_, `aiohttp `_ + or `tornado `_), `eventlet `_ or `gevent `_. For development and testing, any WSGI compliant multi-threaded server can also be used. diff --git a/examples/README.rst b/examples/README.rst index a0ae1f65..19b6b53d 100644 --- a/examples/README.rst +++ b/examples/README.rst @@ -13,3 +13,13 @@ aiohttp ------- Examples that are compatible with the aiohttp framework for asyncio. + +sanic +----- + +Examples that are compatible with the sanic framework for asyncio. + +tornado +------- + +Examples that are compatible with the tornado framework. diff --git a/examples/tornado/app.py b/examples/tornado/app.py new file mode 100755 index 00000000..71f30457 --- /dev/null +++ b/examples/tornado/app.py @@ -0,0 +1,101 @@ +import os + +import tornado.ioloop +from tornado.options import define, options, parse_command_line +import tornado.web + +import socketio + +define("port", default=8888, help="run on the given port", type=int) +define("debug", default=False, help="run in debug mode") + +sio = socketio.AsyncServer(async_mode='tornado') + + +async def background_task(): + """Example of how to send server generated events to clients.""" + count = 0 + while True: + await sio.sleep(10) + count += 1 + await sio.emit('my response', {'data': 'Server generated event'}, + namespace='/test') + + +class MainHandler(tornado.web.RequestHandler): + def get(self): + self.render("app.html") + + +@sio.on('my event', namespace='/test') +async def test_message(sid, message): + await sio.emit('my response', {'data': message['data']}, room=sid, + namespace='/test') + + +@sio.on('my broadcast event', namespace='/test') +async def test_broadcast_message(sid, message): + await sio.emit('my response', {'data': message['data']}, namespace='/test') + + +@sio.on('join', namespace='/test') +async def join(sid, message): + sio.enter_room(sid, message['room'], namespace='/test') + await sio.emit('my response', {'data': 'Entered room: ' + message['room']}, + room=sid, namespace='/test') + + +@sio.on('leave', namespace='/test') +async def leave(sid, message): + sio.leave_room(sid, message['room'], namespace='/test') + await sio.emit('my response', {'data': 'Left room: ' + message['room']}, + room=sid, namespace='/test') + + +@sio.on('close room', namespace='/test') +async def close(sid, message): + await sio.emit('my response', + {'data': 'Room ' + message['room'] + ' is closing.'}, + room=message['room'], namespace='/test') + await sio.close_room(message['room'], namespace='/test') + + +@sio.on('my room event', namespace='/test') +async def send_room_message(sid, message): + await sio.emit('my response', {'data': message['data']}, + room=message['room'], namespace='/test') + + +@sio.on('disconnect request', namespace='/test') +async def disconnect_request(sid): + await sio.disconnect(sid, namespace='/test') + + +@sio.on('connect', namespace='/test') +async def test_connect(sid, environ): + await sio.emit('my response', {'data': 'Connected', 'count': 0}, room=sid, + namespace='/test') + + +@sio.on('disconnect', namespace='/test') +def test_disconnect(sid): + print('Client disconnected') + + +def main(): + parse_command_line() + app = tornado.web.Application( + [ + (r"/", MainHandler), + (r"/socket.io/", socketio.get_tornado_handler(sio)), + ], + template_path=os.path.join(os.path.dirname(__file__), "templates"), + static_path=os.path.join(os.path.dirname(__file__), "static"), + debug=options.debug, + ) + app.listen(options.port) + tornado.ioloop.IOLoop.current().start() + + +if __name__ == "__main__": + main() diff --git a/examples/tornado/latency.py b/examples/tornado/latency.py new file mode 100755 index 00000000..883da64c --- /dev/null +++ b/examples/tornado/latency.py @@ -0,0 +1,41 @@ +import os + +import tornado.ioloop +from tornado.options import define, options, parse_command_line +import tornado.web + +import socketio + +define("port", default=8888, help="run on the given port", type=int) +define("debug", default=False, help="run in debug mode") + +sio = socketio.AsyncServer(async_mode='tornado') + + +class MainHandler(tornado.web.RequestHandler): + def get(self): + self.render("latency.html") + + +@sio.on('ping_from_client') +async def ping(sid): + await sio.emit('pong_from_server', room=sid) + + +def main(): + parse_command_line() + app = tornado.web.Application( + [ + (r"/", MainHandler), + (r"/socket.io/", socketio.get_tornado_handler(sio)), + ], + template_path=os.path.join(os.path.dirname(__file__), "templates"), + static_path=os.path.join(os.path.dirname(__file__), "static"), + debug=options.debug, + ) + app.listen(options.port) + tornado.ioloop.IOLoop.current().start() + + +if __name__ == "__main__": + main() diff --git a/examples/tornado/requirements.txt b/examples/tornado/requirements.txt new file mode 100644 index 00000000..354f4a7a --- /dev/null +++ b/examples/tornado/requirements.txt @@ -0,0 +1,4 @@ +tornado==5.0.2 +python-engineio +python_socketio +six==1.10.0 diff --git a/examples/tornado/static/style.css b/examples/tornado/static/style.css new file mode 100644 index 00000000..d20bcad9 --- /dev/null +++ b/examples/tornado/static/style.css @@ -0,0 +1,4 @@ +body { margin: 0; padding: 0; font-family: Helvetica Neue; } +h1 { margin: 100px 100px 10px; } +h2 { color: #999; margin: 0 100px 30px; font-weight: normal; } +#latency { color: red; } diff --git a/examples/tornado/templates/app.html b/examples/tornado/templates/app.html new file mode 100755 index 00000000..d71ebd29 --- /dev/null +++ b/examples/tornado/templates/app.html @@ -0,0 +1,91 @@ + + + + python-socketio test + + + + + +

python-socketio test

+

Send:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
+ + +
+
+ +
+

Receive:

+

+ + diff --git a/examples/tornado/templates/latency.html b/examples/tornado/templates/latency.html new file mode 100755 index 00000000..b238cd1c --- /dev/null +++ b/examples/tornado/templates/latency.html @@ -0,0 +1,64 @@ + + + + Socket.IO Latency + + + +

Socket.IO Latency

+

(connecting)

+ + + + + + + + diff --git a/setup.py b/setup.py index c2998ed4..d16ecd84 100755 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ platforms='any', install_requires=[ 'six>=1.9.0', - 'python-engineio>=1.2.1' + 'python-engineio>=2.2.0' ], tests_require=[ 'mock', diff --git a/socketio/__init__.py b/socketio/__init__.py index 9f7f7114..2c798bcc 100644 --- a/socketio/__init__.py +++ b/socketio/__init__.py @@ -8,6 +8,7 @@ from .zmq_manager import ZmqManager from .server import Server from .namespace import Namespace +from .tornado import get_tornado_handler if sys.version_info >= (3, 5): # pragma: no cover from .asyncio_server import AsyncServer from .asyncio_manager import AsyncManager @@ -26,4 +27,4 @@ 'Namespace'] if AsyncServer is not None: # pragma: no cover __all__ += ['AsyncServer', 'AsyncNamespace', 'AsyncManager', - 'AsyncRedisManager'] + 'AsyncRedisManager', 'get_tornado_handler'] diff --git a/socketio/tornado.py b/socketio/tornado.py new file mode 100644 index 00000000..007c4924 --- /dev/null +++ b/socketio/tornado.py @@ -0,0 +1,8 @@ +import sys +if sys.version_info >= (3, 5): + from engineio.async_tornado import get_tornado_handler as \ + get_engineio_handler + + +def get_tornado_handler(socketio_server): + return get_engineio_handler(socketio_server.eio)