flask-socketio可以实现全双工通信,本文使用的版本是:
python==2.7
Flask-SocketIO==4.3.2
gevent-websocket==0.10.1
下面直接看服务端的代码:
其中前端直接访问也要访问这个name_space
# coding: utf-8
import json
from flask import Flask, request
from flask_socketio import SocketIO, emit
from monitor_system import SystemInfoMonitor
name_space = '/websocket' # 名称空间,写对才能通信
app = Flask(__name__, template_folder="template")
app.secret_key = 'tong-yi-tai-wan'
socketio = SocketIO(app, cors_allowed_origins='*') # 允许跨域
client_query = [] # 这里也可以用自带的join_room之类的方法
@socketio.on('connect', namespace=name_space) # 有客户端连接会触发该函数
def on_connect():
# 建立连接 sid:连接对象ID
client_id = request.sid
client_query.append(client_id)
# emit(event_name, broadcasted_data, broadcast=False, namespace=name_space, room=client_id) #指定一个客户端发送消息
# emit(event_name, broadcasted_data, broadcast=True, namespace=name_space) #对name_space下的所有客户端发送消息
print(u'new connection,id=[%s] connected, now have [%s] connections' % (client_id, len(client_query)))
@socketio.on('disconnect', namespace=name_space) # 有客户端断开WebSocket会触发该函数
def on_disconnect():
# 连接对象关闭 删除对象ID
client_query.remove(request.sid)
print(u'connection id=[%s] exited, now have [%s] connections' % (request.sid, len(client_query)))
# on('消息订阅对象', '命名空间区分')
@socketio.on('message', namespace=name_space)
def on_message(message):
""" 服务端接收消息 """
print(u'从id=%s客户端中收到消息,内容如下:' % request.sid)
print(message)
#指定一个客户端发送消息
# emit('my_response_message', "我收到了你的信息", broadcast=False, namespace=name_space, room= request.sid)
# 对name_space下的所有客户端发送消息
emit('my_response_message', u"我收到了你的信息", broadcast=True, namespace=name_space)
@app.route('/sender')
def get_monitor_system_info():
data = SystemInfoMonitor().run()
for client in client_query:
# client.send(json.dumps(data))
# emit('monitor', json.dumps(data), broadcast=True, namespace=name_space)
# 前端要监听这个my_response_message
emit('my_response_message', json.dumps(data), broadcast=False, namespace=name_space, room=client)
return "ok"
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', port=9999, debug=True)
后台可以写一个sender方法,然后通过apscheduler模块,启动一个定时任务,每隔几秒就调用一次sender方法,将数据推送给前端,这样也不会有阻塞。前端只需要处理好监听就可以跟我通信获取到数据。但是这样就会后台持续刷新接口。
看下官方文档,正确的做法应该如下:
# coding: utf-8
import json
from threading import Lock
import psutil
from flask import Flask, request
from flask_socketio import SocketIO, emit
from app.config import conf
from app.syscfg.models import SystemMessage
from worker import rclient
name_space = '/websocket'
app = Flask(__name__, template_folder="template")
app.secret_key = 'tong-yi-tai-wan'
socketio = SocketIO(app, cors_allowed_origins='*')
client_query = []
thread = None
thread_lock = Lock()
SYSTEM_INFO = json.dumps({})
POWER_INFO = {
'percent': 100, 'power_plugged': True, 'remain_time': 'unlimited'
}
def get_count_for_sysmsg():
from app import create_app
with create_app().app_context():
sysmsg_count = SystemMessage.query.filter(SystemMessage.read == 0).count()
return sysmsg_count
def background_thread():
"""Example of how to send server generated events to clients."""
while True:
# 监控
monitor_data = get_system_info()
socketio.emit('my_response_message', monitor_data, namespace=name_space)
# 电池
resp = get_power_info()
socketio.emit('power', json.dumps(resp), namespace=name_space)
# 站内信数量
count = get_count_for_sysmsg()
socketio.emit('message', json.dumps({'count': count}), namespace=name_space)
socketio.sleep(3)
@socketio.on('connect', namespace=name_space) # 有客户端连接会触发该函数
def on_connect():
global thread
with thread_lock:
if thread is None:
thread = socketio.start_background_task(background_thread)
emit('my_response_message', SYSTEM_INFO)
emit('power', json.dumps(POWER_INFO))
@socketio.on('disconnect', namespace=name_space) # 有客户端断开WebSocket会触发该函数
def on_disconnect():
# 连接对象关闭 删除对象ID
# client_query.remove(request.sid)
print(u'connection id=[%s] exited, now have [%s] connections' % (request.sid, len(client_query)))
# on('消息订阅对象', '命名空间区分')
@socketio.on('message', namespace=name_space)
def on_message(message):
""" 服务端接收消息 """
print(u'从id=%s客户端中收到消息,内容如下:' % request.sid)
print(message)
# emit('my_response_message', "我收到了你的信息", broadcast=False, namespace=name_space, room= request.sid) #指定一个客户端发送消息
emit('my_response_message', u"我收到了你的信息", broadcast=True, namespace=name_space) # 对name_space下的所有客户端发送消息
我后来部署需要nginx做代理,nginx的主要配置如下:
location /socket.io {
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 300s;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://127.0.0.1:9999/socket.io;
}
前端部分获取后台如下(访问82,监听转发到9999端口):