Django-WebSocket-Channels项目使用
文章目录
一、简述:django实现websocket
之前django-websocket退出到3.0之后,被废弃。官方推荐大家使用channels。
channels通过升级http协议 升级到websocket协议。保证实时通讯。也就是说,我们完全可以用channels实现我们的即时通讯。而不是使用长轮询和计时器方式来保证伪实时通讯。
他通过改造django框架,使django既支持http协议又支持websocket协议。
配置环境
Python3.8.6版本
Django==2.2.16
channels==3.0.4
channels_redis==4.0.0
项目结构目录
C:.
├─application
asgi.py 001文件
urls.py 002文件
settings.py 003文件
├─apps
│ └─web
│ ├─dclient
│ │ ├─views
notice_view.py 004文件
apps.py 005文件
router.py 006文件
urls.py 007文件
view.py 008文件
└─conf
1.asgi 001文件
"""
ASGI config for django_poll project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/
"""
import os
from channels.http import AsgiHandler
from channels.routing import ProtocolTypeRouter, URLRouter
from apps.web.dclient.urls import urlpatterns
from apps.web.op_drf.channelsmiddleware import QueryAuthMiddleware
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_poll.settings')
application = ProtocolTypeRouter(
{
"http": AsgiHandler(), # http走Django默认的asgi
# "websocket": URLRouter(urlpatterns), # websocket走channels
"websocket": QueryAuthMiddleware(URLRouter(urlpatterns)), # websocket走channels
# "websocket": WsTokenVerify(URLRouter(urlpatterns)),
}
)
2.urls 002文件
urlpatterns = [
...
re_path(r'^dclient/', include('apps.web.dclient.router')),
]
3.settings 003文件
INSTALLED_APPS = [
"channels", # 需要添加这个
]
# 文件最后添加以下配置
# 配置asgi
ASGI_APPLICATION = "application.asgi.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
'CONFIG': {"hosts": ["redis://%s:%s/%s" % (REDIS_HOST, str(REDIS_PORT), str(REDIS_DB))], },
}
}
4.notice_view 004文件
import json
import time
from django.core.cache import cache
from apps.web.dclient.view import push_msg
from apps.web.op_drf.response import SuccessResponse
from apps.web.op_drf.viewsets import GenericViewSet
from rest_framework.request import Request
class NoticeMessageView(GenericViewSet):
"""
装修记录表
"""
serializer_class = None
authentication_classes = []
permission_classes = []
JWT_AUTH_COOKIE = ''
def send_msg(self, request: Request, *args, **kwargs):
"""
车牌消息推送
"""
sn = self.request.query_params.get("sn")
content = self.request.query_params.get("content")
push_msg(sn, content)
return SuccessResponse()
def car_push(self, request: Request, *args, **kwargs):
"""
demo
{
"AlarmInfoPlate":
{
"channel": 0,
"deviceName": "IVS",
"ipaddr": "192.168.1.106",
"result":
{
"PlateResult":
{
"bright": 0,
"carBright": 0,
"carColor": 0,
"colorType": 1,
"colorValue": 0,
"confidence": 100,
"direction": 0,
"gioouts":
[],
"imagePath": "%2Fmmc%2FVzIPCCap%2F2023_02_10%2F1040291504_%CB%D5J2AX70.jpg",
"isoffline": 0,
"license": "苏J2AX70",
"location":
{
"RECT":
{
"bottom": 647,
"left": 1395,
"right": 1577,
"top": 587
}
},
"plateid": 12331,
"timeStamp":
{
"Timeval":
{
"decday": 10,
"dechour": 10,
"decmin": 40,
"decmon": 2,
"decsec": 29,
"decyear": 2023,
"sec": 1675996829,
"usec": 157567
}
},
"timeUsed": 112,
"triggerType": 4,
"type": 1
}
},
"serialno": "d604b906-3354aa7a",
"user_data": ""
}
}
"""
AlarmInfoPlate = self.request.data.get("AlarmInfoPlate")
try:
serial_num = AlarmInfoPlate.get("serialno")
license = AlarmInfoPlate.get("result").get("PlateResult").get("license")
data = {
"serialno": serial_num,
"AlarmInfoPlate": AlarmInfoPlate,
}
# 处理业务逻辑
"""
1.车牌信息
2.设备sn号
3.商户信息
"""
merchant_info = {
"merchant_id": int(time.time()),
"license": license,
"company_id": 35,
"serial_num": serial_num
}
push_msg(serial_num, AlarmInfoPlate)
cache.set(serial_num, json.dumps(merchant_info))
return SuccessResponse(data=data)
except Exception as e:
print(e)
return SuccessResponse()
5.apps 005文件
from django.apps import AppConfig
class WcompanyConfig(AppConfig):
name = 'apps.web.dclient'
6.router 006文件
from django.urls import re_path
from rest_framework.routers import DefaultRouter
from apps.web.dclient.views.notice_view import NoticeMessageView
router = DefaultRouter()
urlpatterns = [
# re_path('^test/send_msg/$', NoticeMessageView.as_view({'get': 'send_msg'})), # 主动推送消息给客户端
re_path('^car/car_push/$', NoticeMessageView.as_view({'post': 'car_push'})), # 主动推送消息给客户端
]
urlpatterns += router.urls
7.urls 007文件
from django.urls import re_path
from rest_framework.routers import DefaultRouter
from apps.web.dclient.view import ChatConsumerSec
router = DefaultRouter()
urlpatterns = [
# 车牌&商户信息获取
re_path(r'ws/carwith', ChatConsumerSec.as_asgi()),
]
urlpatterns += router.urls
8.view 008文件
import time
from asgiref.sync import async_to_sync
from channels.db import database_sync_to_async
from channels.exceptions import StopConsumer
from channels.generic.websocket import AsyncWebsocketConsumer, AsyncJsonWebsocketConsumer, WebsocketConsumer
import json
from channels.layers import get_channel_layer
from django.core.cache import cache
class ChatConsumerSec(AsyncWebsocketConsumer):
def __init__(self, *args, **kwargs):
super().__init__(args, kwargs)
self.msg = None
self.room_group_name = None
@database_sync_to_async
def get_redis(self):
serial_num = self.scope.get("sn")
msg = cache.get(serial_num)
if msg:
self.msg = msg
async def connect(self):
self.room_group_name = self.scope['room_group_name']
user = self.scope.get('user_id')
if not user:
print("User is not")
await self.close()
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
# 开启连接
await self.accept()
await self.get_redis()
if self.msg:
await self.send(text_data=self.msg)
async def disconnect(self, close_code):
# 断开连接时调用
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
raise StopConsumer()
async def websocket_receive(self, message):
"""
更加msg类型进行判断
close 关闭连接
merchant_info 获取商户信息
"""
data = message.get('text', '{}')
try:
data = json.loads(data)
msg_type = data.get("msg_type")
if msg_type in ["close", "merchant_info"]:
message = data.get('message')
if msg_type == "merchant_info":
if self.msg:
await self.send(text_data=self.msg)
elif msg_type == "close":
await self.close()
if message:
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'notification_message',
'message': message
}
)
else:
await self.close()
except Exception as e:
print(e)
async def notification_message(self, event):
message = event['message']
await self.send(text_data=json.dumps({
'message': message,
}))
async def chat_message(self, event):
# message = event['message']
message = event.get("event")
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
}))
def push_msg(sn, content):
"""
:param username: 上面定义的组的名字
:param event: 你要返回的消息内容,可以是str,dict
:return:
"""
sn = "chat-%s" % sn
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
sn,
{
"type": "chat_message", # 这个就是你上面 ChatConsumer 定义的 chat_message方法
"event": content # 是chat_message方法接受的参数
}
)