Django webSocket
源代码:https://github.com/wztshine/django_websocket_demo
参考自:
https://www.cnblogs.com/wupeiqi/articles/9593858.html
https://www.bilibili.com/video/BV1aM4y137Qu?p=10&spm_id_from=pageDriver
配置
- 安装
pip install channels
pip install django
- 创建项目
命令行输入:
django-admin startproject ws_channel # ws_channel 是项目名,会在当前路径下出现这样一个文件夹
- 进入项目文件夹,修改
ws_channel/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'channels', # 这里修改了:添加 channels 模块
]
# ws_channel 是当前 settings.py 文件所在的文件夹,即 python 的包名;asgi 是包下的 asgi.py 模块,它支持异步和 websockt
ASGI_APPLICATION = "ws_channel.asgi.application"
Django 3.0 以上版本,会自动生成一个 asgi.py 文件, 和 settings.py 同级目录。
- 修改
ws_channel/asgi.py
(如果你用的django不是3.0以上版本,自己手动创建这个文件)
"""
ASGI config for ws_channel project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
"""
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', 'ws_channel.settings')
# application = get_asgi_application()
application = ProtocolTypeRouter({
"http": get_asgi_application(), # 自动处理 http 请求
'websocket': URLRouter(routing.websocket_urlPatterns), # 使用自定义的路由系统,来处理 websocket 连接
})
- 创建编写
ws_channel/routing.py
(settings.py 同级目录下)
from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import re_path
from app01 import consumers
websocket_urlPatterns = [
re_path(r"", consumers.ChatConsumer.as_asgi()), # 匹配路由
]
- 新建一个 app01 的应用,并在应用下编写 consumers.py
- 在你当前的工程目录下,运行cmd命令:
django-admin startapp app01 # 创建一个叫 app01 的应用
- 编写
app01/consumers.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
"""这个函数是用来建立连接的。当客户端发起连接时,服务端会自动触发这个函数"""
print('开始握手')
self.accept() # 接受连接
def websocket_receive(self, message):
"""客户端发来消息时,服务端自动调用这个方法接受消息"""
print('接收到消息', message)
self.send(text_data='收到了') # send 方法可以发送消息
def websocket_disconnect(self, message):
"""
服务端主动调用 self.close() 时,或者客户端调用 close() 方法来关闭连接时,服务端都会自动运行这个方法,关闭连接
:param message:
:return:
"""
print('客户端断开连接了')
raise StopConsumer()
简单的聊天室
在上面的前提下,在 ws_channel/settings.py
中注册 app01
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config', # 注册 app01
'channels',
]
在 ws_channel/urls.py
中添加路由:
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index), # 新路由
]
在 app01/views.py
中编写视图:
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request, 'index.html')
在 app01 目录下新建文件夹:templates
,并编写 index.html
:
app01/templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<style>
.message{
100%;
height: 300px;
border: 1px solid #dddddd;
}
</style>
<body>
<div class="message" id="message"></div>
<div>
<input type="text" id="txt">
<!-- 添加了 onclick 事件 -->
<input type="button" value="send" onclick="sendMessage()">
<input type="button" value="close" onclick="ws_close()">
</div>
<script>
// 创建 WebSocket 对象, 尝试握手连接
socket = new WebSocket("ws://127.0.0.1:8000")
// onopen 函数会在 websocket 和服务端建立连接成功后,自动调用执行
socket.onopen = function (event){
let tag = document.createElement("div")
tag.innerText = '[连接成功]'
document.getElementById('message').append(tag)
}
// onmessage 会在 websocket 接收到服务端的消息后,自动调用执行
socket.onmessage = function (event){
let tag = document.createElement("div")
tag.innerText = event.data
document.getElementById('message').append(tag)
}
// onclose 会在服务端主动发起断开时,自动调用执行
socket.onclose = function (event){
let tag = document.createElement("div")
tag.innerText = '[服务端断开]'
document.getElementById('message').append(tag)
}
function ws_close(){
socket.close() // 客户端主动关闭 websocket 连接
}
// 点击按钮触发
function sendMessage(){
let content = document.getElementById('txt').value
socket.send(content) // 客户端向后端发消息
}
</script>
</body>
</html>
修改 app01/consumers.py
:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
clients = []
class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
"""这个函数是用来建立连接的。当客户端发起连接时,服务端会自动触发这个函数"""
print("有人来了...")
self.accept() # 接受连接
clients.append(self) # 存储每个客户端连接
def websocket_receive(self, message):
"""客户端发来消息时,服务端自动调用这个方法接受消息"""
print('接收到消息', message)
for client in clients: # 遍历客户端,给每个客户端发送消息
client.send(message['text']) # send 方法可以发送消息
if message['text'] == 'close':
self.close() # 可以关闭连接
# 触发异常,就不会执行 websocket_disconnect() 方法了;如果不触发下面的异常,服务端主动调用 close() 后还会继续执行 websocket_disconnect()
raise StopConsumer()
def websocket_disconnect(self, message):
"""
服务端主动调用 self.close() 时,或者客户端调用 close() 方法来关闭连接时,服务端自动运行这个方法,来关闭连接
:param message:
:return:
"""
print('客户端断开连接了')
raise StopConsumer()
在项目目录下,命令行执行:
python manage.py runserver
打开浏览器,同时开两个窗口,都访问:http://127.0.0.1:8000/index
你在任意一个窗口发送消息,另一个窗口同样能接收到