文章目录
WebSocket
在讲Websocket
之前,先了解下 long poll
和 ajax轮询
的原理。
ajax轮询
ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
long poll
long poll
其实原理跟 ajax
轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
ajax
轮询 需要服务器有很快的处理速度和资源(速度)。long poll
需要有很高的并发,也就是说同时接待客户的能力(场地大小)。
Websocket
WebSocket
是一种在单个TCP
连接上进行全双工通讯的协议。WebSocket
允许服务端主动向客户端推送数据。在WebSocket
协议中,客户端浏览器和服务器只需要完成一次握手就可以创建持久性的连接,并在浏览器和服务器之间进行双向的数据传输。
WebSocket
的请求头中重要的字段:
Connection
和Upgrade
:表示客户端发起的WebSocket
请求Sec-WebSocket-Version
:客户端所使用的WebSocket
协议版本号,服务端会确认是否支持该版本号Sec-WebSocket-Key
:一个Base64
编码值,由浏览器随机生成,用于升级request
WebSocket
的响应头中重要的字段:
HTTP/1.1 101 Swi tching Protocols
:切换协议,WebSocket
协议通过HTTP
协议来建立运输层的TCP
连接Connection
和Upgrade
:表示服务端发起的WebSocket
响应Sec-WebSocket-Accept
:表示服务器接受了客户端的请求,由Sec-WebSocket-Key
计算得来
WebSocket
协议的优点:
- 支持双向通信,实时性更强
- 数据格式比较轻量,性能开销小,通信高效
- 支持扩展,用户可以扩展协议或者实现自定义的子协议(比如支持自定义压缩算法等)
WebSocket
协议的优点:
- 少部分浏览器不支持,浏览器支持的程度与方式有区别
- 长连接对后端处理业务的代码稳定性要求更高,后端推送功能相对复杂
- 成熟的
HTTP
生态下有大量的组件可以复用,WebSocket
较少
WebSocket
的应用场景:
- 即时聊天通信,网站消息通知
- 在线协同编辑,如腾讯文档
- 多玩家在线游戏,视频弹幕,股票基金实施报价
Channels
Django
本身不支持WebSocket
,但可以通过集成Channels
框架来实现WebSocket
Channels
是针对Django项目的一个增强框架,可以使Django
不仅支持HTTP
协议,还能支持WebSocket
,MQTT
等多种协议,同时Channels
还整合了Django
的auth
以及session
系统方便进行用户管理及认证。
channels中文件和配置的含义
asgi.py
:介于网络协议服务和Python
应用之间的接口,能够处理多种通用协议类型,包括HTTP
、HTTP2
和WebSocket
channel_layers
:在settings.py中配置。类似于一个通道,发送者(producer)在一段发送消息,消费者(consumer)在另一端进行监听routings.py
:相当于Django中的urls.py
consumers.py
:相当于Django中的views.py
WSGI
WSGI(Python Web Server Gateway Interface)
:为Python语言定义的Web服务器和Web
应用程序或者框架之间的一种简单而通用的接口。
ASGI
ASGI(Asynchronous Web Server Gateway Interface)
:异步网关协议接口,一个介于网络协议服务和Python
应用之间的标准接口,能够处理多种通用的协议类型,包括HTTP
,HTTP2
和WebSocket
。
WSGI
是基于HTTP
协议模式的,不支持WebSocket
,而ASGI
的诞生则是为了解决Python
常用的WSGI
不支持当前Web
开发中的一些新的协议标准。同时,ASGI
对于WSGI
原有的模式的支持和WebSocket
的扩展,即ASGI
是WSGI
的扩展。
Django中使用
1、安装channels
,要注意版本的对应,在channels
官网中可以得到对应的django
版本
pip install channels==2.1.7
2、修改settings.py
文件,
# APPS中添加channels
INSTALLED_APPS = [
'django.contrib.staticfiles',
... ...
'channels',
]
# 指定ASGI的路由地址
ASGI_APPLICATION = 'webapp.routing.application' #ASGI_APPLICATION 指定主路由的位置为webapp下的routing.py文件中的application
3、setting.py
的同级目录下创建routing.py
路由文件,routing.py
类似于Django
中的url.py
指明websocket
协议的路由
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
# 第一种设置的方法
from channels.security.websocket import AllowedHostsOriginValidator
application = ProtocolTypeRouter({
# 普通的HTTP协议在这里不需要写,框架会自己指明
'websocket': AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter(
# 指定去对应应用的routing中去找路由
chat.routing.websocket_urlpatterns
)
),
)
})
# 第二种设置的方法,需要手动指定可以访问的IP
from channels.security.websocket import OriginValidator
application = ProtocolTypeRouter({
# 普通的HTTP协议在这里不需要写,框架会自己指明
'websocket': OriginValidator(
AuthMiddlewareStack(
URLRouter(
# 指定去对应应用的routing中去找路由
chat.routing.websocket_urlpatterns
)
),
# 设置可以访问的IP列表
['*']
)
})
ProtocolTypeRouter:ASIG
支持多种不同的协议,在这里可以指定特定协议的路由信息,我们只使用了websocket
协议,这里只配置websocket
即可
AllowedHostsOriginValidator:指定允许访问的IP
,设置后会去Django
中的settings.py
中去查找ALLOWED_HOSTS
设置的IP
AuthMiddlewareStack:用于WebSocket
认证,继承了Cookie Middleware
,SessionMiddleware,
SessionMiddleware。django
的channels
封装了django
的auth
模块,使用这个配置我们就可以在consumer中通过下边的代码获取到用户的信息
def connect(self):
self.user = self.scope["user"]
self.scope
类似于django中的request,包含了请求的type、path、header、cookie、session、user等等有用的信息
URLRouter: 指定路由文件的路径,也可以直接将路由信息写在这里,代码中配置了路由文件的路径,会去对应应用下的routeing.py
文件中查找websocket_urlpatterns
chat/routing.py
内容如下
from django.urls import path
from chat.consumers import ChatConsumer
websocket_urlpatterns = [
path('ws/chat/', EchoConsumer), # 这里可以定义自己的路由
path('ws/<str:username>/',MessagesConsumer) # 如果是传参的路由在连接中获取关键字参数方法:self.scope['url_route']['kwargs']['username']
]
routing.py
路由文件跟django
的url.py
功能类似,语法也一样,意思就是访问ws/chat/
都交给ChatConsumer
处理。
4、在要使用WebSocket的应用中创建consumers.py
,consumers.py
是用来开发ASGI
接口规范的python
应用,而Django
中的view.py
是用来开发符合WSGI
接口规范的python
应用。
首先了解下面的意思:
event loop
事件循环、event handler
事件处理器、sync
同步、async
异步
下面是一个同步的consumers.py
:
from channels.consumer import SyncConsumer
class EchoConsumer(SyncConsumer):
def websocket_connect(self, event):
self.send({
'type'