阅读:50
pip install -U channels
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
...
'channels',
)
ASGI_APPLICATION = 'bug_Project_name.asgi.application'
import os
from channels.routing import ProtocolTypeRouter,URLRouter
from django.core.asgi import get_asgi_application
from . import routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bug_Project2.settings')
#application = get_asgi_application()
application = ProtocolTypeRouter({
"http":get_asgi_application(),
"websockt":URLRouter(routing.websoctet_urlpatterns )
})
from django.urls import re_path
from web.views import consumers
websoctet_urlpatterns = [
re_path('ws/(?P<group>\w+)/$',consumers.ChatConsumer.as_asgi()),
]
# -*- coding:UTF-8 -*-
# author:Administrator
# contact: 913632472@qq.com
# datetime:2022/1/10 11:55
# software: PyCharm
"""
文件说明:
"""
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
class ChatConsumer(WebsocketConsumer):
def websocket_connect(self,message):
# 有客户端来向后端发送websocket连接请求时,自动触发
# 后端允许客户端创建连接
print('有人来了')
self.accept()
#给客户端发送小时
self.send("欢迎啊")
def websocket_receive(self, message):
# 浏览器基于websocket向后端发送数据,自动触发接受消息
text = message['text']
print("接受到消息-->", text)
res = "{}SB".format(text)
if text == "关闭":
# 客户端通过发送命令 主动断开连接
self.close()
print('通过命令关闭')
raise StopConsumer # 如果服务端断开连接,执行StopConsumer异常 那么websocket_disconnect不会执行
self.send(res)
# self.close() #后端主动断开websocket
def websocket_disconnect(self, message):
# 客户端与服务端断开连接,自动触发
print("客户端断开连接")
raise StopConsumer
django中需要了解的
<html lang="en">
<head>
<meta charset="UTF-16">
<title>title</title>
<style>
.message{
height: 300px;
border: 1px solid #dddddd;
width: 100%;
}
</style>
</head>
</html>
<div class="message" id="message"></div>
<div>
<input type="text" placeholder="请输入" id="txt">
<input type="button" value="发送" onclick="sendMessage()">
</div>
<script>
socket = new WebSocket("ws://127.0.0.1:8000/room/123/")
//创建好连接后触发
socket.onopen = function (event) {
let tag = document.createElement("div");
tag.innerText = "[连接成功]";
document.getElementById("message").appendChild(tag);
}
//当websocket接受要服务端发来的信息 会自动触发这个函数
socket.onmessage = function (event){
let tag = document.createElement("div");
tag.innerText = event.data;
document.getElementById("message").appendChild(tag);
}
function sendMessage(){
let tag = document.getElementById("txt");
socket.send(tag.value);
}
function closeConn() {
socket.close(); //服务端发送断开请求
}
</script>
"""基于内存实现"""
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer",
}
}
"""基于redis实现"""
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
},
},
}
Channels引入了一个layer的概念,channel layer是一种通信系统,允许多个consumer实例之间互相通信,以及与外部Djanbo程序实现互通。
channel layer主要实现了两种概念抽象:
channel name: channel实际上就是一个发送消息的通道,每个Channel都有一个名称,每一个拥有这个名称的人都可以往Channel里边发送消息
group: 多个channel可以组成一个Group,每个Group都有一个名称,每一个拥有这个名称的人都可以往Group里添加/删除Channel,也可以往Group里发送消息,Group内的所有channel都可以收到,但是无法发送给Group内的具体某个Channel
了解了上边的概念,接下来我们利用channel layer实现真正的聊天室,能够让多个客户端发送的消息被彼此看到
官方推荐使用redis作为channel layer,所以先安装channels_redis
pip install channels_redis
# 基于同步实现通信
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_sync #把django的异步切换成同步
class ChatConsumer(WebsocketConsumer):
def websocket_connect(self,message):
# 获取群号,获取路由匹配中的
self.accept()
group=self.scope["url_route"]['kwargs'].get('group')
print('WebSocket建立连接:', group)
#将客户端的连接对象加入到某个地方 (内存 or redis)
async_to_sync(self.channel_layer.group_add)(group,self.channel_name)
def websocket_receive(self, message):
print("fas")
group = self.scope["url_route"]['kwargs'].get('group')
#通知组内的所有客户端 执行xx.oo的方法,在次方法可以自己去定义任意的功能
async_to_sync(self.channel_layer.group_send)(group,{"type":"xx.oo",'message':message})
def xx_oo(self,event):
text = event['message']['text']
self.send(text)
def websocket_disconnect(self, message):
# 客户端与服务端断开连接,自动触发
print("客户端断开连接")
group = self.scope["url_route"]['kwargs'].get('group')
async_to_sync(self.channel_layer.group_discard)(group,self.channel_name)
raise StopConsumer