django channels(websocket)的使用,实现网页间实时聊天

news2024/12/25 2:09:39

目录

一、创建项目mysite和应用chat

1. 创建项目mysite

2. 创建应用chat并添加应用到settings.py的INSTALLED_APPS中

3. 添加模板文件

4. 添加视图及路由

5.配置根路由指定chat应用的路由

6. 集成channels

二、实现聊天服务器

1. 创建一个新文件chat/templates/chat/room.html,并添加以下内容

2. 创建视图room及配置路由

3. 配置消费者consumers.py

4. 配置routings.py

5. 再次配置asgi.py

6. 启用通道层CHANNEL_LAYERS

7. 再次配置consumers.py

三、改进:将消费者重写为异步


版本

python==3.7

django==3.2.18

channels==3.0.3
channels-redis==4.0.0

一、创建项目mysite和应用chat

1. 创建项目mysite

使用pycharm创建项目mysite

使用命令创建项目mysite

django-admin startproject mysite

2. 创建应用chat并添加应用到settings.py的INSTALLED_APPS中

 python manage.py startapp chat

# settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'chat',
]

 

 完成之后项目的目录结构:

3. 添加模板文件

在chat应用下新建templates文件夹,并右键选择Mark Direcory as >> Template Folder,templates的文件夹将变为紫色,并创建index.html文件

 index.html添加以下内容

<!-- chat/templates/chat/index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Chat Rooms</title>
</head>
<body>
    What chat room would you like to enter?<br>
    <input id="room-name-input" type="text" size="100"><br>
    <input id="room-name-submit" type="button" value="Enter">

    <script>
        document.querySelector('#room-name-input').focus();
        document.querySelector('#room-name-input').onkeyup = function(e) {
            if (e.keyCode === 13) {  // enter, return
                document.querySelector('#room-name-submit').click();
            }
        };

        document.querySelector('#room-name-submit').onclick = function(e) {
            var roomName = document.querySelector('#room-name-input').value;
            window.location.pathname = '/chat/' + roomName + '/';
        };
    </script>
</body>
</html>

4. 添加视图及路由

# chat/views.py
from django.shortcuts import render


def index(request):
    return render(request, "index.html")
# chat/urls.py
from django.urls import path

from . import views


urlpatterns = [
    path("", views.index, name="index"),
]

5.配置根路由指定chat应用的路由

# mysite/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("chat/", include("chat.urls")),
    path("admin/", admin.site.urls),
]

此时启动django项目,浏览器访问http://127.0.0.1:8000/chat/

 

 这里在输入框中输入字符串后点击Enter会报404的错误,所以我们接下来继续配置

6. 集成channels

a.安装channels

pip3 install -i https://pypi.douban.com/simple channels==3.0.3

 指定安装channels==3.0.3,不要安装最新的4.0版本,否则websocket在连接时会报404错误

b. 调整mysite/asgi.py里的代码

import os

from channels.routing import ProtocolTypeRouter
from django.core.asgi import get_asgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

application = ProtocolTypeRouter(
    {
        "http": get_asgi_application(),
    }
)

c.在settings.py中添加channels应用及配置ASGI_APPLICATION

# mysite/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'chat',
    'channels'
]

ASGI_APPLICATION = "mysite.asgi.application"

二、实现聊天服务器

1. 创建一个新文件chat/templates/chat/room.html,并添加以下内容

<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Chat Room</title>
</head>
<body>
    <textarea id="chat-log" cols="100" rows="20"></textarea><br>
    <input id="chat-message-input" type="text" size="100"><br>
    <input id="chat-message-submit" type="button" value="Send">
    {{ room_name|json_script:"room-name" }}
    <script>
        const roomName = JSON.parse(document.getElementById('room-name').textContent);

        const chatSocket = new WebSocket(
            'ws://'
            + window.location.host
            + '/ws/chat/'
            + roomName
            + '/'
        );

        chatSocket.onmessage = function(e) {
            const data = JSON.parse(e.data);
            document.querySelector('#chat-log').value += (data.message + '\n');
        };

        chatSocket.onclose = function(e) {
            console.error('Chat socket closed unexpectedly');
        };

        document.querySelector('#chat-message-input').focus();
        document.querySelector('#chat-message-input').onkeyup = function(e) {
            if (e.keyCode === 13) {  // enter, return
                document.querySelector('#chat-message-submit').click();
            }
        };

        document.querySelector('#chat-message-submit').onclick = function(e) {
            const messageInputDom = document.querySelector('#chat-message-input');
            const message = messageInputDom.value;
            chatSocket.send(JSON.stringify({
                'message': message
            }));
            messageInputDom.value = '';
        };
    </script>
</body>
</html>

2. 创建视图room及配置路由

# chat/views.py
from django.shortcuts import render


def index(request):
    return render(request, "chat/index.html")


# 新添加
def room(request, room_name):
    return render(request, "room.html", {"room_name": room_name})
# chat/urls.py
from django.urls import path

from . import views


urlpatterns = [
    path("", views.index, name="index"),
    path("<str:room_name>/", views.room, name="room"),  # 新添加
]

此时启动django项目,浏览器打开控制台输入地址http://127.0.0.1:8000/chat/

输入myroom,并Enter

 输入消息并点击Send没有任何事发生,所以接下来继续配置消费者consumers

3. 配置消费者consumers.py

在chat应用下添新建consumers.py文件,并添加以下内容

# chat/consumers.py
import json

from channels.generic.websocket import WebsocketConsumer


class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        pass

    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]

        self.send(text_data=json.dumps({"message": message}))

目录结构

 

4. 配置routings.py

在chat应用下新建routing.py文件,并添加以下内容

# chat/routing.py
from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r"ws/chat/(?P<room_name>\w+)/$", consumers.ChatConsumer.as_asgi()),
]

5. 再次配置asgi.py

# mysite/asgi.py
import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application

from chat.routing import websocket_urlpatterns

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
# Initialize Django ASGI application early to ensure the AppRegistry
# is populated before importing code that may import ORM models.
django_asgi_app = get_asgi_application()

import chat.routing

application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
            AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
        ),
    }
)

运行迁移命令

python manage.py migrate

 此时启动django项目,浏览器打开控制台访问 http://127.0.0.1:8000/,输入myroom并Enter后,不再报ws连接500的错误了

 此时输入消息并点击Send,消息将出现在聊天框中,但是打开新标签页输入同一网址(http://127.0.0.1:8000/chat/myroom/)时,再发消息并不会出现在新标签页的网址消息聊天框中

 要做到能接收到另一个标签页发送的消息,还需要继续配置启动通道层CHANNEL_LAYERS

 

6. 启用通道层CHANNEL_LAYERS

  • 先安装 channels-redis

pip3 install -i https://pypi.douban.com/simple channels-redis==4.0.0

  • settings.py中添加CHANNEL_LAYERS的配置(请确认在这之前已经安装了redis)
# settings.py
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}
  • 确保通道层可以与Redis通信。打开Django shell并运行以下命令:  
$ python3 manage.py shell
>>> import channels.layers
>>> channel_layer = channels.layers.get_channel_layer()
>>> from asgiref.sync import async_to_sync
>>> async_to_sync(channel_layer.send)('test_channel', {'type': 'hello'})
>>> async_to_sync(channel_layer.receive)('test_channel')
{'type': 'hello'}

 

7. 再次配置consumers.py

将以下代码替换之前chat/consumers.py里的代码

# chat/consumers.py
import json

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer


class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
        self.room_group_name = f"chat_{self.room_name}"

        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name, self.channel_name
        )

        self.accept()

    def disconnect(self, close_code):
        # Leave room group
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name, self.channel_name
        )

    # Receive message from WebSocket
    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]

        # Send message to room group
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name, {"type": "chat.message", "message": message}
        )

    # Receive message from room group
    def chat_message(self, event):
        message = event["message"]

        # Send message to WebSocket
        self.send(text_data=json.dumps({"message": message}))

此时启动django项目,浏览器打开两个标签页,都进入到http://127.0.0.1:8000/chat/myroom/下,再次发送消息,另一个标签页就能接收到啦~

 到目前为止,一个基本的全功能聊天服务器就完成了

三、改进:将消费者重写为异步

我们之前写的consumers.py里的代码都是同步的。同步使用很方便,因为他们可以调用常规的同步I/O函数 例如那些不需要编写特殊代码就可以访问Django模型。然而异步消费者可以提供更高级别的性能,因为它们在处理请求时不需要创建额外的线程。

所以我们再次重写chat/consumers.py里的代码,用以下代码替换之前写的同步代码

# chat/consumers.py
import json

from channels.generic.websocket import AsyncWebsocketConsumer


class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
        self.room_group_name = f"chat_{self.room_name}"

        # Join room group
        await self.channel_layer.group_add(self.room_group_name, self.channel_name)

        await self.accept()

    async def disconnect(self, close_code):
        # Leave room group
        await self.channel_layer.group_discard(self.room_group_name, self.channel_name)

    # Receive message from WebSocket
    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]

        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name, {"type": "chat.message", "message": message}
        )

    # Receive message from room group
    async def chat_message(self, event):
        message = event["message"]

        # Send message to WebSocket
        await self.send(text_data=json.dumps({"message": message}))

这个新的代码是ChatConsumer非常类似于原来的代码,有以下区别:

  • ChatConsumer 现在继承自 AsyncWebsocketConsumer ,而不是 WebsocketConsumer
  • 所有方法都是 async def 而不仅仅是 def
  • await 用于调用执行I/O的异步函数。
  • 在通道层调用方法时,不再需要async_to_sync。

此时再次启动django项目,浏览器打开两个标签页,都进入到http://127.0.0.1:8000/chat/myroom/下,再次发送消息,这时聊天服务器是完全异步的

参考: Tutorial — Channels 4.0.0 documentation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/446770.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

响应式开发HTNL5CSS3视频播放器

目录 前言 一、本视频播放器需要实现的功能 ​二、代码分布结构 三、部分主要代码 1.index01.html 2.video1.css 3.video1.js 四、images图片资源及视频 五、运行效果 前言 1.本文讲解的响应式开发技术&#xff08;HTML5CSS3Bootstrap&#xff09;的HTML5视频播放器等…

TS-黑马(二)

目录&#xff1a; &#xff08;1&#xff09;ts-类型-字面量与nullish类型 &#xff08;2&#xff09;ts-类型-泛型 &#xff08;3&#xff09;ts-意义 &#xff08;4&#xff09;ts-class语法 &#xff08;1&#xff09;ts-类型-字面量与nullish类型 我们子啊传递参数的时…

以 100GB SSB 性能测试为例,通过 ByteHouse 云数仓开启你的数据分析之路

动手点关注 干货不迷路 I. 传统数仓的演进&#xff1a;云数仓 近年来&#xff0c;随着数据“爆炸式”的增长&#xff0c;越来越多的数据被产生、收集和存储。而挖掘海量数据中的真实价值&#xff0c;从其中提取商机并洞见未来&#xff0c;则成了现代企业和组织不可忽视的命题。…

总的所有的全部的完全的整个的整体的: all entire full gross whole total

all entire full gross whole total 所有的,完全的,完整的全部的,整个的,整体的,总的 all entire full gross whole total all entire full gross whole total一些词典的解释allentirefullgrosswholetotal 区别辨析entire、whole、complete、full、total、gross与alltotal&…

【JavaWeb】HTMLCSS

一:B/S 软件的结构 二&#xff1a;前端的开发流程 3、网页的组成部分 页面由三部分内容组成&#xff01; 分别是内容&#xff08;结构&#xff09;、表现、行为。 内容&#xff08;结构&#xff09;&#xff0c;是我们在页面中可以看到的数据。我们称之为内容。一般内容 我们…

【Linux】共享内存(shm)代码实现

文章目录 共享内存介绍最快的IPC形式共享内存示意图共享内存数据结构共享内存函数shmget函数shmfig shmat函数说明&#xff1a; shmdt函数shmctl函数 共享内存的原理小结 共享内存的特点生命周期共享内存的大小共享内存为什么快共享内存没有任何的保护机制即同步互斥扩展内容 代…

《软件开发本质论》读书笔记

目录 第一部分——价值的循环开发取向频繁交付小的&#xff0c;价值大&#xff0c;代价小的特性 测试同时要有业务测试和开发测试开发测试应在代码开发的同时完成甚至先于代码完成 重构第一部分总结 第二部分——说明与论述团队目的自主专精 五卡法预测软件计划控制好自己所参与…

二叉树一定是完全二叉树

一、树的概念及其结构 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 1、树的特点 ①有一个特殊的结点&…

ARM Coresight 及 DS-5 介绍 5 - ARM Cortex-M DS-5 加载 ELF 文件运行

文章目录 1.1.1 DS-5 工程创建1.1.2 DS-5 加载 ELF 脚本创建1.1.3 DS-5 脚本读写 Memory1.1.4 DS-5 扫描脚本 1.1.1 DS-5 工程创建 在使用ARM DS-5 连接 board(或者PFGA)之前首先需要能够扫描到相应的硬件信息&#xff0c;比如对应的cpu的相关信息&#xff1a;coresight 相关组…

【Zblog搭建博客网站】windows环境搭建属于自己的博客并发布上线 - cpolar内网穿透

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 转载自远程内网穿透的文章&#xff1a;【Zblog建站】搭建属于自己的博客网站&#…

【C++】初始化列表,explicit 关键字,静态成员

初始化列表 构造函数内可以对于成员的赋值&#xff0c;故不能称为是初始化&#xff0c;初始化只能初始化依次&#xff0c;C中类对象的初始化可由初始化列表完成 内置类型成员可以通过设置缺省值实现初始化&#xff0c;但对于const修饰的成员、引用成员、没有默认构造函数的自…

hive解析json

目录 一、背景 二、hive 解析 json 数据函数 1、get_json_object 2、json_tuple 3、使用嵌套子查询(exploderegexp_replacesplitjson_tuple)解析json数组 4、使用 lateral view 解析json数组 一、背景 我们进行ETL(Extract-Transfer-Load) 过程中,经常会遇到从不同数据…

编码拓展:链接库

一.认识链接库 1.1库 计算机中&#xff0c;有些文件专门用于存储可以重复使用的代码块&#xff0c;例如功能实用的函数或者类&#xff0c;我们通常将它们称为库文件&#xff0c;简称“库”&#xff08;Library&#xff09;。 以 C 语言为例&#xff0c;如下为大家展示的就是…

JUC高级十-并发加锁原理之AbstractQueuedSynchronizer(AQS)

1. 前置知识 公平锁和非公平锁可重入锁自旋锁LockSupport数据结构之双向链表设计模式之模板设计模式 AQS重要性 JAVA ------>JVM AQS ------>AQS 2. AQS入门级别理论知识 2.1 是什么? 2.1.1 字面意思 Abstract Queued Synchronizer----抽象的队列同步器 源码位置: …

【Microsoft Edge】如何彻底卸载 Edge

文章目录 一、问题描述二、卸载 Edge2.1 卸载正式版 Edge2.2 卸载非正式版 Edge2.2.1 卸载通用的 WebView22.2.2 卸载 Canary 版 Edge2.2.3 卸载其他版本2.3 卸载 Edge Update 2.4 卸载 Edge 的 Appx 额外安装残留2.5 删除日志文件2.6 我就是想全把 Edge 都删了 一、问题描述 …

预测模型的局部评价?

预测模型的局部评价 为什么要进行局部评价&#xff1f; 首先是临床决策曲线分析通常会给预测模型的使用规定一个阈值范围&#xff0c;相应地预测模型的评价也应该局限在这个范围之内才是合理的&#xff1b; 其次&#xff0c;全局性地评价往往不够敏感&#xff0c;即好的模型和坏…

本地搭建属于自己的ChatGPT:基于Python+ChatGLM-6b+Streamlit+QDrant+DuckDuckGo

本地部署chatglm及缓解时效性问题的思路&#xff1a; 模型使用chatglm-6b 4bit&#xff0c;推理使用hugging face&#xff0c;前端应用使用streamlit或者gradio。 微调对显存要求较高&#xff0c;还没试验。可以结合LoRA进行微调。 缓解时效性问题&#xff1a;通过本地数据库…

C语言实现银行家算法

一.银行家算法 1.由来 银行家算法最初是由荷兰计算机科学家艾兹赫尔迪杰斯特拉&#xff08;Edsger W. Dijkstra&#xff09;于1965年提出的。当时他正致力于解决多道程序设计中产生的死锁问题。在多道程序设计中&#xff0c;由于不同进程之间共享有限的系统资源&#xff0c;如…

【JavaEE初阶】多线程(一)认识线程 线程的创建 Thread的用法

摄影分享&#xff01; 文章目录 认识线程&#xff08;Thread&#xff09;概念执行多线程编程创建线程的写法1.继承Thread&#xff0c;重写run2.实现Runnable接口3.使用匿名内部类&#xff0c;继承Thread4.使用匿名内部类&#xff0c;实现Runable5.使用Lambda表达式 Thread用法…

C语言模拟银行排队叫号(链队)

一.队列 队列是一种具有先进先出&#xff08;FIFO&#xff09;特性的线性数据结构&#xff0c;它只允许在队列的两端进行插入和删除操作。队列的一端称为队尾&#xff08;rear&#xff09;&#xff0c;另一端称为队头&#xff08;front&#xff09;。新元素总是插入在队列的队…