安装:pipenv install channels-redis
后端开发
注册应用
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
...
'channels', # 添加channels
)
ASGI配置
asgi.py
配置asgi的时候,需要结合当前文件的目录结构,==>
wsgi.py同级目录
import os
import sys
import django
from channels.routing import get_default_application
# application加入查找路径中
app_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir))
sys.path.append(os.path.join(app_path, 'zanhu')) # ../project/project,应用的路径
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
django.setup()
application = get_default_application()
setting配置
在settings.py中添加
...
ASGI_APPLICATION = "myproject.asgi.application"
...
Consumer配置
Channels的原理中说了:consumer.py相当于Django中的view.py
用来处理请求
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class MessagesConsumer(AsyncWebsocketConsumer):
"""处理私信应用中WebSocket请求"""
# 定义方法的地方,前面添加 async
# 调用方法的地方,前面添加 await
async def connect(self):
"""WebSocket连接"""
# 校验用户是否是合法用户
if self.scope['user'].is_anonymous: # is_anonymous
# 未登录的用户拒绝连接
await self.close()
else:
# 加入聊天组,监听频道
# channel_layer ==> get_channel_layer() ==> ChannelLayerManager() 频道层管理器实例
# 每两个私信的人建立一个聊天组
# group_add("第一个参数是组名", "第二个参数是频道的名字")
# 频道名字,使用默认就可以:"%s.%s!%s" % (prefix, self.client_prefix, uuid.uuid4().hex,)
await self.channel_layer.group_add(self.scope['user'].username, self.channel_name)
# 接受WebSocket连接
await self.accept()
async def receive(self, text_data=None, bytes_data=None):
"""接收私信"""
await self.send(text_data=json.dumps(text_data))
async def disconnect(self, code):
"""离开聊天组"""
# 把当前用户从当前监听的频道组里面移除
await self.channel_layer.group_discard(self.scope['user'].username, self.channel_name)
Routing配置
- 这里就相当于Django中的urls.py
- 主要用来匹配不同的请求
from channels.auth
from django.urls import pathimport AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from proejct.messager.consumers import MessagesConsumer
from proejct.notifications.consumers import NotificationsConsumer
application = ProtocolTypeRouter({
'websocket':
AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter([
path('ws/<str:username>/', MessagesConsumer),
])
)
)
})
前端代码
WebSocket基本使用
// WebSocket构造函数,用于新建WebSocket实例
var ws = new WebSocket('websocket地址', '请求类型');
ws.readyState:返回实例对象当前的状态
CONNNECTING: 值为0, 表示正在连接
OPEN: 值为1, 表示连接成功, 可以通信了
CLOSING: 值为2, 表示连接正在关闭
CLOSED: 值为3, 表示连接已关闭, 或者打开连接失败
// ws.readyState:实例对象的使用
switch (ws.readyState) {
case ws.CONNECTING:
//
break;
case ws.OPEN:
//
break;
case ws.CLOSING:
//
break;
case ws.CLOSED:
//
break;
default:
// ...
break;
}
// ws.onopen 用于指定连接成功后的回调函数
ws.onopen = function () {
ws.send('连接成功!')
};
// ws.onclose 用于指定连接关闭后的回调函数
ws.onclose = function () {
ws.send('连接关闭!')
};
// ws.onmessage 用于指定收到服务器数据后的回调函数
ws.onmessage = function (event) {
if (typeof event.data === String) {
console.log("received string")
} else {
console.log("xxx")
}
};
// ws.send() // 发送数据内容
// ws.onerror 指定报错时的回调函数
ws.onerror = function (event) {
// 报错处理
};
使用
$(function () {
// 滚动条下拉到底
function scrollConversationScreen() {
$("input[name='message']").focus();
$('.messages-list').scrollTop($('.messages-list')[0].scrollHeight);
}
// AJAX POST发送消息
$("#send").submit(function () {
$.ajax({
url: '/messages/send-message/',
data: $("#send").serialize(),
cache: false,
type: 'POST',
success: function (data) {
$(".send-message").before(data); // 将接收到的消息插入到聊天框
$("input[name='message']").val(''); // 消息发送框置为空
scrollConversationScreen(); // 滚动条下拉到底
}
});
return false;
});
// const:固定常量
// "https:" ? "wss" : "ws" ==> JS中的三元运算符
// WebSocket连接,使用wss(https)或者ws(http)
const ws_scheme = window.location.protocol === "https:" ? "wss" : "ws";
const ws_path = ws_scheme + "://" + window.location.host + "/ws/" + currentUser + "/";
const ws = new ReconnectingWebSocket(ws_path);
// 监听后端发送过来的消息
// event 监听事件
ws.onmessage = function (event) {
const data = JSON.parse(event.data);
if (data.sender === activeUser) { // 发送者为当前选中的用户
$(".send-message").before(data.message); // 将接收到的消息插入到聊天框
scrollConversationScreen(); // 滚动条下拉到底
}
}
});
评论区