python异步编程-channels使用,创建websocket服务

news2024/11/26 3:07:21

目录

  • channels介绍
  • 准备工作
    • 创建python虚拟环境
    • 安装channels
    • 安装django
    • 安装daphne
    • 创建django项目
    • 创建chat应用
  • 配置instance项目
  • 简单聊天室页面
    • 在chat应用中创建模板
      • base.html
      • room.html
    • 添加视图
    • 添加路由
      • 添加urls.py
      • 项目路由添加chat转发路由
      • 启动同步服务器
  • 搭建websocket服务
    • 创建channels路由
      • 新增协议使用者
      • 新增websocket路由
      • 创建asgi的application
      • 配置asgi应用
      • 修改项目的asgi文件
  • 启动通道层
    • settings中配置
  • daphne部署
  • 验证
  • 总结

channels介绍

channels是一个用来构建实时web应用的强大工具,比如聊天室、在线游戏,多人协作、实时通知等。今天简单介绍一下,用channels搭建一个简易的websocket服务。

准备工作

创建python虚拟环境

mkdir instance
cd install
python -m venv venv

# 激活虚拟环境
source venv/bin/activate # linux
./venv/bin/activate.bat # window

安装channels

pip install channels

安装django

pip install django

安装daphne

这个稍后会用到,用来启动异步服务器使用

pip install daphne

创建django项目

django-admin startproject instance 

创建chat应用

django-admin startapp chat

配置instance项目

在instance/instance目录下,修改settings.py文件

建议最好用pycharm打开

# instance/instance/settings.py 

...
INSTALLED_APPS = [
	'daphne', # 新增
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites',
    'django.contrib.sitemaps',
    'chat', # 新增
    'channels', # 新增
]
...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates']
        ,
        'APP_DIRS': True,
        'OPTIONS': { # 此处options可以不配置,与我们的功能无关
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
...

简单聊天室页面

在chat应用中创建模板

需要创建两个模板文件,其中room.html 继承base.html

base.html

主要定义了页面的基本结构,具体内容在子模板中填充
instance/chat/template/chat/base.html

{% load static %}
<!DOCTYPE html>
<html>
<head>
    <meta charset=""utf-8>
    <title>{% block title %}Education{% endblock %}</title>
    <link href="{% static "css/base.css" %}" rel="stylesheet">
</head>
<body>
<div id="header">
    <a href="/" class="logo">Education</a>
    <ul class="menu">
        {% if request.user.is_authenticated %}
        <li><a href="{% url "logout" %}">Sign out</a> </li>
        {% else %}
        <li><a href="{% url "login" %}">Sign in</a> </li>
        {% endif %}
    </ul>
</div>
<div id="content">
    {% block content %}
    {% endblock %}
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
    $(document).ready(function(){
        {% block domready %}
        {% endblock %}
    });
</script>
</body>
</html>

room.html

主要实现了一个输入框一个发送按钮,以及可以展示消息的div元素,页面简陋,能看即可

除了页面结构,还加了websocket链接的js代码

{% extends "chat/base.html" %}

{% block title %} Chat room for "{{ course.title }}" {% endblock %}

{% block content %}
<div id="chat">

</div>
<div id="chat-input">
    <input id="chat-message-input" type="text">
    <input id="chat-message-submit" type="submit" value="Send">
</div>
{% endblock %}

{% block domready %}
        var url = 'ws://' + window.location.host + '/ws/chat/room/' + '{{ course }}' + '/';
        console.log(url);
        var chatSocket = new WebSocket(url);
        console.log(chatSocket);

        chatSocket.onmessage = function(e) {
            var data = JSON.parse(e.data);
            var message = data.message;

            var $chat = $('#chat');
            $chat.append('<div class="message">' + message + '</div>');
            $chat.scrollTop($chat[0].scrollHeight);
        };

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

        var $input = $('#chat-message-input');
        var $submit = $('#chat-message-submit');

        $submit.click(function(){
            var message = $input.val();
            if(message) {
                chatSocket.send(JSON.stringify({'message': message}));

                $input.val('');

                $input.focus();
            }
        });

        $input.focus();
        $input.keyup(function (e){
            if(e.which === 13) {
                $submit.click();
            }
        });
{% endblock %}

添加视图

编辑chat目录下views.py

from django.shortcuts import render

def course_chat_room(request, course_id):
    return render(request, 'chat/room.html', {'course': 1})

添加路由

添加urls.py

chat目录下新增urls.py

from django.urls import path

from chat import views


app_name = 'chat'

urlpatterns = [
    path('room/<int:course_id>/', views.course_chat_room, name='course_chat_room'),
]

项目路由添加chat转发路由

编辑instance/instance/urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('chat/', include('chat.urls', namespace='chat')), # 新增
    ...,
]

启动同步服务器

到这一步就可以启动wsgi服务器,查看页面

python manage.py runserver

启动后,在浏览器中打开网站http://127.0.0.1:8000/chat/room/1/ 可以看到页面
我这里有样式设置,所以可能你们电脑上看到的样式不一样,但是基本结构时一样的
在这里插入图片描述

搭建websocket服务

我们前面已经将channels 应用添加到settings中的INSTALLED_APPS中

所以可以直接配置channels

创建channels路由

新增协议使用者

channels中一个很重要的概念就是消费者,也叫使用者,本质上就是协议请求接收之后的处理者

chat目录下新建consumers.py

这里使用了异步实现,将注释掉的代码恢复,就是同步代码

import json

from channels.generic.websocket import WebsocketConsumer, AsyncWebsocketConsumer
from asgiref.sync import async_to_sync
from django.utils import timezone


# class ChatConsumer(WebsocketConsumer):
class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = self.scope['user']
        self.id = self.scope['url_route']['kwargs']['course_id']
        self.room_group_name = 'chat_%s' % self.id
        # async_to_sync(self.channel_layer.group_add)(self.room_group_name, self.channel_name)
        await self.channel_layer.group_add(self.room_group_name, self.channel_name)
        # accept connection
        await self.accept()

    async def disconnect(self, code):
        # async_to_sync(self.channel_layer.group_dicard)(self.room_group_name, self.channel_name)
        await self.channel_layer.group_dicard(self.room_group_name, self.channel_name)

    # receive message from WebSocket
    async def receive(self, text_data=None, bytes_data=None):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]
        # send message to WebSocket
        # self.send(text_data=json.dumps({'message': message}))
        now = timezone.now()

        # send message to room group
        await self.channel_layer.group_send(self.room_group_name, {
            "type": "chat_message",
            "message": message,
            "user": self.user.username,
            "datetime": now.isoformat(),
        })

    async def chat_message(self, event):
        await self.send(text_data=json.dumps(event))

新增websocket路由

chat下新增routing.py

from django.urls import re_path
from chat import consumers


websocket_urlpatterns = [
    re_path(r'ws/chat/room/(?P<course_id>\d+)/$', consumers.ChatConsumer.as_asgi()),
]

创建asgi的application

在instance/instance下新建routing.py

这里我们使用的ProtocolTypeRouter 协议路由,区分不同的协议使用不同的路由方式
http一定要配置,否则正常的http请求无法处理,会报错
其次就是新增了websocket协议
如此创建之后,才能应用websocket协议

import os

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

import chat.routing


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'instance.settings')


application = ProtocolTypeRouter({
    'http': get_asgi_application(),
    'websocket': AuthMiddlewareStack(
        URLRouter(
            chat.routing.websocket_urlpatterns
        )
    )
})

配置asgi应用

instance/instance/settings.py中新增配置项

...
ASGI_APPLICATION = 'instance.routing.application'
...

修改项目的asgi文件

修改instance/instance/asgi.py

import os
import django
from django.core.asgi import get_asgi_application
from channels.routing import get_default_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'instance.settings')
django.setup()
# application = get_asgi_application()
application = get_default_application()

OK,走到这一步,就可以在聊天室内发送消息了,只不过我们为了模拟而已,所以没有两个用户,只是把发送过来的消息再转发回client端,并显示在页面上

你是不是用了python manage.py runserver 启动,发现没有任何变化,然后打开F12 发现websocket链接失败

别急,往下看

启动通道层

通道层类似于传话筒,能够实现不同应用之间的通信,也可以支持使用者实例之间的通信

一般常用的通道层后台是redis
这里我们为了演示,就直接本地内存作为通道层缓存

settings中配置

配置下面的项,则自动启动通道层
不配置或者保留为空,则不启动

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

daphne部署

如果你使用了python manage.py runserver 发现没有效果,可以尝试daphne部署

django项目的测试服务器,默认启动的都是wsgi程序,不支持异步处理

而websocket的应用一般都是异步(同步的话就没有了意义)

所以我们要使用能够部署异步服务器的工具

就是daphne

daphne 支持asgi应用,基于我们上面写好的代码和创建的项目结构,启动方式如下

daphne instance.asgi:application
# 或者
daphne instance.routing:application

此时应该就可以了!

验证

启动之后,可以浏览器中多打开几个页面,因为我们没有做鉴权,所以每个页面都会创建一个websocket链接,相当于多个client

在其中一个页面中发送消息,那么其他页面也会同步刷新

之后优化一下页面结构,那就是一个简易聊天室了
在这里插入图片描述
在这里插入图片描述

总结

channels是一个能够处理实时协议的强大工具,这个协议可以是http,websocket,也可以是其他的需要实时通讯的情况。

daphne命令可以启动验证,也可以直接使用在生产环境,是安全的

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

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

相关文章

H5 Q绑查询系统源码

源码名称&#xff1a;H5 Q绑查询系统源码 源码介绍&#xff1a;H5Q绑查询系统源码由2个H5单页组成&#xff0c;无需数据库或PHP环境。上传后修改接口即可运行。 需求环境&#xff1a;H5 下载地址&#xff1a; https://www.51888w.com/208.html

鸿蒙内核源码分析(重定位篇) | 与国际接轨

一个程序从源码到被执行&#xff0c;当中经历了3个过程&#xff1a; 编译&#xff1a;将.c文件编译成.o文件&#xff0c;不关心.o文件之间的联系.静态链接&#xff1a;将所有.o文件合并成一个.so或.out文件&#xff0c;处理所有.o文件节区在目标文件中的布局.动态链接&#xf…

将所有PPT中的字体颜色白色改成黑色---使用AI提高效率

背景 在工作中&#xff0c;遇到一个PPT&#xff0c;老板需求&#xff0c;将PPT页面的所有的字体从白色改成黑色&#xff0c; 检查了发现&#xff0c;这个ppt有几个问题&#xff0c;没有使用母版&#xff0c;都是每一页进行编写&#xff0c; 一共500多页&#xff0c; 如果每个…

Markdown中使用 LaTeX 绘图 -- TikZ

Markdown中使用 LaTeX 绘图 -- TikZ 1 介绍1.1 概述1.2 与其他图包对比 2 示例 & 学习[The TikZ and PGF Packages](https://tikz.dev/)[Graphics with TikZ in LaTeX](https://tikz.net/)[TikZ PGF Manual](https://www.bu.edu/math/files/2013/08/tikzpgfmanual.pdf)[在 …

中科亿海微伺服控制FOC解决方案

随着科技的不断进步和应用的不断扩展&#xff0c;电机控制技术的需求也在不断增加&#xff0c;主要的电机控制需求包括高精度控制、高效率、高可靠性和智能化等。 FOC&#xff08;磁场定向控制&#xff09;&#xff0c;也被称作VC&#xff08;矢量控制&#xff09;&#xff0c…

从用户体验说起,集运系统需要哪些重要的功能?

良好的用户体验是服务成功的关键&#xff0c;更是客户持续选择的理由。用户体验层面最简单的表达其实就是&#xff1a;别让我等、别让我想、别让我烦。 在当今时代&#xff0c;用户的期望正在不断提高&#xff0c;他们追求快捷、高效和透明的服务。因此&#xff0c;集运系统在…

生信圆桌x生信菜鸟团:生物信息学初学者的交流与成长社区

生信菜鸟团是一个专门为生物信息学初学者和爱好者打造的在线社区&#xff0c;致力于为广大生信学习者提供一个分享知识、交流经验、共同成长的平台。随着生物信息学在各大领域的快速发展&#xff0c;越来越多的研究者和学生开始涉足这一领域&#xff0c;但生信知识的广度和深度…

人才流失预测项目

在本项目中&#xff0c;通过数据科学和AI的方法&#xff0c;分析挖掘人力资源流失问题&#xff0c;并基于机器学习构建解决问题的方法&#xff0c;并且&#xff0c;我们通过对AI模型的反向解释&#xff0c;可以深入理解导致人员流失的主要因素&#xff0c;HR部门也可以根据分析…

备战秋招60天算法挑战,Day22

题目链接&#xff1a; https://leetcode.cn/problems/missing-number/ 视频题解&#xff1a; https://www.bilibili.com/video/BV1HS42197Hc/ LeetCode 268.丢失的数字 题目描述 给定一个包含 [0, n] 中 n 个数的数组 nums &#xff0c;找出 [0, n] 这个范围内没有出现在数组…

基于Python flask的岗位招聘数据分析系统,应用Python、Flask框架、Pyecharts、Wordcloud等技术

基于Python Flask的岗位招聘数据分析系统旨在为企业人力资源部门和求职者提供一个全面的数据分析平台&#xff0c;通过对招聘数据的深度挖掘和可视化展示&#xff0c;帮助用户做出更明智的决策。该系统采用了Python、Flask框架&#xff0c;并结合Pyecharts、Wordcloud等技术&am…

Matplotlib基本操作

1.什么是Matplotlib matplotlib 是一个广泛使用的 Python 图形库&#xff0c;用于生成静态、动态和交互式的可视化图表。它最初由 John D. Hunter 创建&#xff0c;并首次发布于2003年。matplotlib 提供了一个面向对象的 API&#xff0c;允许用户创建多种类型的图表&#xff0c…

[JavaScript版本五子棋小游戏]

目录 全部运行代码&#xff1a;五子棋游戏的基本步骤&#xff1a;代码剖析&#xff1a;1. 初始化游戏界面2. 管理游戏状态3. 玩家交互4. 电脑AI5. 胜负判定6. 游戏控制 本文通过实现一个基本的五子棋游戏&#xff0c;展示了如何使用HTML、CSS和JavaScript来构建一个简单的交互式…

ATGM332D-F8N低功耗、小尺寸单北斗多频定位导航模块规格书

ATGM332D-F8N主要 特征 &#xff1a; 1、多频点单北斗接收机 支持B1C独立定位通道数目&#xff1a;176通道支持北斗二号、北斗三号支持B1I、B1C、B2I、B3I、B2a、B2b 定位精度 单点定位精度&#xff1a;<1.0mCEP50推算定位误差&#xff1a;<3&#xff05;&#xff08;定位…

伦敦银行情的软件有什么选择?

普通投资者做伦敦银交易&#xff0c;多以技术分析为主、基本分析为辅的方法作为自己的交易策略&#xff0c;既然提到技术分析&#xff0c;那么伦敦银行情的软件就至关重要&#xff0c;因为我们需要通过这些软件才能看到行情并进行分析&#xff0c;那看伦敦银行情的软件有什么好…

2024.8.21

130124202408211006 DATE #:20240821 ITEM #:DOC WEEK #:WEDNESDAY DAIL #:捌月拾捌 TAGS < BGM "琴师--要不要买菜" > < theme oi-contest > < [NULL] > < [空] > < [空] > 此情可待成追忆&#xff0c;只是当时已惘然 -- 《锦瑟》…

「OC」视图控制器的懒加载策略

「OC」视图控制器的懒加载策略 文章目录 「OC」视图控制器的懒加载策略懒加载懒加载的优点常见的懒加载实现方法使用懒加载的注意事项 控制器的懒加载参考资料 懒加载 懒加载&#xff08;Lazy Loading&#xff09;是一种设计模式&#xff0c;其核心思想是在需要时才进行对象的…

Verilog刷题笔记55

题目&#xff1a; Exams/ece241 2014 q5a You are to design a one-input one-output serial 2’s complementer Moore state machine. The input (x) is a series of bits (one per clock cycle) beginning with the least-significant bit of the number, and the output (Z)…

更快更强,SLS 推出高性能 SPL 日志查询模式

作者&#xff1a;无哲 引言 随着数字化进程的持续深化&#xff0c;可观测性一直是近年来非常火热的话题&#xff0c;在可观测的三大支柱 Log/Trace/Metric 中&#xff0c;日志&#xff08;Log&#xff09;数据一般是最为常见的&#xff0c;企业迈向可观测性的第一步&#xff…

《黑神话:悟空》总销量已破 450 万份,总销售额超过15亿元,对于单机游戏来说,这一成绩意味着什么?

《黑神话&#xff1a;悟空》总销量突破450万份&#xff0c;总销售额超过15亿元&#xff0c;意味着几个关键点&#xff1a; 市场认可度高&#xff1a;这样的销量和销售额表明游戏受到了广泛的玩家欢迎&#xff0c;市场认可度极高。对于单机游戏而言&#xff0c;这代表了其在游戏…

深入浅出:你需要了解的用户数据报协议(UDP)

文章目录 **UDP概述****1. 无连接性****2. 尽最大努力交付****3. 面向报文****4. 多种交互通信支持****5. 较少的首部开销** **UDP报文的首部格式****详细解释每个字段** **UDP的多路分用模型****多路分用的实际应用** **检验和的计算方法****伪首部的详细内容****检验和计算步…