zoukankan      html  css  js  c++  java
  • WebSocket介绍与WebSocket在Django3中的实现

    关于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:

    大概流程:

    1. 下载

    2. 注册到setting.py里的app

    3. 在setting.py同级的目录下注册channels使用的路由----->routing.py

    4. 将routing.py注册到setting.py

    5. 把urls.py的路由注册到routing.py里

    6. 编写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 import ProtocolTypeRouter
    3 ​
    4 application = ProtocolTypeRouter({
    5     # 暂时为空
    6 })
     

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

    1 INSTALLED_APPS = [
    2     'channels'
    3 ]
    4 ​
    5 ASGI_APPLICATION = "ChannelsReady.routing.application"

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

    出现以下情况:
    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 import WebsocketConsumer
     2 # 这里除了 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 class ChatService(WebsocketConsumer):
    14     # 当Websocket创建连接时
    15     def connect(self):
    16         pass
    17     
    18     # 当Websocket接收到消息时
    19     def receive(self, text_data=None, bytes_data=None):
    20         pass
    21     
    22     # 当Websocket发生断开连接时
    23     def disconnect(self, code):
    24         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文件中增加ASGIHTTP请求处理

     
    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客户端:

    1.基于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 关闭连接
    2425             this.testsocket.send(this.message)
    26             this.testsocket.onmessage = (res) => {
    27                 console.log("WS的返回结果",res.data);         
    28             }
    2930         },
    31         close_socket(){
    32             this.testsocket.close()
    33         }
    3435     },
    36     mounted(){
    37         this.testsocket = new WebSocket("ws://127.0.0.1:8000/ws/") 
    383940         // 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>
    
    基于vue websocket客户端实现

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

    广播消息:

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

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

     1 socket_list = []
     2 ​
     3 class ChatService(WebsocketConsumer):
     4     # 当Websocket创建连接时
     5     def connect(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 <template>
     2     <div>
     3         <input type="text" v-model="message">
     4         <input type="text" v-model="user">
     5 
     6         <p><input type="button" @click="send" value="发送"></p>
     7         <p><input type="button" @click="close_socket" value="关闭"></p>
     8     </div>
     9 </template>
    10 
    11 
    12 <script>
    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 = new Date().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             return uuid;
    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 </script>

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

     1 from channels.generic.websocket import WebsocketConsumer
     2 user_dict ={}
     3 list = []
     4 import json
     5 class ChatService(WebsocketConsumer):
     6     # 当Websocket创建连接时
     7     def connect(self):
     8         self.accept()
     9         username = self.scope.get("url_route").get("kwargs").get("username")
    10         user_dict[username] =self
    11         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     def disconnect(self, code):
    32         pass
  • 相关阅读:
    ASP.NET MVC案例——————拦截器
    Windows Azure Virtual Network (10) 使用Azure Access Control List(ACL)设置客户端访问权限
    Windows Azure Storage (20) 使用Azure File实现共享文件夹
    Windows Azure HandBook (5) Azure混合云解决方案
    Windows Azure Service Bus (6) 中继(Relay On) 使用VS2013开发Service Bus Relay On
    Azure PowerShell (9) 使用PowerShell导出订阅下所有的Azure VM的Public IP和Private IP
    Windows Azure Service Bus (5) 主题(Topic) 使用VS2013开发Service Bus Topic
    Azure China (9) 在Azure China配置CDN服务
    Windows Azure Storage (19) 再谈Azure Block Blob和Page Blob
    Windows Azure HandBook (4) 分析Windows Azure如何处理Session
  • 原文地址:https://www.cnblogs.com/hls91/p/14885945.html
Copyright © 2011-2022 走看看