当前位置: 首页 > article >正文

【玩转全栈】---- Django 基于 Websocket 实现群聊(解决channel连接不了)

学习视频:

14-11 群聊(一)_哔哩哔哩_bilibili

目录

Websocket 连接不了?

收发数据

断开连接

完整代码

聊天室的实现

聊天室一

聊天室二

settings 配置

consumer 配置

多聊天室


Websocket 连接不了?

基于这篇博客:

【全栈开发】---- 一文掌握 Websocket 原理,并用 Django 框架实现_django websocket-CSDN博客

        之前这篇博客虽然大致原理都介绍了,但最终的代码并没有实现,这是因为博主当时遇见了一个问题,尽管我按照教程来的,但是 websocket 服务就是连不上,后面也参考了许多博客,也去官网看了,还去 github 上抄项目来对比,都解决不了,后来急得我转 SpringBoot 去了。但偶然间发现了这篇博客:

https://blog.csdn.net/qq_25218219/article/details/131752459Django的websocket

最终问题才得以解决,再次感谢这位博主!!!

解决办法很简单,基于上面学习视频的配置后,需要在注册组件的 “channels” 前面添加一个组件    "daphne"

INSTALLED_APPS = ["daphne","channels","django.contrib.admin","django.contrib.auth","django.contrib.contenttypes","django.contrib.sessions","django.contrib.messages","django.contrib.staticfiles","app01.apps.App01Config"
]

然后运行就能连上 asgi 了:

大致原因是 pip install channels 按照命令默认按照的是最新版的 channels ,可能与 Django 版本并不匹配。

收发数据

websocket 模式中,服务端和客户端都能主动收发数据:

在客户端发数据:

function sendMessage(){var txt = document.getElementById("txt")console.log(txt.value)socket.send(txt.value)}

在服务端收数据:

    def websocket_receive(self, message):# 收数据messageprint("接收消息-->",message["text"])

在服务端发数据:

使用 send() 方法即可

    def websocket_connect(self,message):print("发送连接请求")self.accept()# 发数据self.send("来了呀客官")def websocket_receive(self, message):# 收数据messageprint("接收消息-->",message["text"])self.send(message["text"])

在客户端收数据:

这里的服务端发数据在发送 websocket 连接函数和接收消息函数中都可,相对于,在客户端收数据也对应两种方法,一个是 socket.onopen ,创建好连接后自动触发(握手环节,服务端执行self.accept());还有一个就是 socket.onmessage ,用于正常接收数据。

socket.onopen = function(event){console.log(event.value)let lag = document.createElement("div")lag.innerText = "[websocket连接成功]"document.getElementById("message").appendChild(lag)}{#收数据#}
socket.onmessage = function (event){var data = event.dataconsole.log("客户端接收到消息-->",data)let lag = document.createElement("div")lag.innerText = datadocument.getElementById("message").appendChild(lag)}

断开连接

在服务端断开连接一般是经过下面这个函数:

    def websocket_disconnect(self, message):# 浏览器关闭也会自动发送断开链接请求print("断开连接")# 服务端同意断开连接raise StopConsumer()

        这个函数不仅仅关闭浏览器的请求链接,还会关闭服务端链接,实现完全断连。在类中其他函数中可使用 self.close()  来调用此关闭链接函数,实现完全断连;而如果用 raiseStopConsumer() ,则表示仅仅断开服务器连接,也不会执行 websocket_disconnect 函数。

服务器断开连接时,客户端也会触发一个函数:

socket.onclose = function (event){}

并且客户端也可以设置按钮,主动断开连接:

function closeOnn(){socket.close()}

完整代码

index.html:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>.message{height: 300px;width: 100%;border: 1px solid #dddddd;}</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="closeOnn()">
</div>
<script>socket = new WebSocket("ws://127.0.0.1:8080/room/123/"){#创建好连接后自动触发(握手环节,服务端执行self.accept())#}socket.onopen = function(event){console.log(event.value)let lag = document.createElement("div")lag.innerText = "[websocket连接成功]"document.getElementById("message").appendChild(lag)}{#发数据#}function sendMessage(){var txt = document.getElementById("txt")console.log(txt.value)socket.send(txt.value)}{#收数据#}socket.onmessage = function (event){var data = event.dataconsole.log("客户端接收到消息-->",data)let lag = document.createElement("div")lag.innerText = datadocument.getElementById("message").appendChild(lag)}{#服务器主动断开连接,触发#}socket.onclose = function (event){}function closeOnn(){socket.close()}
</script>
</body>
</html>

consumers.py:

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
# socket = new WebSocket("ws://127.0.0.1:8000/room/123/")
class ChatConsumer(WebsocketConsumer):print("进入消费者")def websocket_connect(self,message):print("发送连接请求")self.accept()# 发数据self.send("来了呀客官")def websocket_receive(self, message):# 收数据messageprint("接收消息-->",message["text"])# 服务器主动断开连接if message["text"] == "关闭":self.close()# 如果在这儿加上下面代码,执行StopConsumer异常,那么就不会执行websocket_disconnectraise StopConsumer()# returnself.send(message["text"])# 调用self.close()方法默认都会调用下面这个函数def websocket_disconnect(self, message):# 浏览器关闭也会自动发送断开链接请求print("断开连接")# 服务端同意断开连接raise StopConsumer()

聊天室的实现

当然,上面只是介绍 websocket 的一般使用,还并没有实际应用,下面将以聊天室场景进行应用。

聊天室一

前面的操作都是基于 self 来的。服务端仅仅关心自己与对应浏览器的连接通道,而不会联系到其它浏览器。可使用列表存储各个用户,某用户想断开连接或者主动退出浏览器时,再到列表中删除用户:

需要注意的是,用户添加到列表中后,后续的一系列操作需要在列表中循环操作每一个对象,以实现群聊

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumerCONN_LIST = []
class ChatConsumer(WebsocketConsumer):print("进入消费者")def websocket_connect(self,message):print("发送连接请求")self.accept()CONN_LIST.append(self)def websocket_receive(self, message):res = message["text"]# 收数据messageprint("接收消息-->",res)for conn in CONN_LIST:conn.send(res)# 调用self.close()方法默认都会调用下面这个函数def websocket_disconnect(self, message):# 浏览器关闭也会自动发送断开链接请求print("断开连接")CONN_LIST.remove(self)# 服务端同意断开连接raise StopConsumer()

结果:

聊天室二

        聊天室一虽然能实现简单的群聊功能,但是使用列表来储存各个用户,其实效率会很低,并且功能也不强大,Django 的 channels 组件中有一个更加厉害的东西叫  channel layers,可以帮助我们更加方便地去实现这种群聊。

参考文章:django channels - 武沛齐 - 博客园

settings 配置

layers 需要在 setting 中进行配置:

CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer",}
}

consumer 配置

再修改 Consumer 代码:

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_syncclass ChatConsumer(WebsocketConsumer):print("进入消费者")def websocket_connect(self,message):print("发送连接请求")self.accept()# 将这个客户端的连接对象加入到某个地方(内存或redis),channel_name 随机给个名字async_to_sync(self.channel_layer.group_add)("111",self.channel_name)def websocket_receive(self, message):res = message["text"]# 收数据messageprint("接收消息-->",res)async_to_sync(self.channel_layer.group_send)("111",{"type":"send_to","message":message})def send_to(self,event):# 群中每一个连接对象都发送text = event["message"]["text"]self.send(text)# 调用self.close()方法默认都会调用下面这个函数def websocket_disconnect(self, message):# 浏览器关闭也会自动发送断开链接请求print("断开连接")async_to_sync(self.channel_layer.group_discard)("111",self.channel_name)# 服务端同意断开连接raise StopConsumer()

部分解释:

需要注意的是,这里的 channel_layer 操作都是异步进行的,需要自己导入 async_to_sync 进行异步转同步操作。

async_to_sync((self.channel_layer.group_add)("111",self.channel_name)

这里的作用是将本连接对象存入 channel_layer 中,并且 group 名为 "111" ,self.channel_name 的作用是连接对象存储时,随机给一个名字。

async_to_sync(self.channel_layer.group_send)("111",{"type":"send_to","message":message})
def send_to(self,event):# 群中每一个连接对象都发送text = event["message"]["text"]self.send(text)

这里的作用是为 "111" 群聊中每个连接对象执行 type 对应的方法,并传入 message 给每个连接对象;下面的 send_to 方法就是为每一个连接对象发送 text 消息。

async_to_sync(self.channel_layer.group_discard)("111",self.channel_name)

这里的作用是为群聊中的每一个连接对象关闭连接。

上诉代码已能实现聊天室功能,但还不够高级,因为群聊 id 是固定的。下面介绍在浏览器中打开多个聊天室,各个聊天室之间有不同的 id ,各个聊天室之前互不干扰。

多聊天室

实现思路是通过 http get 传参将群号传给视图函数,视图函数给 index.html 页面,在 index 页面构造 websocket url 并加入群号,在 consumer 中获取群号,并替换群号为原先的固定群号。

实现:

视图函数传参:

def index(request):QQ_number = request.GET.get('qq')return render(request, 'index.html', {'QQ_number': QQ_number})

index 页面 websocket 传参:

socket = new WebSocket("ws://127.0.0.1:8080/room/{{ QQ_number }}/")

routings 中正则接收参数:

websocket_urlpatterns = [re_path(r'^room/(?P<group>\w+)/$', consumers.ChatConsumer.as_asgi()),
]

consumer 中接收 group 并修改群号为 group:

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_syncclass ChatConsumer(WebsocketConsumer):print("进入消费者")def websocket_connect(self,message):print("发送连接请求")self.accept()group = self.scope['url_route']['kwargs'].get('group')# 将这个客户端的连接对象加入到某个地方(内存或redis),channel_name 随机给个名字async_to_sync(self.channel_layer.group_add)(group,self.channel_name)def websocket_receive(self, message):group = self.scope['url_route']['kwargs'].get('group')res = message["text"]# 收数据messageprint("接收消息-->",res)async_to_sync(self.channel_layer.group_send)(group,{"type":"send_to","message":message})def send_to(self,event):# 群中每一个连接对象都发送text = event["message"]["text"]self.send(text)# 调用self.close()方法默认都会调用下面这个函数def websocket_disconnect(self, message):group = self.scope['url_route']['kwargs'].get('group')# 浏览器关闭也会自动发送断开链接请求print("断开连接")async_to_sync(self.channel_layer.group_discard)(group,self.channel_name)# 服务端同意断开连接raise StopConsumer()

结果:

这样即能实现多聊天室,各个聊天室互不打扰。

相关文章:

【玩转全栈】---- Django 基于 Websocket 实现群聊(解决channel连接不了)

学习视频&#xff1a; 14-11 群聊&#xff08;一&#xff09;_哔哩哔哩_bilibili 目录 Websocket 连接不了&#xff1f; 收发数据 断开连接 完整代码 聊天室的实现 聊天室一 聊天室二 settings 配置 consumer 配置 多聊天室 Websocket 连接不了&#xff1f; 基于这篇博客&…...

如何快速解决django报错:cx_Oracle.DatabaseError: ORA-00942: table or view does not exist

我们在使用django连接oracle进行编程时&#xff0c;使用model进行表映射对接oracle数据时&#xff0c;默认表名组成结构为&#xff1a;应用名_类名&#xff08;如&#xff1a;OracleModel_test&#xff09;&#xff0c;故即使我们库中存在表test&#xff0c;运行查询时候&#…...

Selenium之简介

Selenium简介 首先&#xff0c;让我们看看官网是怎么定义的 Selenium是一个支持web浏览器自动化的一系列工具和库的综合项目&#xff0c;提供了扩展来模拟用户和浏览器的交互&#xff0c;用于扩展浏览器分配的分发服务器&#xff1b;用于W3C WebDriver规范的基础架构 其实&a…...

pip 安装某个包之后,Jupyter Lab仍旧显示包冲突;例如:Numba needs NumPy 2.1 or less. Got NumPy 2.2.

异常提示 Numba needs NumPy 2.1 or less. Got NumPy 2.2. --------------------------------------------------------------------------- ImportError Traceback (most recent call last) Cell In[8], line 53 import pywt4 import matplot…...

本地安装git

下载git 通过官网 下载 &#xff1a;Git - Downloading Package 若此页面无法直达&#xff0c;请删掉download/win尝试 2.双击运行安装 选择安装目录&#xff1a; 选择配置&#xff0c;默认不动 git安装目录名 默认即可 Git 的默认编辑器&#xff0c;建议使用默认的 Vim 编辑器…...

小程序内表格合并功能实现—行合并

功能介绍&#xff1a;支付宝小程序手写表格实现行内合并&#xff0c;依据动态数据自动计算每次需求合并的值&#xff0c;本次记录行内合并&#xff0c;如果列内合并&#xff0c;同理即可实现 前端技术&#xff1a;grid布局 display&#xff1a;grid 先看实现效果: axml&…...

SSE协议介绍和python实现

概述&#xff1a; SSE&#xff08;Server-Sent Events&#xff09;协议是一种允许服务器向客户端实时推送更新的技术&#xff0c;基于HTTP协议&#xff0c;常用于实时数据推送特点&#xff1a; 单向通信&#xff1a;服务器向客户端推送数据&#xff0c;客户端无法发送数据。基…...

甘肃旅游服务平台+论文源码视频演示

4 系统设计 4.1系统概要设计 甘肃旅游服务平台并没有使用C/S结构&#xff0c;而是基于网络浏览器的方式去访问服务器&#xff0c;进而获取需要的数据信息&#xff0c;这种依靠浏览器进行数据访问的模式就是现在用得比较广泛的适用于广域网并且没有网速限制要求的小程序结构&am…...

Spring Boot 3虚拟线程的使用

在Spring Boot非Web应用中&#xff0c;使用虚拟线程时程序提前终止的问题及解决方案&#xff0c;可以通过以下步骤深入理解和验证&#xff1a; 问题根源分析 JVM退出机制 Java中&#xff0c;当所有非守护线程结束时&#xff0c;JVM会立即退出。即使存在正在运行的守护线程&…...

3、pytest实现参数化

在 pytest 中&#xff0c;参数化&#xff08;parametrization&#xff09;是一种强大的功能&#xff0c;可以让你用不同的输入数据重复执行同一个测试函数。这种功能非常有用&#xff0c;可以帮助你显著减少重复代码并提高测试覆盖率。 参数化的主要作用是&#xff1a; 测试多…...

【解决】Linux命令报错:Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64

报错命令 yum install zabbix-web-mysql-scl zabbix-apache-conf-scl centos使用scl切换软件版本时提示Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64 报错原因 CentOS7的SCL源在2024年6月30日停止维护了。 当scl源里面默认使用了centos官方的地址&#x…...

WebRTC中音视频服务质量QoS之FEC+NACK调用流程

WebRTC中音视频服务质量QoS之FECNACK调用流程 WebRTC中音视频服务质量QoS之FECNACK调用流程 WebRTC中音视频服务质量QoS之FECNACK调用流程前言一、WebRTC中FEC基础原理1. FEC基础操作 异或操作XOR2、 FEC中 行向和纵向 计算3、 WebRTC中 媒体包分组和生成FEC的包数① kFecRateT…...

神经网络知识点整理

目录 ​一、深度学习基础与流程 二、神经网络基础组件 三、卷积神经网络&#xff08;CNN&#xff09;​编辑 四、循环神经网络&#xff08;RNN&#xff09;与LSTM 五、优化技巧与调参 六、应用场景与前沿​编辑 七、总结与展望​编辑 一、深度学习基础与流程 机器学习流…...

远程办公新体验:用触屏手机流畅操作电脑桌面

在数字化浪潮的推动下&#xff0c;远程办公已从“应急选项”转变为职场常态。无论是居家隔离、差旅途中&#xff0c;还是咖啡厅临时办公&#xff0c;高效连接公司电脑的需求从未如此迫切。然而&#xff0c;传统的远程控制软件常因操作复杂、画面卡顿或功能限制而影响效率。如今…...

【面试八股】:常见的锁策略

常见的锁策略 synchronized &#xff08;标准库的锁不够你用了&#xff09;锁策略和 Java 不强相关&#xff0c;其他语言涉及到锁&#xff0c;也有这样的锁策略。 1. 悲观锁&#xff0c;乐观锁&#xff08;描述的加锁时遇到的场景&#xff09; 悲观锁&#xff1a;预测接下来…...

【python】OpenCV—Hand Detection

文章目录 1、功能描述2、代码实现3、效果展示4、完整代码5、参考6、其它手部检测和手势识别的方案 更多有趣的代码示例&#xff0c;可参考【Programming】 1、功能描述 基于 opencv-python 和 mediapipe 进行手部检测 2、代码实现 导入必要的库函数 import cv2 import media…...

es6的100个问题

基础概念 解释 let、const 和 var 的区别。什么是块级作用域&#xff1f;ES6 如何实现它&#xff1f;箭头函数和普通函数的主要区别是什么&#xff1f;解释模板字符串&#xff08;Template Literals&#xff09;的用途&#xff0c;并举例嵌套变量的写法。解构赋值的语法是什么…...

【Git 常用指令速查表】

Git 常用指令速查表 Git 常用指令速查表目录1. 初始化仓库2. 提交代码流程3. 分支管理4. 远程仓库操作5. 撤销操作6. 查看状态与日志7. 其他实用指令完整操作示例常用场景速查表 Git 常用指令速查表 目录 初始化仓库提交代码流程分支管理远程仓库操作撤销操作查看状态与日志其…...

Flink中聚合算子介绍

前言 在flink api中&#xff0c;聚合算子是非常常用的。所谓的聚合就是在分组的基础上做比较计算的操作。下面通过几个简单案例来说明聚合算子的用法和注意事项。 聚合算子案例 因为flink的api操作流程比较固定&#xff0c;从获取执行环境》获取数据源》执行数据转换操作》输…...

【基础】Windows 中通过 VSCode 使用 GCC 编译调试 C++

准备 安装 VSCode 及 C 插件。通过 MSYS2 安装 MinGW-w64 工具链&#xff0c;为您提供必要的工具来编译代码、调试代码并配置它以使用IntelliSense。参考&#xff1a;Windows 中的 Linux 开发工具链 验证安装&#xff1a; gcc --version g --version gdb --version三个核心配…...

Web Services 简介

Web Services 简介 概述 Web Services 是一种网络服务技术,允许不同的应用程序通过互联网进行交互和数据交换。随着互联网的普及和发展,Web Services 已经成为企业级应用中不可或缺的一部分。本文将详细介绍 Web Services 的概念、特点、应用场景以及相关的技术架构。 什么…...

数据仓库 - 转转 - 一面凉经

面试流程 自我介绍 Python 中&#xff0c;如何在数据清洗过程中应对内存不够的情况 如何避免&#xff0c;在使用Pandas处理大规模数据时&#xff0c;经常会遇到“SettingWithCopyWarning”警告 在Hive中&#xff0c;当您使用动态分区功能进行数据插入时&#xff0c;可能会遇…...

2025跳槽学习计划

&#xff08;1&#xff09;编程基础&#xff1a; 目录学习资料Chttps://www.bilibili.com/video/BV1z64y1U7hs?spm_id_from333.1387.favlist.content.clickLinuxPytorchhttps://www.bilibili.com/video/BV1if4y147hS?spm_id_from333.1387.favlist.content.clickopencv数据结…...

算法 | 2024最新算法:鳑鲏鱼优化算法原理,公式,应用,算法改进研究综述,matlab代码

2024最新鳑鲏鱼优化算法(BFO)研究综述 鳑鲏鱼优化算法(Bitterling Fish Optimization, BFO)是2024年提出的一种新型群智能优化算法,受鳑鲏鱼独特的繁殖行为启发,通过模拟其交配、产卵和竞争机制进行全局优化。该算法在多个领域展现出优越性能,尤其在解决复杂非线性问题中…...

具身系列——Diffusion Policy算法实现CartPole游戏

代码原理分析 1. 核心思想 该代码实现了一个基于扩散模型&#xff08;Diffusion Model&#xff09;的强化学习策略网络。扩散模型通过逐步去噪过程生成动作&#xff0c;核心思想是&#xff1a; • 前向过程&#xff1a;通过T步逐渐将专家动作添加高斯噪声&#xff0c;最终变成…...

前端性能优化:深入解析哈希算法与TypeScript实践

/ 示例&#xff1a;开放寻址哈希表核心实现 class OpenAddressingHashTable<T> {private size: number;private keys: (string | null)[];private values: (T | null)[];private tombstone Symbol(Deleted);constructor(size: number 53) {this.size size;this.keys …...

知识就是力量——物联网应用技术

基础知识篇 一、常用电子元器件1——USB Type C 接口引脚详解特点接口定义作用主从设备关于6P引脚的简介 2——常用通信芯片CH343P概述特点引脚定义 CH340概述特点封装 3——蜂鸣器概述类型驱动电路原文链接 二、常用封装介绍贴片电阻电容封装介绍封装尺寸与功率关系&#xff1…...

(windows)conda虚拟环境下open-webui安装与启动

一、创建conda环境 重点强调下&#xff0c;如果用python pip安装&#xff0c;一定要选择python3.11系列版本&#xff0c;我选的3.11.9。 如果你的版本不是这个系列&#xff0c;将会出现一些未知的问题。 conda create -n open-webui python3.11 -y如下就创建好了 二、安装o…...

oracle密码过期 ORA-28001解决方案: the password has expired

** oracle密码过期 ORA-28001解决方案: the password has expired ** oracle 11g 默认密码过期时间为180天密码过期后&#xff0c;访问数据库会出现如下异常java.sql.SQLException: ORA-28001: the password has expired 查询密码过期设定 select * from dba profiles where…...

GStreamer —— 3.1、Qt+GStreamer制作多功能播放器,支持本地mp4文件、rtsp流、usb摄像头等(可跨平台,附源码)

🔔 GStreamer 相关音视频技术、疑难杂症文章合集(掌握后可自封大侠 ⓿_⓿)(记得收藏,持续更新中…) 运行效果...