skip to Main Content

I am using a combination of DRF 3.11.0 and Channels 2.4.0 to implement a backend, and it is hosted on Heroku on 1 dyno with a Redis resource attached. I have a socket on my React frontend that successfully sends/received from the backend server.

I am having an issues where any message sent back to the front end over the socket is being sent twice. I have confirmed through console.log that the front end is only pinging the back end once. I can confirm through print() inside of the API call that the function is only calling async_to_sync(channel_layer.group_send) once as well. The issue is coming from my consumer – when I use print(self.channel_name) inside of share_document_via_videocall(), I can see that two instances with different self.channel_names are being called (specific.AOQenhTn!fUybdYEsViaP and specific.AOQenhTn!NgtWxuiHtHBw. It seems like the consumer has connected to two separate channels, but I’m not sure why. When I put print() statements in my connect() I only see it go through the connect process once.

How do I ensure that I am only connected to one channel?

in settings.py:

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            #"hosts": [('127.0.0.1', 6379)],
            "hosts": [(REDIS_HOST)],
        },
    },
}

Consumer:

import json
from asgiref.sync import async_to_sync
from channels.db import database_sync_to_async

from channels.generic.websocket import AsyncWebsocketConsumer
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from .exceptions import ClientError
import datetime
from django.utils import timezone

class HeaderConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        print("connecting")

        await self.accept()
        print("starting")
        print(self.channel_name)
        await self.send("request_for_token")


    async def continue_connect(self):
        print("continuing")
        print(self.channel_name)

        await self.get_user_from_token(self.scope['token'])

        await self.channel_layer.group_add(
            "u_%d" % self.user['id'],
            self.channel_name,
        )
        #... more stuff


    async def disconnect(self, code):
        await self.channel_layer.group_discard(
            "u_%d" % self.user['id'],
            self.channel_name,
        )


    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        if 'token' in text_data_json:
            self.scope['token'] = text_data_json['token']
            await self.continue_connect()


    async def share_document_via_videocall(self, event):

        # Send a message down to the client
        print("share_document received")
        print(event)
        print(self.channel_name)
        print(self.user['id'])

        await self.send(text_data=json.dumps(
            {
                "type": event['type'],
                "message": event["message"],
            },
        ))

    @database_sync_to_async
    def get_user_from_token(self, t):
        try:
            print("trying token" + t)
            token = Token.objects.get(key=t)
            self.user = token.user.get_profile.json()
        except Token.DoesNotExist:
            print("failed")
            self.user = AnonymousUser()

REST API call:

class ShareViaVideoChat(APIView):
    permission_classes = (permissions.IsAuthenticated,)

    def post(self, request, format=None):
        data = request.data
        recipient_list = data['recipient_list']

        channel_layer = get_channel_layer()
        for u in recipient_list:
            if u['id'] != None:
                print("sending to:")
                print('u_%d' % u['id'])
                async_to_sync(channel_layer.group_send)(
                    'u_%d' % u['id'],
                    {'type': 'share_document_via_videocall',
                     'message': {
                             'document': {'data': {}},
                             'sender': {'name': 'some name'}
                         }
                     }
                )

        return Response()

2

Answers


  1. with respect to you getting to calls with different channel names are you sure your frontend has not connected twice to the consumer? Check in the debug console in your browser.

    Login or Signup to reply.
  2. i get same problem with nextjs as a frontend of Django channels WebSocket server.
    and after searching i found the problem related with tow things:

    1- react strict mode (the request sending twice) :

    to disable react strict mode in next.js , go to module name "next.config.js" , and change the value for strict mode to false , as the following :

    /** @type {import('next').NextConfig} */
    const nextConfig = {
      reactStrictMode: false,
    }
    
    module.exports = nextConfig
    

    2- in nextjs the code run twice (outside useEffect Hook) , one on server side and the second on the client side (which means each user will connect to websocket server twice, and got two channels name , and join to same group twice each time with different channel name . ) ,

    so i changed my codes to connect with Django channels server only from client side , if you like see my full code / example , kindly visit the following URL , and note the checking code about "typeof window === "undefined":

    frontend nextjs code :
    https://stackoverflow.com/a/72288219/12662056

    i don’t know if my problem same your problem , but i hope that helpful.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search