Flask:使用SocketIO实现WebSocket与前端Vue进行实时推送(gevent-websocket、flask-socketio、flask不出现running on 127..问题)

逃离我推掉我的手 2023-09-30 13:18 78阅读 0赞

前言

本文旨在记录使用Flask框架过程中与前端Vue对接过程中,存在WebSocket总是连接失败导致前端取不到数据的问题。以及在使用WebSocket相关功能的库包gevent-websocket之后,导致运行Flask项目之后,控制台没有显示running on 127.0.0.1:5000 问题、以及没有输出log日志记录的问题、以及总是报错Websocket connection to‘ws://127.0.0.1:5000/socket.io/?EIO=4&transport=websocket’failed:Error during Websocket handshake:Unexpected response code:400’的问题!该篇文章花费了笔者和笔者同学较多精力和时间,转载请说明来源!

如下图所示:只有三行控制台输出的记录、总是报错(该错在网上没有解决方法)等。
在这里插入图片描述

在这里插入图片描述

技术选型:前端Vue、后端Flask。

核心问题

需要着重注意的是,Flask框架中有原生的Websocket写法,也有对Websocket封装之后的依赖包SocketIO写法,所以在进行与前端对接的过程中,需要与前端对接好接口标准。在本次项目中,后端最开始用的是封装好WebSocket后的socketio进行编写,而前端使用了原生的websocket-vue写法,导致一直对接不上,获取不到数据。以及所有的报错或者各种bug问题,笔者都推测是跟gevent-websocket这个包有关。

在前端更改为vue-socketio之后,成功解决对接失败问题。(也可以后端改用原生写法,总之两边需要同时使用一个标准。)前端Vue可以参考Vue的文档去看使用哪种写法即可。

Flask的原生WebSocket(flask-sockets)与封装SocketIO

Flask-Sockets和Flask-SocketIO之间的主要区别在于前者仅仅将WebSocket协议(通过使用gevent-websocket项目)进行包装,因此它只适用于原生支持WebSocket协议的浏览器,对于那些不支持WebSocket协议的较老的浏览器,就无法使用它了。

Flask-SocketIO则不同,它不仅实现了WebSocket协议,并且对于那些不支持WebSocket协议的旧版浏览器,使用它也能够实现相同的效果。新版旧版的浏览器都能使用他。可以这么理解,flask把websocket功能封装在了socketio这个新的包里面。

另一个区别是Flask-SocketIO实现了SocketIO Javascript库公开的消息传递协议。
而Flask-Sockets只是实现通信通道,发送的是完全取决于应用程序。

1、Flask-SocketIO(封装写法)

使用SocketIO之前需要导入该包,即pip install flask-socketio。也可以直接在代码中import该包中的两个功能。
即:from flask_socketio import SocketIO, emit。

下面是服务端代码:(关于如何在实战中应用,可以看笔者上一篇关于flask博客中的代码实现,大致思路是使用线程)

  1. from flask import Flask, render_template
  2. from flask_socketio import SocketIO, emit
  3. app = Flask(__name__)
  4. app.config['SECRET_KEY'] = 'secret!'
  5. socketio = SocketIO(app)
  6. @app.route('/')
  7. def index():
  8. return render_template('index.html')
  9. @socketio.on('my event', namespace='/test')
  10. def test_message(message):
  11. emit('my response', {
  12. 'data': message['data']})
  13. @socketio.on('my broadcast event', namespace='/test')
  14. def test_message(message):
  15. emit('my response', {
  16. 'data': message['data']}, broadcast=True)
  17. @socketio.on('connect', namespace='/test')
  18. def test_connect():
  19. emit('my response', {
  20. 'data': 'Connected'})
  21. @socketio.on('disconnect', namespace='/test')
  22. def test_disconnect():
  23. print('Client disconnected')
  24. if __name__ == '__main__':
  25. socketio.run(app)

而对于js来说,客户端代码十分简单,直接上代码:(注意是socketio的标准)

  1. $(document).ready(function(){
  2. var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
  3. //注意如果使用var socket = io.connect(location.protocol + ‘//’ + document.domain.....的写法,这里的protocol是http协议,而不是走的是ws,笔者推测是对ws进行了封装,导致最终走的是http协议。
  4. //上面代码中的/test 就是namespace
  5. socket.on('my response', function(msg) {
  6. $('#log').append('<p>Received: ' + msg.data + '</p>');
  7. });.
  8. $('form#emit').submit(function(event) {
  9. socket.emit('my event', {data: $('#emit_data').val()});
  10. return false;
  11. });
  12. $('form#broadcast').submit(function(event) {
  13. socket.emit('my broadcast event', {data: $('#broadcast_data').val()});
  14. return false;
  15. });
  16. });

2、Flask-Sockets(原生Websocket写法)

服务端:

  1. from flask import Flask
  2. from flask_sockets import Sockets
  3. import datetime
  4. import time
  5. import random
  6. app = Flask(__name__)
  7. sockets = Sockets(app)
  8. @sockets.route('/echo')
  9. def echo_socket(ws):
  10. while not ws.closed:
  11. now = datetime.datetime.now().isoformat() + 'Z'
  12. ws.send(now) #发送数据
  13. time.sleep(1)
  14. @app.route('/')
  15. def hello():
  16. return 'Hello World!'
  17. if __name__ == "__main__":
  18. from gevent import pywsgi
  19. from geventwebsocket.handler import WebSocketHandler
  20. server = pywsgi.WSGIServer(('', 5000), app, handler_class=WebSocketHandler)
  21. print('server start')
  22. server.serve_forever()

客户端代码:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <script src="https://cdn.bootcss.com/jquery/3.2.0/jquery.js"></script>
  7. </head>
  8. <body>
  9. <div id="time" style="width: 300px;height: 50px;background-color: #0C0C0C;
  10. color: white;text-align: center;line-height: 50px;margin-left: 40%;font-size: 20px"></div>
  11. <script>
  12. var ws = new WebSocket("ws://127.0.0.1:5000/echo"); #连接server
  13. //这是websocket的前端原生写法,直接连接ws。
  14. ws.onmessage = function (event) {
  15. content = document.createTextNode(event.data); # 接收数据
  16. $("#time").html(content);
  17. };
  18. </script>
  19. </body>
  20. </html>

3、Bug 1:控制台输出没有Running on 127.0.0.1以及没有输出日志

在安装了gevent-websocket的这个包之后,会顺带安装gevent这个包,需要注意的是,gevent这个包会导致项目运行之后,控制台不会输出running on这个bug和 没有Log输出日志的bug。

经过笔者查证之后,发现是gevent-websocket这个包太老了,2017年的就已经停止更新了。所以这个包如果使用的话,会顺带导致一些对于新版本的Flask兼容性问题,所以导致了控制台的上述两个Bug存在。

解决方案:删掉gevent、gevent-websocket这两个包,可以下载 simple-websocket这个包来替代这两个包完成功能开发。

解决之后,控制台可以正常显示了。
在这里插入图片描述

3、 Bug 2:显示连接错误。

在连接错误之后,推测这种报4的错误(网上全是3的错误),应该是没有安装gevent-websocket这个包,但是安装了之后又会造成第一类bug,所以可以直接安装simple-websocket这个依赖包。
在这里插入图片描述

参考文章:https://www.cnblogs.com/wangkun122/articles/9117882.html

发表评论

表情:
评论列表 (有 0 条评论,78人围观)

还没有评论,来说两句吧...

相关阅读

    相关 socketio 服务器

    如果面试官问你:要把服务器端的数据时时显示在浏览器上怎么实现?我想有很多人会回答使用Ajax技术定时去访问一个资源,没错,使用Ajax的确能实现,但面试官要的绝对不是这个答案。

    相关 socketio 服务器

    如果面试官问你:要把服务器端的数据时时显示在浏览器上怎么实现?我想有很多人会回答使用Ajax技术定时去访问一个资源,没错,使用Ajax的确能实现,但面试官要的绝对不是这个答案。