Channels官方文档
- 安装
# 安装channels
python -m pip install -U channels["daphne"]
# 插件redis用于实现组之间群发消息的
python -m pip install channels_redis
- 配置 settings.py
# 1. 添加app
INSTALLED_APPS = (
"daphne",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.sites",
...
)
# 2. 添加redis插件, 用户存储用户组, 实现群聊功能
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("127.0.0.1", 6379)],
},
},
}
# 3. 添加asgi, 可以再项目文件里找到asgi.py文件
ASGI_APPLICATION = "myproject.asgi.application"
asgi.py
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application
# 1. 添加websocket的app路由routing.py文件
from chat.routing import websocket_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'websocket_test.settings')
django_asgi_app = get_asgi_application()
# 2. 改写asgi的路由, 让其可以支持http, 与 websocket协议
application = ProtocolTypeRouter(
{
"http": django_asgi_app,
"websocket": AllowedHostsOriginValidator(
AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
),
}
)
新建App一个app叫 python manage.py chap chap/routing.py - 用于提供websocket接口
from django.urls import re_path
from . import views
websocket_urlpatterns = [
re_path(r"ws/chat/(?P<room_name>\w+)/$", views.ChatConsumer.as_asgi()),
re_path(r'room/(?P<room_name>\w+)/$', views.TestWebsocket.as_asgi()) # 设置组名字
]
chap/urls.py - 用于提供主页
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
# path("<str:room_name>/", views.room, name="room"),
path('test/', views.test)
]
chap/views.py
# chat/views.py
from django.shortcuts import render
def index(request):
return render(request, "index.html")
def room(request, room_name):
return render(request, "room.html", {"room_name": room_name})
# 1. 配置视图文件A
def test(request):
return render(request, 'test.html')
import json
from channels.generic.websocket import WebsocketConsumer, StopConsumer
class ChatConsumer(WebsocketConsumer):
def connect(self):
print('有人来连接了')
self.accept()
def disconnect(self, close_code):
pass
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json["message"]
print(message)
self.send(text_data=json.dumps({"message": message}))
# chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
# chat/consumers.py
import json
from asgiref.sync import async_to_sync # 必须
from channels.generic.websocket import WebsocketConsumer # 继承视图
########################
##使用插件redis实现的群聊
########################
# 2. ASGI群聊视图
class TestWebsocket(WebsocketConsumer):
def connect(self):
# room/(?P<room_name>\w+)/
# 1. 获取当前用户组
# ['url_route']固定获取路由 -> kwargs所有字段路由参数 -> ['room_name']房间名字
self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
# 2. 分组名字
self.room_group_name = f"chat_{self.room_name}"
# 3. 把当前用户添加到分组里(内存 or redis)
async_to_sync(self.channel_layer.group_add)(
self.room_group_name, self.channel_name
)
# 4. 连接成功
self.accept()
def disconnect(self, close_code):
# 1. 再当前组中删除此用户
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name, self.channel_name
)
# Receive message from WebSocket
def receive(self, text_data):
# 1. 获取客户端上传的内容
text_data_json = json.loads(text_data)
# 2. 获取用户传过来的数据
message = text_data_json["message"]
# 3. 执行回调函数type:chat_message 给当前组的所有人发送消息
async_to_sync(self.channel_layer.group_send)(
self.room_group_name, {"type": "chat.message", "message": message}
)
# 给组内发送消息
def chat_message(self, event):
# 1. 获取消息
message = event["message"]
# 2. 发送消息
self.send(text_data=json.dumps({"message": message}))
###########
## 这里是使用本地列表实现的群聊
###########
# CONN_LIST = []
# class TestWebsocket(WebsocketConsumer):
# def websocket_connect(self, message):
# print('有人来连接了')
# self.accept()
# self.send('来啦老弟!!')
# CONN_LIST.append(self)
# def websocket_disconnect(self, message):
# print('有人断开连接了')
# CONN_LIST.remove(self)
# raise StopConsumer()
# # self.close()
# def websocket_receive(self, message):
# print(f'有人来发送消息了:{message}')
# msg = message['text']
# if msg == '关闭':
# # 服务端主动断开连接
# self.close()
# return
# for conn in CONN_LIST:
# conn.send(msg)
# # self.send(f'xxx发送了消息:{msg}')
前端 test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="message" id="msg"></div>
<input type="text" placeholder="请输入" id="txt">
<input type="button" value="发送" onclick="SendMessage()">
<input type="button" value="关闭连接" onclick="CloseMessage()">
<script>
socket = new WebSocket('ws://127.0.0.1:8000/room/chen/');
// 连接好之后除法
socket.onopen = function(event){
let msg = document.createElement('div');
msg.innerText = '[连接成功]';
document.getElementById('msg').appendChild(msg);
}
// 接受消息
socket.onmessage = function (event){
let msg = document.createElement('div');
msg.innerText = event.data;
document.getElementById('msg').appendChild(msg);
}
// 发送消息
function SendMessage() {
let text = document.getElementById('txt');
socket.send(text.value);
}
// 客户端主动断开
function CloseMessage() {
socket.close()
}
socket.onclose = function(event){
let msg = document.createElement('div');
msg.innerText = '[断开连接]';
document.getElementById('msg').appendChild(msg);
}
</script>
</body>
</html>