测试环境以及版本
说明:其实django3.0已经自带websocket的处理器了,我是用channels实现的。感兴趣的可以直接使用asgi实现。django3以上项目路径下会自带这个asgi.py文件。
Django==3.1.5
python==3.7
channels==3.0.3
channels-redis==3.2.0
项目结构树
django项目中的settings文件
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"channels", # 放在最上面
]
同级新建routings.py文件
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import 你的app.routing as device
application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter(
device.websocket_urlpatterns
)
),
})
app下新建routings.py文件
from django.conf.urls import url
from django.urls import path
from app.consumers import DeviceConsumer
websocket_urlpatterns = [
url(r'ws/device/?$', DeviceConsumer.as_asgi()),
]
app下新建consumers.py文件
import json
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncWebsocketConsumer # 异步,实现更好的性能
from cies_device.models import Device
from codex.exceptions import DBDataCRUDError
web_clients = {}
class DeviceConsumer(AsyncWebsocketConsumer):
actuator_status = {}
async def connect(self):
# connect方法在连接建立时触发
self.room_group_name = 'chat_test'
for i, j in self.scope["headers"]:
if i.decode() == "clientlite":
k = i.decode()
v = j.decode().split(":")[-1]
self.room_group_name = v
if web_clients.get(k, []):
if v not in web_clients[k]:
web_clients[k].append(v)
else:
web_clients[k] = [v]
print(web_clients)
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# disconnect在连接关闭时触发
# Leave room group
# 断开连接后将注册的channel删除
if "clientlite" in list(web_clients.keys()) and self.room_group_name != "chat_test":
web_clients["clientlite"].remove(self.room_group_name)
# Send message to room group
await self.channel_layer.group_send(
"chat_test",
{
'type': 'device_message',
'message': list(self.actuator_status.values())[0] if self.actuator_status else {}
}
)
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
@database_sync_to_async
def save_to_db(self, data):
# 创建数据
sub_uids = []
try:
f = Device._meta.many_to_many[0].related_model
sub_devices = data.get("devices", [])
for i in sub_devices:
uid = i.get("uid", "")
f.objects.update_or_create(i, **{"uid": uid})
sub_uids.append(uid)
data.pop("devices")
uid = data.get("uid", "")
ip = data.get("ip", "")
res = Device.objects.update_or_create(data, **{"uid": uid})
# 更新状态
if ip in self.actuator_status.keys():
if self.actuator_status[ip]["actuator_status"] != data.get("actuator_status", ""):
self.actuator_status[ip]["actuator_status"] = data.get("actuator_status", "")
else:
self.actuator_status[ip] = {"uid": uid, "actuator_status": data.get("actuator_status", "")}
if sub_uids:
fn = Device._meta.many_to_many[0].name
fl_obj = f.objects.filter(uid__in=sub_uids)
eval("res[0].{}".format(fn)).add(*fl_obj)
except Exception as e:
print(e)
raise DBDataCRUDError("数据入库失败 {}".format(e))
return "数据入库成功"
# Receive message from WebSocket
async def receive(self, text_data):
# receive方法会在收到消息后触发
text_data_json = json.loads(text_data)
if self.room_group_name != "chat_test":
h5_msg = text_data_json.pop("actuator")
if h5_msg == "Info":
await self.save_to_db(text_data_json)
else:
await self.channel_layer.group_send(
"chat_test",
{
'type': 'device_message',
'message': text_data
}
)
# Receive message from room group
async def device_message(self, event):
message = event['message']
print("收到数据{} 并发送".format(message))
# Send message to WebSocket 【最后在这里发送返回前端页面】
await self.send(text_data=json.dumps(message))
测试websocket需注意的点
import time
from django.conf import settings# 这必须有
from cies import settings as s# 这必须有
settings.configure(s)# 这必须有
import os# 这必须有
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cies.settings")# 这必须有
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
#
# # Create your tests here.
#
#
def send_group_msg(channel_name, message):
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)("{}".format(channel_name), {
"type": "device_message",
"message": message,
})
if __name__ == '__main__':
n = 1
#
while n<10000:
d = "dadda"+str(n)
send_group_msg("chat_test", d)
n += 1
time.sleep(0.5)
然后就可以随处调用了