tornado.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import asyncio
  2. import json
  3. import tornado
  4. import tornado.httpserver
  5. import tornado.ioloop
  6. import tornado.websocket
  7. from tornado.web import StaticFileHandler
  8. from . import STATIC_PATH
  9. from ..session import AsyncBasedSession, ThreadBasedWebIOSession, get_session_implement
  10. from ..utils import get_free_port
  11. def webio_handler(task_func, debug=True):
  12. class WSHandler(tornado.websocket.WebSocketHandler):
  13. def check_origin(self, origin):
  14. return True
  15. def get_compression_options(self):
  16. # Non-None enables compression with default options.
  17. return {}
  18. def send_msg_to_client(self, controller: AsyncBasedSession):
  19. for msg in controller.get_task_messages():
  20. self.write_message(json.dumps(msg))
  21. def open(self):
  22. print("WebSocket opened")
  23. self.set_nodelay(True)
  24. self._close_from_session = False # 是否从session中关闭连接
  25. if get_session_implement() is AsyncBasedSession:
  26. self.controller = AsyncBasedSession(task_func, on_task_message=self.send_msg_to_client,
  27. on_session_close=self.close)
  28. else:
  29. self.controller = ThreadBasedWebIOSession(task_func, on_task_message=self.send_msg_to_client,
  30. on_session_close=self.close_from_session,
  31. loop=asyncio.get_event_loop())
  32. print('open return, ThreadBasedWebIOSession.thread2session', ThreadBasedWebIOSession.thread2session)
  33. def on_message(self, message):
  34. # print('on_message', message)
  35. data = json.loads(message)
  36. self.controller.send_client_event(data)
  37. def close_from_session(self):
  38. self._close_from_session = True
  39. self.close()
  40. def on_close(self):
  41. if not self._close_from_session:
  42. self.controller.close(no_session_close_callback=True)
  43. print("WebSocket closed")
  44. return WSHandler
  45. def start_server(target, port=0, host='', debug=True,
  46. websocket_max_message_size=None,
  47. websocket_ping_interval=None,
  48. websocket_ping_timeout=None,
  49. **tornado_app_settings):
  50. """Start a Tornado server to serve `target` function
  51. :param target: task function. It's a coroutine function is use AsyncBasedSession or
  52. a simple function is use ThreadBasedWebIOSession.
  53. :param port: server bind port. set ``0`` to find a free port number to use
  54. :param host: server bind host. ``host`` may be either an IP address or hostname. If it's a hostname,
  55. the server will listen on all IP addresses associated with the name.
  56. set empty string or to listen on all available interfaces.
  57. :param debug: Tornado debug mode
  58. :param int websocket_max_message_size: Max bytes of a message which Tornado can accept.
  59. Messages larger than the ``websocket_max_message_size`` (default 10MiB) will not be accepted.
  60. :param int websocket_ping_interval: If set to a number, all websockets will be pinged every n seconds.
  61. This can help keep the connection alive through certain proxy servers which close idle connections,
  62. and it can detect if the websocket has failed without being properly closed.
  63. :param int websocket_ping_timeout: If the ping interval is set, and the server doesn’t receive a ‘pong’
  64. in this many seconds, it will close the websocket. The default is three times the ping interval,
  65. with a minimum of 30 seconds. Ignored if ``websocket_ping_interval`` is not set.
  66. :param tornado_app_settings: Additional keyword arguments passed to the constructor of ``tornado.web.Application``.
  67. ref: https://www.tornadoweb.org/en/stable/web.html#tornado.web.Application.settings
  68. :return:
  69. """
  70. kwargs = locals()
  71. app_options = ['debug', 'websocket_max_message_size', 'websocket_ping_interval', 'websocket_ping_timeout']
  72. for opt in app_options:
  73. if kwargs[opt] is not None:
  74. tornado_app_settings[opt] = kwargs[opt]
  75. if port == 0:
  76. port = get_free_port()
  77. print('Listen on %s:%s' % (host or '0.0.0.0', port))
  78. handlers = [(r"/io", webio_handler(target)),
  79. (r"/(.*)", StaticFileHandler, {"path": STATIC_PATH, 'default_filename': 'index.html'})]
  80. app = tornado.web.Application(handlers=handlers, **tornado_app_settings)
  81. app.listen(port, address=host)
  82. tornado.ioloop.IOLoop.current().start()