Skip to main content

channels

Django Channels - Channels 4.0.0 dockmentation

Django Channels - Channels 4.0.0 dockmentation

Django Channels - Channels 4.0.0 dockmentation

tip

Django 默认不支持 websocket 需要安装对应的组件 channels daphne 学习版本 : Django=5.0.3 channels=3.0.2

配置

webSocket/settings.py
INSTALLED_APPS = [
'daphne', # 将这个放在第一位
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'channels' # 第一步 : 注册 channels
]

# 第二步: 添加 asgi_application, 对应的这个 asgi 其实是 asgi.py 文件
# ASGI_APPLICATION = 'APP.asgi.application'
ASGI_APPLICATION = 'webSocket.asgi.application'
webSocket/asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from . import routing


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webSocket.settings')

# application = get_asgi_application()
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket" : URLRouter(routing.websocket_urlpatterns),
})
webSocket/routing.py
from django.urls import re_path
from app01 import consumers

websocket_urlpatterns = [
re_path(r"ws/(?P<group>\w+)/$", consumers.ChatConsumer.as_asgi()),
]
app01/consumers.py
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_sync

class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
# 有客户端向后端发送 websocket 连接时自动触发
# 接收 URL 传来的参数
# 接收客户端连接
self.accept()

def websocket_disconnect(self, message):
# 客户端与服务端断开连接时, 自动触发
raise StopConsumer()


def websocket_receive(self, message):
# 浏览器基于 websocket 先后端发送数据, 自动触发用于接收客户端消息
print(message)
self.send("不要回复不要回复")

随后启动对应的程序, 看到下面的标识就表示成功了

20240613110935

websocket 收发数据

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.message {
height: 300px;
border: 1px solid #dddddd;
width: 100%;
}
</style>
</head>
<body>
<div class="message" id="message"></div>
<div>
<input type="text" placeholder="请输入" id="txt">
<input type="button" value="i*" onclick="sendMessage();">
<input type="button" value="退出" onclick="closeConn();">
</div>

<script>
// 1. 实例化一个 webSocket 对象
socket = new WebSocket("ws://127.0.0.1:4000/ws/admin/")

// 2. 客户端发送消息给服务端
function sendMessage(){
socket.send(document.getElementById('txt').value)
}

// 3. 当 websocket 客户端接收服务端消息时, 自动触发
socket.onmessage = function (event) {
let tag = document.createElement("div")
tag.innerText = event.data
document.getElementById("message").appendChild(tag)
}

// 当服务端和客户端刚创建好之后自动触发
socket.onopen = function (event){
let tag = document.createElement("div")
tag.innerText = "[连接成功]"
document.getElementById("message").appendChild(tag)
}

function closeConn(){
socket.close()
}

</script>

</body>
</html>
app01/consumers.py
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
Author : 14894
date : 2024/3/16
File Name : consumers.py
Description :
"""
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_sync

class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
# 有客户端向后端发送 websocket 连接时自动触发
# 接收客户端连接
print("有人访问了")
self.accept()

# 服务端向客户端发送消息
self.send("来了呀客官")

def websocket_disconnect(self, message):
# 客户端与服务端断开连接时, 自动触发
print("{}断开连接".format(message))
raise StopConsumer()


def websocket_receive(self, message):
# 浏览器基于 websocket 先后端发送数据, 自动触发用于接收客户端消息
# 发送 123 , 接收到的消息: {'type': 'websocket.receive', 'text': '123'}
print("接收到消息-->", message)
self.send("{} SB".format(message))

群聊

简易实现

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.message {
height: 300px;
border: 1px solid #dddddd;
width: 100%;
}
</style>
</head>
<body>
<div class="message" id="message"></div>
<div>
<input type="text" placeholder="请输入" id="txt">
<input type="button" value="i*" onclick="sendMessage();">
<input type="button" value="退出" onclick="closeConn();">
</div>

<script>
// 1. 实例化一个 webSocket 对象
socket = new WebSocket("ws://127.0.0.1:4000/room/admin/")

// 2. 客户端发送消息给服务端
function sendMessage(){
socket.send(document.getElementById('txt').value)
}

// 3. 当 websocket 客户端接收服务端消息时, 自动触发
socket.onmessage = function (event) {
let tag = document.createElement("div")
tag.innerText = event.data
document.getElementById("message").appendChild(tag)
}

// 当服务端和客户端刚创建好之后自动触发
socket.onopen = function (event){
let tag = document.createElement("div")
tag.innerText = "[连接成功]"
document.getElementById("message").appendChild(tag)
}

function closeConn(){
socket.close()
}



</script>

</body>
</html>
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
Author : 14894
date : 2024/3/17
File Name : consumers.py
Description :
"""
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer

CONN_LIST = []

class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
# 有客户端向后端发送 websocket 连接时自动触发
# 接收客户端连接
print("有人访问了")
self.accept()
CONN_LIST.append(self)

def websocket_disconnect(self, message):
# 客户端与服务端断开连接时, 自动触发
print("{}断开连接".format(message))
raise StopConsumer()


def websocket_receive(self, message):
# 浏览器基于 websocket 先后端发送数据, 自动触发用于接收客户端消息
# 发送 123 , 接收到的消息: {'type': 'websocket.receive', 'text': '123'}
print("接收到消息-->", message)
for item in CONN_LIST:
item.send("{} SB".format(message))

完美实现

setting.py 中添加一处设置, 这项设置的作用是维护客户端连接并将其保存在内存中

setting.py
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer",
}
}
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
Author : 14894
date : 2024/3/17
File Name : consumers.py
Description :
"""
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_sync

class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):

# 获取群号
group = self.scope["url_route"]["kwargs"].get("group")
print(group)
# 将客户端连接对象加入到某个地方 [内存 / redis]
#self.channel_layer.group_add("群号名称", self.channel_name)
# 使用 async_to_sync 将异步功能转换为同步功能
async_to_sync(self.channel_layer.group_add)(group, self.channel_name)
self.accept()

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()


def websocket_receive(self, message):
# 浏览器基于 websocket 先后端发送数据, 自动触发用于接收客户端消息
# 发送 123 , 接收到的消息: {'type': 'websocket.receive', 'text': '123'}
print("接收到消息-->", message)
group = self.scope["url_route"]["kwargs"].get("group")
#self.channel_layer.send(group, {'messgae': '123'})
print(self.channel_layer)
# 向 00001 组内的所有客户端, 执行 xxx_oo 方法, 在 xxx_oo 方法中定义对应功能
# async_to_sync(self.channel_layer.group_send)(group, {'messgae': '123'})
async_to_sync(self.channel_layer.group_send)(group, {'type': 'send.message', 'message': message})

def send_message(self, event):
inputdata = event['message']["text"]
print("发送你")
self.send(inputdata)

问题

Cannot import ASGI_APPLICATION module 'MSF.asgi'

ASGI_APPLICATION = 'myapp.asgi.application'
|
|
ASGI_APPLICATION = 'myapp.settings'

# asgi.py 将 asgi.py 这里的配置替换上面的就可以
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myapp.settings')

TypeError: 'module' object is not callable

典型特征 : 无论请求什么都是 500

20240613111514

403 错误

介绍 : 在做某个项目时, 发现无论我怎么请求都是 403 , 但是我的代码编写没有任何问题

20240613111603

问题:

  • CSRF 问题
  • 配置问题 (我的就是这个问题)
setting.py
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware', # 注释此行
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSESS':(
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSESS':(
'rest_framework.permissions.IsAuthenticated',
)
}