server.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import socket
  2. import logging
  3. from traceback import format_exc
  4. from threading import Thread
  5. from ssl import SSLError
  6. from websocket import websocket
  7. from connection import Connection
  8. from frame import CLOSE_NORMAL
  9. from errors import HandshakeError
  10. class Server(object):
  11. """
  12. Websocket server, manages multiple client connections.
  13. Example usage:
  14. >>> import twspy
  15. >>> class GameServer(twspy.Server):
  16. >>> def onopen(self, client):
  17. >>> # client connected
  18. >>> def onclose(self, client):
  19. >>> # client disconnected
  20. >>> def onmessage(self, client, message):
  21. >>> # handle message from client
  22. >>> GameServer(8000).run()
  23. """
  24. def __init__(self, port, hostname='', loglevel=logging.INFO, protocols=[],
  25. secure=False, **kwargs):
  26. """
  27. Constructor for a simple websocket server.
  28. `hostname` and `port` form the address to bind the websocket to.
  29. `loglevel` values should be imported from the logging module.
  30. logging.INFO only shows server start/stop messages, logging.DEBUG shows
  31. clients (dis)connecting and messages being sent/received.
  32. `protocols` is a list of supported protocols, passed directly to the
  33. websocket constructor.
  34. `secure` is a flag indicating whether the server is SSL enabled. In
  35. this case, `keyfile` and `certfile` must be specified. Any additional
  36. keyword arguments are passed to websocket.enable_ssl (and thus to
  37. ssl.wrap_socket).
  38. """
  39. logging.basicConfig(level=loglevel,
  40. format='%(asctime)s: %(levelname)s: %(message)s',
  41. datefmt='%H:%M:%S')
  42. scheme = 'wss' if secure else 'ws'
  43. logging.info('Starting server at %s://%s:%d', scheme, hostname, port)
  44. self.sock = websocket()
  45. self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  46. if secure:
  47. self.sock.enable_ssl(server_side=True, **kwargs)
  48. self.sock.bind((hostname, port))
  49. self.sock.listen(5)
  50. self.clients = []
  51. self.protocols = protocols
  52. def run(self):
  53. while True:
  54. try:
  55. sock, address = self.sock.accept()
  56. client = Client(self, sock)
  57. self.clients.append(client)
  58. logging.debug('Registered client %s', client)
  59. thread = Thread(target=client.receive_forever)
  60. thread.daemon = True
  61. thread.start()
  62. except SSLError as e:
  63. logging.error('SSL error: %s', e)
  64. except HandshakeError as e:
  65. logging.error('Invalid request: %s', e.message)
  66. except KeyboardInterrupt:
  67. logging.info('Received interrupt, stopping server...')
  68. break
  69. except Exception as e:
  70. logging.error(format_exc(e))
  71. self.quit_gracefully()
  72. def quit_gracefully(self):
  73. for client in self.clients:
  74. client.close(CLOSE_NORMAL)
  75. def remove_client(self, client, code, reason):
  76. self.clients.remove(client)
  77. self.onclose(client, code, reason)
  78. def onopen(self, client):
  79. logging.debug('Opened socket to %s', client)
  80. def onmessage(self, client, message):
  81. logging.debug('Received %s from %s', message, client)
  82. def onping(self, client, payload):
  83. logging.debug('Sent ping "%s" to %s', payload, client)
  84. def onpong(self, client, payload):
  85. logging.debug('Received pong "%s" from %s', payload, client)
  86. def onclose(self, client, code, reason):
  87. msg = 'Closed socket to %s' % client
  88. if code is not None:
  89. msg += ' [%d]' % code
  90. if len(reason):
  91. msg += ': ' + reason
  92. logging.debug(msg)
  93. def onerror(self, client, e):
  94. logging.error(format_exc(e))
  95. class Client(Connection):
  96. def __init__(self, server, sock):
  97. self.server = server
  98. super(Client, self).__init__(sock)
  99. def __str__(self):
  100. return '<Client at %s:%d>' % self.sock.getpeername()
  101. def send(self, message, fragment_size=None, mask=False):
  102. logging.debug('Sending %s to %s', message, self)
  103. Connection.send(self, message, fragment_size=fragment_size, mask=mask)
  104. def onopen(self):
  105. self.server.onopen(self)
  106. def onmessage(self, message):
  107. self.server.onmessage(self, message)
  108. def onping(self, payload):
  109. self.server.onping(self, payload)
  110. def onpong(self, payload):
  111. self.server.onpong(self, payload)
  112. def onclose(self, code, reason):
  113. self.server.remove_client(self, code, reason)
  114. def onerror(self, e):
  115. self.server.onerror(self, e)
  116. if __name__ == '__main__':
  117. import sys
  118. port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
  119. Server(port, loglevel=logging.DEBUG).run()