django中使用websocket

第一步:

安装所需的pip包:channels,channels-redis

第二步:

修改settings.py添加相应配置,INSTALLED_APPS中加入channels,并加入CHANNEL_LAYERS

# channles
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [(REDIS_HOST, REDIS_PORT)],
            "symmetric_encryption_keys": [SECRET_KEY],
            "prefix": 'cybercity:asgi',
        },
    },
}

第三步修改asgi的入口文件如下

import os

from django.core.asgi import get_asgi_application

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

django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter({
    'websocket': SessionMiddlewareStack(
        URLRouter(
            practice.routing.websocket_urlpatterns,
        ),
    ),
    "http": django_asgi_app,
})

第四步添加consumer.py文件,放到对应的app里

(1)consumer.py的内容如下所示:

from channels.generic.websocket import AsyncJsonWebsocketConsumer
from practice import status
from practice.exceptions import ClientError


class NoticeConsumer(AsyncJsonWebsocketConsumer):
    """ 考试事件消息推送 """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 定义消息推送房间
        self.room = "notice"
        self.is_closed = False

    async def connect(self):
        """
        Called when the websocket is handshaking as part of initial connection.
        """
        # self.room_name = self.scope["url_route"]["kwargs"]["login_id"]
        await self.accept()

    async def receive_json(self, content, **kwargs):
        """
        Called when we get a text frame. Channels will JSON-decode the payload
        for us and pass it as the first argument.
        """
        try:
            command = content.get("command", None)
            if command is None:
                raise ClientError("COMMAND_IS_ACCESSARY")
            if command == "join":
                await self.join_room()
            elif command == "leave":
                await self.leave_room()
            else:
                raise ClientError("COMMAND_ERROR")

        except ClientError as e:
            await self.send_json({"error": e.code})

    async def disconnect(self, code):
        """
        Called when the WebSocket closes for any reason.
        """
        self.is_closed = True
        try:
            await self.leave_room()
        except ClientError:
            pass

    async def join_room(self):
        """
        Called by receive_json when someone sent a join command.
        """
        if status.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
            # 开启通知
            await self.channel_layer.group_send(self.room, {
                "type": "notice.join",
                "room": self.room
            })
        # 加入消息组

        await self.channel_layer.group_add(
            self.room,
            self.channel_name,
        )
        # 给连接的ws客户端发送确认消息
        await self.send_json({
            "join": self.room
        })

    async def leave_room(self):
        """
        Called by receive_json when someone sent a leave command.
        """
        if status.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
            await self.channel_layer.group_send(
                self.room, {
                    "type": "notice.leave",
                    "room": self.room
                })
        # 离开消息组
        await self.channel_layer.group_discard(
            self.room,
            self.channel_name,
        )
        # 给连接的ws客户端发送确认消息
        # 如果ws客户端连接断开则不发送确认消息
        if not self.is_closed:
            await self.send_json({
                "leave": self.room,
            })

    async def notice_join(self, event):
        """
        Called when someone has joined our chat.
        """
        await self.send_json(
            {
                "msg_type": status.MSG_TYPE_ENTER,
                "room": event["room"]
            }, )

    async def notice_leave(self, event):
        """
        Called when someone has left our chat.
        """
        await self.send_json(
            {
                "msg_type": status.MSG_TYPE_LEAVE,
                "room": event["room"],
            }, )

    async def notice_message(self, event):
        """
        Called when someone has messaged our chat.
        """
        await self.send_json(
            {
                "msg_type": status.MSG_TYPE_MESSAGE,
                "room": self.room,
                "message": event["message"],
                "created": event["created"],
                "topic": event["topic"],
            }, )

(2)status.py的内容如下所示:

NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS = True

MSG_TYPE_MESSAGE = 0  # For standard messages
MSG_TYPE_WARNING = 1  # For yellow messages
MSG_TYPE_ALERT = 2  # For red & dangerous alerts
MSG_TYPE_MUTED = 3  # For just OK information that doesn't bother users
MSG_TYPE_ENTER = 4  # For just OK information that doesn't bother users
MSG_TYPE_LEAVE = 5  # For just OK information that doesn't bother users

MESSAGE_TYPES_CHOICES = (
    (MSG_TYPE_MESSAGE, 'MESSAGE'),
    (MSG_TYPE_WARNING, 'WARNING'),
    (MSG_TYPE_ALERT, 'ALERT'),
    (MSG_TYPE_MUTED, 'MUTED'),
    (MSG_TYPE_ENTER, 'ENTER'),
    (MSG_TYPE_LEAVE, 'LEAVE'),
)

MESSAGE_TYPES_LIST = [
    MSG_TYPE_MESSAGE,
    MSG_TYPE_WARNING,
    MSG_TYPE_ALERT,
    MSG_TYPE_MUTED,
    MSG_TYPE_ENTER,
    MSG_TYPE_LEAVE,
]

(3)exceptions.py的内容如下所示:

class BaseError(Exception):
    """ 自定义异常基类 """
    def __init__(self, code):
        super().__init__(code)
        self.code = code


class ClientError(BaseError):

第五步添加routing.py文件,即websocket接口路由相关

from django.urls import path

from practice.consumers import NoticeConsumer, BroadcastConsumer

websocket_urlpatterns = [
    path("stream/notice/", NoticeConsumer.as_asgi()),
    path("stream/broadcast/", BroadcastConsumer.as_asgi()),
]

推送消息

import os
import json
import datetime

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django_redis import get_redis_connection


channel_layer = get_channel_layer()
redis_conn = get_redis_connection("default")


def vulnerability_approved(message):
    """管理员漏洞审核通过信息展示界面推送通知"""
    async_to_sync(channel_layer.group_send)(
        "broadcast", {
            "type": "notice.message",
            "message": message,
            "created": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "topic": "vulnerability_approved"
        })

你可能感兴趣的:(django中使用websocket)