Flask-Socketio的使用

WebSocket

WebSocket是一种网络协议,可以用来实现在客户端和服务器之间的双向实时通信,前端可以向后端发送请求,同时后端也可以向前端进行实时数据推送。传统的HTTP协议是无状态的,每次请求都需要重新建立连接,而利用WebSocket可以在建立连接之后保持连接状态,双方可以随时发送信息。

在一个实时的Web应用程序中,我们可以使用Vue作为前端框架,使用Flask作为后端框架。前后端都有相关的工具来支持WebSocket,使得开发WebSocket应用更加容易。

案例介绍

相关概念

在介绍案例之前,我们可以简单了解一下WebSocket的工作原理。WebSocket可以实现服务器和客户端之间的双向通信,我们可以想象是在双方之间建立了一个通道,前后端都可以进行信息发送和信息接收。具体来说,我们会在后面见到emiton的操作,分别代表发送信息以及接收信息。

如果客户端想要使用websocket接受服务器推送的话,Socketio是一个不错的选择。Socket.io将Websocket、轮询机制以及其它的实时通信方式(ajax等)封装成了通用的接口,并且在服务端也实现了这些实时机制的相应代码。所以,使用Socket.io便不需要担心浏览器兼容问题。

在socketio中还有两个重要的概念,分别为namespaceevent,前后端如果要进行双向通信,这些信息应该同属于一个namespace。而event则是在信息发送和接收的时候使用到,我们可以触发一个event,也可以监听一个event,这样就形成了一个通道,使得数据能够正确传递。在后面我们也可以在代码中看到对应的概念。

Flask SocketIO

Flask SocketIO是在Flask框架中对WebSocket的支持,通过它我们能够在Flask中很方便地完成WebSocket应用的编写。可以通过pip来进行依赖的安装:

1
pip install flask-socketio

之后,我们可以通过如下的代码来获取对应的SocketIO对象。这里为了解决跨域问题,我们使用了CORS以及cors_allowed_origins='*'

1
2
3
4
5
6
7
from flask import Flask
from flask_cors import CORS
from flask_socketio import SocketIO

app = Flask(__name__)
CORS(app, cors_allowed_origins='*')
socketIO = SocketIO(app, cors_allowed_origins='*')

下一步,我们可以完成一个通信方法。可以看到,在代码中我们指定了namespace/api,同时还调用了SocketIO对象的onemit方法。首先,后端会监听/api下的my event事件,如果监听到了,就会触发该函数。在函数中,我们可以接收推送的信息data,然后后端调用emit方法,触发my response事件,同时传递数据。这里传递的数据就是在得到的data基础上进行了一层包装。

1
2
3
4
5
@socketIO.on("my event", namespace="/api")
def testSocketIO(data):
print(data)
print("test socket io...")
socketIO.emit("my response", {'dataInBackend': data}, namespace="/api")

SocketIO应用的启动和传统的Flask有所区别,需要使用SocketIO对象来run。如果使用的是dev模式,则需要额外增加属性allow_unsafe_werkzeug=True

1
2
if __name__ == '__main__':
socketIO.run(app, allow_unsafe_werkzeug=True)

Vue 前端展现

在前端我们同样需要SocketIO的支持。这里我们使用Vue框架作为前端的实现。在Vue中有两种方式来使用SocketIO,一种是直接使用官方包SocketIO,第二种是使用VueSocketio。第二种方式是对SocketIO的一层封装,更适合在Vue项目中使用,不过在案例实现的过程中,第二种方法会有版本不匹配的问题出现,因此这里还是选择使用第一种方式。

首先需要通过npm安装相关依赖:

1
npm install socket.io-client

之后,我们可以在需要使用WebSocket的组件中进行使用。下面来介绍一个简单案例。首先展现组件的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<h1>SocketIO Shower</h1>
<div>{{ this.data }}</div>
</template>

<script>
import io from 'socket.io-client';

export default {
name: "SocketIOShower",
data() {
return {
data: {}
}
},
mounted() {
const socket = io('http://127.0.0.1:5000/api');
socket.emit('my event', {'data': "data from fronend"});
socket.on('my response', (data) => {
this.data = data
})
}
}
</script>

在测试组件中,我们的data中返回一个data字段,用于承接后端推送的信息,并将其渲染在页面中。在组件mounted的声明周期,我们通过io获取一个SocketIO对象,然后通过该对象分别调用了emit以及on方法。注意这里在对象初始化的时候,我们已经在url中指定了namespace,实际上namespace就相当于path的拼接。

对应到前面Flask的案例代码,首先前端通过emit触发my event事件传递数据{'data': "data from fronend"},后端flask监听了对应namespace下的该事件,于是会在控制台中进行打印,同时调用emit方法触发my response事件,向前端传送信息。前端使用了on方法来监听my response事件,因此能够得到后端传送来的数据,同时更新this.data,进行页面渲染。

最终我们可以在前端页面上看见如下效果:

动态推送

上面的案例中,后端只是向前端完成了一次简单的数据推送,更常见的场景是后端持续向前端进行数据推送,我们可以简单修改上面的示例代码来模拟对应场景:

1
2
3
4
5
6
7
@socketIO.on("my event", namespace="/api")
def testSocketIO(data):
print(data)
print("test socket io...")
for i in range(100):
time.sleep(1)
socketIO.emit("my response", {'dataInBackend': data, 'nowIndex': i}, namespace="/api")

之后在前端页面上,我们就可以看到动态变化的信息了。

注意事项

在使用过程中,可能出现如下异常情况。在Flask后端中出现下面的提示信息,同时前端控制台上会一直提示400以及跨域错误。

1
The client is using an unsupported version of the Socket.IO or Engine.IO protocols

这是因为相关依赖的版本不能匹配。在使用过程中,前后端使用的SocketIO以及相关依赖的版本需要能够匹配上,否则会无法工作。匹配的版本可以在Introduction — Flask-SocketIO documentation中进行查看,目前的对应关系如下所示。

JavaScript Socket.IO version Socket.IO protocol revision Engine.IO protocol revision Flask-SocketIO version python-socketio version python-engineio version
0.9.x 1, 2 1, 2 Not supported Not supported Not supported
1.x and 2.x 3, 4 3 4.x 4.x 3.x
3.x and 4.x 5 4 5.x 5.x 4.x

参考文章

  1. vue flask nginx socketio实现服务器推送(demo实现一个聊天室) - 掘金 (juejin.cn)
  2. Flask-SocketIO — Flask-SocketIO documentation

Flask-Socketio的使用
http://example.com/2023/04/18/Flask-Socketio的使用/
作者
EverNorif
发布于
2023年4月18日
许可协议