Django Channels WebSocket подключается, но сразу же закрывается (приложение для живого чата)

Я создаю веб-сайт с прямой трансляцией, где пользователи могут смотреть занятия в режиме реального времени и взаимодействовать с помощью веб-чата.

Я интегрировал Каналы Django, Повторите и HLS.js для воспроизведение видео.

Чат подключается через WebSocket к каналам Django.
Однако проблема в следующем:

  • Веб-сокет подключается успешно, но после этого немедленно закрывается.
  • В консоли браузера отображаются многочисленные попытки повторно подключиться, но проблема не устраняется.

В консоли JavaScript нет четкого сообщения об ошибке, кроме "WebSocket-соединение закрыто".

<время работы/>

Мои настройки

  • Джанго 5.1.8
  • каналы
  • каналы-redis
  • Запущен сервер Redis (redis-cli ping возвращает PONG)
  • Сервер разработки (локальный компьютер, runserver + daphne для каналов)
<время работы/>

Фрагменты кода

На стороне HTML/JS (live_stream.html)

<script>
  // WebSocket Chat functionality
  const streamId = "UDOOSDIHOH49849";  // Your stream_id
  let chatSocket = null;
  let reconnectAttempts = 0;
  const maxReconnectAttempts = 5;

  // You can fetch real username from Django template if available
  const username = "Anonymous";  // 🔥 You can dynamically change this later

  function connectWebSocket() {
      chatSocket = new WebSocket(
          'ws://' + window.location.host + '/ws/chat/' + streamId + '/'
      );

      chatSocket.onopen = function(e) {
          console.log('WebSocket connection established');
          reconnectAttempts = 0;
      };

      chatSocket.onmessage = function(e) {
          const data = JSON.parse(e.data);
          const chatBox = document.getElementById('chatMessages');
          const newMessage = document.createElement('p');
          newMessage.innerHTML = `<strong>${data.username}:</strong> ${data.message}`;
          chatBox.appendChild(newMessage);
          chatBox.scrollTop = chatBox.scrollHeight;
      };

      chatSocket.onclose = function(e) {
          console.log('WebSocket connection closed');
          if (reconnectAttempts < maxReconnectAttempts) {
              console.log('Attempting to reconnect...');
              reconnectAttempts++;
              setTimeout(connectWebSocket, 3000);
          } else {
              console.error('Maximum reconnection attempts reached');
          }
      };

      chatSocket.onerror = function(e) {
          console.error('WebSocket error:', e);
      };
  }

  // Initial connection
  connectWebSocket();

  // Send button functionality
  document.getElementById('sendBtn').onclick = function() {
      const input = document.getElementById('chatInput');
      const message = input.value.trim();
      if (message !== '' && chatSocket && chatSocket.readyState === WebSocket.OPEN) {
          chatSocket.send(JSON.stringify({
              'message': message,
              'username': username   // Send both message and username
          }));
          input.value = '';
      }
  };

  // Enter key support
  document.getElementById('chatInput').addEventListener('keypress', function(e) {
      if (e.key === 'Enter') {
          document.getElementById('sendBtn').click();
      }
  });

</script>
<время работы/>

views.py

from django.contrib.auth.decorators import login_required
from django.shortcuts import render

@login_required
def watch_stream(request, stream_id="UDOOSDIHOH49849"):
    return render(request, 'video/live_stream.html', {
        'stream_id': stream_id,
        'user': request.user,
    })
<время работы/>

consumers.py

# consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        from videoconference_app.models import LiveStream  # Import here to avoid app loading issues
        try:
            self.stream_id = self.scope['url_route']['kwargs']['stream_id']
            self.user = self.scope['user']
            self.room_group_name = f'chat_{self.stream_id}'

            await self.channel_layer.group_add(
                self.room_group_name,
                self.channel_name
            )

            await self.accept()

            # Notify others about new user joining
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'chat_message',
                    'message': f"{self.user.username if self.user.is_authenticated else 'Anonymous'} joined the chat!",
                    'username': self.user.username if self.user.is_authenticated else 'Anonymous'
                }
            )

        except Exception as e:
            print(f"WebSocket connect error: {e}")
            await self.close()

    async def disconnect(self, close_code):
        try:
            await self.channel_layer.group_discard(
                self.room_group_name,
                self.channel_name
            )
        except Exception as e:
            print(f"WebSocket disconnect error: {e}")

    async def receive(self, text_data):
        try:
            text_data_json = json.loads(text_data)
            message = text_data_json.get('message', '')
            username = text_data_json.get('username', 'Anonymous')

            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'chat_message',
                    'message': message,
                    'username': username
                }
            )
        except Exception as e:
            print(f"WebSocket receive error: {e}")

    async def chat_message(self, event):
        try:
            await self.send(text_data=json.dumps({
                'message': event['message'],
                'username': event.get('username', 'Anonymous')
            }))
        except Exception as e:
            print(f"WebSocket chat_message error: {e}")

    @database_sync_to_async
    def get_stream(self):
        from videoconference_app.models import LiveStream
        try:
            return LiveStream.objects.get(stream_key=self.stream_id)
        except LiveStream.DoesNotExist:
            return None

<время работы/>

routing.py

from django.urls import re_path
from videoconference_app import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<stream_id>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
<время работы/>

asgi.py

import os
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from channels.auth import AuthMiddlewareStack
import videoconference_app.routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'course.settings')

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter(
            videoconference_app.routing.websocket_urlpatterns
        )
    ),
})
<время работы/>

settings.py ( Каналы + Redis)

ASGI_APPLICATION = 'course.asgi.application'

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}
<время работы/>

То, что Я Уже Пробовал

  • Подтверждено, что Сервер Redis активен (PONG подтверждено).
  • Проверил, что URL веб-сокета указан правильно (ws:// с момента использования http://localhost).
  • Подтверждено, что Дафна и Джанго работают вместе (daphne course.asgi:application).
  • Никаких видимых ошибок в терминале Django нет, за исключением случайных журналов отключения WebSocket.
  • Клиент JavaScript корректно повторяет попытку, но сервер немедленно закрывает соединение.
<время работы/>

Дополнительная информация

  • Страница трансляции защищена с помощью @login_required, поэтому доступ к ней могут получить только прошедшие проверку подлинности пользователи.
  • Объект user доступен в шаблоне Django.
<время работы/>

Запрос

Что может быть причиной немедленного закрытия WebSocket после подключения?
Я что-то упустил в логике пользователя, настройке ASGI или конфигурации каналов?

Мы были бы признательны за любые советы или отладку!

Заранее благодарю вас.

Вернуться на верх