如何通过Python Flask代理Node.js的Socket.IO服务?
在Flask中连接Node.js Socket.IO服务的解决提示
当然可以实现Flask应用连接Node.js的Socket.IO服务,我之前也踩过代理/socket.io端点的坑,给你几个实用的解决方向:
一、Flask端配置反向代理(重点处理WebSocket)
普通的HTTP请求转发比较容易,但Socket.IO依赖WebSocket,所以得用支持WebSocket的代理方式。推荐用Werkzeug的DispatcherMiddleware配合gevent来实现:
from flask import Flask, send_static_file from werkzeug.middleware.dispatcher import DispatcherMiddleware from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler import requests app = Flask(__name__) # 提供静态页面 @app.route('/') def serve_index(): return send_static_file('index.html') # 配置代理,将/socket.io请求转发到Node.js服务 proxy_app = DispatcherMiddleware(app, { '/socket.io': lambda environ: requests.get( 'http://localhost:8888' + environ['PATH_INFO'], headers={k: v for k, v in environ.items() if k.startswith('HTTP_')}, params=environ['QUERY_STRING'], stream=True ).raw }) if __name__ == '__main__': # 用gevent的WSGI服务器,支持WebSocket server = WSGIServer(('0.0.0.0', 5000), proxy_app, handler_class=WebSocketHandler) server.serve_forever()
如果不想用gevent,也可以写一个路由转发,但要注意WebSocket的升级请求处理(不过这种方式对WebSocket的支持不如上面的方法稳定):
from flask import Flask, request, Response import requests app = Flask(__name__) @app.route('/') def index(): return app.send_static_file('index.html') @app.route('/socket.io/<path:path>', methods=['GET', 'POST']) def proxy_socketio(path): target_url = f'http://localhost:8888/socket.io/{path}' # 处理查询参数 if request.query_string: target_url += f'?{request.query_string.decode("utf-8")}' # 转发请求 response = requests.request( method=request.method, url=target_url, headers={k: v for k, v in request.headers if k != 'Host'}, data=request.get_data(), cookies=request.cookies, allow_redirects=False ) # 构造响应返回 headers = [(k, v) for k, v in response.raw.headers.items()] return Response(response.content, status=response.status_code, headers=headers)
二、前端Socket.IO客户端的正确配置
前端要连接Flask的地址,而不是直接连Node.js的8888端口,这样请求才会走Flask的代理:
// 你的静态页面index.html中 <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script> <script> // 连接Flask的根路径,指定socket.io的路径 const socket = io('/', { path: '/socket.io' }); socket.on('connect', () => { console.log('成功通过Flask代理连接到Socket.IO服务!'); }); socket.on('message', (msg) => { console.log('收到Node.js消息:', msg); }); </script>
三、Node.js Socket.IO服务的跨域配置
因为Flask运行在5000端口,Node.js在8888,必须允许Flask的域名跨域访问:
const http = require('http'); const { Server } = require('socket.io'); const server = http.createServer(); const io = new Server(server, { cors: { origin: 'http://localhost:5000', // 允许Flask的地址 methods: ['GET', 'POST'], credentials: true } }); io.on('connection', (socket) => { console.log('用户通过Flask代理连接'); socket.emit('message', 'Hello from Node.js Socket.IO!'); }); server.listen(8888, () => { console.log('Socket.IO服务运行在8888端口'); });
四、调试排查技巧
- 打开浏览器开发者工具,查看Network标签下的/socket.io请求,检查请求是否成功转发,响应状态码是否正常(101表示WebSocket升级成功)。
- 确保Flask用支持WebSocket的服务器运行,比如用
gunicorn -k gevent app:app启动,而不是Flask自带的开发服务器(自带的不支持WebSocket)。 - 检查Node.js的日志,看是否有连接请求进来,确认代理是否生效。
内容的提问来源于stack exchange,提问作者barha




