Django Channels Websockets 立即连接和断开连接
Posted
技术标签:
【中文标题】Django Channels Websockets 立即连接和断开连接【英文标题】:Django Channels Websockets Connecting and Disconnecting right away 【发布时间】:2017-03-06 00:45:11 【问题描述】:首先是一些背景知识,我正在学习本教程: https://blog.heroku.com/in_deep_with_django_channels_the_future_of_real_time_apps_in_django 在 Windows 64 位上使用 Redis 的 Vagrant 发行版从这里: https://github.com/ServiceStack/redis-windows 使用 Python 3.4 和 Django 1.10
我能够很好地克隆、构建和运行项目。问题是,当我通过浏览器访问 django 视图时,它可以正常加载聊天,但是当我在控制台中发送消息时,它说 websocket 连接然后几乎立即断开连接并且消息不发送。如果我手动将数据输入数据库,那么当我第一次加载视图时会显示消息,但由于某种原因,javascript 应用程序无法保持 websocket 打开以发送消息以进行广播。下面是相关代码:
consumers.py
import re
import json
import logging
from channels import Group
from channels.sessions import channel_session
from .models import Room
log = logging.getLogger(__name__)
@channel_session
def ws_connect(message):
# Extract the room from the message. This expects message.path to be of the
# form /chat/label/, and finds a Room if the message path is applicable,
# and if the Room exists. Otherwise, bails (meaning this is a some othersort
# of websocket). So, this is effectively a version of _get_object_or_404.
try:
prefix, label = message['path'].strip('/').split('/')
if prefix != 'chat':
log.debug('invalid ws path=%s', message['path'])
return
room = Room.objects.get(label=label)
except ValueError:
log.debug('invalid ws path=%s', message['path'])
return
except Room.DoesNotExist:
log.debug('ws room does not exist label=%s', label)
return
log.debug('chat connect room=%s client=%s:%s',
room.label, message['client'][0], message['client'][1])
# Need to be explicit about the channel layer so that testability works
# This may be a FIXME?
Group('chat-'+label, channel_layer=message.channel_layer).add(message.reply_channel)
message.channel_session['room'] = room.label
@channel_session
def ws_receive(message):
# Look up the room from the channel session, bailing if it doesn't exist
try:
label = message.channel_session['room']
room = Room.objects.get(label=label)
except KeyError:
log.debug('no room in channel_session')
print('no room in channel_session')
return
except Room.DoesNotExist:
log.debug('recieved message, buy room does not exist label=%s', label)
print('recieved message, buy room does not exist label=%s', label)
return
# Parse out a chat message from the content text, bailing if it doesn't
# conform to the expected message format.
try:
data = json.loads(message['text'])
except ValueError:
log.debug("ws message isn't json text=%s", text)
print("ws message isn't json text=%s", text)
return
if set(data.keys()) != set(('handle', 'message')):
log.debug("ws message unexpected format data=%s", data)
print("ws message unexpected format data=%s", data)
return
if data:
log.debug('chat message room=%s handle=%s message=%s',
room.label, data['handle'], data['message'])
print('chat message room=%s handle=%s message=%s',
room.label, data['handle'], data['message'])
m = room.messages.create(**data)
# See above for the note about Group
Group('chat-'+label, channel_layer=message.channel_layer).send('text': json.dumps(m.as_dict()))
@channel_session
def ws_disconnect(message):
try:
label = message.channel_session['room']
room = Room.objects.get(label=label)
Group('chat-'+label, channel_layer=message.channel_layer).discard(message.reply_channel)
except (KeyError, Room.DoesNotExist):
pass
聊天.js
$(function()
// When we're using HTTPS, use WSS too.
var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
var chatsock = new WebSocket(ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname);
chatsock.onmessage = function(message)
var data = JSON.parse(message.data);
var chat = $("#chat")
var ele = $('<tr></tr>')
ele.append(
$("<td></td>").text(data.timestamp)
)
ele.append(
$("<td></td>").text(data.handle)
)
ele.append(
$("<td></td>").text(data.message)
)
chat.append(ele)
;
$("#chatform").on("submit", function(event)
var message =
handle: $('#handle').val(),
message: $('#message').val(),
chatsock.send(JSON.stringify(message));
$("#message").val('').focus();
return false;
);
);
views.py
import random
import string
from django.db import transaction
from django.shortcuts import render, redirect
from haikunator import Haikunator
from .models import Room
def about(request):
return render(request, "chat/about.html")
def new_room(request):
"""
Randomly create a new room, and redirect to it.
"""
haikunator = Haikunator()
new_room = None
while not new_room:
with transaction.atomic():
label = haikunator.haikunate()
if Room.objects.filter(label=label).exists():
continue
new_room = Room.objects.create(label=label)
return redirect(chat_room, label=label)
def chat_room(request, label):
"""
Room view - show the room, with latest messages.
The template for this view has the WebSocket business to send and stream
messages, so see the template for where the magic happens.
"""
# If the room with the given label doesn't exist, automatically create it
# upon first visit (a la etherpad).
room, created = Room.objects.get_or_create(label=label)
# We want to show the last 50 messages, ordered most-recent-last
messages = reversed(room.messages.order_by('-timestamp')[:50])
return render(request, "chat/room.html",
'room': room,
'messages': messages,
)
models.py
from __future__ import unicode_literals
from django.db import models
from django.utils import timezone
class Room(models.Model):
name = models.TextField()
label = models.SlugField(unique=True)
def __unicode__(self):
return self.label
class Message(models.Model):
room = models.ForeignKey(Room, related_name='messages')
handle = models.TextField()
message = models.TextField()
timestamp = models.DateTimeField(default=timezone.now, db_index=True)
def __unicode__(self):
return '[timestamp] handle: message'.format(**self.as_dict())
# @property
# def formatted_timestamp(self):
# return self.timestamp.strftime('%b %-d %-I:%M %p')
def as_dict(self):
return 'handle': self.handle, 'message': self.message, 'timestamp': self.formatted_timestamp
我一直在使用 javascript 来查看是否可以让 websockets 保持打开状态,我也尝试过 Simple Websocket Client https://chrome.google.com/webstore/detail/simple-websocket-client/pfdhoblngboilpfeibdedpjgfnlcodoo?hl=en
这和那些让我相信它与 python 代码有关的事情是一样的吗?
编辑 1: 这是javascript应用程序的开发者控制台 http://imgur.com/AyIGB0z
这是运行 daphne 后的控制台输出 http://imgur.com/p2yyZiJ
【问题讨论】:
【参考方案1】:渠道变化很快,因此您的消费者只发生了一些细微的变化。注意message.reply_channel.send('accept': True)
的底线
consumers.py
@channel_session
def ws_connect(message):
prefix, label = message['path'].strip('/').split('/')
room = Room.objects.get(label=label)
Group('chat-' + label).add(message.reply_channel)
message.channel_session['room'] = room.label
message.reply_channel.send('accept': True)
@channel_session
def ws_receive(message):
label = message.channel_session['room']
room = Room.objects.get(label=label)
data = json.loads(message['text'])
m = room.messages.create(handle=data['handle'], message=data['message'])
Group('chat-'+label).send('text': json.dumps(m.as_dict()))
@channel_session
def ws_disconnect(message):
label = message.channel_session['room']
Group('chat-'+label).discard(message.reply_channel)
另外,当您使用Haikunator
时,您需要定义它的参数。如:
haikunator = Haikunator(
adjectives=['custom', 'adjectives'],
nouns=['custom', 'nouns'],
seed='random seed'
)
haikunator.haikunate(
delimiter='-',
token_length=4,
token_hex=False,
token_chars='0123456789'
)
希望这会有所帮助。
【讨论】:
即使添加了“接受”消息,Chrome 仍然无法正常工作。 Firefox 和 Safari 也可以使用它。以上是关于Django Channels Websockets 立即连接和断开连接的主要内容,如果未能解决你的问题,请参考以下文章
Django Channels Consumer 未连接到 websocket
使用 django-channels/websocket 更新实时位置的有效方法
代码发布项目——django实现websocket(使用channels)基于channels实现群聊功能gojs插件paramiko模块
无法使用 django-channels 连接到 websocket,docker 上的 nginx 作为服务