使用Flask-SocketIO完成服务端和客户端的双向通信

 

介绍:flask-socketio模块实际上是封装了flask对websocket的支持,websocket在连接建立阶段是通过HTTP的握手方式进行的,这可以看做是为了兼容浏览器或者使用一些现成的功能来实现,这样一种捷径。当连接建立之后,客户端和服务端之间就不再进行HTTP通信了,所有信息交互都由websocket接管。Flask-SocketIO使Flask应用程序可以访问客户端和服务器之间的低延迟双向通信,使客户端建立与服务器的永久连接。

适用的场景:后台产生新的数据,需要在前台页面马上展示出来,例如数据监控、统计图实时变化更新等。

当然,我们可以使用ajax来完成,通过ajax使得前台定时去后台索要数据,但如果消息频繁,ajax需要不断的建立和释放连接,效果明显不如后端直接推送数据到前台更加合适。

 

 

Flask-SocketIO的使用

 

 

首先安装依赖:

 

pip install flask-socketio

 

一个简单的示例(对Flask代码加了一层包装):

from flask import Flask, render_template
from flask_socketio import SocketIO

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

if __name__ == '__main__':
socketio.run(app)

 

SocketIO发送消息

SocketIO可以使用send()和emit()函数向连接的客户端发送消息,两个函数有些区别,send()用于发送未命名事件消息,而emit()用于发送已命名事件消息。

 

实例代码:

 

@socketio.on('message')
def handle_message(message):
     send(message, namespace='/chat')

@socketio.on('my event')
def handle_my_custom_event(json):
      emit('my response', json, namespace='/chat')

 

namespace表示传入消息的命名空间,前台可以对应这个命名空间选择接收消息,如:

$(document).ready(function() {
        namespace = '/test';
        var socket = io.connect(location.protocol
    + '//' + document.domain + ':'
    + location.port + namespace);

        socket.on('server_response', function(res) {
            //res表示接收的数据,这里做数据的处理
        });

    });

 

一个简单使用SocketIO的完整实例


功能:后台五秒随机产生十个数字,在前台模版中动态刷新显示

 

 

后台代码:

 

 

#encoding:utf-8
#!/usr/bin/env python
from flask import Flask, render_template
from flask_socketio import SocketIO
import random
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

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

@socketio.on('connect', namespace='/test_conn')
def test_connect():
    while True:
        socketio.sleep(5)
        t = random_int_list(1, 100, 10)
        socketio.emit('server_response',
                      {'data': t},
                      namespace='/test_conn')

def random_int_list(start, stop, length):
    start, stop = (int(start), int(stop)) if start <= stop else (int(stop), int(start))
    length = int(abs(length)) if length else 0
    random_list = []
    for i in range(length):
        random_list.append(random.randint(start, stop))
    return random_list

if __name__ == '__main__':
    socketio.run(app, debug=True)

 

页面模版:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
    <script type="text/javascript" src="//cdn.bootcss.com/socket.io/1.5.1/socket.io.min.js"></script>
</head>
<body>
<h2 id="t"></h2>
<script type="text/javascript">
    $(document).ready(function() {
        namespace = '/test_conn';
        var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
        socket.on('server_response', function(res) {
            var t = res.data;
            $("#t").text(t);
        });

    });
</script>
</body>
</html>

 

最后,我们使用SocketIO结合Echarts实现一个简单的实时监控图

效果如图所示:

后台代码:

#encoding:utf-8
#!/usr/bin/env python
import psutil
import time
from threading import Lock
from flask import Flask, render_template
from flask_socketio import SocketIO

async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()

# 后台线程 产生数据,即刻推送至前端
def background_thread():
    count = 0
    while True:
        socketio.sleep(5)
        count += 1
        t = time.strftime('%M:%S', time.localtime())
        # 获取系统时间(只取分:秒)
        cpus = psutil.cpu_percent(interval=None, percpu=True)
        # 获取系统cpu使用率 non-blocking
        socketio.emit('server_response',
                      {'data': [t, cpus], 'count': count},
                      namespace='/test')
        # 注意:这里不需要客户端连接的上下文,默认 broadcast = True


@app.route('/')
def index():
    return render_template('index.html', async_mode=socketio.async_mode)

@socketio.on('connect', namespace='/test')
def test_connect():
    global thread
    with thread_lock:
        if thread is None:
            thread = socketio.start_background_task(target=background_thread)

if __name__ == '__main__':
    socketio.run(app, debug=True)

 

页面模版:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>系统监控走势图</title>
    <script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
    <script type="text/javascript" src="//cdn.bootcss.com/socket.io/1.5.1/socket.io.min.js"></script>
    <!-- ECharts 3 引入 -->
    <script src="http://echarts.baidu.com/dist/echarts.min.js"></script>
</head>

<body>
    <div id="main" style="height:500px;border:1px solid #ccc;padding:10px;"></div>

    <script type="text/javascript">

    var myChart = echarts.init(document.getElementById('main'));

    myChart.setOption({
        title: {
            text: '系统监控走势图'
        },
        tooltip: {},
        legend: {
            data:['cpu']
        },
        xAxis: {
            data: []
        },
        yAxis: {},
        series: [{
            name: 'cpu',
            type: 'line',
            data: []
        }]
    });


    var time = ["","","","","","","","","",""],
        cpu = [0,0,0,0,0,0,0,0,0,0]


    //准备好统一的 callback 函数
    var update_mychart = function (res) {
    //res是json格式的response对象

        // 隐藏加载动画
        myChart.hideLoading();

        // 准备数据
        time.push(res.data[0]);
        cpu.push(parseFloat(res.data[1]));
        if (time.length >= 10){
            time.shift();
            cpu.shift();
        }

        // 填入数据
        myChart.setOption({
            xAxis: {
                data: time
            },
            series: [{
                name: 'cpu', // 根据名字对应到相应的系列
                data: cpu
            }]
        });

    };

    // 首次显示加载动画
    myChart.showLoading();


    // 建立socket连接,等待服务器“推送”数据,用回调函数更新图表
    $(document).ready(function() {
        namespace = '/test';
        var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);

        socket.on('server_response', function(res) {
            update_mychart(res);
        });

    });

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

 

关注后端技术精选,号内回复“学习资料”,领取100套小程序源码+小程序开发视频和Java经典书籍电子版!

Java知音_ CSDN认证博客专家 Java知音
欢迎微信搜索【Java知音】关注我的公众号,号内回复“后端面试”,送你一份精心准备的Java面试题(提纲+解析),Java知音每天推送精选好文,已经有十几万开发者关注,欢迎加入我们,共同交流,纠错
**问题概述**:服务端接收不到前端(客户端)发到服务端的请求 **主要问题**:服务端接收不到前端发到服务端的请求,同样前端也接收不到服务端发来的数据。 当有客户端进行连接的时候,服务器端控制台可以打印出客户端GET请求的响应码(400)和客户端的身份信息(sid),当有客户端关闭的时候也可以打印出消息。 考虑过前后端的socket.io版本差异,但经过测试后已经基本排除 ​ 测试方法是:前端使用node.js写的服务器,版本无论是1.x还是2.x都是可以正常工作的。 考虑过CORS跨域请求问题,但经过测试也可以大致排除 ​ 测试方法是:首先前端的人确认在客户端上已经处理过这个问题,再者前端使用node.js写的服务器尝试删除掉关于跨域请求的任何设置依旧可以正常工作。 考虑过命名空间问题,但经过测试已经排除 **尝试过使用Flask框架的扩展包Flask-socketio进行连接/数据收发** 代码示例: ```python # -*- encoding: utf-8 -*- from flask import Flask def create_app(): app = Flask(__name__) socketio.init_app(app) return app app = create_app() @socketio.on('test', namespace='/test') def test(data): print('客户端插入的消息', data) emit('message', data, broadcast=True) if __name__ == '__main__': app.debug = True socketio.run(app, host='0.0.0.0', port=8000, debug=True, log_output=True) ``` **尝试过使用python-socketio包进行连接/数据收发** 代码示例: ```python from flask import Flask sio = socketio.Server(async_mode='threading', cors_allowed_origins='*', engineio_logger=True) app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' app.wsgi_app = socketio.WSGIApp(sio, app.wsgi_app) @sio.event def connect(sid, environ): print('客户端已连接', sid) @sio.event def disconnect(sid): print('连接断开 ', sid) @sio.event def login_request(data): # 数据收发 print(data) sio.emit('login_response',[responseData]) if __name__ == '__main__': app.run(host='0.0.0.0', port=8000, debug=True, threaded=True) ``` 尝试过使用基于Django框架的Django-socketio,但是由于Django框架过于庞大功能太多不够灵活,以及路由问题,在尝试使用socketio的时候没能驾驭的了,完全连接不上。失败。 **前端的状态码一直是400或101** _开发系统环境尝试过:CentOS,Ubuntu,windows_
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页