flask 启动 websocket socket.io

创建日期: 2024-08-20 14:34 | 作者: 风波 | 浏览次数: 17 | 分类: Flask

参考: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

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 不匹配,则无法通信

17 浏览
0 评论