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
  • 相关阅读:
    迅为RK3399开发板嵌入式linux开发指南
    迅为iMX8M Mini开发板NXP i.MX8系列ARM cortex A53 M4核心板
    谱聚类(上篇)
    html 新增标签
    前段文件上传
    vue设置cookie和获取cookie
    vue 中使用element ui 回显问题
    vue实现表格自建与表格内容填写
    Delphi Datasnap Post请求
    Nginx SSL 配置https
  • 原文地址:https://www.cnblogs.com/hls91/p/14885945.html
Copyright © 2011-2022 走看看