二级域名网站建设规范,三个字简洁的公司名称,wordpress主题,私人订制管理中心1. Channels 介绍
Django 中的 HTTP 请求是建立在请求和响应的简单概念之上的。浏览器发出请求#xff0c;Django服务调用相应的视图函数#xff0c;并返回响应内容给浏览器渲染。但是没有办法做到 服务器主动推送消息给浏览器。
因此#xff0c;WebSocket 就应运而生了。…1. Channels 介绍
Django 中的 HTTP 请求是建立在请求和响应的简单概念之上的。浏览器发出请求Django服务调用相应的视图函数并返回响应内容给浏览器渲染。但是没有办法做到 服务器主动推送消息给浏览器。
因此WebSocket 就应运而生了。WebSocket 是一种基于 HTTP 基础上进行全双工通讯的协议。WebSocket允许服务端主动向客户端推送数据。在WebSocket协议中浏览器和服务器只需要完成一次握手就可以创建持久性的连接并在浏览器和服务器之间进行双向的数据传输。
Django Channels 实现了 WebSocket 能力。Channels 允许 Django 以非常类似于传统 HTTP 的方式支持WebSockets。Channels 也允许在运行 Django 的服务器上运行后台任务HTTP 请求表现以前一样但也支持通过 Channels 进行路由。
Django channels安装pip install channels
2. channels 单人聊天
配置文件 settings.py:
INSTALLED_APPS [...app01.apps.App01Config,channels
]
ASGI_APPLICATION djangoProject.asgi.application主路由文件 urls.py:
from app01 import views as vw1urlpatterns [path(admin/, admin.site.urls),path(chatone, vw1.chat)
]主业务视图文件 app01/views.py:
from django.shortcuts import renderdef chat(request):return render(request, chatting.html)主业务html文件 chatting.html:
!DOCTYPE html
html langen
headmeta charsetUTF-8titleTitle/titlestyle.message {height: 300px;border: 1px solid #dddddd;width: 100%;}/style
/head
bodydiv classmessage idmessage/divdivinput typetext placeholder请输入聊天内容 idtxtinput typebutton value点击发送 onclicksendMessage()input typebutton value关闭连接 onclickcloseConn()/divscript// 实例化websocket对象并客户端主动以websocket方式连接服务端wbsocket new WebSocket(ws://127.0.0.1:8080/room/123/);// 创建好websocket连接成功后自动触发服务端执行self.accept()后wbsocket.onopen function (event) {var tag document.createElement(div);tag.innerText [连接成功];document.getElementById(message).appendChild(tag);};// 创建连接失败后自动触发wbsocket.onerror function (event) {var tag document.createElement(div);tag.innerText [连接失败];document.getElementById(message).appendChild(tag);};// 当websocket接收到服务器发来的消息时会自动触发wbsocket.onmessage function (event) {var tag document.createElement(div);tag.innerText event.data;document.getElementById(message).appendChild(tag);};// 当服务端主动断开客户端时自动触发服务端执行self.close()后wbsocket.onclose function (event) {var tag document.createElement(div);tag.innerText [连接已断开];document.getElementById(message).appendChild(tag);};// 页面上客户端点击向服务端关闭连接时触发function closeConn() {wbsocket.close(); // 客户端主动断开连接服务端会执行 websocket_disconnect()var tag document.createElement(div);tag.innerText [连接已断开啦];document.getElementById(message).appendChild(tag);}// 页面上客户端点击向服务端发送消息时触发function sendMessage() {var info document.getElementById(txt);wbsocket.send(info.value); // 客户端给服务端发数据}/script/body
/htmlwebsocket路由文件 routings.py:
from django.urls import re_path
from app01 import consumers as consm1websocket_urlpatterns [re_path(rroom/, consm1.ChatConsumer.as_asgi())
]处理websocket业务文件 app01/consumers.py:
from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumerclass ChatConsumer(WebsocketConsumer):def websocket_connect(self, message):客户端向服务端发送websocket连接的请求时自动触发。print(1 客户端和服务端开始建立连接)self.accept()def websocket_receive(self, message):客户端基于websocket向服务端发送数据时自动触发接收消息。print(f2 服务端接收客户端的消息, message is {message})recv_data message[text]if recv_data exit: # 服务端主动关闭websocket连接时前端会执行对应的 oncloseself.close()# raise StopConsumer() # raise主动抛异常后websocket_disconnect 就不在执行了多用于只处理服务端向客户端断开的场景returnsend_data f服务端主动推送消息{recv_data}self.send(text_datasend_data)def websocket_disconnect(self, message):客户端与服务端断开websocket连接时自动触发(不管是客户端向服务端断开还是服务端向客户端断开都会执行)print(3 客户端和服务端断开连接)self.close()raise StopConsumer()3. channel_layer 群组聊天
对于大多数情况来说发送到单人的 channel 并没有用更多的情况下希望可以以广播的方式将message 一次性发送给多个 channel 或者 consumer这不仅适用于想在向房间内的每个人发送消息还适用于发送给连接了多个浏览器/标签/设备的用户。
channel_layer 是一种通信系统。它允许多个消费者实例相互交谈借助 channel_layer 可以很方便的实现群聊功能我们无需手动管理 websocket 连接。channel 官方推荐的是配置channel_redis它是一个使用Redis作为传输的Django维护层。安装pip install channels_redis。
channel_layer 属于纯粹的异步接口如果想要改成同步代码调用需要使用async_to_sync做转换 from asgiref.sync import async_to_sync。
配置文件 settings.py:
ASGI_APPLICATION djangoProject.asgi.application# 开发环境使用
CHANNEL_LAYERS {default: {BACKEND: channels.layers.InMemoryChannelLayer}
}# 真实生产环境上使用
CHANNEL_LAYERS {default: {BACKEND: channels_redis.core.RedisChannelLayer,CONFIG: {hosts: [redis://127.0.0.1:6379/1, ], # 无密码连接redis# hosts: [redis://:password127.0.0.1:6379/1, ], # 有密码连接redis# symmetric_encryption_keys: [SECRET_KEY]}}
}主路由文件 urls.py
from django.contrib import admin
from django.urls import path
from app01 import views as vw1
from app02 import views as vw2urlpatterns [path(admin/, admin.site.urls),path(chatone, vw1.chat),path(chatgroup, vw2.groupchat) # 群聊
]主业务视图 app02/views.py
from django.shortcuts import renderdef groupchat(request):groupid request.GET.get(groupID) # 获取群组IDreturn render(request, groupchatting.html, {group_num: groupid})主业务html文件 chatting.html: js在实例化websocket对象时需要改动。
!DOCTYPE html
html langen
headmeta charsetUTF-8titleTitle/titlestyle.message {height: 300px;border: 1px solid #dddddd;width: 100%;}/style
/head
bodydiv 群组内聊天[ 群ID: {{ group_num }} ] /divdiv classmessage idmessage/divdivinput typetext placeholder请输入群聊内容 idtxtinput typebutton value点击发送 onclicksendMessage()input typebutton value关闭连接 onclickcloseConn()/divscript// 实例化websocket对象并客户端主动以websocket方式连接服务端wbsocket new WebSocket(ws://127.0.0.1:8080/group/{{ group_num }}/);// 创建好websocket连接成功后自动触发服务端执行self.accept()后wbsocket.onopen function (event) {var tag document.createElement(div);tag.innerText [连接成功];document.getElementById(message).appendChild(tag);};// 创建连接失败后自动触发wbsocket.onerror function (event) {var tag document.createElement(div);tag.innerText [连接失败];document.getElementById(message).appendChild(tag);};// 当websocket接收到服务器发来的消息时会自动触发wbsocket.onmessage function (event) {var tag document.createElement(div);tag.innerText event.data;document.getElementById(message).appendChild(tag);};// 页面上客户端点击向服务端关闭连接时触发function closeConn() {wbsocket.close(); // 客户端主动断开连接服务端会执行 websocket_disconnect()var tag document.createElement(div);tag.innerText [连接已断开];document.getElementById(message).appendChild(tag);}// 页面上客户端点击向服务端发送消息时触发function sendMessage() {var info document.getElementById(txt);wbsocket.send(info.value); // 客户端给服务端发数据}/script/body
/htmlwebsocket 路由文件 routings.py
from django.urls import re_path
from app01 import consumers as consm1
from app02 import consumers as consm2websocket_urlpatterns [re_path(rroom/, consm1.ChatConsumer.as_asgi()),re_path(rgroup/(?PgroupID\w)/$, consm2.GroupChatConsumer.as_asgi()) # 群聊
]websocket 群聊业务文件 consumers.py 注意从路由url中获取参数时要使用 self.scope[url_route][kwargs].get(groupID)这里的 groupID 必须和routings中的路由分组名 re_path(rgroup/(?PgroupID\w)/$ 保持一致
from asgiref.sync import async_to_sync
from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumerclass GroupChatConsumer(WebsocketConsumer):group_id Nonedef websocket_connect(self, message):# 接受客户端连接self.accept()print( 客户端和服务端已成功建立连接 )# 从url获取群组id这个groupID必须和routings里的路由分组名保持一致self.group_id self.scope[url_route][kwargs].get(groupID)# 将当前的连接加入到名为self.group_id的组中async_to_sync(self.channel_layer.group_add)(self.group_id, self.channel_name)def websocket_receive(self, message):print(fcurrent self is {self}, id is {id(self)}, groupID is {self.group_id})# 组内所有的客户端执行type对应的函数可以在此函数中自定义任意功能async_to_sync(self.channel_layer.group_send)(self.group_id, {type: send_msg, message: message})def send_msg(self, event):input_msg event[message].get(text)send_msg f组内主动推送消息{input_msg}self.send(text_datasend_msg)def websocket_disconnect(self, message):# self.channel_name从组self.group_id中删除并断开连接async_to_sync(self.channel_layer.group_discard)(self.group_id, self.channel_name)print( 客户端和服务端已断开连接 )raise StopConsumer()