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

问题描述

我只是以“异步”和“线程”的基本知识跳入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类中还是在外部或其他文件添加所有内容?以及何时使用@classmethod?现在,当我在处理程序中添加代码但我只有几个测试客户端时,代码就没有问题。

解决方法

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

您也许可以看一下龙卷风网络套接字聊天示例, here

第一个好的变化是,他们的客户(侍者)是set() 确保默认情况下每个客户端仅包含一次。并且它被定义和访问为类变量。因此,您将不使用self.waiters而是使用cls.waiters或ClassName.waiters(在本例中为ChatSocketHandler.waiters)来访问它。

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

第二个变化是他们更新了每个客户端(您可以在此处选择 因为@classmethod不会将更新发送给所有人,而是仅发送给部分),因为 他们不想接收实例(自己),但不想接收类(cls)和 引用类变量(在这种情况下,是服务员,缓存和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))在每次调用时始终为空。添加客户只会将其添加到该实例的世界视图中。

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

@classmethod使方法调用独立于实例。这意味着您只能在那些方法中访问类变量。但是在 消息代理,这几乎就是我们想要的:

  • 将客户端添加到类变量,该变量对于处理程序的所有实例都是相同的。而且由于它是一个集合,所以每个客户端都是唯一的。

  • 接收消息时,将其发送给所有(或部分客户)

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

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

从示例:

@classmethod
    def send_updates(cls,exc_info=True)

请记住,您已经在waiters.append(self)中添加了服务员,因此每个服务员实际上都是一个实例,并且您“简单地”调用该实例(该实例代表一个调用者)write_message()方法。因此,此消息不会广播,而是一一发送给每个呼叫者。在这里您可以按某些条件(例如主题或小组)分开...

因此,简而言之:对于独立于特定实例(例如您的调用方或客户端)的方法使用@classmethod,并且您想对自己的“全部”或“全部”子集进行操作客户。但是您只能在那些方法中访问类变量。因为这是他们的目的,所以应该没问题;)