我应该在tornado websocket服务器中的何处添加代码?

2024-04-24 17:04:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我只是带着“异步”和“线程”的基本知识跳到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?。目前,当我在处理程序中添加代码时,代码没有问题,但是我只有很少的测试客户机


Tags: 代码importselfinfoidmessagedefsocket
1条回答
网友
1楼 · 发布于 2024-04-24 17:04:12

也许不是完整的解决方案,但只是一些想法

你可以看看tornado websocket聊天示例, here

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

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

第二个变化是它们更新每个客户机(您可以在这里选择) 将更新作为@classmethod发送给所有人(但仅发送给部分人),因为 他们不希望接收实例(self),但希望接收类(cls)和 参考类变量(在其情况下为waiters、cache和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调用中,都会创建一个新的处理程序实例,称为self。而且self中的每个参数对于实例来说都是唯一的,并且与调用方法的实际客户机相关。这有助于在每次通话中识别客户机。 因此,基于实例的客户端列表(self.clients)在每次调用时总是为空。添加客户机只会将其添加到该实例的世界视图中

但有时您希望让一些变量(如客户机列表)与从类创建的所有实例相同。 这就是类变量(直接在类定义下定义的变量)和@classmethod装饰器发挥作用的地方

@classmethod使方法调用独立于a实例。这意味着您只能访问这些方法中的类变量。但是在一个 message broker这正是我们想要的:

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

  • 接收消息时,将其发送给所有(或客户端的子集)

  • 所以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)中添加了waiters,因此每个waiter实际上都是一个实例,您只是“简单地”调用实例(该实例代表调用方)write_message()方法。所以这不是广播而是一个接一个地发送给每个来电者。这将是一个地方,你可以分开一些标准,如主题或组

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

相关问题 更多 >