参考:https://medium.com/@adrianhuber17/how-to-build-a-simple-real-time-application-using-flask-react-and-socket-io-7ec2ce2da977
安装依赖
pip install flask-socketio eventlet -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
flask 代码
import base64
import time
from log import logger
from baseapp import app
from flask import Flask,render_template,request
from flask_socketio import SocketIO, emit
import subprocess
import requests
socketio = SocketIO(app,debug=True,cors_allowed_origins='*',async_mode='eventlet', path='/ws/socket.io') # path 为可选参数,如果 path 不填写,那么默认是 '/socket.io'
@app.route('/home')
def main():
return render_template('player.html')
@socketio.on("my_event")
def checkping():
for x in range(5):
logger.debug("ping 172.10.0.1 ...")
cmd = 'curl -vvv 172.17.0.1 2>&1 | head -n 1'
listing1 = subprocess.run(cmd,stdout=subprocess.PIPE,text=True,shell=True)
sid = request.sid
emit('server', {"data1":x, "data": str(time.time()) + " " + listing1.stdout.strip()}, room=sid)
socketio.sleep(1)
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Cache-control" content="no-cache" charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="/static/ajax/libs/socket.io/3.0.4/socket.io.js" integrity="sha512-aMGMvNYu8Ue4G+fHa359jcPb1u+ytAF+P2SCb+PxrjCdO3n3ZTxJ30zuH39rimUggmTwmh2u7wvQsDTHESnmfQ==" crossorigin="anonymous"></script>
<link href="/static/css/bootstrap/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="/static/js/bootstrap/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script type="text/javascript" src="/static/js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="/static/js/pcm-player.js"></script>
<script type="text/javascript" src="/static/js/base64.min.js"></script>
<style>
.navbar-brand {
font-family: 'Merriweather';
font-size: 30px;
}
.inner {
text-align: center;
margin: auto;
margin-top: 10px;
padding: 10px;
border-style: solid;
width: 50%;
color: black;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href={{ url_for('main') }}>Flask</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</nav>
<div class="container">
<div class="inner">
<div id="header"></div><br>
<button class="btn btn-primary" id="checkbutton" onClick="myupdate()">Submit</button>
<div id="demo"></div>
</div>
</div>
<script>
var player = new PCMPlayer({
inputCodec: 'Int16',
channels: 2,
sampleRate: 12000,
flushTime: 500,
fftSize: 32768
});
const socket = io(); //socketio connection to server//
$(document).ready(function() {});
socket.on("connect", () => {
console.log("connected");
document.getElementById("header").innerHTML = "<h3>" + "Websocket Connected" + "</h3";
});
socket.on("disconnect", () => {
console.log("disconnected");
document.getElementById("header").innerHTML = "<h3>" + "Websocket Disconnected" + "</h3>";
});
function myupdate() {
//Event sent by Client
console.log("send message");
socket.emit("my_event", function() {});
//socket.emit("tts-stream", {"text": "金城银行作为一家数字银行,主要提供安全、在线、普惠的智能金融服务。它专注于服务中小微企业以及广大消费者,通过数字化方式来提升金融服务的效率和便捷性。"});
}
// Event sent by Server//
socket.on("server", function(msg) {
let myvar = JSON.parse(msg.data1);
//Check if entire data is sent by server//
if (myvar == "4") {
document.getElementById("demo").innerHTML = "";
document.querySelector('#checkbutton').innerText = "Submit";
document.getElementById("checkbutton").style.cursor = "pointer";
document.getElementById("checkbutton").disabled = false;
document.getElementById("checkbutton").className = "btn btn-primary";
} else {
document.getElementById("demo").innerHTML += msg.data + "<br>";
document.getElementById("checkbutton").disabled = true;
document.getElementById("checkbutton").innerHTML = "Loading..";
document.getElementById("checkbutton").style.cursor = "not-allowed";
document.getElementById("checkbutton").style.pointerEvents = "auto";
}
});
socket.on("tts-stream-pcm", function(msg) {
var b64pcm = msg.b64pcm;
//console.log(b64pcm);
//const binString = atob(b64pcm);
//var binpcm = Uint8Array.from(binString, (m) => m.codePointAt(0));
var binpcm = Base64.toUint8Array(b64pcm);
player.feed(binpcm);
});
</script>
</body>
</html>
创建 socket 的时候,还可以指定 url path(参考:https://github.com/miguelgrinberg/Flask-SocketIO/issues/302)
const socket = io({'path': '/ws/socket.io'});
启动服务代码
socketio.run(app, host='0.0.0.0', port=80)
或者
gunicorn --reload --bind 0.0.0.0:80 -w 1 --threads 30 --worker-class eventlet app:app
注意⚠️:使用 gunicorn
启动的时候需要增加参数 --worker-class eventlet
,具体可以参考:https://flask-socketio.readthedocs.io/en/latest/deployment.html
socket.io 文档
文档:https://socket.io/docs/v4/client-api/
socket.on("connect", () => {
const engine = socket.io.engine;
console.log(engine.transport.name); // in most cases, prints "polling"
engine.once("upgrade", () => {
// called when the transport is upgraded (i.e. from HTTP long-polling to WebSocket)
console.log(engine.transport.name); // in most cases, prints "websocket"
});
engine.on("packet", ({ type, data }) => {
// called for each packet received
});
engine.on("packetCreate", ({ type, data }) => {
// called for each packet sent
});
engine.on("drain", () => {
// called when the write buffer is drained
});
engine.on("close", (reason) => {
// called when the underlying connection is closed
});
});
socket.io.on("error", (error) => {
// ...
});
socket.io.on("ping", () => {
// ...
});
socket.io.on("reconnect", (attempt) => {
// ...
});
import { io } from "socket.io-client";
const socket = io("ws://example.com/my-namespace", {
reconnectionDelayMax: 10000,
auth: {
token: "123"
},
query: {
"my-key": "my-value"
}
});
指定 namespace
- 前端指定 namespace
socket = io("ws://example.com/my-namespace")
namespace 是 my-namespace
- 后端指定 namespace
@socketio.on('my event', namespace='/my-namespace')
def handle_my_custom_namespace_event(json):
print('received json: ' + str(json))
如果前后端 namespace 不匹配,则无法通信