关于WebSocket:
WebSocket 协议在2008年诞生,2011年成为国际标准。现在所有浏览器都已经支持了。
WebSocket 的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。
-
webSocket是一种在单个TCP连接上进行全双工通信的协议
-
客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
-
浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
远古时期解决方案就是轮询:客户端以设定的时间间隔周期性地向服务端发送请求,频繁地查询是否有新的数据改动(浪费流量和资源)
WebSocket 的其他特点:
-
建立在 TCP 协议之上,服务器端的实现比较容易。
-
与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
-
数据格式比较轻量,性能开销小,通信高效。
-
可以发送文本,也可以发送二进制数据。
-
没有同源限制,客户端可以与任意服务器通信。
-
协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
WebSocket使用场景:
-
聊天软件:微信,QQ,这一类社交聊天的app
-
弹幕:各种直播的弹幕窗口
-
在线教育:可以视频聊天、即时聊天以及其与别人合作一起在网上讨论问题…
WebSocket与HTTP:
相对于 HTTP 这种非持久的协议来说,WebSocket 是一个持久化的协议。
HTTP 的生命周期通过 Request 来界定,也就是一个 Request 一个 Response ,那么在 HTTP1.0 中,这次 HTTP 请求就结束了。
在 HTTP1.1 中进行了改进,有一个 keep-alive,在一个 HTTP 连接中,可以发送多个 Request,接收多个 Response。
但是请记住 Request = Response, 在 HTTP 中永远是这样,也就是说一个 Request 只能有一个 Response。而且这个 Response 也是被动的,不能主动发起
短连接型:
基于HTTP
短连接如何保障数据的即时性
HTTP
的特性就是无状态的短连接,当地小有名气的健忘鬼 即一次请求一次响应断开连接失忆 ,这样服务端就无法主动的去寻找客户端给客户端主动推送消息
1.轮询
即: 客户端不断向服务器发起请求索取消息
优点: 基本保障消息即时性
缺点: 大量的请求导致客户端和服务端的压力倍增
客户端:有没有新消息呀?(Request)
服务端:emmm 没有(Response)
客户端:嘿 现在有没有新信息嘞?(Request)
服务端:没有。。。(Response)
客户端:啦啦啦,有没有新信息?(Request)
服务端:没有啊 你有点烦哎(Response)
客户端:那现在有没有新消息?(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端:有没有新消息呀?(Request)
服务端:没有哦。。。(Response)
2.长轮询
即: 客户端向服务器发起请求,在HTTP
最大超时时间内不断开请求获取消息,超时后重新发起请求
优点: 基本保障消息即时性
缺点: 长期占用客户端独立线程,长期占用服务端独立线程(消耗大量线程),服务器压力倍增
客户端:喂 有新的信息吗(Request)
服务端:emmm 没有 等有了就给你!(Response)
客户端:这样啊 那我很闲 我等着(Request)
从上面可以看出这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,体现HTTP协议的被动性。这样非常消耗资源
轮询 需要服务器有很快的处理速度和资源。长轮询 需要有很高的并发。
长连接型:
基于socket
长连接,由于长连接是双向且有状态的保持连接,所以服务端可以有效的主动的向客户端推送数据
1.socketio
长连接协议
优点:消息即时,兼容性强
缺点:接入复杂度高,为保障兼容性冗余依赖过多较重
2.websocket
长连接协议
优点:消息即时,轻量级,灵活适应多场景,机制更加成熟
缺点:相比socket
兼容性较差
客户端:喂 有新的信息吗
服务端:emmm 没有 等有了就给你!
客户端:那麻烦你了!
服务端:没事哦
服务端:来啦来啦 有新的消息
服务端:神奇宝贝不神奇了是什么?
客户端:收到 宝贝
客户端:嘻嘻嘻
经过一次 HTTP 请求,就可以源源不断信息传送!
总体来说,Socketio
紧紧只是为了解决通讯而存在的,而Websocket
是为了解决更多更复杂的场景通讯而存在的
这里推荐Websocket
的原因是因为,我们的Django
框架甚至是Flask
框架,都有成熟的第三方库
而且Tornado
框架集成Websocket。
Django实现WebSocket:
大概流程:
- 下载
- 注册到setting.py里的app
- 在setting.py同级的目录下注册channels使用的路由----->routing.py
- 将routing.py注册到setting.py
- 把urls.py的路由注册到routing.py里
- 编写wsserver.py来处理websocket请求
使用Django
来实现Websocket
服务的方法很多在这里我们推荐技术最新的**Channels
库**来实现
1.安装DjangoChannels
pip install channels==2.3
注意:不是所有的服务端都支持websocket
django默认不支持,通过第三方模块channles来实现
2.配置DjangoChannels
1.需要在配置文件中注册channles应用
INSTALLED_APPS = [
# 1.需要先注册channels
'channels'
]
2.在setting文件同一文件夹下创建routing.py文件,文件内写一下内容
from channels.routing import ProtocolTypeRouter,URLRouter
application = ProtocolTypeRouter({
'websocket':URLRouter([
# 路由与视图函数对应关系
])
})
3.还需要在配置文件中配置以下参数
ASGI_APPLICATION = 'demo.routing.application'
# demo 项目文件夹名称
3.启动带有Channels提供的ASGI的Django项目
注意配置完成后,django就会即支持http协议也支持websocket协议
4.创建Websocket服务
1.创建一个新的应用chats
python manage.py startapp chats
2.在settings.py
中注册chats
1 INSTALLED_APPS = [
2 'chats',
3 'channels'
4 ]
3.在chats
应用中新建文件chatService.py
from channels.generic.websocket import WebsocketConsumer
class ChatService(WebsocketConsumer):
# 当Websocket创建连接时
def connect(self):
pass
# 当Websocket接收到消息时
def receive(self, text_data=None, bytes_data=None):
pass
# 当Websocket发生断开连接时
def disconnect(self, code):
pass
5.为Websocket处理对象增加路由
1.在chats
应用中,新建urls.py
1 from django.urls import path
2 from chats.chatService import ChatService
3 websocket_url = [
4 path("ws/",ChatService)
5 ]
2.回到项目routing.py
文件中增加ASGI
非HTTP
请求处理
1 from channels.routing import ProtocolTypeRouter,URLRouter
2 from chats.urls import websocket_url
3
4 application = ProtocolTypeRouter({
5 "websocket":URLRouter(
6 websocket_url
7 )
8 })
websocket客户端:
基于vue的websocket客户端
1 <template>
2 <div>
3 <input type="text" v-model="message">
4 <p><input type="button" @click="send" value="发送"></p>
5 <p><input type="button" @click="close_socket" value="关闭"></p>
6 </div>
7 </template>
8
9
10 <script>
11 export default {
12 name:'websocket1',
13 data() {
14 return {
15 message:'',
16 testsocket:''
17 }
18 },
19 methods:{
20 send(){
21
22 // send 发送信息
23 // close 关闭连接
24
25 this.testsocket.send(this.message)
26 this.testsocket.onmessage = (res) => {
27 console.log("WS的返回结果",res.data);
28 }
29
30 },
31 close_socket(){
32 this.testsocket.close()
33 }
34
35 },
36 mounted(){
37 this.testsocket = new WebSocket("ws://127.0.0.1:8000/ws/")
38
39
40 // onopen 定义打开时的函数
41 // onclose 定义关闭时的函数
42 // onmessage 定义接收数据时候的函数
43 // this.testsocket.onopen = function(){
44 // console.log("开始连接socket")
45 // },
46 // this.testsocket.onclose = function(){
47 // console.log("socket连接已经关闭")
48 // }
49 }
50 }
51 </script>
广播消息:
客户端保持不变,同时打开多个客户端
服务端存储每个链接的对象
socket_list = []
class ChatService(WebsocketConsumer):
# 当Websocket创建连接时
def connect(self):
self.accept() # 保持状态
socket_list.append(self)
# 当Websocket接收到消息时
def receive(self, text_data=None, bytes_data=None):
print(text_data) # 打印收到的数据
for ws in socket_list: # 遍历所有的WebsocketConsumer对象
ws.send(text_data) # 对每一个WebsocketConsumer对象发送数据
点对点消息:
客户端将用户名拼接到url,并在发送的消息里指明要发送的对象
t() # 保持状态
socket_list.append(self)
# 当Websocket接收到消息时
def receive(self, text_data=None, bytes_data=None):
print(text_data) # 打印收到的数据
for ws in socket_list: # 遍历所有的WebsocketConsumer对象
ws.send(text_data) # 对每一个WebsocketConsumer对象发送数据
#### 点对点消息:
客户端将用户名拼接到url,并在发送的消息里指明要发送的对象
服务端存储用户名以及websocketConsumer,然后给对应的用户发送信息