使用Flask-socketio完成Flask Websocket
flask-socketio
为了方便所以将代码以及注释放上面, 在代码之后有对这个框架的解释(由于本人比较菜,只能解释一点皮毛 )^__^
flask后端文件 flask_ws.py :
from flask import Flask, render_template, request
from flask_socketio import SocketIO, Namespace, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO()
# 解决跨域问题
socketio.init_app(app, cors_allowed_origins='*')
# 用来存放客户端的 sid,即 session id
# 可以不单独定义字典存放 sid与namespace,flask-socketio 默认将 sid 存放在 room 中
socket_pool = {}
# Websocket 通过namespace 和 sid 标识具体客户端
# 第一个 Websocket 类
class MyCustomNamespace(Namespace):
name_space = '/wechat'
# 连接成功调用的方法
def on_connect(self):
global socket_pool
print("connect..")
print('-----------------')
print(self.server.manager)
print(self.socketio)
print('-----------------')
print(request.namespace)
socket_pool[request.sid] = MyCustomNamespace.name_space
print(socket_pool)
# 断开连接调用的方法
def on_disconnect(self):
global socket_pool
print("disconnect...")
del socket_pool[request.sid]
print(socket_pool)
# 往 接收客户端标消息识为 'message' 的方法
def on_message(self, data):
print(data)
# 把消息发送到客户端的 'response' 标识的方法中, 一般是 on_response()
emit('response', '123', to=request.sid)
print('--------------')
print(request.sid)
print('--------------')
print(socketio.server.manager.rooms)
# 往 接收客户端标消息识为 'hello' 的方法
def on_hello(self, data):
print('hello world')
# 发送消息
def send(self, data):
# 向 namespace中的所有 Websocket 连接广播消息, namespace参数不能少, to缺省是广播模式
emit('response', data, namespace=name_space)
# 向 sid 所标识的客户端 单播
emit('response', data, namespace=name_space, to=request.sid)
# 为了详细展示不同类,这里就不做继承
# 第二个 Websocket 类
class MyCustomNamespace1(Namespace):
name_space = '/wechat1'
def on_connect(self):
global socket_pool
print("connect..")
print(request.namespace)
socket_pool[request.sid] = MyCustomNamespace1.name_space
print(socket_pool)
def on_disconnect(self):
global socket_pool
print("disconnect...")
del socket_pool[request.sid]
print(socket_pool)
def on_message(self, data):
print(data)
emit('response', '123')
self.send('asd')
# 发送消息
def send(self, data):
emit('response', data, namespace=MyCustomNamespace1.name_space)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/sendll/')
def sendll():
for sid,namespace_value in socket_pool.items():
print(sid)
emit('response', '123456', namespace=namespace_value, to=sid)
print('ok')
return 'ok'
@app.route('/<file>')
def index1(file):
return render_template(file)
# #等同于类中的 on_message 方法
# @socketio.on('message', namespace="/wechat")
# def handle_message(message):
# print('received message: ' + message['data'])
# socketio.emit("response", {'age': 18}, namespace="/wechat")
#
# #等同于类中的 on_connect 方法
# @socketio.on('connect', namespace="/wechat")
# def connect():
# print("connect..")
#
# #等同于类中的 on_disconnect 方法
# @socketio.on('disconnect', namespace="/wechat")
# def connect():
# print("disconnect...")
if __name__ == '__main__':
# 注册WebSocket 以及 命名空间 MyCustomNamespace.name_space ('/wechat')
socketio.on_namespace(MyCustomNamespace( MyCustomNamespace.name_space ))
# 注册WebSocket 以及 命名空间 MyCustomNamespace1.name_space ('/wechat1')
# 有多个类或者命名空间可以继续注册
socketio.on_namespace(MyCustomNamespace1( MyCustomNamespace1.name_space ))
# 启动app, 运行在 8080 端口
socketio.run(app, host='127.0.0.1', port=8080)
前端测试文件1 index.html 连接的命名空间是 /wechat
<html>
<head>
<script type="text/javascript"
src="https://code.jquery.com/jquery-3.4.0.min.js"></script>
<script type="text/javascript"
src="./socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
var ws = io.connect('ws://127.0.0.1:8080/wechat');
ws.emit("message", { "data": "zhangsan" })
ws.on('connect', function(data) {
ws.emit('message', { 'data': 'I\'m connected!' });
});
ws.on('disconnect', function(data){
ws.emit('message', { 'data': 'I\'m disconnected!' });
});
ws.on('response', function(data) {
console.log(data);
});
ws.on
</script>
</head>
<body>
<h1>为了艾泽拉斯</h1>
</body>
</html>
前端测试文件2 index2.html
<html>
<head>
<script type="text/javascript"
src="https://code.jquery.com/jquery-3.4.0.min.js"></script>
<script type="text/javascript"
src="./socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
var ws = io.connect('ws://127.0.0.1:8080/wechat1');
ws.emit("message", { "data": "zhangsan" })
ws.on('connect', function(data) {
ws.emit('message', { 'data': 'I\'m connected!' });
});
ws.on('disconnect', function(data){
ws.emit('message', { 'data': 'I\'m disconnected!' });
});
ws.on('response', function(data) {
console.log(data);
});
ws.on
</script>
</head>
<body>
<h1>为了艾泽拉斯</h1>
</body>
</html>
先放上连接成功的图片(后端)
先放上连接成功的图片(前端)
说明,我所使用的扩展包版本如下:
后端
- Flask: V1.1.2
- eventlet: V0.31.1
- Python: V3.6.5
- Flask-SocketIO: V5.1.0
前端:
jquery-3.4.0
Socket.IO v4.1.2
ps: 前端的Socket.IO文件好像 1.3及以下版本连接Web后台时会一直报 400 错误,这是个大坑
好了,接下来讲讲这个flask-socketio组件。
这个组件将WebSocket封装的已经很完善了,尤其是断线重连机制,可以说是省去了很多麻烦,具体的优点大家可以去看大佬们的博客~我就不在这里卖弄了,嘻嘻嘻。
我就来讲讲这个组件与是如何存放用户的socket标识的
话不多说先上原理图(个人理解)
- 1.在框架启动时先初始化了 SocketIO()
- 2.在SocketIO初始化的时候同时初始化了Server()并存放在自己的server属性中。
- 3.在Server初始化的时候又初始化了BaseManager(),并将初始化的BaseManager 存放在 Server实例的 manager属性中。而存放与用户 连接的 socket标识就存放在 BaseManager的rooms中
描述的具体流程如上图所示
以下是我输出的 rooms 中的内容
此时我使用两个客户端连接了 '/wechat', sid:lgrl3XcO-CXxvF9uAAAE,cmzqIqmXEiXHibHPAAAF
一个客户端连接了 '/wechat1', sid:x8wAcozzFtKAaxeCAAAB
{
'/wechat1': {
None: bidict({'x8wAcozzFtKAaxeCAAAB': 'kdA3cUoQTHArrgdaAAAA'}),
'x8wAcozzFtKAaxeCAAAB': bidict({'x8wAcozzFtKAaxeCAAAB': 'kdA3cUoQTHArrgdaAAAA'})
},
'/wechat': {
None: bidict({'lgrl3XcO-CXxvF9uAAAE': 'bVaIf8vaTfou1AbZAAAC', 'cmzqIqmXEiXHibHPAAAF': 'la_OeJPnarS66rTiAAAD'}),
'lgrl3XcO-CXxvF9uAAAE': bidict({'lgrl3XcO-CXxvF9uAAAE': 'bVaIf8vaTfou1AbZAAAC'}),
'cmzqIqmXEiXHibHPAAAF': bidict({'cmzqIqmXEiXHibHPAAAF': 'la_OeJPnarS66rTiAAAD'})
}
}
当然,在一开我贴出来的代码中并没有使用这个 rooms,为了方便我使用了字典存放sid 与 对应的 namespace。
入站第一篇,为了诸君在找资料时少踩点坑,哈哈哈
为什么要写这篇博客,是因为我初学WebSocket被坑太多了,被逼无奈去研究了下框架源码(菜鸡一枚,无法深入研究),且略见成效,愿同诸君共享,少些曲折。
本文来自博客园,作者:糖烤栗子&,转载请注明原文链接:https://www.cnblogs.com/grocerystore/p/15073138.html