知识点回顾:

- 什么是轮训?
  - 通过定时器让程序每隔n秒执行一次操作。
- 什么是长轮训?
  - 浏览器向后端发起请求,后端会将请求 hang 住,最多hang 30s。
  如果一直不返回数据:则最多等待30s,紧接着用户立即再发送请求。
  如果有数据返回:则操作数据并立即再发送请求。
PS:后台可以使用队列或redis的列表来hang住请求。

- 轮训和长轮训目的?
由于Http请求是无状态、短连接所以服务端无法向客户端实时推送消息,
所以,我们就是可以使用:轮训和长轮训去服务端获取实时数据。

 

websocket

websocket是什么?

websocket是一套类似于http的协议。
websocket协议:\r\n分割、创建连接后不断开、 验证+数据加密;

websocket本质:

- 就是一个创建连接后不断开的socket
- 当连接成功之后:

  - 客户端(浏览器)会自动向服务端发送消息,包含: Sec-WebSocket-Key: 校验
  - 服务端接收之后,会对于该数据进行加密:base64(sha1(swk + magic_string))

  - 构造响应头:
    HTTP/1.1 101 Switching Protocols\r\n
    Upgrade:websocket\r\n
    Connection: Upgrade\r\n
    Sec-WebSocket-Accept: 加密后的值\r\n
    WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n

  - 发给客户端(浏览器)

- 建立:双工通道,接下来就可以进行收发数据
  - 发送的数据是加密,解密,根据payload_len的值进行处理:
    - payload_len <=125
    - payload_len ==126
    - payload_len ==127
  - 获取内容:
    - mask_key
    - 数据
    根据mask_key和数据进行位运算,就可以把值解析出来。

在项目中使用:

- django: channel
- flask: gevent-websocket
- tornado: 内置

flask示例

安装:pip3 install gevent-websocket

作用:
- 处理Http、Websocket协议的请求 -> socket
- 封装Http、Websocket相关数据 -> request

基本结构:

from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer


@app.route('/test')
def test():
    ws = request.environ.get('wsgi.websocket')
    ws.receive()
    ws.send(message)
    ws.close()
    return render_template('index.html')


if __name__ == '__main__':
    http_server = WSGIServer(('0.0.0.0', 5000,), app, handler_class=WebSocketHandler)
    http_server.serve_forever()

 

实现一个web聊天室

后端:

from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
from flask import Flask, render_template, request
import pickle

app = Flask(__name__)
app.secret_key = 'xfsdfqw'


@app.route('/index')
def index():
    return render_template('index.html')


WS_LIST = []


@app.route('/test')
def test():
    ws = request.environ.get('wsgi.websocket')
    if not ws:
        return '请使用WebSocket协议'
    # websocket连接已经成功
    WS_LIST.append(ws)
    while True:
        # 等待用户发送消息,并接受
        message = ws.receive()

        # 关闭:message=None
        if not message:
            WS_LIST.remove(ws)
            break

        for item in WS_LIST:
            item.send(message)

    return "asdfasdf"


if __name__ == '__main__':
    http_server = WSGIServer(('0.0.0.0', 5000,), app, handler_class=WebSocketHandler)
    http_server.serve_forever()

前端:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <style>
        .msg-item {
            padding: 5px;
            border: 1px;
            margin: 0 5px;
        }
    </style>
</head>
<body>
<h1>首页</h1>
<div>
    <h2>发送消息</h2>
    <input id="msg" type="text"/> <input type="button" value="发送" onclick="sendMsg()">
    <h2>接收消息</h2>
    <div id="container">

    </div>
</div>

<script src="/static/jquery-3.3.1.min.js"></script>
<script>

    ws = new WebSocket('ws://192.168.12.42:5000/test');
    ws.onmessage = function (event) {
        var tag = document.createElement('div');
        tag.className = 'msg-item';
        tag.innerText = event.data;
        $('#container').append(tag);
    }

    function sendMsg() {
        ws.send($('#msg').val());
    }

</script>
</body>
</html>

 

 

问题:
a. 实时消息推送,利用什么技术实现?
- 轮训 ,优点:简单; 缺点:请求次数多,服务器压力大,消息延迟。
- 长轮训 ,优点:实时接收数据,兼容性好; 缺点:请求次数比原来少,但是相对来也不少。
- websocket ,优点:代码简单,不再反复创建连接。 缺点:兼容性。
b. 在框架中使用WebSocket:
- django,channel
- flask,gevent-websocket
- tornado,内置

 

posted on 2018-05-17 20:28  Py行僧  阅读(193)  评论(0编辑  收藏  举报