python websocket django vue_WebSocket介绍与WebSocket在Django中的实现

关于WebSocket:

WebSocket 协议在2008年诞生,2011年成为国际标准。现在所有浏览器都已经支持了。

WebSocket 的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。

1. webSocket是一种在单个TCP连接上进行全双工通信的协议

2. 客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。

3. 浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

远古时期解决方案就是轮训:客户端以设定的时间间隔周期性地向服务端发送请求,频繁地查询是否有新的数据改动(浪费流量和资源)

WebSocket 的其他特点:

建立在 TCP 协议之上,服务器端的实现比较容易。

与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

数据格式比较轻量,性能开销小,通信高效。

可以发送文本,也可以发送二进制数据。

没有同源限制,客户端可以与任意服务器通信。

协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

WebSocket使用场景:

1. 聊天软件:微信,QQ,这一类社交聊天的app

2. 弹幕:各种直播的弹幕窗口

3.在线教育:可以视频聊天、即时聊天以及其与别人合作一起在网上讨论问题…

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

Channels安装如果你是Windows操作系统的话,那么必要条件就是Python3.7

pip install channels

2.配置DjangoChannels

1.创建项目 ChannelsReady

django-admin startprobject ChannelsReady

2.在项目的settings.py同级目录中,新建文件routing.py

1 #routing.py

2 from channels.routing importProtocolTypeRouter3 ​4 application =ProtocolTypeRouter({5 #暂时为空

6 })

3.在项目配置文件settings.py中写入

1 INSTALLED_APPS =[2 'channels'

3 ]4 ​5 ASGI_APPLICATION = "ChannelsReady.routing.application"

3.启动带有Channels提供的ASGI的Django项目

出现以下情况:

Django version 3.0.2, using settings 'ChannelsReady.settings'Starting ASGI/Channels version 2.4.0 development server at http://0.0.0.0:8000/Quit the server with CTRL-BREAK.

很明显可以看到 ASGI/Channels, 这样就算启动完成了

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

1 from channels.generic.websocket importWebsocketConsumer2 #这里除了 WebsocketConsumer 之外还有

3 #JsonWebsocketConsumer

4 #AsyncWebsocketConsumer

5 #AsyncJsonWebsocketConsumer

6 #WebsocketConsumer 与 JsonWebsocketConsumer 就是多了一个可以自动处理JSON的方法

7 #AsyncWebsocketConsumer 与 AsyncJsonWebsocketConsumer 也是多了一个JSON的方法

8 #AsyncWebsocketConsumer 与 WebsocketConsumer 才是重点

9 #看名称似乎理解并不难 Async 无非就是异步带有 async / await

10 #是的理解并没有错,但对与我们来说他们唯一不一样的地方,可能就是名字的长短了,用法是一模一样的

11 #最夸张的是,基类是同一个,而且这个基类的方法也是Async异步的

12

13 classChatService(WebsocketConsumer):14 #当Websocket创建连接时

15 defconnect(self):16 pass

17

18 #当Websocket接收到消息时

19 def receive(self, text_data=None, bytes_data=None):20 pass

21

22 #当Websocket发生断开连接时

23 defdisconnect(self, code):24 pass

5.为Websocket处理对象增加路由

1.在chats应用中,新建urls.py

1 from django.urls importpath2 from chats.chatService importChatService3 websocket_url =[4 path("ws/",ChatService)5 ]

2.回到项目routing.py文件中增加ASGI非HTTP请求处理

1 from channels.routing importProtocolTypeRouter,URLRouter2 from chats.urls importwebsocket_url3 ​4 application =ProtocolTypeRouter({5 "websocket":URLRouter(6 websocket_url7 )8 })

websocket客户端:

1.基于vue的websocket客户端

1

2

3

4

5

6

7

8 ​9 ​10

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

基于vue websocket客户端实现

------------------------>

广播消息:

客户端保持不变,同时打开多个客户端

服务端存储每个链接的对象

1 socket_list =[]2 ​3 classChatService(WebsocketConsumer):4 #当Websocket创建连接时

5 defconnect(self):6 self.accept() #保持状态

7 socket_list.append(self)8 ​9 #当Websocket接收到消息时

10 def receive(self, text_data=None, bytes_data=None):11 print(text_data) #打印收到的数据

12 for ws in socket_list: #遍历所有的WebsocketConsumer对象

13 ws.send(text_data) #对每一个WebsocketConsumer对象发送数据

14 ​

点对点消息:

客户端将用户名拼接到url,并在发送的消息里指明要发送的对象

1

2

3

4

5

6

7

8

9

10

11

12

13 export default{14 name:'websocket1',15 data() {16 return{17 message:'',18 testsocket:'',19 user:''

20 }21 },22 methods:{23 send(){24

25 //send 发送信息

26 //close 关闭连接

27 var data1 = {"message":this.message,"to_user":this.user}28

29 this.testsocket.send(JSON.stringify(data1))30 this.testsocket.onmessage = (res) =>{31 console.log("WS的返回结果",res.data);32 }33

34 },35 close_socket(){36 this.testsocket.close()37 },38 generate_uuid: function() {39 var d = newDate().getTime();40 if (window.performance && typeof window.performance.now === "function") {41 d += performance.now(); //use high-precision timer if available

42 }43 var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(44 /[xy]/g,45 function(c) {46 var r = (d + Math.random() * 16) % 16 | 0;47 d = Math.floor(d / 16);48 return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);49 }50 );51 returnuuid;52 },53

54 },55 mounted(){56 var username = this.generate_uuid();57 console.log(username)58 this.testsocket = new WebSocket("ws://127.0.0.1:8000/ws/"+ username +"/")59 console.log(this.testsocket)60

61 this.testsocket.onmessage = (res) =>{62 console.log("WS的返回结果",res.data);63 }64

65 //onopen 定义打开时的函数

66 //onclose 定义关闭时的函数

67 //onmessage 定义接收数据时候的函数

68 //this.testsocket.onopen = function(){

69 //console.log("开始连接socket")

70 //},

71 //this.testsocket.onclose = function(){

72 //console.log("socket连接已经关闭")

73 //}

74 }75 }76

基于vue 点对点消息客户端

服务端存储用户名以及websocketConsumer,然后给对应的用户发送信息

1 from channels.generic.websocket importWebsocketConsumer2 user_dict ={}3 list =[]4 importjson5 classChatService(WebsocketConsumer):6 #当Websocket创建连接时

7 defconnect(self):8 self.accept()9 username = self.scope.get("url_route").get("kwargs").get("username")10 user_dict[username] =self11 print(user_dict)12

13 #list.append(self)

14

15

16 #当Websocket接收到消息时

17 def receive(self, text_data=None, bytes_data=None):18 data =json.loads(text_data)19 print(data)20 to_user = data.get("to_user")21 message = data.get("message")22

23 ws =user_dict.get(to_user)24 print(to_user)25 print(message)26 print(ws)27 ws.send(text_data)28

29

30 #当Websocket发生断开连接时

31 defdisconnect(self, code):32 pass

服务端

你可能感兴趣的:(python,websocket,django,vue)