Django系列之Channels
1. Channels 介绍
Django 中的 HTTP 请求是建立在请求和响应的简单概念之上的。浏览器发出请求,Django服务调用相应的视图函数,并返回响应内容给浏览器渲染。但是没有办法做到 服务器主动推送消息给浏览器。
因此,WebSocket 就应运而生了。WebSocket 是一种基于 HTTP 基础上进行全双工通讯的协议。WebSocket允许服务端主动向客户端推送数据。在WebSocket协议中,浏览器和服务器只需要完成一次握手就可以创建持久性的连接,并在浏览器和服务器之间进行双向的数据传输。
Django Channels 实现了 WebSocket 能力。Channels 允许 Django 以非常类似于传统 HTTP 的方式支持WebSockets。Channels 也允许在运行 Django 的服务器上运行后台任务,HTTP 请求表现以前一样,但也支持通过 Channels 进行路由。
Django channels安装:pip install channels
2. channels 单人聊天
配置文件 settings.py:
INSTALLED_APPS = [...'app01.apps.App01Config','channels'
]
ASGI_APPLICATION = 'djangoProject.asgi.application'
主路由文件 urls.py:
from app01 import views as vw1urlpatterns = [path('admin/', admin.site.urls),path("chatone", vw1.chat)
]
主业务视图文件 app01/views.py:
from django.shortcuts import renderdef chat(request):return render(request, "chatting.html")
主业务html文件 chatting.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>.message {height: 300px;border: 1px solid #dddddd;width: 100%;}</style>
</head>
<body><div class="message" id="message"></div><div><input type="text" placeholder="请输入聊天内容:" id="txt"><input type="button" value="点击发送" onclick="sendMessage()"><input type="button" value="关闭连接" onclick="closeConn()"></div><script>// 实例化websocket对象,并客户端主动以websocket方式连接服务端wbsocket = new WebSocket("ws://127.0.0.1:8080/room/123/");// 创建好websocket连接成功后自动触发(服务端执行self.accept()后)wbsocket.onopen = function (event) {var tag = document.createElement("div");tag.innerText = '[连接成功!]';document.getElementById("message").appendChild(tag);};// 创建连接失败后自动触发wbsocket.onerror = function (event) {var tag = document.createElement("div");tag.innerText = '[连接失败!]';document.getElementById("message").appendChild(tag);};// 当websocket接收到服务器发来的消息时会自动触发wbsocket.onmessage = function (event) {var tag = document.createElement("div");tag.innerText = event.data;document.getElementById("message").appendChild(tag);};// 当服务端主动断开客户端时自动触发(服务端执行self.close()后)wbsocket.onclose = function (event) {var tag = document.createElement("div");tag.innerText = '[连接已断开!]';document.getElementById("message").appendChild(tag);};// 页面上客户端点击向服务端"关闭连接"时触发function closeConn() {wbsocket.close(); // 客户端主动断开连接,服务端会执行 websocket_disconnect()var tag = document.createElement("div");tag.innerText = '[连接已断开啦!]';document.getElementById("message").appendChild(tag);}// 页面上客户端点击向服务端"发送消息"时触发function sendMessage() {var info = document.getElementById("txt");wbsocket.send(info.value); // 客户端给服务端发数据}</script></body>
</html>
websocket路由文件 routings.py:
from django.urls import re_path
from app01 import consumers as consm1websocket_urlpatterns = [re_path(r'room/', consm1.ChatConsumer.as_asgi())
]
处理websocket业务文件 app01/consumers.py:
from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumerclass ChatConsumer(WebsocketConsumer):def websocket_connect(self, message):"""客户端向服务端发送websocket连接的请求时自动触发。"""print("1 > 客户端和服务端开始建立连接")self.accept()def websocket_receive(self, message):"""客户端基于websocket向服务端发送数据时,自动触发接收消息。"""print(f"2 > 服务端接收客户端的消息, message is {message}")recv_data = message["text"]if recv_data == "exit": # 服务端主动关闭websocket连接时,前端会执行对应的 oncloseself.close()# raise StopConsumer() # raise主动抛异常后,websocket_disconnect 就不在执行了,多用于`只处理服务端向客户端断开`的场景returnsend_data = f"服务端主动推送消息:{recv_data}"self.send(text_data=send_data)def websocket_disconnect(self, message):"""客户端与服务端断开websocket连接时自动触发(不管是客户端向服务端断开还是服务端向客户端断开都会执行)"""print("3 > 客户端和服务端断开连接")self.close()raise StopConsumer()
3. channel_layer 群组聊天
对于大多数情况来说,发送到单人的 channel 并没有用,更多的情况下希望可以以广播的方式将message 一次性发送给多个 channel 或者 consumer,这不仅适用于想在向房间内的每个人发送消息,还适用于发送给连接了多个浏览器/标签/设备的用户。
channel_layer 是一种通信系统。它允许多个消费者实例相互交谈,借助 channel_layer 可以很方便的实现群聊功能,我们无需手动管理 websocket 连接。channel 官方推荐的是配置channel_redis,它是一个使用Redis作为传输的Django维护层。安装:pip install channels_redis。
channel_layer 属于纯粹的异步接口,如果想要改成同步代码调用,需要使用async_to_sync做转换:
from asgiref.sync import async_to_sync。
配置文件 settings.py:
ASGI_APPLICATION = 'djangoProject.asgi.application'# 开发环境使用
CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}
}# 真实生产环境上使用
CHANNEL_LAYERS = {"default": {"BACKEND": "channels_redis.core.RedisChannelLayer","CONFIG": {"hosts": ["redis://127.0.0.1:6379/1", ], # 无密码连接redis# "hosts": ["redis://:password@127.0.0.1:6379/1", ], # 有密码连接redis# "symmetric_encryption_keys": [SECRET_KEY]}}
}
主路由文件 urls.py:
from django.contrib import admin
from django.urls import path
from app01 import views as vw1
from app02 import views as vw2urlpatterns = [path('admin/', admin.site.urls),path("chatone", vw1.chat),path("chatgroup", vw2.groupchat) # 群聊
]
主业务视图 app02/views.py:
from django.shortcuts import renderdef groupchat(request):groupid = request.GET.get("groupID") # 获取群组IDreturn render(request, "groupchatting.html", {"group_num": groupid})
主业务html文件 chatting.html: js在实例化websocket对象时需要改动。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>.message {height: 300px;border: 1px solid #dddddd;width: 100%;}</style>
</head>
<body><div> 群组内聊天[ 群ID: {{ group_num }} ] </div><div class="message" id="message"></div><div><input type="text" placeholder="请输入群聊内容:" id="txt"><input type="button" value="点击发送" onclick="sendMessage()"><input type="button" value="关闭连接" onclick="closeConn()"></div><script>// 实例化websocket对象,并客户端主动以websocket方式连接服务端wbsocket = new WebSocket("ws://127.0.0.1:8080/group/{{ group_num }}/");// 创建好websocket连接成功后自动触发(服务端执行self.accept()后)wbsocket.onopen = function (event) {var tag = document.createElement("div");tag.innerText = '[连接成功!]';document.getElementById("message").appendChild(tag);};// 创建连接失败后自动触发wbsocket.onerror = function (event) {var tag = document.createElement("div");tag.innerText = '[连接失败!]';document.getElementById("message").appendChild(tag);};// 当websocket接收到服务器发来的消息时会自动触发wbsocket.onmessage = function (event) {var tag = document.createElement("div");tag.innerText = event.data;document.getElementById("message").appendChild(tag);};// 页面上客户端点击向服务端"关闭连接"时触发function closeConn() {wbsocket.close(); // 客户端主动断开连接,服务端会执行 websocket_disconnect()var tag = document.createElement("div");tag.innerText = '[连接已断开!]';document.getElementById("message").appendChild(tag);}// 页面上客户端点击向服务端"发送消息"时触发function sendMessage() {var info = document.getElementById("txt");wbsocket.send(info.value); // 客户端给服务端发数据}</script></body>
</html>
websocket 路由文件 routings.py:
from django.urls import re_path
from app01 import consumers as consm1
from app02 import consumers as consm2websocket_urlpatterns = [re_path(r'room/', consm1.ChatConsumer.as_asgi()),re_path(r"group/(?P<groupID>\w+)/$", consm2.GroupChatConsumer.as_asgi()) # 群聊
]
websocket 群聊业务文件 consumers.py:
注意:从路由url中获取参数时,要使用 self.scope["url_route"]["kwargs"].get("groupID"),这里的 groupID 必须和routings中的路由分组名 re_path(r"group/(?P<groupID>\w+)/$" 保持一致!!!
from asgiref.sync import async_to_sync
from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumerclass GroupChatConsumer(WebsocketConsumer):group_id = Nonedef websocket_connect(self, message):# 接受客户端连接self.accept()print(">>> 客户端和服务端已成功建立连接 <<<")# 从url获取群组id,这个groupID必须和routings里的路由分组名保持一致self.group_id = self.scope["url_route"]["kwargs"].get("groupID")# 将当前的连接加入到名为self.group_id的组中async_to_sync(self.channel_layer.group_add)(self.group_id, self.channel_name)def websocket_receive(self, message):print(f"current self is {self}, id is {id(self)}, groupID is {self.group_id}")# 组内所有的客户端,执行type对应的函数,可以在此函数中自定义任意功能async_to_sync(self.channel_layer.group_send)(self.group_id, {"type": "send_msg", "message": message})def send_msg(self, event):input_msg = event["message"].get("text")send_msg = f"组内主动推送消息:{input_msg}"self.send(text_data=send_msg)def websocket_disconnect(self, message):# self.channel_name从组self.group_id中删除并断开连接async_to_sync(self.channel_layer.group_discard)(self.group_id, self.channel_name)print(">>> 客户端和服务端已断开连接 <<<")raise StopConsumer()
相关文章:
Django系列之Channels
1. Channels 介绍 Django 中的 HTTP 请求是建立在请求和响应的简单概念之上的。浏览器发出请求,Django服务调用相应的视图函数,并返回响应内容给浏览器渲染。但是没有办法做到 服务器主动推送消息给浏览器。 因此,WebSocket 就应运而生了。…...
HTTP——五、与HTTP协作的Web服务器
HTTP 一、用单台虚拟主机实现多个域名二、通信数据转发程序 :代理、网关、隧道1、代理2、网关3、隧道 三、保存资源的缓存1、缓存的有效期限2、客户端的缓存 一台 Web 服务器可搭建多个独立域名的 Web 网站,也可作为通信路径上的中转服务器提升传输效率。…...
pyspark笔记 Timestamp 类型的比较
最近写pyspark遇到的一个小问题。 假设我们有一个pyspark DataFrame叫做dart 首先将dart里面timestamp这一列转化成Timestamp类型 dartdart.withColumn(timestamp,col(timestamp).cast(TimestampType()))查看timestamp的前5个元素 dart.select(timestamp).show(5,truncateFal…...
SpringBoot 集成 Redis
本地Java连接Redis常见问题: bind配置请注释掉保护模式设置为noLinux系统的防火墙设置redis服务器的IP地址和密码是否正确忘记写访问redis的服务端口号和auth密码 集成Jedis jedis是什么 Jedis Client是Redis官网推荐的一个面向java客户端,库文件实现…...
黑客学习笔记(网络安全)
一、首先,什么是黑客? 黑客泛指IT技术主攻渗透窃取攻击技术的电脑高手,现阶段黑客所需要掌握的远远不止这些。 以前是完全涉及黑灰产业的反派角色,现在大体指精通各种网络技术的程序人员 二、为什么要学习黑客技术?…...
[openCV]基于拟合中线的智能车巡线方案V1
import cv2 as cv import os import numpy as np# 遍历文件夹函数 def getFileList(dir, Filelist, extNone):"""获取文件夹及其子文件夹中文件列表输入 dir:文件夹根目录输入 ext: 扩展名返回: 文件路径列表"""newDir d…...
MyBatis-Plus 和达梦数据库实现高效数据持久化
一、添加依赖 首先,我们需要在项目的 pom.xml 文件中添加 MyBatis-Plus 和达梦数据库的依赖: <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifac…...
已注销【888】
元神密码 - 飞书云文档 (feishu.cn)...
Ceph错误汇总
title: “Ceph错误汇总” date: “2020-05-14” categories: - “技术” tags: - “Ceph” - “错误汇总” toc: false original: true draft: true Ceph错误汇总 1、执行ceph-deploy报错 1.1、错误信息 ➜ ceph-deploy Traceback (most recent call last):File "/us…...
DataTable过滤某些数据
要过滤DataTable中的某些数据,可以使用以下方法: 使用Select方法:可以使用DataTable的Select方法来筛选满足指定条件的数据行。该方法接受一个字符串参数作为过滤条件,返回一个符合条件的数据行数组。 DataTable filteredTable …...
JAVASE---继承和多态
继承 比如,狗和猫,它们都是一个动物,有共同的特征,我们就可以把这种特征抽取出来。 像这样把相同的可以重新放到一个类里面,进行调用,这就是继承。 概念 继承(inheritance)机制:是面向对象程…...
Centos7升级gcc、g++版本(转载)
Centos7默认的 gcc版本是 4.8.5 默认使用yum install gcc安装出来的gcc版本也是是4.8.5。 1.首先查看自己的 gcc 版本 gcc -v g -v如果出现:bash: g: 未找到命令... 则安装g:遇到暂停时,输入y继续安装 yum install gcc-c然后输入…...
第一章:继承
系列文章目录 文章目录 系列文章目录前言继承的概念及定义继承的概念继承定义定义格式继承关系和访问限定符继承基类成员访问方式的变化 基类和派生类对象赋值转换(公有继承)继承中的作用域派生类的默认成员函数继承与友元继承与静态成员不能被继承的类复…...
git面试题
文章目录 git经常用哪些指令git出现代码冲突怎么解决你们团队是怎么管理git分支的如何实现Git的免密操作 git经常用哪些指令 产生代码库 新建一个git代码库 git init下载远程项目和它的整个代码历史 git clone 远程仓库地址配置 显示配置 git config --list [--global]编辑配置…...
Github Copilot在JetBrains软件中登录Github失败的解决方案
背景 我在成功通过了Github Copilot的学生认证之后,在VS Code和PyCharm中安装了Github Copilot插件,但在PyCharm中插件出现了问题,在登录Github时会一直Retrieving Github Device Code,最终登录失败。 我尝试了网上修改DNS&…...
使用 github 同步谷歌浏览器书签
想必使用谷歌浏览器Chrome的用户一定非常头疼的一件事就是:账户不能登录,书签收藏夹不能同步,换一台电脑书签收藏夹没有了! 下面教大家一招亲测有效适用的方法解决书签同步问题,在任何电脑都可以同步了 1、去下载谷歌…...
Eclipse进行debug
目录 基本步骤三种执行方式 -- 键盘快捷键variables面板移除debug过的项目通过eclipse调用具有软件界面的项目进行debug各个variable颜色具有的意义 基本步骤 点击eclipse右上角debug按钮 调出debug面板 点击小蜘蛛图标(不是点绿色三角的Run) 此时会进…...
13-5_Qt 5.9 C++开发指南_基于信号量的线程同步_Semaphore
文章目录 1. 信号量的原理2. 双缓冲区数据采集和读取线程类设计3. QThreadDAQ和QThreadShow 的使用4. 源码4.1 可视化UI设计框架4.2 qmythread.h4.3 qmythread.cpp4.4 dialog.h4.5 dialog.cpp 1. 信号量的原理 信号量(Semaphore)是另一种限制对共享资源进行访问的线程同步机制…...
golang使用泛型实现mapreduce操作
1.使用面向对象的方式写 package streamimport ("fmt""log""reflect""sort""strconv""strings" )type Stream[T any] struct {data []TkeyBy stringsortByNum stringsortByStr []string }func FromElem…...
2023华数杯数学建模C题思路分析 - 母亲身心健康对婴儿成长的影响
# 1 赛题 C 题 母亲身心健康对婴儿成长的影响 母亲是婴儿生命中最重要的人之一,她不仅为婴儿提供营养物质和身体保护, 还为婴儿提供情感支持和安全感。母亲心理健康状态的不良状况,如抑郁、焦虑、 压力等,可能会对婴儿的认知、情…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
