Flask中使用web socket
这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战
软硬件环境
- windows 10 64bit
- anaconda3 with python 3.7
- pycharm 2020.1.2
- flask 1.1.2
- flask-socketio 4.3.1
什么是websocket
WebSocket
是一种网络通讯协议,与HTTP
不同的是,WebSocket
提供全双工通信。也就是说,传统的方式,只有当客户端发起请求后,服务器端才会发送数据,而WebSocket
可以让服务器主动发送数据给客户端,它是服务器推送技术的一种。
下图是WebSocket
和HTTP
的区别
websocketd
websocketd
是一款非常不错的WebSocket
服务器,我们首先利用它来了解下标准WebSocket
的交互方式。先来下载windows
版本的websocketd
,解压之后进入目录
下面开始编写测试脚本,使用python
语言,文件名是test.py
,脚本的作用是周期性的向标准输出stdout
打印一个数值
from sys import stdout
from time import sleep
for count in range(0, 10):
print(count + 1)
stdout.flush()
sleep(0.5)
开启服务
websocketd --port 8080 python test.py
现在需要一个WebSocket
客户端,我们来到chrome
商店,安装插件 chrome.google.com/webstore/de… , 然后打开插件,输入WebSocket
的url
就可以看到服务器端的输出了
flask-socketio
Flask-SocketIO
使Flask
应用程序能够访问客户端和服务器之间的低延迟双向通信。客户端应用程序可以使用Javascript
,C++
,Java
和Swift
中的任何SocketIO
官方客户端库或任何兼容的客户端来建立与服务器的永久连接。
异步模式
flask-socketio
需要底层异步服务的支持,比较灵活的是,它会自己检测当前环境中存在的异步服务,且使用顺序为 eventlet
--> gevent
--> werkzeug
eventlet
性能最佳,支持长轮询和WebSocket
传输gevent
在许多不同的配置中得到支持。gevent
完全支持长轮询传输,但与eventlet
不同,gevent
没有本机WebSocket
支持。要添加对WebSocket
的支持,目前有两种选择:安装gevent-websocket
包为gevent
增加WebSocket
支持,或者可以使用带有WebSocket
功能的uWSGI Web
服务器。gevent
的使用也是一种高性能选项,但略低于eventlet
werkzeug
也可以使用基于werkzeug
的Flask
开发服务器,但需要注意的是,它缺乏其他两个选项的性能,因此只应用于简单的开发环境,而且它也仅支持长轮询传输
安装
直接使用pip
来安装
pip install eventlet
pip install flask-socketio
示例代码
先看服务端代码
文件run.py
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'
socketio = SocketIO()
socketio.init_app(app, cors_allowed_origins='*')
name_space = '/dcenter'
@app.route('/')
def index():
return render_template('index.html')
@app.route('/push')
def push_once():
event_name = 'dcenter'
broadcasted_data = {'data': "test message!"}
socketio.emit(event_name, broadcasted_data, broadcast=False, namespace=name_space)
return 'done!'
@socketio.on('connect', namespace=name_space)
def connected_msg():
print('client connected.')
@socketio.on('disconnect', namespace=name_space)
def disconnect_msg():
print('client disconnected.')
@socketio.on('my_event', namespace=name_space)
def mtest_message(message):
print(message)
emit('my_response',
{'data': message['data'], 'count': 1})
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', port=5000, debug=True)
代码中,我们设定了命名空间namespace
,因为客户端在进行连接的时候需要用到的url
就包含namespace
,另外还有个概念,就是event
,connect
、disconnect
还有自定义的my_event
都是
文件index.html
的代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SocketIO Demo</title>
<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.bootcss.com/socket.io/1.5.1/socket.io.min.js"></script>
</head>
<body>
<h2>Demo of SocketIO</h2>
<div id="t"></div>
<script>
$(document).ready(function () {
namespace = '/dcenter';
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
socket.on('dcenter', function (res) {
var t = res.data;
if (t) {
$("#t").append(t).append('<br/>');
}
});
});
</script>
</body>
</html>
启动服务后,我们访问 http://127.0.0.1:5000, 并保持这个页面不动,因为,后面接收到的消息会显示在这个页面上
这时候,使用浏览器访问 http://127.0.0.1:5000/push
我们使用这种方式来触发服务器发送消息给客户端,再次回到之前的页面,可以看到已经把服务器端发送过来的消息显示在了页面上
注意到,上面javascript
中,url
中使用的协议是http
,而不是ws
,这一点跟原生的WebSocket
有点区别。
cors跨域错误
如果是前后端分离的系统中,会出现如下的错误
Traceback (most recent call last):
File "src\gevent\greenlet.py", line 854, in gevent._gevent_cgreenlet.Greenlet.run
File "C:\ProgramData\Anaconda3\envs\FlaskTutorial\lib\site-packages\gevent\baseserver.py", line 34, in _handle_and_close_when_done
return handle(*args_tuple)
File "C:\ProgramData\Anaconda3\envs\FlaskTutorial\lib\site-packages\gevent\server.py", line 233, in wrap_socket_and_handle
with _closing_socket(self.wrap_socket(client_socket, **self.ssl_args)) as ssl_socket:
TypeError: wrap_socket() got an unexpected keyword argument 'cors_allowed_origins'
2020-09-09T08:16:07Z <Greenlet at 0x1812f1e8bf8: _handle_and_close_when_done(<bound method StreamServer.wrap_socket_and_handle , <bound method StreamServer.do_close
of <WSGIServer, (<gevent._socket3.socket [closed] at 0x1812fc41cc8)> failed with TypeError
这是跨域的问题,经测试发现,使用之前介绍过的flask-cors
来处理,其实并不起作用,我们需要在socketio
初始化的时候加入必要的参数
socketio = SocketIO()
socketio.init_app(app, cors_allowed_origins='*')
详细的信息可以参考这个链接, github.com/eventlet/ev…
源码下载
Flask系列教程
更多Flask
教程,请移步
参考资料
转载自:https://juejin.cn/post/6999451024482107406