WebSocket长连接

     阅读:23

WebSocket长连接

1、概述

image-20211111155015888

image-20211111155202002

1.1 定义

image-20211111160606614

1.2 原理

image-20211111162538141

2、Django中配置WebSocket

2.1安装第三方法包

  • pip install channels
    

2.2 Django 中的配置

  • Settings中的配置
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01.apps.App01Config',
        'channels',#注册APP
    ]
    
  • 添加配置
    • 定位到Django3中的asgi.py下的application
    ASGI_APPLICATION = "djangoWS.asgi.application"
    
    • 修改asgy.py文件
    • 默认只支持http协议,修改其内容使得即支持HTTP又要支持WebSocket;
    import os
    
    from django.core.asgi import get_asgi_application
    from channels.routing import ProtocolTypeRouter,URLRouter
    from . import routing
    
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoWS.settings')
    # 默认只支持http请求
    # application = get_asgi_application()
    
    application=ProtocolTypeRouter({
        "http":get_asgi_application(),
        "websocket":URLRouter(routing.websocket_urlpatterns),
    })
    
    
  • Settings.py的同级目录下,创建routing.py;

    # -*- coding: utf-8 -*-
    '''
    @Time    : 2021/11/12 9:00
    @Author  : ziqingbaojian
    @File    : routing.py
    '''
    
    from django.urls import re_path
    from app01 import consumers
    
    websocket_urlpatterns=[
        re_path(r'ws/(?P<group>\w+)/$',consumers.ShowNum.as_asgi())
    ]
    
  • app01下创建consumers.py,编写处理事务的逻辑。

    # -*- coding: utf-8 -*-
    '''
    @Time    : 2021/11/12 9:11
    @Author  : ziqingbaojian
    @File    : consumers.py
    '''
    from channels.generic.websocket import WebsocketConsumer
    from channels.exceptions import StopConsumer
    
    
    # 继承WebsocketConsumer
    class ShowNum(WebsocketConsumer):
        def websocket_connect(self, message):
            # 有客户端来向后端发送WebSocket连接请求时,自动触发
            # 允许客户端的连接
            self.accept()
            # raise StopConsumer()# 不创建连接,直接使用raise将异常抛出;
    
        def websocket_receive(self, message):
            # 基于WebSocket想后端发送数据,自动触发接收数据
            # 接收到前端传回的消息
            self.send("不要回答,不要回答,不要回答")
    
        def websocket_disconnect(self, message):
            # 客户端与服务端断开连接时,自动触发
            raise StopConsumer()
    

2.3 django中需要了解的

  • wsgi:django3以前django属于同步的,wsgi是处理Socket
  • asgi : 相当于wsgi+异步+WebSocket
  • 普通启动,默认使用的是wsgi**

    image-20211112093454845

  • 基于asgi/channels启动

    image-20211112093744090

3、聊天室

  • 访问到地址界面,http请求

  • 让客户端向服务端主动发送websocket连接,服务端收到连接后(握手)。

    • 前端
    //websocket的协议,发送数据要带ws相当于,http请求以http开头一样;
    socket= new WebSocket("ws://127.0.0.1:8001/ws/123/")
    
    • 服务端
    from channels.generic.websocket import WebsocketConsumer
    from channels.exceptions import StopConsumer
    
    # 继承WebsocketConsumer
    class ShowNum(WebsocketConsumer):
        def websocket_connect(self, message):
            # 有客户端来向后端发送WebSocket连接请求时,自动触发
            # 允许客户端的连接(握手)
            print("连接来拉")
            self.accept()
            '''两次请求,连接一次握手一次'''
            # raise StopConsumer()# 不创建连接,直接使用raise将异常抛出;
    

3.1 收发消息( 客户端–>服务端)

  • 客户端
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            .top{
                height: 300px;
                width: 100%;
                border: 1px solid #ddd;
            }
        </style>
    </head>
    <body>
    <div class="top"></div>
    <input type="text" placeholder="请输入" id="txt">
    <button onclick="sendMessage()">发送</button>
    <script>
        //websocket的协议,发送数据要带ws相当于,http请求以http开头一样;
        socket= new WebSocket("ws://127.0.0.1:8001/ws/123/")
        function sendMessage(){
            var tag=document.getElementById("txt");
            socket.send(tag.value);
        }
    </script>
    </body>
    </html>
    
  • 服务端
    # -*- coding: utf-8 -*-
    '''
    @Time    : 2021/11/12 9:11
    @Author  : ziqingbaojian
    @File    : consumers.py
    '''
    from channels.generic.websocket import WebsocketConsumer
    from channels.exceptions import StopConsumer
    
    # 继承WebsocketConsumer
    class ShowNum(WebsocketConsumer):
    
        def websocket_receive(self, message):
            # 基于WebSocket想后端发送数据,自动触发接收数据
            # 接收到前端传回的消息
            print(message)
            self.send("不要回答,不要回答,不要回答")
    

    image-20211112102841586

3.2 收发消息(服务端–>客户端)

  • 服务端
    from channels.generic.websocket import WebsocketConsumer
    from channels.exceptions import StopConsumer
    
    
    # 继承WebsocketConsumer
    class ShowNum(WebsocketConsumer):
        def websocket_connect(self, message):
            # 有客户端来向后端发送WebSocket连接请求时,自动触发
            # 允许客户端的连接(握手)
            print("连接来拉")
            self.accept()
            '''两次请求,连接一次握手一次'''
            # raise StopConsumer()# 不创建连接,直接使用raise将异常抛出;
            # 服务端给客户端发送消息
            self.send("来了呀,哈哈哈")
    
    
  • 客户端
    <script>
        //websocket的协议,发送数据要带ws相当于,http请求以http开头一样;
        socket= new WebSocket("ws://127.0.0.1:8001/ws/123/")
        function sendMessage(){
            var tag=document.getElementById("txt");
            socket.send(tag.value);
        }
        //当websocket接收到服务端发送来的消息的时候会自动触发这个函数
        socket.onmessage=function (event){
            console.log(event.data)
        }
        
    </script>
    

3.2 前端补充回调函数

<script>
	//创建好连接之后自动触发(服务端执行完(self.accpet()之后会立即执行)
    socket.onopen=function(event){
        let tag= document.createElement("div");
        tag.innerText="连接成功";
        document.getElementById("top").appendChild(tag);
    }
    //连接成功之后进行提示
</script>

3.3 关闭连接

  • 客户端主动关闭
    • 客户端
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            .top{
                height: 300px;
                width: 100%;
                border: 1px solid #ddd;
            }
        </style>
    </head>
    <body>
    <div class="top"></div>
    <input type="text" placeholder="请输入" id="txt">
    <button onclick="sendMessage()">发送</button>
    <button onclick="closeConn()">关闭连接</button>
    
    
    
    <script>
        //websocket的协议,发送数据要带ws相当于,http请求以http开头一样;
        socket= new WebSocket("ws://127.0.0.1:8001/ws/123/")
        function sendMessage(){
            var tag=document.getElementById("txt");
            socket.send(tag.value);
        }
    
        //当websocket接收到服务端发送来的消息的时候会自动触发这个函数
        socket.onmessage=function (event){
            console.log(event.data)
        }
        function closeConn(){
            socket.close();//向服务端发送断开连接的请求
        }
    </script>
    </body>
    </html>
    
    • 服务端
    # -*- coding: utf-8 -*-
    from channels.generic.websocket import WebsocketConsumer
    from channels.exceptions import StopConsumer
    
    # 继承WebsocketConsumer
    class ShowNum(WebsocketConsumer):
    
        def websocket_disconnect(self, message):
            # 客户端与服务端断开连接时,自动触发
            print("断开连接了")
            raise StopConsumer()
    
  • 服务端主动关闭连接
    • 服务端
    from channels.generic.websocket import WebsocketConsumer
    from channels.exceptions import StopConsumer
    
    
    # 继承WebsocketConsumer
    class ShowNum(WebsocketConsumer):
    
    
        def websocket_receive(self, message):
            # 基于WebSocket想后端发送数据,自动触发接收数据
            # 接收到前端传回的消息
            print(message)
            text=message['text']
            print(text)
            self.send("不要回答,不要回答,不要回答")
            if text=="close":
                # 法一
                self.close()# 会触发前端的onCllose方法;
                return # 不在执行后期的代码
                # 法二
                # raise StopConsumer()# 如果服务端断开连接时,执行了StopConsumer异常,那么websocket_disconnect方法不在执行;
     
    
    • 客户端
    <script>
        //websocket的协议,发送数据要带ws相当于,http请求以http开头一样;
        socket= new WebSocket("ws://127.0.0.1:8001/ws/123/")
        function sendMessage(){
            var tag=document.getElementById("txt");
            socket.send(tag.value);
        }
        //服务端主动断开连接的时候会被触发
        socket.onclose=function (event){
            console.log("连接已断开")
        }
    </script>
    

3.4 整合代码示例

  • 服务端
    from channels.generic.websocket import WebsocketConsumer
    from channels.exceptions import StopConsumer
    
    # 继承WebsocketConsumer
    class ShowNum(WebsocketConsumer):
        def websocket_connect(self, message):
            # 有客户端来向后端发送WebSocket连接请求时,自动触发
            # 允许客户端的连接(握手)
            print("连接来拉")
            self.accept()
            '''两次请求,连接一次握手一次'''
            # raise StopConsumer()# 不创建连接,直接使用raise将异常抛出;
            # 服务端给客户端发送消息
            self.send("来了呀,哈哈哈")
    
        def websocket_receive(self, message):
            # 基于WebSocket想后端发送数据,自动触发接收数据
            # 接收到前端传回的消息
            print(message)
            text=message['text']
            print(text)
            self.send("不要回答,不要回答,不要回答")
            if text=="close":
                # 法一
                self.close()# 会触发前端的onCllose方法;
                return # 不在执行后期的代码
                # 法二
                # raise StopConsumer()# 如果服务端断开连接时,执行了StopConsumer异常,那么websocket_disconnect方法不在执行;
        def websocket_disconnect(self, message):
            # 客户端与服务端断开连接时,自动触发,包括关闭页面与浏览器的情况
            print("断开连接了")
            raise StopConsumer()
    
  • 客户端
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            .top{
                height: 300px;
                width: 100%;
                border: 1px solid #ddd;
            }
        </style>
    </head>
    <body>
    <div class="top"></div>
    <input type="text" placeholder="请输入" id="txt">
    <button onclick="sendMessage()">发送</button>
    <button onclick="closeConn()">关闭连接</button>
    <script>
        //websocket的协议,发送数据要带ws相当于,http请求以http开头一样;
        socket= new WebSocket("ws://127.0.0.1:8001/ws/123/")
        function sendMessage(){
            var tag=document.getElementById("txt");
            socket.send(tag.value);
        }
        //服务端主动断开连接的时候会被触发
        socket.onclose=function (event){
            console.log("连接已断开")
        }
    
        //当websocket接收到服务端发送来的消息的时候会自动触发这个函数
        socket.onmessage=function (event){
            console.log(event.data)
        }
        function closeConn(){
            socket.close();//向服务端发送断开连的请求
        }
    </script>
    </body>
    </html>
    

3.5 群聊功能

  • 配置django中的配置文件或者,自定义设置连接池

4 、vue中使用Webscoket

  • 参考文献1:https://www.cnblogs.com/niuben/p/14607900.html

  • 参考文献2 :https://www.cnblogs.com/qisi007/p/10213886.html

  • 方法一
    <template>
      <div class="test">
    
      </div>
    </template>
    
    <script>
      export default {
        name : 'test',
        data() {
          return {
            websock: null,
          }
        },
        created() {
          this.initWebSocket();
        },
        destroyed() {
          this.websock.close() //离开路由之后断开websocket连接
        },
        methods: {
          initWebSocket(){ //初始化weosocket
            if()
            const wsuri = "ws://127.0.0.1:8080";
            this.websock = new WebSocket(wsuri);
            this.websock.onmessage = this.websocketonmessage;
            this.websock.onopen = this.websocketonopen;
            this.websock.onerror = this.websocketonerror;
            this.websock.onclose = this.websocketclose;
          },
          websocketonopen(){ //连接建立之后执行send方法发送数据
            let actions = {"test":"12345"};
            this.websocketsend(JSON.stringify(actions));
          },
          websocketonerror(){//连接建立失败重连
            this.initWebSocket();
          },
          websocketonmessage(e){ //数据接收
            const redata = JSON.parse(e.data);
          },
          websocketsend(Data){//数据发送
            this.websock.send(Data);
          },
          websocketclose(e){  //关闭
            console.log('断开连接',e);
          },
        },
      }
    </script>
    <style lang='less'>
     
    </style>
    
    
  • 方法二
    <template>
        <div>
            <button @click="send">发消息</button>
        </div>
    </template>
    
    <script>
    export default {
        data () {
            return {
                path:"ws://192.168.0.200:8005/qrCodePage/ID=1/refreshTime=5",
                socket:""
            }
        },
        mounted () {
            // 初始化
            this.init()
        },
        methods: {
            init: function () {
                if(typeof(WebSocket) === "undefined"){
                    alert("您的浏览器不支持socket")
                }else{
                    // 实例化socket
                    this.socket = new WebSocket(this.path)
                    // 监听socket连接
                    this.socket.onopen = this.open
                    // 监听socket错误信息
                    this.socket.onerror = this.error
                    // 监听socket消息
                    this.socket.onmessage = this.getMessage
                }
            },
            open: function () {
                console.log("socket连接成功")
            },
            error: function () {
                console.log("连接错误")
            },
            getMessage: function (msg) {
                console.log(msg.data)
            },
            send: function () {
                this.socket.send(params)
            },
            close: function () {
                console.log("socket已经关闭")
            }
        },
        destroyed () {
            // 销毁监听
            this.socket.onclose = this.close
        }
    }
    </script>
    
    <style>
    
    </style>