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 или конфигурации каналов?
Мы были бы признательны за любые советы или отладку!
Заранее благодарю вас.