轮询、长轮询和websocket

一、轮询

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

1、后端代码

from flask import Flask, render_template, request, jsonify

app = Flask(__name__)

USERS = {
    1: {‘name‘: ‘明凯‘, ‘count‘: 300},
    2: {‘name‘: ‘厂长‘, ‘count‘: 200},
    3: {‘name‘: ‘7酱‘, ‘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/[email protected]/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>LPL第一打野投票</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‘: ‘7酱‘, ‘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/[email protected]/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>LPL第一打野投票</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‘: ‘7酱‘, ‘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/[email protected]/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>LPL第一打野投票</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>

原文地址:https://www.cnblogs.com/Zzbj/p/10222887.html

时间: 2024-07-29 15:54:10

轮询、长轮询和websocket的相关文章

Apollo 3 定时/长轮询拉取配置的设计

前言 如上图所示,Apollo portal 更新配置后,进行轮询的客户端获取更新通知,然后再调用接口获取最新配置.不仅仅只有轮询,还有定时更新(默认 5 分钟一次).目的就是让客户端能够稳定的获取到最新的配置. 一起来看看他的设计. 核心代码 具体的类是 RemoteConfigRepository,每一个 Config -- 也就是 namespace 都有一个 RemoteConfigRepository 对象,表示这个 Config 的远程配置仓库,可以利用这个仓库请求远程服务,得到配置

长轮询,websocket

短轮询:server立即响应client的请求 长轮询:server经过一段时间后响应client的请求 长连接:server在响应client的请求之后依然保持连接,client可以使用此连接进行下一次请求 webSocket:以上三种是基于http协议,websocket是新的协议,取消了client和server的概念.上面的三种会多次传送http的header.

python之轮询、长轮询、websocket

轮询 ajax轮询 ,ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息. 1.后端代码 from flask import Flask,render_template app = Flask(__name__) UUUU = { '1':{'name':'王','count':1}, '2':{'name':'李','count':1}, '3':{'name':'赵','count':1}, } @app.route('/index') def index(

轮询、长轮询、长连接、socket连接、WebSocket

轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接. 优点:后端程序编写比较容易. 缺点:请求中有大半是无用,浪费带宽和服务器资源.(而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增加了每次传输的数据量) 实例:适于小型应用. 长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接(或到了设定的超时时间关闭连接),客户端处理完响应信息后再向服务器发送新的请求. 优点:在无消息的情况下不会

轮询,长轮询,websocket原理

服务端朝客户端主动推送消息 轮询:效率低,基本不用 让浏览器定时朝后端发送请求(通过ajax向后端偷偷发送数据),比如每隔五秒钟发一次请求,那么你的数据延迟就可能会高达五秒 不足之处 数据延迟 消耗资源过大 请求次数太多 长轮询:兼容性好,一般大公司都会考虑使用它 # 队列+ajax 服务端给每个客户端建立队列,让浏览器通过ajax朝服务端要数据,去各自的队列中获取 如果没有数据则会阻塞但是不会一直阻塞,比如阻塞你30秒,还没有数据则返回,然后让客户端浏览器再次发送请求数据的请求 相对于轮询 基

Web 通信 之 长连接、长轮询(long polling)(转载)

基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易懂的话来说,就是客户端不停的向服务器发送请求以获取最新的数据信息.这里的“不停”其实是有停止的,只是我们人眼无法分辨是否停止,它只是一种快速的停下然后又立即开始连接而已. 二.长连接.长轮询的应用场景 长连接.长轮询一般应用与WebIM.ChatRoom和一些需要及时交互的网站应用中.其真实案例有:WebQQ

Web 通信 之 长连接、长轮询(long polling)

基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易懂的话来说,就是客户端不停的向服务器发送请求以获取最新的数据信息.这里的“不停”其实是有停止的,只是我们人眼无法分辨是否停止,它只是一种快速的停下然后又立即开始连接而已. 二.长连接.长轮询的应用场景 长连接.长轮询一般应用与WebIM.ChatRoom和一些需要及时交互的网站应用中.其真实案例有:WebQQ

Web 通信 之 长连接、长轮询(转)

Web 通信 之 长连接.长轮询(long polling) 基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易懂的话来说,就是客户端不停的向服务器发送请求以获取最新的数据信息.这里的"不停"其实是有停止的,只是我们人眼无法分辨是否停止,它只是一种快速的停下然后又立即开始连接而已. 二.长连接.长轮询的应用场景 长连接.长轮询一般应用与W

长轮询实现消息推送

一.应用场景 浏览器与服务器之间保持一个长连接(http链接),服务器有最新的数据生成时及时推送到前端展现.典型场景:新邮件到达通知. 二.业界常用的解决方案 定时轮询,长轮询,websocket(HTML5新增的能力) 其中长轮询兼容性较好,应用的较为广泛,但是切忌在移动网络中应用该技术. 三.长连接前端代码 /** *pns模型层 *@constructs M2012.Model.Pns.PnsModel *@extends Backbone.Model *@example *new M20