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。