" name="sm-site-verification"/>
侧边栏壁纸
博主头像
PySuper博主等级

千里之行,始于足下

  • 累计撰写 203 篇文章
  • 累计创建 14 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录
Web

Django WebSocket (三):Channels + WebSocket 实现在线聊天

PySuper
2021-01-27 / 0 评论 / 0 点赞 / 8 阅读 / 7020 字
温馨提示:
所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊

安装: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配置

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();  // 滚动条下拉到底
        }
    }
});
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区