简体   繁体   English

使用 Django Channels 从外部 Consumer 类发送消息

[英]Send message using Django Channels from outside Consumer class

I am building an online game, which uses Django channels 2.1.5 for websockets.我正在构建一个在线游戏,它使用 Django 通道 2.1.5 进行 websockets。

I am able to build the connection between the client and the server, and also able to send data between them only inside the consumer class:我能够在客户端和服务器之间建立连接,并且也能够仅在消费者类内部在它们之间发送数据:

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)
...

Now, What I wish to do, is to call the function render , -which is inside the consumer class-, from another module现在,我想做的是从另一个模块调用函数render ,它位于消费者类中

I tried this:我试过这个:

from .. import consumer

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

But failed because I can't use the "self" parameter from outside the class.但失败了,因为我不能在课外使用“self”参数。

Can anybody think of a way to achieve my goal?有人能想出一种方法来实现我的目标吗?

Ps: I don't care about synchronization at this occasion. Ps:我不在乎这个场合的同步。

Firstly you need your consumer instance to subscribe to a group.首先,您需要您的消费者实例来订阅一个组。

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()
...

Then if you are outside of your consumer you will need to send a message to that group so that all the consumers that have registered onto the group get the message.然后,如果您在消费者之外,则需要向该组发送一条消息,以便所有注册到该组的消费者都能收到该消息。

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}
    )

However, you also need to remember to remove your consumer from the group when it disconnects.但是,您还需要记住在断开连接时将您的使用者从组中删除。

class GameConsumer(WebsocketConsumer):
    ....

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

If you want to limit which open connections get this render message you need to build your group name accordingly.如果您想限制哪些打开的连接收到此渲染消息,您需要相应地构建您的组名。

eg if you are thinking about some online game then maybe you include a MATCH-ID in the group name that is shared by all connections for the same match.例如,如果您正在考虑某个在线游戏,那么您可能会在组名称中包含一个MATCH-ID ,该名称由同一比赛的所有连接共享。

A good reference for this is the channels Layers documentation but do remember when running this in production you will need a messaging layer (normally Redis) set up.对此的一个很好的参考是通道层文档,但请记住,在生产中运行它时,您需要设置一个消息传递层(通常是 Redis)。

First make a little change for your Consumer like首先为你的消费者做一些改变,比如

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
        }))

then call the function outside/anywhere from your app like然后从您的应用程序外部/任何地方调用该函数,例如

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

The correct code for the disconnect function (using channels 2.3.0) would be:断开连接功能(使用通道 2.3.0)的正确代码是:

class GameConsumer(WebsocketConsumer):
    ....

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM