python-websocket-channels-单对单聊天

该博客介绍了如何使用Django Channels和WebSockets实现一个简单的实时聊天应用程序。通过修改settings.py、asgi.py和routing.py设置ASGI支持和通道层,以及创建consumers.py处理WebSocket连接、接收和发送消息。代码示例包括同步和异步消费者实现,以及错误处理。整个过程无需视图函数,直接在consumers.py中处理数据交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

视频demo

测试websocket发送信息

GIFdemo

在这里插入图片描述


代码结构

在这里插入图片描述

代码细节

Talking_view/settings.py

  • APP里添加了 ‘channels’
  • MIDDLEWARE 里禁止了csrf
  • 添加 ASGI_APPLICATION 和 CHANNEL_LAYERS
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels',
    'chat',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

ASGI_APPLICATION = 'Talking_view.routing.application'

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

Talking_view/asgi.py

确定部分的加载方式.

import os

from django.core.asgi import get_asgi_application

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

application = get_asgi_application()

Talking_view/routing.py

创建websocket,并且指定了服务指向哪个APP chat.routing.websocket_urlpatterns

# -*- coding: utf-8 -*-
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter

import chat.routing

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

chat/routing.py

跟django正常的添加接口的方式是一样的.

# -*- coding: utf-8 -*-

from django.urls import path, re_path

from . import consumers

websocket_urlpatterns = [
    path(r'ws/chat/<from_id>/<to_id>/', consumers.As_ChatConsumer.as_asgi()),
    re_path(r'', consumers.Err_ChatConsumer.as_asgi()),
]

chat/consumers.py

  • T_ChatConsumer 是同步的
  • As_ChatConsumer 是异步的
# -*- coding: utf-8 -*-
# @file    : consumers.py
# @project : Talking_view

# 定义一个字典,用于存放当前在线的用户
from channels.exceptions import StopConsumer
from channels.generic.websocket import AsyncWebsocketConsumer, WebsocketConsumer

CONSUMER_OBJECT_DICT = {}


class T_ChatConsumer(WebsocketConsumer):

    def websocket_connect(self, message):
        """
        客户端浏览器发来连接请求之后就会被触发
        :param message:
        :return:
        """
        print(self.scope)
        from_id = self.scope['url_route']['kwargs']['from_id']
        to_id = self.scope['url_route']['kwargs']['to_id']
        # 添加进在线用户列表。添加之前,可以做一系列操作,例如查看用户是否合法访问等
        # 服务端接收连接,向客户端浏览器发送一个加密字符串
        self.accept()
        # 连接成功
        if from_id not in CONSUMER_OBJECT_DICT:
            CONSUMER_OBJECT_DICT.update({from_id: self})
            self.send(text_data=str({from_id: "连接成功!"}))
            print('连接', {'from_id': from_id, 'to_id': to_id, 'channel_name': self.channel_name}, self)
        else:
            print('请勿重复连接')
            return

    def websocket_receive(self, message):
        """
        客户端浏览器向服务端发送消息,此方法自动触发
        :param message:
        :return:
        """
        from_id = self.scope['url_route']['kwargs']['from_id']
        to_id = self.scope['url_route']['kwargs']['to_id']

        if to_id in CONSUMER_OBJECT_DICT:
            values = (CONSUMER_OBJECT_DICT[to_id])
            message.update({
                "from_id": from_id,
                "to_id": to_id
            })
            values.send(text_data=str(message))
            self.send(text_data=str({"code": 200, "msg": "发送成功"}))
        else:
            self.send(text_data=str({"code": 400, "msg": "对方不在线"}))

    def websocket_disconnect(self, message):
        """
        客户端浏览器主动断开连接
        :param message:
        :return:
        """
        # 服务端断开连接
        from_id = self.scope['url_route']['kwargs']['from_id']
        if from_id in CONSUMER_OBJECT_DICT:
            CONSUMER_OBJECT_DICT.pop(from_id)
        print('断开', {'from_id': from_id, 'channel_name': self.channel_name}, self)
        raise StopConsumer()


class As_ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        from_id = self.scope['url_route']['kwargs']['from_id']
        to_id = self.scope['url_route']['kwargs']['to_id']
        # 服务端接收连接,向客户端浏览器发送一个加密字符串
        await self.accept()
        # 连接成功
        if from_id not in CONSUMER_OBJECT_DICT:
            CONSUMER_OBJECT_DICT.update({from_id: self})
            await self.send(text_data=str({from_id: "连接成功!"}))
            print('连接', {'from_id': from_id, 'to_id': to_id, 'channel_name': self.channel_name}, self)
        else:
            print('请勿重复连接')
            return

    async def websocket_receive(self, message):

        from_id = self.scope['url_route']['kwargs']['from_id']
        to_id = self.scope['url_route']['kwargs']['to_id']

        if to_id in CONSUMER_OBJECT_DICT:
            values = (CONSUMER_OBJECT_DICT[to_id])
            message.update({
                "from_id": from_id,
                "to_id": to_id
            })
            await values.send(text_data=str(message))
            await self.send(text_data=str({"code": 200, "msg": "发送成功"}))
        else:
            await self.send(text_data=str({"code": 400, "msg": "对方不在线"}))

    async def websocket_disconnect(self, message):
        """
        客户端浏览器主动断开连接
        :param message:
        :return:
        """
        # 服务端断开连接
        from_id = self.scope['url_route']['kwargs']['from_id']
        if from_id in CONSUMER_OBJECT_DICT:
            CONSUMER_OBJECT_DICT.pop(from_id)
        print('断开', {'from_id': from_id, 'channel_name': self.channel_name}, self)
        raise StopConsumer()


class Err_ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        print("强制断开连接")
        # await self.accept()
        # await self.send(text_data=str({"msg": "非法请求"}))
        await self.close()
        raise StopConsumer()


用到的库及版本

aioredis==1.3.1
asgiref==3.5.2
async-timeout==4.0.2
attrs==21.4.0
autobahn==22.4.2
Automat==20.2.0
certifi==2022.6.15
cffi==1.15.0
channels==3.0.4
channels-redis==3.4.0
charset-normalizer==2.0.12
constantly==15.1.0
cryptography==37.0.2
daphne==3.0.2
Django==3.2.9
django-cors-headers==3.12.0
hiredis==2.0.0
hyperlink==21.0.0
idna==3.3
incremental==21.3.0
msgpack==1.0.3
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.21
pymongo==4.1.1
pyOpenSSL==22.0.0
pytz==2022.1
requests==2.28.0
service-identity==21.1.0
six==1.16.0
sqlparse==0.4.2
Twisted==22.4.0
twisted-iocpsupport==1.0.2
txaio==22.2.1
typing_extensions==4.2.0
urllib3==1.26.9
zope.interface==5.4.0

总结

这个是不需要操作什么试图函数之类的,就正常的编写处理数据即可.(consumers.py).
无论是同步还是异步都是可以的,在一定量级的情况下,没有什么体验差别.
Err_ChatConsumer 这个是将 非法请求拦截在了这里,因为在上面的demo中我是固定了格式的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值