• 利用flask-socketio实现前后端实时通信的功能


    一、需求

    准备实现一个前后端实时发送消息的功能,即后端发送一条消息,前端就可以接收到该消息并显示在页面上。


    二、解决方法

    利用Flask-SocketIO实现,Flask-SocketIO使Flask应用程序能够访问客户端和服务器之间的低延迟双向通信。能够很好的解决我们的问题


    三、具体实现

    步骤一:安装

    pip install eventlet
    pip install flask-socketio
    
    • 1
    • 2

    注意:flask-socketio对版本要求比较严格,如果报错,请更换如下包的版本:

    Flask 1.1.4
    Flask-SocketIO 4.3.1
    python-engineio 3.13.2
    python-socketio 4.6.0
    
    • 1
    • 2
    • 3
    • 4

    步骤二:服务器代码

    import time
    from flask import Flask, request, render_template
    from flask_socketio import SocketIO
    from threading import Lock
    
    thread = None
    thread_lock = Lock()
    app = Flask(__name__)
    socketio = SocketIO()
    socketio.init_app(app, cors_allowed_origins='*') # 用socketio初始化flask的app
    
    flag1 = 0
    flag2 = 0
    flag3 = 0
    
    
    @app.route('/', methods=['POST', 'GET'])
    def index():
        global flag1
        global flag2
        global flag3
        if request.method == 'POST':
            # 模拟执行你的业务逻辑一
            time.sleep(5)
            # 利用全局变量表明业务逻辑一已完成
            flag1 = 1
            print("步骤1完成")
            # 模拟执行你的业务逻辑二
            time.sleep(2)
            # 利用全局变量表明业务逻辑二已完成
            flag2 = 1
            print("步骤2完成")
            # 模拟执行你的业务逻辑三
            time.sleep(3)
            # 利用全局变量表明业务逻辑三已完成
            flag3 = 1
            print("步骤3完成")
    
        return render_template('t1.html')
    
    
    @socketio.on('connect', namespace='/test_conn')
    def test_connect():
        """
        此函数在建立socket连接时被调用
        """
        print("socket 建立连接")
        global thread
        with thread_lock:
            print(thread)
            if thread is None:
                # 如果socket连接,则开启一个线程,专门给前端发送消息
                thread = socketio.start_background_task(target=background_thread)
    
    
    @socketio.on('disconnect', namespace='/test_conn')
    def disconnect_msg():
        """
        此函数在socket断开时被调用
        """
        print('client disconnected!')
    
    
    def background_thread():
        """
        该线程专门用来给前端发送消息
        :return:
        """
        global flag1
        global flag2
        global flag3
        while True:
            if flag1 == 1:
                print("进入判断一")
                socketio.emit('server_response',
                              {'data': '步骤一已完成'}, namespace='/test_conn')
                flag1 = 2
            socketio.sleep(2)
            if flag2 == 1 and flag1 == 2:
                print("进入判断二")
                socketio.emit('server_response',
                              {'data': '步骤二已完成'}, namespace='/test_conn')
                flag2 = 2
            socketio.sleep(2)
            if flag3 == 1 and flag2 == 2 and flag1 == 2:
                print("进入判断三")
                socketio.emit('server_response',
                              {'data': '步骤三已完成'}, namespace='/test_conn')
                flag3 = 2
            socketio.sleep(2)
    
    
    if __name__ == '__main__':
        socketio.run(app, host='127.0.0.1', port=5000, debug=True)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94

    步骤三:前端代码

    前端代码是用vue+element ui编写。(代码中所涉及的js文件可以在百度上自行下载,有很多)

    DOCTYPE html>
    <html>
    
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    
        <link rel="stylesheet" href="static/css/index.css"> 
        <script src="static/js/vue.js">script> 
        <script src="static/js/index.js">script> 
        <script src="static/js/axios.min.js">script>  
        <script type="text/javascript" src="static/js/jquery-1.4.2.min.js">script>  
        <script type="text/javascript" src="static/js/socket.io.min.js">script>  
    head>
    
    <body>
        <div id="test">
            <el-button type="primary" size="small" @click="onSubmit()">提交el-button>
            <div id = "t">div>
        div>
    
    body>
    
    <script>
    	// 新建vue环境
        new Vue({
            el: '#test',
            data() {return{} },
            methods: {
                onSubmit() {
                	// 向后端发送post请求
                    axios.post('http://127.0.0.1:5000/');
                    // 建立socket连接,此时会触发后端的@socketio.on('connect', namespace='/test_conn')路由,执行test_connect函数
                    let socket = io.connect('http://127.0.0.1:5000/test_conn');
                    socket.on('server_response', function (res) {
                    	//接收到后端发送过来的消息
                        var t = res.data;
                        console.log(t)
                        if (t) {
                        	// 显示在页面上
                            $("#t").append(t).append('
    '
    ); } }); } } })
    script> html>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    结果展示

    启动flask项目,并访问:
    在这里插入图片描述
    点击提交后,前端发送post请求给后端,并建立socket连接:
    在这里插入图片描述
    同时执行index()函数:
    在这里插入图片描述
    同时前端页面接收到后端发送的消息:
    在这里插入图片描述
    至此,前后端交互功能完成!

    四、注意事项

    1、我们不能直接在socket连接函数中写一个while循环去发送信息,例如这样:

    @socketio.on('connect', namespace='/test_conn')
    def test_connect():
        while True:
            socketio.sleep(5)
            t = random.randint(1, 100)
            socketio.emit('server_response',
                          {'data': t},namespace='/test_conn')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    虽然这样写,emit函数每次都会执行,理论上客户端应该可以定时收到服务端的随机数。但是结果是客户端根本接收不到,连soketio.on函数都没有触发运行。
    原因应该是当服务端陷入死循环,会影响与客户端之间的websocket连接,总之写while true需谨慎。
    所以,在flask_socketio的示例程序中,我们 用后台线程 进行while循环以解决这个问题。

    2、websocket连接的一个典型应用是实现一个网络聊天室,所以就会有群发和私聊的功能,我们此处演示的示例,是一个群发功能,也就是我们并没有指定socket的room空间,所以当我们从后端发送给前端消息时,所有打开此页面的人,都可以看到我们发送的消息,也就是一个群发功能。

  • 相关阅读:
    C++ Reference: Standard C++ Library reference: C Library: cctype: toupper
    .NetCore Web Api 利用ActionFilterAttribute统一接口返回值格式
    14天机器学习DAY1-5|线性回归原理小结
    XmlViewResolver类简介说明
    关于两种常用特殊字符判断方法
    Java利用RedisTemplate给redis存储map集合数据
    GAN生成手写数字(TensorFlow,Mnist数据集)
    R语言绘图-3-Circular-barplot图
    .Net Core中使用NEST简单操作Elasticsearch
    解决表单action属性传参时值为null的问题
  • 原文地址:https://blog.csdn.net/bradyM/article/details/127787708