我在哪里添加代码在龙卷风 websocket 服务器?

where do i add code in tornado websocket server?

我只是带着“异步”和“线程”的基本知识进入 websocket 编程,我有这样的东西

import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
import uuid
import json
import datetime

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

    def open(self):
        self.id = str(uuid.uuid4())
        self.user_info = self.request.remote_ip +' - '+ self.id
        print (f'[{self.user_info}] Conectado')

        client = {"sess": self, "id" : self.id}
        self.clients.append(client.copy())
      
    def on_message(self, message):
        print (f'[{self.user_info}] Mensaje Recivido: {message}')
        print (f'[{self.user_info}] Respuesta al Cliente: {message[::-1]}')
        self.write_message(message[::-1])
        self.comm(message)
 
    def on_close(self):
        print (f'[{self.user_info}] Desconectado')
        for x in self.clients:
            if x["id"] == self.id :
                self.clients.remove(x)

    def check_origin(self, origin):
        return True

application = tornado.web.Application([
    (r'/', WSHandler),
])
 
 
if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(80)
    myIP = socket.gethostbyname(socket.gethostname())
    print ('*** Websocket Server Started at %s***' % myIP)
    tornado.ioloop.IOLoop.instance().start()

我的问题是我应该在哪里添加代码?我应该将所有内容都添加到 WSHandler class 内部还是外部,还是另一个文件中?什么时候使用@class方法?现在,当我在处理程序中添加代码时,代码没有问题,但我只有几个测试客户端。

可能不是完整的解决方案,只是一些想法..

你也许可以看看tornado websocket chat example, here.

第一个好的变化是,他们的客户(服务员)是一个 set() 这确保默认情况下每个客户端只包含一次。它作为 class 变量定义和访问。因此,您不使用 self.waiters,而是使用 cls.waiters 或 ClassName.waiters(在本例中为 ChatSocketHandler.waiters)来访问它。

class ChatSocketHandler(tornado.websocket.WebSocketHandler):
    waiters = set()

第二个变化是他们更新每个客户端(你可以在这里选择 以@class方法将更新发送给部分而不是全部,因为 他们不想接收实例 (self) 但 class (cls) 和 参考 class 变量(在他们的例子中,服务员、缓存和 cach_size)

我们可以在这里忘记缓存和缓存大小。

像这样:

@classmethod
    def send_updates(cls, chat):
        logging.info("sending message to %d waiters", len(cls.waiters))
        for waiter in cls.waiters:
            try:
                waiter.write_message(chat)
            except:
                logging.error("Error sending message", exc_info=True)

每次 API 调用都会创建一个新的处理程序实例,称为 selfself 中的每个参数对于实例来说都是独一无二的,并且与调用您的方法的实际客户端相关。这有助于在每次呼叫时识别客户。 因此,像 (self.clients) 这样基于实例的客户端列表在每次调用时始终为空。添加客户端只会将其添加到此实例的世界视图中。

但有时您希望某些变量(例如客户端列表)对于从您的 class 创建的所有实例都相同。 这是 class 变量(您直接在 class 定义下定义的变量)和 @classmethod 装饰器发挥作用的地方。

@classmethod 使方法调用独立于 a 实例。这意味着您只能在这些方法中访问 class 变量。但在一个情况下 消息代理这正是我们想要的:

  • 将客户端添加到 class 变量,这对于您的处理程序的所有实例都是相同的。并且由于它被定义为一个集合,每个客户端都是唯一的。

  • 收到消息时,将它们发送给所有(或部分客户)

  • 所以 on_message 是一个“普通”实例方法,但它调用类似的东西:send_updates() 最后是 @classmethod

  • send_updates() 遍历所有(或子集)客户端(服务员),并使用它最终发送实际更新。

来自示例:

@classmethod
    def send_updates(cls, chat):
        logging.info("sending message to %d waiters", len(cls.waiters))
        for waiter in cls.waiters:
            try:
                waiter.write_message(chat)
            except:
                logging.error("Error sending message", exc_info=True)

请记住,您使用 waiters.append(self) 添加了服务员,因此每个服务员实际上都是一个实例,您“只是”调用这些实例(实例代表调用者)write_message()方法。所以这不是广播,而是一一发送给每个呼叫者。在这里您可以按主题或组等标准分隔...

简而言之:将 @classmethod 用于独立于特定实例(如您的情况下的调用者或客户端)的方法,并且您想对“全部”或“全部”的子集执行操作你的客户。但是您只能在这些方法中访问 class 变量。这应该没问题,因为这是他们的目的 ;)