感谢:(1)Django+channels配置与编写:https://blog.csdn.net/weixin_40105587/article/details/87375886
(2)Django+channels配置:https://www.jianshu.com/p/d6394ca19d92
(3)channels文档:https://channels.readthedocs.io/en/stable/introduction.html
一、环境版本
python 3.8
Django 3.1.5
channels 3.0.3
(安装channels时会自动安装Twisted、daphne等必须的依赖包)
windows环境下安装channels,在安装twisted时会报错,提示
error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": https://visualstudio.microsoft.com/downloads/ ---------------------------------------- ERROR: Failed building wheel for twisted Running setup.py clean for twisted Failed to build twisted
解决方法: 前往https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted下载.whl格式文件。
cp38对应python3.8版本,win_amd64对应64位系统
PS:一定要对应python的版本,否则可能会安装出错!!!
将下载的.whl文件放进对应python3.8版本的安装目录的Scripts文件夹,在该文件夹下执行 pip install Twisted-20.3.0-cp38-cp38-win_amd64.whl
(如果是在虚拟环境,则在 项目所在盘:\项目名\venv\Scripts)
安装完后再次,pip install channels
二、添加到项目
参考channels文档:https://channels.readthedocs.io/en/stable/installation.html
(我的项目配置与文档有一点区别)
1、在 项目的settings.py文件的INSTALLED_APPS添加 “channels”:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
...
'你的项目app名',
'channels',
)
ASGI_APPLICATION = '项目名.routing.application'
2、修改在settings.py同级目录的asgi.py(Django3以上版本会自动创建改文件)
(ps:我这里如果安装文档一样填写,用daphne启动项目时会报错,所以做了改动)
(daphne启动:daphne -p 8991 项目名.asgi:application)
import os
import django
from channels.routing import get_default_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pay_python.settings')
django.setup()
application = get_default_application()
# application = ProtocolTypeRouter({
# "http": django_asgi_app,
# 'websocket': AuthMiddlewareStack(
# URLRouter(
# aliPay.ali_routing.websocket_urlpatterns
# )
# ),
# })
3、在settings.py 同级目录创建routing.py,其作用与 urls.py相同,主要用于分发webscoket请求,内容如下
# -*- coding: utf-8 -*-
"""
Created with:PyCharm
@Author: Jcsim
@Date: 2021-2-25 10:41
@Project: pay_python
@File: routing.py
@Blog:https://blog.csdn.net/weixin_38676276
@Description:
@Python:
"""
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter({
# "http": django_asgi_app,
'websocket': AuthMiddlewareStack(
URLRouter(
# 后面会完善
)
),
})
4、在项目app目录下新增app_routing.py,app_consumers.py
app_routing.py内容:
# -*- coding: utf-8 -*- """ Created with:PyCharm @Author: Jcsim @Date: 2021-2-25 10:45 @Project: pay_python @File: ali_routing.py @Blog:https://blog.csdn.net/weixin_38676276 @Description: 接收ws请求处理 @Python: """ from django.urls import path from aliPay.ali_consumers import OrderStatusConsumers, AsyncOrderStatusConsumers # 建议url前缀使用 ws/xxx 用于区分ws请求和http请求 websocket_urlpatterns = [ path('ws/qosws', OrderStatusConsumers.as_asgi()), # path('ws/asqosws/<tradeNo>', AsyncOrderStatusConsumers.as_asgi()), ]
app_consumers.py内容:
consumer类中的函数依次用于处理连接(connect)、断开连接(disconnect)、接收消息(receive)和处理对应类型的消息
# -*- coding: utf-8 -*- """ Created with:PyCharm @Author: Jcsim @Date: 2021-2-25 10:47 @Project: pay_python @File: ali_consumers.py @Blog:https://blog.csdn.net/weixin_38676276 @Description: @Python: """ from channels.generic.websocket import WebsocketConsumer, AsyncWebsocketConsumer import json # 我项目的日志操作 from aliPay.log import logger # 数据库模型 from aliPay.models import Orders, WsChannels from utils import idCreater class OrderStatusConsumers(WebsocketConsumer): def connect(self): self.accept() def disconnect(self, close_code): pass def receive(self, text_data): # 这里是接受数据后的操作,下面的方法按需修改 # logger.info("接受订单号,发送数据") text_data_json = json.loads(text_data) trade_no = text_data_json['tradeNo'] stop = False order_info = Orders.objects.filter(orders_id=trade_no).first() order_status = order_info.orders_status status = 201 password = "******" if order_status == 20: password = order_info.project.password status = 200 stop = True data = { "status": status, "password": password } # 推送信息到前端 self.send(text_data=json.dumps(data)) # 我项目ws推送的结束逻辑 if stop: self.close() # 异步通讯 redis版本需为5.0及以上 class AsyncOrderStatusConsumers(AsyncWebsocketConsumer): async def connect(self): self.tradeNo = self.scope['url_route']['kwargs']['tradeNo'] self.tradeNo_group_name = 'chat_%s' % self.tradeNo # Join room group await self.channel_layer.group_add( self.tradeNo_group_name, self.channel_name ) await self.accept() async def disconnect(self, close_code): # Leave room group await self.channel_layer.group_discard( self.tradeNo_group_name, self.channel_name ) # Receive message from WebSocket async def receive(self, text_data): # text_data_json = json.loads(text_data) # message = text_data_json['message'] logger.info("接受订单号,发送数据aaaaaaaaaaaa") text_data_json = json.loads(text_data) trade_no = text_data_json['tradeNo'] # Send message to room group await self.channel_layer.group_send( self.tradeNo_group_name, { 'type': 'chat_message', # type 用于指定该消息的类型,根据消息类型调用不同的函数去处理消息 'message': trade_no # message 内为消息主体 } ) # Receive message from room group async def chat_message(self, event): trade_no = event['tradeNo'] stop = False # while not stop: order_info = Orders.objects.filter(orders_id=trade_no).first() order_status = order_info.orders_status password = "******" if order_status == 20: password = order_info.project.password stop = True data = { "order_status": order_status, "password": password } # Send message to WebSocket await self.send(text_data=json.dumps(data)) if stop: await self.close()
5、修改 settings.py同级目录的routing.py
# -*- coding: utf-8 -*-
"""
Created with:PyCharm
@Author: Jcsim
@Date: 2021-2-25 10:41
@Project: pay_python
@File: routing.py
@Blog:https://blog.csdn.net/weixin_38676276
@Description:
@Python:
"""
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import aliPay.ali_routing
django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter(
app_name.app_routing.websocket_urlpatterns
)
),
})
6、前端ws请求
前端websocket 教程:https://www.runoob.com/html/html5-websocket.html
<script type="text/javascript">
let createWebSocket = (function () {
return function (urlValue) {
if (window.WebSocket) return new WebSocket(urlValue);
if (window.MozWebSocket) return new MozWebSocket(urlValue);
return false;
}
})();
$(function () {
const tradeNo = $(".orderNO").text()
const socket = createWebSocket("ws://"+window.location.host+"/ws/qosws")
{#console.log("传数据==")#}
let send_data = {
"tradeNo": tradeNo
}
socket.onopen = () => {
$(document).ready(() => {
{#console.log('WebSocket open');//成功连接上Websocket#}
socket.send(JSON.stringify(send_data))
})
}
{#console.log("获取数据==")#}
socket.onmessage = (serverPush) => {
{#console.log(serverPush)#}
let data = JSON.parse(serverPush.data)
$(".password").text(data.password)
{#console.log(data.password)#}
if (data.status === 200){
alert("支付成功,请保存密码")
}
}
const timer2 = self.setInterval(() =>{
socket.send(JSON.stringify(send_data))
}, 3000)
const timer_close = setTimeout(() =>{
{#console.log("===关闭定时器====",10)#}
clearInterval(timer2)
socket.close()
}, 60000*10)
socket.onclose = () =>{
{#console.log("关闭连接")#}
clearInterval(timer2)
clearTimeout(timer_close)
socket.close()
}
})
</script>
7、至此,操作完成。