Django框架:使用channels实现websocket,配置和项目实际使用

news2024/11/22 9:27:07

一、基本配置

依赖包:

Django==3.2
django-cors-headers==3.5.0
redis==4.6.0  #操作redis数据库的
channels==3.0.0  #websocket
channels-redis==4.1.0 #通道层需要,依赖redis包

项目目录结构:

study_websocket

        --study_websocket

                --__init__.py

                --settings.py

                --asgi.py

                --wsgi.py

                --urls.py

        --chat

                --routings.py

                --consumers.py

                --update.py

                --urls.py

                --views.py

1.1、settings.py配置

1、注册跨域、channels、chat应用

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders', #前后端跨域

    'chat.apps.WebsocketConfig',#专门用于写websocket的方法
    'channels', #django通过其实现websocket
]

 

2、跨域配置

##### cors资源跨域共享配置
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)

CORS_ALLOW_HEADERS = (
    'XMLHttpRequest',
    'X_FILENAME',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'Pragma',
    'token' #请求头允许自定义的字符串
)

    3、channels需要的配置

WSGI_APPLICATION = 'study_websocket.wsgi.application'
#channels使用需要添加ASGI_APPLICATION
ASGI_APPLICATION = 'study_websocket.asgi.application'
#通道层:开发阶段使用内存
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels.layers.InMemoryChannelLayer"
    }
}

1.2、在chat应用新增routings.py 和 consumers.py

1、consumers.py设置一个简单消费者

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_sync
import time
 
class ChatView(WebsocketConsumer):
    def websocket_connect(self, message):
        #客户端与服务端进行握手时,会触发这个方法
        #服务端允许客户端进行连接,就是握手成功
        self.accept()
 
    def websocket_receive(self, message):
        #接收到客户端发送的数据
        recv = message.get('text')
        print('接收到的数据,',recv)
        if recv == 'close':
            #服务的主动断开连接
            print('服务器断开连接')
            self.close()
        else:
            #客户端向服务端发送数据
            self.send(f'我收到了,{time.strftime("%Y-%m-%d %H:%M:%S")}')
 
    def websocket_disconnect(self, message):
        #客户端端口连接时,会触发该方法,断开连接
        print('客户端断开连接')
        raise StopConsumer()

2、配置routings.py

from django.urls import path
from . import consumers
 
#这个变量是存放websocket的路由
socket_urlpatterns = [
    path('chat/socket/',consumers.ChatView.as_asgi()),
]

1.3、修改跟settings.py同级目录下的asgi.py

import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter,URLRouter
#导入chat应用中的路由模块
from chat import routings
 
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'study_websocket.settings')
application = ProtocolTypeRouter({
    #http路由走这里
    "http":get_asgi_application(),
    #chat应用下rountings模块下的路由变量socket_urlpatterns
    "websocket":URLRouter(routings.socket_urlpatterns)
})

1.4、启动项目

启动命令:python manage.py runserver 8888

启动提示:如下就是配置成功了

 

在线测试websocket的网站:

EasySwoole-WebSocket在线测试工具EasySwoole在线WebSocket测试工具icon-default.png?t=N6B9http://www.easyswoole.com/wstool.html

服务地址:ws://127.0.0.1:8888/chat/socket/  点击开启连接

连接成功后,就可以向服务端发送数据了。

二、房间组使用(聊天室:待更新)

三、房间组使用(非聊天室)

 概述:

data = {'type':'xxx'}

1、前端只想维护一个全局的websocket对象,通过发送的数据中type的不同获取到不同的数据。

2、在后端给前端主动推送数据时,也是通过这个type来确定要重新渲染的数据。

构建想法:

1、设置一个处理全局websocket的消费者类

2、设置一个全局websocket都进入的房间组

3、在chat应用下新建一个update.py: websocket返回数据 和主动推送数据都放到这个模块中

consumers.py

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_sync
import time
import json

#接收到websocket请求,直接向单个发送需要数据
from websocket.update import base_send

class AllDataConsumers(WebsocketConsumer):
    #统一的房间名
    room_name = 'chat_all_data'
    def connect(self):
        cls = AllDataConsumers
        self.room_group_name = cls.room_name
        #加入到房间组内, self.channel_name是当前
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name, self.channel_name
        )
        headers = self.scope['headers']
        token = None
        for key,value in headers:
            if key == b'token':
                token = value.decode('utf-8')
        if token:
            print(token)
        else:
            print('没有token数据')

        self.accept()

    def disconnect(self, close_code):
        print('有浏览器退出了websocket!!!!')
        # 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=None, bytes_data=None):
        '''
        :param text_data: 接收字符串类型的数据
        :param bytes_data:  接收bytes类型的数据
        :return:
        如果是浏览器直接请求时,就单独给这个浏览器返回结果,无需给房间组内的发送数据
        '''
        try:
            text_data_json = json.loads(text_data)
            the_type = text_data_json.get('type','none')
        except Exception as e:
            self.send(json.dumps({'code':400,'msg':'传递的数据有问题'},ensure_ascii=False))
            self.disconnect(400)
            return
        #个人信息:所有的老人信息
        if the_type == 'all_patient_page_data':
            send_data = base_send(text_data_json)
            self.send(json.dumps(send_data,ensure_ascii=False))
        #来访登记:进行中的来访登记
        if the_type == 'all_active_visit_data':
            send_data = base_send(text_data_json)
            self.send(json.dumps(send_data,ensure_ascii=False))


    #自定义的处理房间组内的数据
    def send_to_chrome(self, event):
        try:
            data = event.get('data')
            #接收房间组广播数据,将数据发送给websocket
            self.send(json.dumps(data,ensure_ascii=False))
        except Exception as e:
            error_logger.exception(str(e),'给全局的websocket推送消息失败')


update.py

import json
from datetime import datetime,timedelta
from django.db.models import Q

#channels包相关
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer


def base_send(data:dict):
    '''
    功能:发起websocket请求时,给当前websocket返回查询到的数据
    '''
    the_type = data.get('type')
    id = data.get('id')
    send_data = {
        'type':the_type,
    }
    #个人管理-首次进入时,没有点击搜索时,这个需要实时获取
    if the_type == 'all_patient_page_data':
        
        send_data['data'] = '数据库查询到的数据:个人管理'
        return send_data

    #来访登记:进行中的来访记录
    if the_type == 'all_active_visit_data':
        send_data['data'] = '数据库查询到的数据:来访记录'
        return send_data

    #


class AllDataConsumersUpdate:
    '''
    功能:在http视图中,给房间组=chat_all_data 推送指定的消息
        在视图函数中,某些数据更新了,需要通知所有的websocket对象
    '''

    def _make_channel_layer(self,send_data):
        '''
        :param send_data: 在http视图中查询好的数据,要给房间组内所有的websocket对象发送数据
        '''
        channel_layer = get_channel_layer()
        #拿到房间组名
        group_name = 'chat_all_data'
        #给该房间组组内发送数据
        async_to_sync(channel_layer.group_send)(
            group_name,
            {
                'type':'send_to_chrome', #消费者中处理的函数
                'data':send_data
            }
        )

    #个人信息:
    def all_patient_page_data(self):
       
        try:
            send_data = {
                'type':'all_patient_page_data',
                'data':'更新数据了,个人信息'
            }
            #把更新的数据发送到房间组内
            self._make_channel_layer(send_data=send_data) 
          
        except Exception as e:
            pass

    #来访登记:
    def all_active_visit_data(self):
        try:
            send_data = {
                'type':'all_patient_page_data',
                'data':'更新数据了,来访登记'
            }
            #把更新的数据发送到房间组内
            self._make_channel_layer(send_data=send_data) 
        except Exception as e:
            error_logger.exception(str(e))

rountings.py

from django.urls import path
from . import consumers
 
#这个变量是存放websocket的路由
socket_urlpatterns = [
    path('chat/socket/',consumers.ChatView.as_asgi()),
    path('socket/all/',consumers.AllDataConsumers.as_asgi()),
]
 

两个视图函数

from chat.update import AllDataConsumersUpdate
#2023-07-25:模拟来访记录变化
update = AllDataConsumersUpdate()
update.all_active_visit_data()

from chat.update import AllDataConsumersUpdate

#2023-07-25:模拟个人信息编变化
update = AllDataConsumersUpdate()
update.all_patient_page_data()

 

1、先使用测试网站连接上:

EasySwoole-WebSocket在线测试工具

2、再访问写的两个视图函数,看websocket是否返回数据

业务逻辑:

1、创建连接时,把websocket对象放到同一个房间组内

2、当是websocket对象主动给后端发送数据时,后端只对这个websocket对象返回数据

3、当在视图中调用主动推送的方法,

        a.把数据发送到房间组内

        b.通过设置好的处理方法send_to_chrome 来实现从房间组内拿到数据,发送给websocket对象

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

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

相关文章

【无标题】小创业公司死亡剧本

感觉蛮真实的;很多小创业公司没有阿里华为的命,却得了阿里华为的病。小的创业公司要想活无非以下几点: 1 现金流,现金流,现金流; 2 产品,找痛点,不要搞伪需求; 3 根据公司…

SpringBoot中配置文件的加载

springboot 启动会扫描一下位置的application.properties或者application.yml文件作为springboot的默认配置文件 file:./config/(项目根目录config文件夹下的配置文件) file:./(项目根目录下的配置文件) classpath:/config/(resources目录config文件下的配置文件) classpat…

Python调用文心千帆的API

文心千帆官网申请使用:点击 1、申请使用 2、使用并创建应用 Python调用 代码(GUI) 代码出处:点我 from tkinter import * from tkinter import messagebox import json import requestsAPI_KEY "API KEY内容" SECRET_KEY "Secret Key…

MySQL的基本概念(数据库类、数据模型、服务启动与连接)

目录 数据库基础 DB和DBMS 数据库的类型 RDBMS的结构 MySQL的服务启动与连接(Windows系统下) 服务启动 客户端连接 数据库基础 DB和DBMS 什么是DB 将大量的数据保存起来,通过计算机加工而成的可以进行高效访问的数据集合就成为数据…

Android Studio Flamingo Logcat使用方式

旧版Android Studio突然打不开了,安装了新的Flamingo。习惯用Log.e看日志,突然发现logcat没有筛选下拉了。o(╥﹏╥)o 还是需要查看官方文档:https://developer.android.google.cn/studio/debug/logcat?hlzh-cn (不知道为啥&…

设备运行健康监控:优化工业运营的关键措施

在现代工业生产中,设备的可靠性和稳定性对于提高生产效率和降低成本至关重要。然而,传统的设备管理方式往往只能实现事后维护和故障处理,无法预防故障的发生,造成了生产中断和不必要的资源浪费。为了更好地应对工业运营中的挑战&a…

决策树学习

决策树学习 决策树决策树基础适用决策树学习的经典目标问题样本的表示训练样本决策树的概念发展历史 经典决策树算法ID3算法属性选择和节点混杂度(Impurity)ID3 Q1: 哪个属性是最佳属性?当前最佳属性节点选择熵(Entropy&#xff0…

在Ubuntu 系统下开发GUI,用哪种开发工具比较好?

在Ubuntu系统下开发GUI,你可以考虑使用以下几种开发工具:Qt Creator:Qt Creator是一个跨平台的集成开发环境,专门用于开发基于Qt框架的应用程序。它提供了丰富的图形界面设计工具和代码编辑器,支持C和QML编程。Qt Crea…

centos7.9 安装openssl 3.1.1

直接看篇教程 #可能版本号随时间会变化,最好去官网看一下再确认wget https://www.openssl.org/source/openssl-3.1.1.tar.gz#解压 tar -xvf openssl-3.1.1.tar.gz -C /usr/local/ #进入安装目录配置环境 cd /usr/local/openssl-3.1.1/./config --prefix/usr/local…

Unity使用Cinemachine插件实现摄像机跟随和震动

一、实现跟随 1、在PackageManager添加插件 2、创建Cinemachine的摄像机,我的项目是2D项目所以创建2D摄像机 3、将Player拖拽到Follow和LookAt 4、创建一个空物体,向它添加PolygonCollider2D,调整好可视范围的大小以后在CinemachineVirtua…

一文弄懂Flink CDC

文章目录 1.CDC概述2.CDC 的实现原理3.为什么选 Flink4.支持的连接器5.支持的 Flink 版本6.Flink CDC特性7.用法实例7.1DataStream API 的用法(推荐)7.2Table/SQL API的用法 1.CDC概述 CDC(Change Data Capture)是一种用于捕获和处理数据源中的变化的技…

刘铁猛C#语言教程——语句1

语句的定义 以下是对该文档的翻译 一条语句对应着一条汇编语言指令或者一条语句对应着一系列有着内在逻辑关联的汇编指令,对于这句话的理解,我们可以观察C#编译器编译的C#程序后得到的汇编语言代码,这样便可以看到语句与指令的关系&#xff…

【Arduino】Teensy® USB Development Board 板子介绍

文章目录 1. Features2. Pins Name3. Getting started Teesy by Arduino1. Install Arduino IDE Software2. Install Teensyduino Software3. Running Blink Program 4. IMPORTANT INFORMATION BEFORE GOING FURTHER WITH USING TEENSY 4.11. I/O 仅耐受 3.3V!2. 电…

【MyBatis 学习一】认识MyBatis 第一个MyBatis查询

目录 一、认识MyBatis 1、MyBatis是什么? 2、为什么要学习MyBatis? 二、配置MyBatis环境 1、建库与建表 2、创建新项目 3、xml文件配置 (1)配置数据库连接 (2)配置 MyBatis 中的 XML 路径 三、测试&#x…

基于4G网络的嵌入式设备远程升级系统设计与实现(学习一)

摘要 随着无线通信技术的不断更新发展,嵌入式设备的联网应用领域得以大规模扩大,远程升级功能成为产品开发中必不可少的一部分。 本文对嵌入式设备远程升级进行了研究,在不改变设备硬件集成度基础上,设计实现了分离式升级的远程…

在Vue-Element中引入jQuery的方法

一、在终端窗口执行安装命令 npm install jquery --save执行完后,npm会自动在package.json中加上jquery 二、在main.js中引入(或者在需要使用的页面中引入即可) import $ from jquery三、使用jquery

结构型设计模式之亨元模式【设计模式系列】

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 设计模式系列 期待你的关注哦!!! 现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。 Now everythi…

第16章 控制脚本

CtrlC组合键会发送SIGINT信号,停止shell中当前运行的进程。 CtrlZ组合键会生成一个SIGTSTP信号,停止shell中运行的任何进程。停止进程会让程序继续保留在内存中,并能从上次停止的位置继续运行。 方括号中的数字是shell分配的作业号&#xff0…

PHP之Smarty使用以及框架display和assign原理

一、Smarty的下载 进入Smarty官网下载&#xff0c;复制目录libs目录即可http://www.smarty.net/http://www.smarty.net/ 二、使用Smarty&#xff0c;创建目录demo,把libs放进去改名为Smarty 三、引入Smarty配置,创建目录&#xff0c;index.php文件配置 <?php…

VuePress在生产环境跳转子页报错 Failed to execute ‘appendChild‘ on ‘Node‘

记录一个使用VuePress时遇到的问题 使用VuePress做了一个文档网页&#xff0c;在开发环境的时候一切正常&#xff0c;但是发布到生产环境后&#xff0c;直接跳转二级页面会报错Failed to execute appendChild on Node 比如主页是http://sun/docs/.vuepress/dist/index.html#/…