【问题标题】:Send message using Django Channels from outside Consumer class使用来自 Consumer 类外部的 Django Channels 发送消息
【发布时间】:2019-04-26 23:02:21
【问题描述】:

我正在构建一个在线游戏,它使用 Django 频道 2.1.5 进行 websockets。

我能够在客户端和服务器之间建立连接,并且只能在消费者类中在它们之间发送数据:

from channels.generic.websocket import WebsocketConsumer
import json
from . import controller

class GameConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()
        print("Wohooo .. Connected to client!")
        self.render()
        controller.startTurn()

    def render(self, type="render", message=None):
        self.send(controller.renderMap(type, message))

    def disconnect(self, close_code):
        print("WebSocket connection is lost...")

    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        controller.handleRecieved(text_data)
...

现在,我想做的是从另一个模块调用函数render,它位于消费者类内部

我试过了:

from .. import consumer

def sendDeployments(owner, armies):
    type = "renderDeployments"
    message = owner + " has " + str(armies) + " to deploy"
    dummyConsumer = consumer.GameConsumer()
    consumer.GameConsumer.render(type, message)

但是失败了,因为我不能使用类外的“self”参数。

谁能想到实现我的目标的方法?

Ps:我现在不关心同步。

【问题讨论】:

    标签: python django websocket django-channels


    【解决方案1】:

    首先,您需要您的消费者实例订阅一个组。

    from asgiref.sync import async_to_sync
    
    class GameConsumer(WebsocketConsumer):
        def connect(self):
            self.accept()
            self.render()
            async_to_sync(self.add_group)('render_updates_group') 
            controller.startTurn()
    ...
    

    然后,如果您不在您的消费者之外,您将需要向该组发送一条消息,以便所有已注册到该组的消费者都能收到该消息。

    from channels.layers import get_channel_layer
    from asgiref.sync import async_to_sync
    
    def sendDeployments(owner, armies):
        type = "renderDeployments"
        message = owner + " has " + str(armies) + " to deploy"
        channel_layer = get_channel_layer()
        async_to_sync(channel_layer.group_send)(
            'render_updates_group',
            {'type': 'render', 'message': message}
        )
    

    但是,您还需要记住在组断开连接时将您的使用者从组中删除。

    class GameConsumer(WebsocketConsumer):
        ....
    
        def disconnect(self, close_code):
             async_to_sync(self.group_name)('render_updates_group')
    

    如果您想限制哪些打开的连接会收到此呈现消息,您需要相应地构建您的组名。

    例如,如果您正在考虑一些在线游戏,那么您可能会在组名中包含一个MATCH-ID,该组名由同一场比赛的所有连接共享。

    channels Layers documentation 是一个很好的参考,但请记住,在生产环境中运行它时,您需要设置消息传递层(通常是 Redis)。

    【讨论】:

      【解决方案2】:

      首先为你的消费者做一点改变,比如

      from asgiref.sync import async_to_sync
      from channels.generic.websocket import WebsocketConsumer
      import json
      
      class EventConsumer(WebsocketConsumer):
          def connect(self):
              # self.room_name = self.scope['url_route']['kwargs']['room_name']
              # self.room_group_name = 'chat_%s' % self.room_name
              self.room_name = 'event'
              self.room_group_name = self.room_name+"_sharif"
              async_to_sync(self.channel_layer.group_add)(
                  self.room_group_name,
                  self.channel_name
              )
              print(self.room_group_name)
              self.accept()
              print("#######CONNECTED############")
      
          def disconnect(self, code):
              async_to_sync(self.channel_layer.group_discard)(
                  self.room_group_name,
                  self.channel_name
              )
              print("DISCONNECED CODE: ",code)
      
          def receive(self, text_data=None, bytes_data=None):
              print(" MESSAGE RECEIVED")
              data = json.loads(text_data)
              message = data['message']
              async_to_sync(self.channel_layer.group_send)(
                  self.room_group_name,{
                      "type": 'send_message_to_frontend',
                      "message": message
                  }
              )
          def send_message_to_frontend(self,event):
              print("EVENT TRIGERED")
              # Receive message from room group
              message = event['message']
              # Send message to WebSocket
              self.send(text_data=json.dumps({
                  'message': message
              }))
      

      然后从您的应用程序外部/任何地方调用该函数,例如

      def event_triger():
          channel_layer = get_channel_layer()
          async_to_sync(channel_layer.group_send)(
              'event_sharif',
              {
                  'type': 'send_message_to_frontend',
                  'message': "event_trigered_from_views"
              }
          ) 
      # here 'event_sharif' is your room_group_name as i defined before in consumer
      # 'type' is like a command, for which method you wants to trigger in your consumer
      

      【讨论】:

      • 嗨,我应该使用路由来实现这一点吗?我是 django 频道的新手,如果这是一个愚蠢的问题,我很抱歉。
      • 非常感谢!这真的对我有用。我是否必须为此方法专门管理断开功能?
      【解决方案3】:

      断开功能的正确代码(使用通道 2.3.0)是:

      class GameConsumer(WebsocketConsumer):
          ....
      
          def disconnect(self, close_code):
              self.channel_layer.group_discard(self.channel_name, 'render_updates_group')
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-11
        • 2017-01-12
        • 2017-06-09
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        • 2018-07-29
        • 1970-01-01
        相关资源
        最近更新 更多