? HTTP协议指超文本传输协议(HyperText Transfer Protocol),是用于从万维网服务器传输超文本到本地浏览器的传送协议,是基于TCP/IP协议之上的应用层协议。
HTTP是基于客户/服务器模式,且面向连接的。典型的HTTP事务处理有如下的过程:
客户与服务器建立连接;
? 现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
? HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
? WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
? Channels是一个采用Django并将其功能拓展到HTTP以外的项目,以处理WebSocket协议。它基于称为ASGI的Python规范构建。
? 接下来基于官方Tutorial1-2简单介绍Channels的使用。
? 首先需要安装Django和Channels
pip install django
pip install channels
? 创建一个Django项目,并进入项目根目录:
django-admin startproject mysite
cd mysite
? 接下来创建Channels的根路由routing.py
,channels的路由配置与Django URLconf相似,它告诉Channels当Channels服务器接收到HTTP请求时要运行什么代码。
? 首先从一个空的路由配置开始,创建文件mysite/routing.py
,包含以下代码:
# mysite/routing.py
from channels.routing import ProtocolTypeRouter
application = ProtocolTypeRouter({
# (http->django views is added by default)
})
? 然后将Channels库在应用列表中进行注册:
# mysite/settings.py
INSTALLED_APPS = [
‘django.contrib.admin‘,
‘django.contrib.auth‘,
‘django.contrib.contenttypes‘,
‘django.contrib.sessions‘,
‘django.contrib.messages‘,
‘django.contrib.staticfiles‘,
# 新添加
‘channels‘,
]
? 继续编辑mtsite/settings.py
将Channels指向根路由配置,添加以下代码:
# mysite/settings.py
# Channels
ASGI_APPLICATION = ‘mysite.routing.application‘
? 此时Channels可以控制runserver
命令,用Channels开发服务器代替标准的Django开发服务器。
? 运行Django项目:
py manage.py runserver
? 将会看到以下输出:
Performing system checks...
System check identified no issues (0 silenced).
You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run ‘python manage.py migrate‘ to apply them.
March 07, 2020 - 22:45:04
Django version 3.0.2, using settings ‘mysite.settings‘
Starting ASGI/Channels version 2.4.0 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
HTTP GET / 200 [0.04, 127.0.0.1:54871]
? 可以看到Starting ASGI/Channels development server at http://127.0.0.1:8000/
,说明Channels开发服务器已从Django开发服务器接管项目。打开初始界面,你将看到熟悉的小火箭:
? 关闭服务器,创建appevent
:
py manage.py startapp event
? 注册app:
# mysite/settings.py
INSTALLED_APPS = [
‘django.contrib.admin‘,
‘django.contrib.auth‘,
‘django.contrib.contenttypes‘,
‘django.contrib.sessions‘,
‘django.contrib.messages‘,
‘django.contrib.staticfiles‘,
‘channels‘,
# 新添加
‘event‘,
]
? 在根目录新建文件夹templates
以及templates/event
文件夹放置html文件,在mysite/settings.py
中添加路径:
# mysite/settings.py
TEMPLATES = [
{
‘BACKEND‘: ‘django.template.backends.django.DjangoTemplates‘,
# 修改
‘DIRS‘: [os.path.join(BASE_DIR, ‘templates‘).replace(‘\\‘, ‘/‘)],
‘APP_DIRS‘: True,
‘OPTIONS‘: {
‘context_processors‘: [
‘django.template.context_processors.debug‘,
‘django.template.context_processors.request‘,
‘django.contrib.auth.context_processors.auth‘,
‘django.contrib.messages.context_processors.messages‘,
],
},
},
]
? 在templates
中添加视图文件event/list.html
,其中WebSocket请求部分模板如下:
<script type="text/javascript">
function Filter() {
if ("WebSocket" in window) {
//alert("您的浏览器支持WebSocket!");
// 重定向URL
let ws = new WebSocket("ws://"+ window.location.host + "/ws/event/list/");
ws.onopen = function () {
ws.send(JSON.stringify({
‘message‘: "测试",
// 需要传输的数据
}));
}
ws.onmessage = function (evt) {
let received_msg = JSON.parse(evt.data);
let feed_back = received_msg[‘feedback‘];
alert(feedback);
// 处理接受数据
}
ws.onclose = function () {
//alert("WebSocket连接已关闭...");
}
}
else {
alert("你的浏览器不支持WebSocket!");
}
}
</script>
? 创建视图功能event/views.py
:
# event/views.py
from django.shortcuts import render
def list(request):
return render(request, ‘event/list.html‘, {})
? 创建路由event/urls.py
:
# event/urls.py
from django.urls import path
from . import views
urlpatterns = [
path(‘list/‘, views.list, name=‘list‘),
]
? 将event app
的路由添加到项目的根路由中:
# mysite/urls.py
from django.conf.urls import include
from django.urls import path
from django.contrib import admin
urlpatterns = [
path(‘admin/‘, admin.site.urls),
path(‘event/‘, include(‘event.urls‘, namespace=‘event‘)),
]
Channels处理请求过程:
1. Channels接受HTTP请求
2. 查询根URLconf查找**消费者(consumer)**
3. 在**消费者(consumer)**中调用各种功能来处理连接的事件
创建消费者(consumer)文件event/consumers.py
:
event/
__init__.py
……
consumers.py
……
urls.py
views.py
? 代码模板如下:
# event/consumers.py
from channels.generic.websocket import WebsocketConsumer
import json
class ListDataConsumer(WebsocketConsumer):
def connect(self):
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‘]
# 数据处理
self.send(text_data=json.dumps({
‘feedback‘: "Accept",
# 返回数据
}))
? 为consumers.py
配置路由,新建event/routing.py
:
# event/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r‘ws/event/list/$‘, consumers.ListDataConsumer),
]
? 接下来将根路由指向event/routing.py
文件:
# mysite/routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import event.routing
application = ProtocolTypeRouter({
# (http->django views is added by default)
‘websocket‘: AuthMiddlewareStack(
URLRouter(
event.routing.websocket_urlpatterns
)
),
})
? 此根路由配置指定当与Channels开发服务器建立连接时,ProtocolTypeRouter
将首先检查连接的类型。如果它是WebSocket连接(ws://或wss://),则该连接将分配给AuthMiddlewareStack
。
? 在AuthMiddlewareStack
将填充的连接的范围覆盖到当前认证的用户,然后将连接到URLRouter
。该URLRouter
会研究基础上,提供连接到路由到特定消费者的HTTP路径,基于url
模式。
? 之后进行数据库模型迁移:
py manage.py makemigrations
py manage.py migrate
? 运行项目:
py manage.py runserver
? 如果连接建立成功,后台应该有如下显示:
HTTP GET /event/list/ 200 [0.06, 127.0.0.1:58855]
WebSocket HANDSHAKING /ws/event/list/ [127.0.0.1:58906]
WebSocket CONNECT /ws/event/list/ [127.0.0.1:58906]
? 使用Channels的一般流程如上,在配置ASGI之后,Channels的服务器会替代原有的Django服务器处理请求。
? 只有在需要使用WebSocket协议进行实时通信时需要配置routing.py
和consumers.py
,由routing.py
指向consumers.py
处理WebSocket请求,其余仅使用HTTP协议请求的使用方式与之前并没有任何区别。
? 因为只是在一个项目中的某个app中使用了Channels,可以参考该项目代码中的event
.
原文:https://www.cnblogs.com/liyishan/p/12624106.html