• 轮询、长轮询和websocket


    一、轮询

    在一些需要进行实时查询的场景下应用
    比如投票系统:
      大家一起在一个页面上投票
      在不刷新页面的情况下,实时查看投票结果

    1、后端代码

    复制代码
    from flask import Flask, render_template, request, jsonify
    
    
    app = Flask(__name__)
    
    USERS = {
        1: {'name': '小米', 'count': 300},
        2: {'name': '小康', 'count': 200},
        3: {'name': '小明', 'count': 600},
    }
    
    
    @app.route('/')
    def index():
        return render_template('Poll.html', users=USERS)
    
    
    @app.route('/vote', methods=['POST'])
    def vote():
        # 接收uid,通过uid给票数 +1
        # 用户提交Json数据过来,用request.json获取
        uid = request.json.get('uid')
        USERS[uid]['count'] += 1
        return "投票成功"
    
    
    @app.route('/get_vote')
    def get_vote():
        # 返回users数据
        # jsonify 是flask自带的序列化器
        return jsonify(USERS)
    
    
    if __name__ == '__main__':
        app.run()
    复制代码

    2、前端代码

    复制代码
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>投票系统</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
        <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>
    </head>
    <style>
        .my-li {
            list-style: none;
            margin-bottom: 20px;
            font-size: 18px;
        }
    </style>
    <body>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <h1>最帅男人投票</h1>
                {% for (uid, user) in users.items() %}
                    <button class="btn btn-success" onclick="vote({{ uid }})">投票</button>
                    <li class="list-group-item-info my-li" id="{{ uid }}">{{ user.name }}目前的票数是: {{ user.count }}</li>
                {% endfor %}
            </div>
        </div>
    </div>
    
    <script>
        // 投票
        function vote(uid) {
            // 向后端发送投票请求
            axios.request({
                url: '/vote',
                method: 'post',
                data: {
                    uid: uid
                }
            }).then(function (response) {
                console.log(response.data);
            })
        }
    
        // 获取最新的投票结果
        function get_vote() {
            axios.request({
                url: '/get_vote',
                method: 'get'
            }).then(function (response) {
                // 获取后端传过来的新数据
                // 重新渲染页面
                let users = response.data;
                for (let uid in users) {
                    //根据uid获取li标签 改变innerText
                    let liEle = document.getElementById(uid);
                    liEle.innerText = `${users[uid]['name']}目前的票数是: ${users[uid]['count']}`
                }
            });
        }
    
        // 页面加载完后,立刻获取数据
        window.onload = function () {
            setInterval(get_vote, 2000)
        }
    
    </script>
    
    </body>
    </html>
    复制代码

    3、轮询

    特点:每隔一段时间不断向后端发送请求
    缺点:消耗大 有延迟

    二、长轮询

    由于上面的轮询是不能实时查看到投票情况的,存在一定的延迟性
    长轮询可以实现实时查看投票情况

    1、后端代码

    复制代码
    from flask import Flask, render_template, request, jsonify, session
    import uuid
    import queue
    
    
    app = Flask(__name__)
    app.secret_key = '切克闹'
    
    
    USERS = {
        1: {'name': '小米', 'count': 300},
        2: {'name': '小康', 'count': 200},
        3: {'name': '小明', 'count': 600},
    }
    
    
    Q_DICT = {
        # uid: q对象
    }
    
    
    @app.route('/')
    def index():
        # 模拟用户登录
        # 模拟用户登录后的唯一id
        user_id = str(uuid.uuid4())
        # 每个用户都有自己的Q对象
        Q_DICT[user_id] = queue.Queue()
        # 把用户的id存到session
        session['user_id'] = user_id
        # 页面展示投票的人的信息
        return render_template('longPoll.html', users=USERS)
    
    
    @app.route('/vote', methods=['POST'])
    def vote():
        # 处理投票,给票数 +1
        # 用户提交Json数据过来,用request.json获取
        uid = request.json.get('uid')
        USERS[uid]['count'] += 1
        # 投票成功后,给每个用户的Q对象put最新的值进去
        for q in Q_DICT.values():
            q.put(USERS)
        return "投票成功"
    
    
    @app.route('/get_vote')
    def get_vote():
        # 请求进来,从session获取用户的id
        user_id = session.get('user_id')
        # 根据用户的id 获取用户的Q对象
        q = Q_DICT.get(user_id)
        # try:
        #     ret = q.get(timeout=30)
        # except queue.Empty:
        #     ret = ''
        ret = q.get()
        return jsonify(ret)
    
    
    if __name__ == '__main__':
        app.run()
    复制代码

    2、前端代码

    复制代码
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>投票系统</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
        <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>
    </head>
    <style>
        .my-li {
            list-style: none;
            margin-bottom: 20px;
            font-size: 18px;
        }
    </style>
    <body>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <h1>最晒男人投票</h1>
                {% for (uid, user) in users.items() %}
                    <button class="btn btn-success" onclick="vote({{ uid }})">投票</button>
                    <li class="list-group-item-info my-li" id="{{ uid }}">{{ user.name }}目前的票数是: {{ user.count }}</li>
                {% endfor %}
            </div>
        </div>
    </div>
    
    <script>
        // 投票
        function vote(uid) {
            // 向后端发送投票请求
            axios.request({
                url: '/vote',
                method: 'post',
                data: {
                    uid: uid
                }
            }).then(function (response) {
                console.log(response.data);
            })
        }
    
        // 获取最新的投票结果
        function get_vote() {
            axios.request({
                url: '/get_vote',
                method: 'get'
            }).then(function (response) {
                // 判断后端的数据是否为空
                if (response.data != '') {
                    // 获取到最新的数据
                    let users = response.data;
                    for (uid in users) {
                        // 根据uid找到每个li标签
                        let liEle = document.getElementById(uid);
                        // 给每个li标签设置最新的数据
                        liEle.innerText = `${users[uid]['name']}目前的票数是: ${users[uid]['count']}`
                    }
                }
                // 获取完数据后,再发送请求,看还有没有人投票,有的话再去获取最新的数据
                get_vote()
            });
        }
    
        // 页面加载完后,立刻获取数据
        window.onload = function () {
            get_vote()
        }
    
    </script>
    
    </body>
    </html>
    复制代码

    3、长轮询

    特点:满足实时更新
    缺点:消耗大
    实现:
      利用queue对象实现请求夯住
      每个请求进来都要生成一个q对象
      如果有人投票 给所有的q对象put数据
      拿数据请求从自己的q对象get数据

    三、websocket

    1、介绍

    http协议
      短连接 无状态 基于TCP/UDP协议进行传输数据(TCP/UDP: 传输协议)

    socket
      socket不是传输协议 跟websocket是两个完全不一样的东西 socket是套接字 API接口

    websocket
      H5出的新协议 浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
      解决轮询问题
      特点:
        1. 握手 基于HTTP进行握手(因此websocket与Http有一定的交集,但不是同一个东西)
        2. 发送数据加密
        3. 保持连接不断开

    2、在Flask中使用websocket

    复制代码
    1. Flask没有websocket,需要安装包  pip install gevent-websocket
    
    
    2. 后端怎样建立一个支持websocket协议连接
    from geventwebsocket.handler import WebSocketHandler
    from gevent.pywsgi import WSGIServer
    
    # 拿到websocket对象
    ws = request.environ.get("wsgi.websocket")
    # 后端发送数据
    ws.send(xxx)
    # 后端接收数据
    ws.receive()
    
    if __name__ == '__main__':
        # app.run()
        # 即支持HTTP 也支持websocket
        http_server = WSGIServer(('0.0.0.0', 5000), app, handler_class=WebSocketHandler)
        http_server.serve_forever()
    
    
    3. 前端怎么发起websocket连接
    let ws = new WebSocket("ws://127.0.0.1:5000")
    # 前端发送数据
    ws.send("xxx")
    # 前端接收数据
    ws.onmessage = function(event){
        # 从接受的对象中获取数据       
        let data = event.data
    }
    
    
    4. 收发消息
    1, 前端发送数据给后端
        前端发送:ws.send('数据')
        后端接收:ws.receive()
        
    
    2, 后端发送数据给前端
        后端发送:ws.send('数据')
        前端接收的是对象:ws.onmessage = function(event){
            let data = event.data
        }
        
    复制代码

    3、Demo

    1.后端
    复制代码
    from flask import Flask, render_template, request
    from geventwebsocket.handler import WebSocketHandler
    from gevent.pywsgi import WSGIServer
    import json
    
    
    app = Flask(__name__)
    
    USERS = {
        1: {'name': '小米', 'count': 300},
        2: {'name': '小康', 'count': 200},
        3: {'name': '小明', 'count': 600},
    }
    
    WEBSOCKET_LIST = []
    
    
    @app.route('/')
    def index():
        return render_template('websocket.html', users=USERS)
    
    
    @app.route('/vote')
    def vote():
        # 处理websocket
        # 判断是什么类型的请求,HTTP还是websocket
        # 看能否获取得到websocket的对象
        ws = request.environ.get("wsgi.websocket")
        if not ws:
            return "这是HTTP协议的请求"
        # 把所有用户的ws对象存到一个列表
        WEBSOCKET_LIST.append(ws)
        while True:
            # 获取前端传过来的uid,给打野票数 +1
            uid = ws.receive()
            # 如果前端主动断开连接
            # 那么后端也关闭与前端的连接
            if not uid:
                WEBSOCKET_LIST.remove(ws)
                ws.close()
                break
            uid = int(uid)
            USERS[uid]["count"] += 1
            data = {
                "uid": uid,
                "name": USERS[uid]["name"],
                "count": USERS[uid]["count"]
            }
            for ws in WEBSOCKET_LIST:
                # 给前端发送新的数据
                ws.send(json.dumps(data))
    
    
    if __name__ == '__main__':
        # app.run()
        # 这样启服务的意思是:即支持HTTP协议,也支持websocket协议
        http_server = WSGIServer(('127.0.0.1', 5000), app, handler_class=WebSocketHandler)
        http_server.serve_forever()
    复制代码
    2.前端
    复制代码
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>投票系统</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
        <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>
    </head>
    <style>
        .my-li {
            list-style: none;
            margin-bottom: 20px;
            font-size: 18px;
        }
    </style>
    <body>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <h1>最晒男人投票</h1>
                {% for (uid, user) in users.items() %}
                    <button class="btn btn-success" onclick="vote({{ uid }})">投票</button>
                    <li class="list-group-item-info my-li" id="{{ uid }}">{{ user.name }}目前的票数是: {{ user.count }}</li>
                {% endfor %}
            </div>
        </div>
    </div>
    
    <script>
        // 向后端发送一个websocket连接请求
        let ws = new WebSocket('ws://127.0.0.1:5000/vote');
        function vote(uid) {
            // 向后端发数据
            ws.send(uid)
        }
        ws.onmessage = function (event) {
            let data = JSON.parse(event.data);
            let liEle = document.getElementById(data.uid);
            liEle.innerText = `${data.name}目前的票数是: ${data.count}`
        }
    </script>
    
    </body>
    </html>
    复制代码
  • 相关阅读:
    更改桌面位置
    三国杀高级技巧!!
    excel中删除空白行方法
    欢迎来稿
    coreseek配置文件分析
    html中a连接触发表单提交
    html点击按钮动态添加input文本框
    页面表单预览数据传递注意事项
    mysql 主从复制(masterslave)
    mysql explain key_len小结
  • 原文地址:https://www.cnblogs.com/yidashi110/p/10222954.html
Copyright © 2020-2023  润新知