Python Channels模块使用

Django初步配置Channels模块

安装Channels模块

pip install channels==3.0.3

***注意:不要直接pip install channels,会导致安装最新的channels包导致后面无法使用asgi文件

在settings文件中注册app

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

在settings文件中使用引入asgi文件

ASGI_APPLICATION = 'long_poll.asgi.application'

***long_poll为自己的项目名称

asgi文件中配置如下:

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

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'long_poll.settings')  # long_poll为自己项目名称

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

创建routing文件并配置路由(与Django创建路由到视图函数类似):

from django.urls import re_path

from app01 import consumers

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

编写视图函数(创建consumers文件):

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer


class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self, message):
        """有客户端来向后端发送连接时,就会自动触发"""
        print('有人来连接了')
        self.accept()

    def websocket_receive(self, message):
        """浏览器基于websocket向后端发送数据时自动触发,用于接收数据"""self.send('不要回复')

    def websocket_disconnect(self, message):
        """客户端向后端断开连接时自动触发"""
        print('断开连接')
        raise StopConsumer()

客户端向服务端发送消息

编写Django视图函数(view.py文件):

from django.shortcuts import render, HttpResponse


def home(request):
    return render(request, 'home.html')

在templates文件夹下创建 home.html  (客户端)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .message {
            height: 500px;
            width: 100%;
            border: 1px solid #e0e0e0;
        }
    </style>
</head>

<body>
<div class="message" id="myDiv"></div>
<div>
    <input type="text" placeholder="请输入" id="txt">
    <input type="button" value="发送" onclick="sendMessage()">
</div>

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>

<script>
    socker=new WebSocket("ws://127.0.0.1:8000/ws/123/") //创建一个WebSocket对象


    function sendMessage(){
        let tag=document.getElementById('txt')
        socker.send(tag.value)   //向服务端发送信息
    }
</script>
</body>
</html>

服务端接收到消息(consumers.py文件)

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer


class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self, message):
        """有客户端来向后端发送连接时,就会自动触发"""
        print('有人来连接了')
        self.accept()

    def websocket_receive(self, message):
        """浏览器基于websocket向后端发送数据时自动触发,用于接收数据"""
        print('我接受到了消息----->',message)
        # self.send('不要回复')

    def websocket_disconnect(self, message):
        """客户端向后端断开连接时自动触发"""
        print('断开连接')
        raise StopConsumer()

服务端给客户端主动发送消息:

客户端(home.html文件)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .message {
            height: 500px;
            width: 100%;
            border: 1px solid #e0e0e0;
        }
    </style>
</head>

<body>
<div class="message" id="myDiv"></div>
<div>
    <input type="text" placeholder="请输入" id="txt">
    <input type="button" value="发送" onclick="sendMessage()">
</div>

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>

<script>
    socker=new WebSocket("ws://127.0.0.1:8000/ws/123/") //创建一个WebSocket对象
    // 回调函数,当websocket接收到服务端发来的消息时自动触发此函数
    socker.onmessage=function (event){
        console.log(event.data)
    }</script>
</body>
</html>

服务端(consumers.py文件):

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer


class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self, message):
        """有客户端来向后端发送连接时,就会自动触发"""
        print('有人来连接了')
        self.accept()

        """握手完成后,给客户端发送消息"""
        self.send('你好呀')

客户端断开连接

客户端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .message {
            height: 500px;
            width: 100%;
            border: 1px solid #e0e0e0;
        }
    </style>
</head>

<body>
<div class="message" id="myDiv"></div>
<div>
    <input type="text" placeholder="请输入" id="txt">
    <input type="button" value="关闭" onclick="closeConn()">
</div>

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>

<script>
    socker=new WebSocket("ws://127.0.0.1:8000/ws/123/") //创建一个WebSocket对象
function closeConn(){ socker.close() //客户端断开连接 } </script> </body> </html>

服务端

     客户端触发socker.close()后,自动触发服务端的websocket_disconnect()函数

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer


class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self, message):
        """有客户端来向后端发送连接时,就会自动触发"""
        self.accept()

        """握手完成后,给客户端发送消息"""
        self.send('你好呀')

    def websocket_receive(self, message):
        """浏览器基于websocket向后端发送数据时自动触发,用于接收数据"""
        self.send('不要回复')

    def websocket_disconnect(self, message):
        """客户端向后端断开连接时自动触发"""
        print('断开连接')
        raise StopConsumer()

基于channels中提供channel layers来实现

在settings中配置

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

使用channel-redis配置(选其中之一就行了)

pip install channel-redis

在settings中配置

CHANNEL_LAYERS = {
    'default': {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [('127.0.0.1', 6379)]
        }
    }
}

views.py文件,接收前端传来的组号

from django.shortcuts import render, HttpResponse


def home(request):
    num = request.GET.get('num')
    return render(request, 'home.html', {'num': num})

home.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .message {
            height: 500px;
            width: 100%;
            border: 1px solid #e0e0e0;
        }
    </style>
</head>

<body>
<div class="message" id="myDiv"></div>
<div>
    <input type="text" placeholder="请输入" id="txt">
    <input type="button" value="发送" onclick="sendMessage()">
    <input type="button" value="关闭" onclick="closeConn()">
</div>

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>

<script>
    socker = new WebSocket("ws://127.0.0.1:8000/ws/{{num}}/") //创建一个WebSocket对象

    // 回调函数,接收服务端发来的消息
    socker.onmessage = function (event) {
        // console.log(event.data)
        // 找到你想要添加元素的div
        var myDiv = document.getElementById('myDiv');
        // 创建一个新的p元素
        var newElement = document.createElement('p');
        // 设置p元素的内容
        newElement.textContent = event.data;
        // 将新创建的p元素添加到div中
        myDiv.appendChild(newElement);
    }

    function sendMessage() {
        let tag = document.getElementById('txt')
        socker.send(tag.value)   //向服务端发送信息
    }

    function closeConn() {
        socker.close()
    }
</script>
</body>
</html>

consumers.py文件

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


class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self, message):
        """有客户端来向后端发送连接时,就会自动触发"""
        self.accept()
        """将连接对象加入到内存或redis中"""
        group = self.scope['url_route']['kwargs'].get('group')
        async_to_sync(self.channel_layer.group_add)(group, self.channel_name)

    def websocket_receive(self, message):
        """浏览器基于websocket向后端发送数据时自动触发,用于接收数据"""
        group = self.scope['url_route']['kwargs'].get('group')
        """通知组内的所有客户端,执行xx_oo的方法,在此方法中可以定义任意的功能"""
        async_to_sync(self.channel_layer.group_send)(group, {'type': 'xx.oo', 'message': message})

    def xx_oo(self, message):
        self.send(message['message']['text'])

    def websocket_disconnect(self, message):
        """客户端向后端断开连接时自动触发"""
        group = self.scope['url_route']['kwargs'].get('group')
        async_to_sync(self.channel_layer.group_discard)(group, self.channel_name)
        raise StopConsumer()

 

posted @ 2024-03-18 17:22  秃头不爱学  阅读(358)  评论(0)    收藏  举报