-->

Tornado: Identify / track connections of websocket

2020-02-17 06:16发布

问题:

I have a basic Tornado websocket test:

import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web

class WSHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        print 'new connection'
        self.write_message("Hello World")

    def on_message(self, message):
        print 'message received %s' % message

    def on_close(self):
      print 'connection closed'


application = tornado.web.Application([
    (r'/ws', WSHandler),
])


if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

I want to be able to handle multiple connections (which it seems to do already) but also to be able to reference other connections. I don't see a way to identify and keep track of individual connections, just to be able to handle events on connection open, receipt of messages, and connection close.

[Edit]
Thought of creating a dict where the key is the Sec-websocket-key and and the value is the WSHandler object... thoughts? I'm not sure how dependable Sec-websocket-key is to be unique.

回答1:

The simplest method is just to keep a list or dict of WSHandler instances:

class WSHandler(tornado.websocket.WebSocketHandler):
    clients = []

    def open(self):
        self.clients.append(self)
        print 'new connection'
        self.write_message("Hello World")

    def on_message(self, message):
        print 'message received %s' % message

    def on_close(self):
        self.clients.remove(self)
        print 'closed connection'

If you want to identify connections, e.g. by user, you'll probably have to send that information over the socket.



回答2:

Cole Maclean asnwer is good as simple solution, when you just need list of all connections. However, if you want something more complex, that can be monitored outside of WSHandler instance - be brave do it like this:

class WSHandler(tornado.websocket.WebSocketHandler):

    def open(self):
        self.id = uuid.uuid4()
        external_storage[self.id] = {'id':self.id}
        print 'new connection'
        self.write_message("Hello World")

    def on_message(self, message):
        #Some message parsing here
        if message.type == 'set_group':
           external_storage[self.id]['group'] = message.group
        print 'message received %s' % message

    def on_close(self):
        external_storage.remove(self.id)
        print 'closed connection'


回答3:

If your users have an access token, this can be appended to your websocket endpoint, say, and fetched in initialising your socket even before it's opened (but please work over SSL).

If an access token is not available, either because the user hasn't supplied one or the token they supplied has expired, the user is not authenticated and you will kill the socket at the earliest opportunity.

However you do this, the access token should be associated to a user who will have an identifier and that identifier can then be tied to the socket even before it has been opened. The identifier can serve as a dictionary key whose value is a set of sockets tied to this user.



回答4:

I have solved this issue by checking the origin of the conexion. So, overrriding the method def check_origin(self, origin) may help. For example:

clients = {}

class WSHandler(tornado.websocket.WebSocketHandler):


    def check_origin(self, origin): 

        clients[origin] = self
        print(clients)
        return True