Flask和Vue框架实现WebSocket消息通信

news2024/12/16 6:59:49

1 安装环境

1.1 安装Flask环境

主要的安装包 Flask、Flask-SocketIO,注意Python版本要求3.6+

# Flask-SocketIO参考地址
https://flask-socketio.readthedocs.io/en/latest/
https://github.com/miguelgrinberg/flask-socketio

更新基础环境

# 更新pip
python -m pip install --upgrade pip

# 更新setuptools
pip install --upgrade setuptools

# 安装Flask
pip install flask
pip install flask_cors

# 安装关于SocketIO的包
# 安装python-socketio时,会自动安装python-engineio依赖
pip install python-socketio
pip install flask-socketio

# eventlet具有WSGI支持的异步框架,主要功能是通过协程实现并发
pip install eventlet

我的“requirements.txt”的包

bidict==0.22.1
blinker==1.7.0
click==8.1.7
colorama==0.4.6
dnspython==2.4.2
eventlet==0.33.3
Flask==3.0.0
Flask-Cors==4.0.0
Flask-SocketIO==5.3.6
greenlet==3.0.1
h11==0.14.0
importlib-metadata==7.0.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
python-engineio==4.8.0
python-socketio==5.10.0
simple-websocket==1.0.0
six==1.16.0
Werkzeug==3.0.1
wsproto==1.2.0
zipp==3.17.0

安装命令

pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

1.2 安装Vue环境

(1)使用vite创建项目

# 创建项目
npm create vite@latest

# 选择框架和语言,我选择的是Vue和TypeScript
√ Project name: ... websocket
√ Select a framework: » Vue
√ Select a variant: » TypeScript

Done. Now run:

  cd websocket
  npm install
  npm run dev

# 进入目录
cd websocket

# 安装相关包
npm install
# 安装包结束后提示如下:
added 44 packages, and audited 45 packages in 14s

(2)安装依赖包

# 安装一个包即可
npm install socket.io-client -S

socket.io-client的参考地址

https://socket.io/docs/v4/client-api/

查看npm包版本和例子的网络地址

https://www.npmjs.com/

备注:npm中命令参数的意思

--global(-g):表示全局全局安装,包安装在了node目录下的node_modules/npm中(可以在任意项目中使用该工具);不使用-g安装,包安装在了工程目录下的node_modules中下。
--save(-S):表示写入package.json文件中的dependencies,这里面的包是发布到生产环境中的,例如:vue、axios等。
--save-dev(-D):表示写入package.json文件中的devDependencies,这里面的包是仅在开发环境中辅助开发,在生产环境中不需要,例如:vite、css-loader等。
不使用参数时:npm使用的是--save(-S)命令

package.json文件

{
  "name": "websocket",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "socket.io-client": "^4.7.2",
    "vue": "^3.3.8"
  },
  "devDependencies": {
    "@types/node": "^20.10.2",
    "@vitejs/plugin-vue": "^4.5.0",
    "typescript": "^5.2.2",
    "vite": "^5.0.0",
    "vue-tsc": "^1.8.22"
  }
}

(3)使用Vscode常见报错

报错1

Cannot find module './App.vue' or its corresponding type declarations.ts(2307)

解决方法

修改项目根目录中 “env.d.ts” 文件,添加如下内容:

/// <reference types="vite/client" />
declare module "*.vue" {
    import { DefineComponent } from "vue"
    const component: DefineComponent<{}, {}, any>
    export default component
}

报错2

Module xx.vue has no default export.Vetur(1192)

解决方法

卸载vetur插件,主要针对vue2项目;
安装Volar插件,全称Vue Language Features (Volar),主要针对vue3和TypeScript项目;

报错3

使用“@”无法导入ts文件,错误符号指向“@”,Cannot find module 'XXXXs' or its corresponding type declarations

解决方法

  • 安装@types/node,会在项目中生成一个vite-env.d.ts文件
npm i @types/node -D
  • 在vite.config.js文件中配置参数
import  defineConfig ] from 'vite';
import vue from '@vitejs/plugin-vue'
 
import path from "path";
 
export default defineConfig({
    plugins: [vue()],
    resolve: {
        // !!!!配置路径别名!!!
        alias: {
            '@': path.resolve(__dirname,'./src'),
        }
    },
});
  • 在 tsconfig.json中配置代码
"compilerOptions":{
	"baseUrl": "./",
	"paths":{
		"@": ["src"],
		"@/*": ["src/*"],
	}
}

(4)启动服务

在vscode中打开,在doc上启动服务

npm run dev

1.3 版本要求

flask-socketio的版本兼容

JavaScript Socket.IO versionSocket.IO protocol revisionEngine.IO protocol revisionFlask-SocketIO versionpython-socketio versionpython-engineio version
0.9.x1, 21, 2Not supportedNot supportedNot supported
1.x and 2.x3, 434.x4.x3.x
3.x and 4.x545.x5.x4.x

1.4 Flask-SocketIO消息隔离

我认为有三种隔离级别

隔离级别对应策略说明
全局/全局命名空间,连接到此命名空间下的客户端会收到消息,系统默认空间
空间namespace特定命名空间,连接到此命名空间的客户端会收到消息,不同命名空间相互隔离
room在某命名空间下的用户组,不同用户组仅仅接收自己所在组的消息,不同组消息隔离

2 Flask项目

2.1 项目布局

在这里插入图片描述

2.2 源代码

main.py

from blueprint_user import init_blueprint
from config_app import socketio, app


# 初始化蓝本
from socket_comm import init_socket

init_blueprint()

init_socket()


if __name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)

config_app.py

from flask import Flask
from flask_cors import CORS

from flask_socketio import SocketIO


def create_app():
    flask_app = Flask(__name__)
    # 设置密钥
    flask_app.config['token'] = '123456'
    CORS(flask_app, supports_credentials=True)
    return flask_app


def create_socketio():
    flask_socketio = SocketIO()
    # 解决跨域问题
    flask_socketio.init_app(app, cors_allowed_origins='*')

    return flask_socketio


app = create_app()

socketio = create_socketio()

name_space_user = "/user"

blueprint_user.py

from flask import Blueprint

from config_app import app, name_space_user, socketio

user = Blueprint("user", __name__)


# 注册蓝本
def init_blueprint():
    app.register_blueprint(user, url_prefix='/user')


@user.route('/')
def index():
    return "Success"


# 使用方法传递参数
@user.route('/broad')
def broad_event():
    event_name = "data_res"
    broad_casted_data = {'data': "test message!"}
    # 发送消息
    socketio.emit(event_name, broad_casted_data, namespace=name_space_user)
    return "success"

socket_comm.py

from flask_socketio import emit, send, join_room, leave_room

from config_app import socketio, name_space_user


def init_socket():
    pass


# 可以用“@socketio.event”替代“@socketio.on('connect')”
# “@socketio.event”可以用来装饰socketio默认的事件,例如:connect等
@socketio.on('connect', namespace=name_space_user)
def connected_msg(auth:dict):
    print(auth)
    print('client connected.')
    if(auth.get("token") == "123"):
        return True
    else:
        return False


@socketio.on('disconnect', namespace=name_space_user)
def disconnect_msg():
    print('client disconnected.')


@socketio.on('data_event', namespace=name_space_user)
def test_event(message):
    print(message)
    # emit(): 发送到指定活动上,对应前端的data_res
    """
        # 对应前端的代码
        socket.on('data_res', (data:string) => {
            console.log('监听消息:');
            console.log(data);
        });
    
    """
    emit('data_res', message, broadcast=True)


@socketio.on('data_msg', namespace=name_space_user)
def test_msg(message):
    print(message)
    # send(): 发送到message,对应前端的message
    """
        # 对饮前端的代码
        socket.on("message", function(data: string){
            console.log("Message" + data)
        })
    """
    send(message, namespace=name_space_user, broadcast=True)


@socketio.on('join', namespace=name_space_user)
def on_join(data):
    username = data['username']
    room = data['room']
    join_room(room)
    print(data)
    send(username + ' has entered the room.', to=room)


@socketio.on('leave', namespace=name_space_user)
def on_leave(data):
    username = data['username']
    room = data['room']
    leave_room(room)
    print(data)
    send(username + ' has left the room.', to=room)


@socketio.on('send_room', namespace=name_space_user)
def send_room(data):
    username = data['username']
    room = data['room']
    print(data)
    send(data, to=room)

3 Vue项目

构建项目使用的是“组合式 API (Composition API)”编写,不是“选项式 API (Options API)”

3.1 项目布局

在这里插入图片描述

3.2 源代码

main.ts

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

createApp(App).mount('#app')

vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'


import path from "path";

// https://vitejs.dev/config/
export default defineConfig({
  // 配置插件列表
  plugins: [vue()],
  resolve: {
    // !!!!配置路径别名!!!
    alias: {
        '@': path.resolve(__dirname,'./src'),
    },
  },

  // 打包配置
  build: {
    target: 'modules',
    // 设置输出路径
    outDir: 'dist'
  },

  // 本地运行配置,及反向代理配置
  server: {
    // 默认启用并允许任何源
    cors: true,
    // 使用默认浏览器中打开应用程序
    open: true,
    // 设置本地端口
    port: 4000,
    // 在本地开发环境中,设置反向代理配置,注意配置rewrite
    proxy: {
      // '/api': {
      //   // 设置代理接口访问实际地址
      //   target: 'http://localhost/5000',
      //   changeOrigin: true,
      //   // 允许websocket代理
      //   ws: true,
      //   // 将api替换为空
      //   rewrite: (path) => path.replace(/^\/api/, '')
      // },
      // '/socket.io': {
      //   target: `ws://127.0.0.1:5000`,
      //   ws: true,
      //   changeOrigin: true
      //  },
    }
  }
})

App.vue

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <div class="app">
    <HelloWorld msg="app" />
  </div>
  
</template>

<style scoped>
.app{
  width: 100%;
  height: 100%;
}
</style>

在”config/ws“目录下:

index.ts


/*

import { io } from 'socket.io-client';

// 第一种连接方法
// 参考地址:https://socket.io/docs/v4/client-api/#io
export function create_socket(token: string) {
    const socket = io('http://127.0.0.1:5000', {
        // 指定传输方式,如WebSocket    
        transports: ['websocket'],
        // 是否自动连接
        autoConnect: true,
        // 是否自动重新连接
        reconnection: true,
        // 重新连接尝试次数
        reconnectionAttempts: 3,
        // 重新连接延迟时间(毫秒)
        reconnectionDelay: 1000,
        // 自定义查询参数
        // query: {
        //     token: token
        // },
        // 其他可选参数...
    });

    return socket;
}

*/


// 第二种连接方法
// 参考地址:https://socket.io/docs/v4/client-api/#manager
import { Manager } from "socket.io-client";

export function create_socket(token: string) {
    const manager = new Manager("ws://127.0.0.1:5000", {
        // 指定传输方式,如WebSocket    
        transports: ['websocket'],
        // 是否自动连接
        autoConnect: true,
        // 是否自动重新连接
        reconnection: true,
        // 重新连接尝试次数
        reconnectionAttempts: 3,
        // 重新连接延迟时间(毫秒)
        reconnectionDelay: 1000,
        // 自定义查询参数
        // query: {
        //     token: token
        // },
        // 其他可选参数...
    });

    const socket = manager.socket("/user", {
        auth: {
            token: token
        }
    });

    return socket
}

在”components“目录下:

HelloWorld.vue

<script setup lang="ts">
import { ref, onMounted } from 'vue'

import { create_socket } from '@/config/ws/index.ts';
const socket = create_socket("123");


defineProps<{ msg: string }>()


function re_open_conn(){
  // // 第一种方法连接
  let connect_res = socket.open();
  console.log(connect_res)
  
  // console.log(connect_res.connected)
  
  // 第二种方法连接
  // 默认通道 connect是通道名称
  // socket.on('connect', () => {
  //   console.log('连接成功');
  // });
}

function close_conn(){
  // 关闭连接,disconnect_res是一个Socket对象
  // socket.disconnect()和socket.close()作用相同
  // 对应后端的代码
  // @socketio.on('disconnect', namespace=name_space_user)
  let disconnect_res = socket.disconnect()
  console.log(disconnect_res)

  // 下面这种方法后端没反应
  // socket.on("disconnect", () => {
  //   console.log("关闭连接")
  // });
}

// 响应式状态
let msg = ref("测试")

function send_msg(){
  console.log("data_event" + msg.value)
  socket.emit("data_event", "Event " + msg.value)
  console.log(" data_msg "+ msg.value);
  socket.emit("data_msg", "Message " + msg.value)
}

// 更改状态、触发更新的函数
function listen_event() {

  socket.on('data_res', (data:string) => {
    console.log('监听事件(event)-对应后端emit()方法');
    console.log(data);
  });

  socket.on("message", function(data:string){
    console.log("监听消息(message)-对应后端send()方法:")
    console.log(data)
  });

}

onMounted(()=>{
  listen_event()
});

function join_room(){
  socket.emit("join", {"username":"zhangsan", "room":"room1"})
}

function leave_room(){
  socket.emit("leave", {"username":"zhangsan", "room":"room1"})
}

function send_room(){
  socket.emit("send_room", {"username":"zhangsan", "room":"room1"})
}


</script>

<template>
  <div class="hello">
    <button v-on:click="re_open_conn">打开连接</button>
    <button v-on:click="close_conn">关闭连接</button>
    <button v-on:click="send_msg">发送消息</button>
    <input v-model="msg"/>

    <br/>
    <button v-on:click="join_room">进入房间</button>
    <button v-on:click="leave_room">离开房间</button>
    <button v-on:click="send_room">房间发送消息</button>
  </div>
</template>

<style scoped>
.hello {
  color: #888;
}
</style>

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Vue + TS</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

4 运行结果

前端

在这里插入图片描述

后端
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1299588.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考高级备考-系统架构师(机考后新版教材的备考过程与资料分享)

软考高级-系统架构设计师 考试复盘1.考试结果2.备考计划3.个人心得 资料分享 考试复盘 1.考试结果 三科压线过&#xff0c;真是太太太太太太太幸运了。上天对我如此眷顾&#xff0c;那不得不分享下我的备考过程以及一些备考资料&#xff0c;帮助更多小伙伴通过考试。 2.备考…

HarmonyOS4.0从零开始的开发教程10Video组件的使用

HarmonyOS&#xff08;九&#xff09;Video组件的使用 概述 在手机、平板或是智慧屏这些终端设备上&#xff0c;媒体功能可以算作是我们最常用的场景之一。无论是实现音频的播放、录制、采集&#xff0c;还是视频的播放、切换、循环&#xff0c;亦或是相机的预览、拍照等功能…

jQuery中点击按钮发送多次请求

jQuery中点击按钮发送多次请求 /* 采用以下常规触发按钮去执行回调函数&#xff0c;可能会发送多次请求。并且会影响到数据库表 原因分析&#xff1a;可能是前端原型&#xff0c;绑定了多次事件。 */ $("#saveBtn").click(function (){$.ajax({}) }/* 解决办法如下&a…

windows下分卷解压文件

我的文件是这样的&#xff1a; 存放路径为&#xff1a;C:\Users\Luli_study\MICCAI_MMAC\fudanuniversity\DDR dataset 首先要进入分卷文件的目录cd&#xff1a; 第一步&#xff1a;cd /path/o/分卷问文件目录 第二步&#xff1a; 执行之后的结果(红色框出来的)&#xff1a; …

基于Spring+Spring boot的SpringBoot在线电子商城管理系统

SSM毕设分享 基于SpringSpring boot的SpringBoot在线电子商城管理系统 1 项目简介 Hi&#xff0c;各位同学好&#xff0c;这里是郑师兄&#xff01; 今天向大家分享一个毕业设计项目作品【基于SpringSpring boot的SpringBoot在线电子商城管理系统】 师兄根据实现的难度和等级…

装修干货,卫生间干湿分离的5个建议。福州中宅装饰,福州装修

你是否也曾为卫生间的干湿分离烦恼&#xff1f;小编今天就给大家带来5点建议&#xff0c;让你轻松解决这个问题&#xff01; ①使用玻璃淋浴房 使用玻璃淋浴房是一种常见的干湿分离方法。玻璃淋浴房可以防水、防滑&#xff0c;而且清洁起来也比较方便。 ②使用淋浴屏风 淋浴屏…

【展望2024】,从软件测试用例开始学习起

1. 测试用例的概念 测试用例就是测试人员向被测试系统发起的一组集合&#xff0c;该集合包括测试环境&#xff0c;测试数据&#xff0c;测试步骤&#xff0c;预期结果 2. 设计测试用例的好处 在测试前都要先设计测试用例&#xff0c;设计测试用例有如下好处&#xff1a; 测…

Java数据结构《二叉排序树的插入删除和查找》(难度系数100)

一、前言&#xff1a; 这是怀化学院的&#xff1a;Java数据结构中的一道难度偏难(偏难理解)的一道编程题(此方法为博主自己研究与学习一名叫qing影的博主&#xff0c;问题基本解决&#xff0c;若有bug欢迎下方评论提出意见&#xff0c;我会第一时间改进代码&#xff0c;谢谢&am…

外贸找客户软件工具:BotMaster 18.5 Crack

BotMaster 拓展您的业务大师增加销量Whatsapp营销&#xff0c;使用 BotMaster 将您的业务提升到新的水平 - 最强大的 WhatsApp 营销软件&#xff0c;可促进销售和发展您的业务。 BotMaster 是一款革命性的 WhatsApp 营销工具&#xff0c;于 2022 年 1 月推出。这款功能强大的工…

zabbix、netdata和glances,做最简单的系统资源监控

软件需要显示服务器的资源信息&#xff08;CPU、内存、网络、硬盘等&#xff09;&#xff0c;但是软件是在Docker容器中运行。 目前方案 通过ssh在主机上远程运行ps、free等指令&#xff0c;获取相应的信息。这种方案需要代码配置主机的IP&#xff0c;以及用户名和密码&#…

语聚AI知识库支持连接数据库,无需上传知识文档,数据分析更高效

数据库系统是企业信息技术基础架构的关键部分&#xff0c;它帮助企业管理和处理其数据&#xff0c;目前已有大量企业通过数据库保存数据&#xff0c;例如员工信息、客户数据、产品销售数据等等。但企业运营变得越来越复杂&#xff0c;数据库中的数据量也在持续增长&#xff0c;…

C++ 学习系列 -- 实现简单的 String

1 标准库 std::string c 中的 std::string 是一个重要的字符串的类, 我们在日常工作中常常与之打交道。 string是C标准库的重要部分&#xff0c;主要用于字符串处理。使用string库需要在同文件中包括该库 #include<string> std::string 实际上是 std::basic_string<…

开源的局域网文件共享工具迎来大更新

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 hello&#xff0c;伙伴们又有几天的时间不见了。 我的文章也要改变一下日更的策略了&#xff0c;变成坚持更…

什么叫应用加速,什么情况需要用到应用加速

应用加速的定义 应用加速依赖节点之间的高速通道、转发集群及智能路由技术&#xff0c;实现各地用户的就近接入&#xff0c;通过高速通道直达源站区域&#xff0c;帮助业务解决用户访问卡顿或者延迟过高的问题。使用高速网络和全球范围内的节点技术&#xff0c;确保传输时间和…

HTML行内元素与块级元素有哪些及区别

文章目录 一、HTML常见的行内元素二、HTML常见的块级元素三、行内元素与块级元素的区别 一、HTML常见的行内元素 最常用的是span&#xff0c;其他还有a、 img、 input、textarea、select、label 还有包括一些文本元素如&#xff1a;br 、b、 strong、sup 、sub、 i、em 、del、…

关于PCIE显卡的电源供电 6pin或 8pin 转接问题

问题背景 市面销售的电源五花八门 非模组电源 有一些只有 68pin&#xff0c;对于 8pin的电源缺少 2pin供电 模组或半模组电源 模组电源可能某些线材丢失半模组电源&#xff0c;如果线材丢失&#xff0c;在默认线材不够的情况下&#xff0c;可能面临跟全模组丢线一样的问题 市…

期末速成数据库极简版【查询】(3)

目录 多表查询 【8】多表连接——内连接 &#x1f642;等值连接 &#x1f642;自然连接 &#x1f642;非等值连接 【9】多表连接——外连接 【10】交叉连接不考 【11】联合查询 【12】扩展多表连接 【13】嵌套查询 &#x1f642; 多表查询 【8】多表连接——内连…

图神经网络简明教程【GNN】

图神经网络&#xff08;GNN&#xff09;是一组在图领域工作的深度学习方法。 这些网络最近已应用于多个领域&#xff0c;包括&#xff1a; 组合优化、推荐系统、计算机视觉—仅举几例。 这些网络还可用于对大型系统进行建模&#xff0c;例如社交网络、蛋白质-蛋白质相互作用网络…

关于DNS服务器地址总是127.0.0.1且无法解析域名地址

问题 笔者尝试nslookup解释域名时&#xff0c;出现服务器变成本地环回口地址&#xff0c;导致无法解析域名 C:\Users\Zsy>nslookup www.baidu.com 服务器: UnKnown Address: 127.0.0.1*** UnKnown 找不到 www.baidu.com: Server failed排查思路 尝试关闭虚拟网卡&#…

C语言数据结构-双向链表

文章目录 1 双向链表的结构2 双向链表的实现2.1 定义双向链表的数据结构2.2 打印链表2.3 初始化链表2.4 销毁链表2.5 尾插,头插2.6 尾删,头删2.7 根据头次出现数据找下标2.8 定点前插入2.9 删除pos位置2.10 定点后插入 3 完整代码3.1 List.h3.2 Lish.c3.3 test.c 1 双向链表的结…