当前位置: 首页 > news >正文

使用WebSocket实现跨多个服务器传输音频及实时语音识别

下面我的项目信息:

项目架构:
A项目(Websocket客户端 / React前端) => B项目(Websocket客户端 / Java后端)=》C项目(Websocket服务端 / Node.js 后端)

项目功能:
A项目有一个开启语音输入的功能,用户开始说话,获取麦克风输入的数据,将获取到的数据传输到B项目,B项目返回一段模拟的识别文字(随机生成)给A项目,(真实逻辑应该是在C项目生成识别文字给到B,B再给我A,这里我偷懒了)。B项目继续将获取到的麦克风数据传输到C项目,C项目将收集到的音频数据存储到内存缓冲区,等A项目停止讲话的时候,C项目会将缓冲区的数据生成为一个 .wav 格式的音频文件,然后再通过 ffmpeg 将 .wav 格式的音频文件转为 .mp3 的音频文件。


上代码:

代码已经上传到了 gitee,

gitee仓库地址:

A项目和C项目:https://gitee.com/tylerzhong/websocket_frontend
B项目:https://gitee.com/tylerzhong/websocket_middleware

A项目的代码:

AudioWebSocket :(项目路径:src\pages\AudioWebSocket\index.jsx)

import { useState, useRef, useEffect } from 'react';const AudioWebSocket = () => {const [resText, setResText] = useState('');const ws = useRef(null);const record = useRef(null);const waveformCanvas = useRef(null);const audioContextRef = useRef(null);// 开始/结束对讲的数据结构const startData = { is_speaking: true, mode: "2pass", wav_name: "h5" };const endData = { is_speaking: false, mode: "2pass", wav_name: "h5" };// 录音处理器类(完整修复:补全 audioData 结构)class Recorder {constructor(stream) {this.sampleBits = 16; // 输出采样数位this.sampleRate = 16000; // 输出采样率// 完整定义 audioData 对象(包含 input 方法)this.audioData = {type: "wav",size: 0, // 录音文件长度buffer: [], // 录音缓存inputSampleRate: 48000, // 输入采样率inputSampleBits: 16, // 输入采样数位outputSampleRate: this.sampleRate, // 输出采样率outputSampleBits: this.sampleBits, // 输出采样数位// 清理缓存clear: function () {this.buffer = [];this.size = 0;},// 向缓存中添加数据input: function (data) {this.buffer.push(new Float32Array(data));this.size += data.length;},// 合并并压缩数据compress: function () {const mergedData = new Float32Array(this.size);let offset = 0;this.buffer.forEach(chunk => {mergedData.set(chunk, offset);offset += chunk.length;});// 按采样率压缩(输入采样率 / 输出采样率)const compressionRatio = this.inputSampleRate / this.outputSampleRate;const compressedLength = Math.floor(mergedData.length / compressionRatio);const compressedData = new Float32Array(compressedLength);for (let i = 0; i < compressedLength; i++) {compressedData[i] = mergedData[i * compressionRatio];}return compressedData;},// 编码为 PCM 格式encodePCM: function () {const pcmData = this.compress();const byteLength = pcmData.length * (this.outputSampleBits / 8);const buffer = new ArrayBuffer(byteLength);const dataView = new DataView(buffer);for (let i = 0; i < pcmData.length; i++) {const value = pcmData[i];const intValue = value < 0 ? Math.max(-1, value) * 0x8000 :  // 负数范围:-32768 ~ 0Math.min(1, value) * 0x7FFF;    // 正数范围:0 ~ 32767dataView.setInt16(i * 2, intValue, true); // 小端序}return new Blob([dataView], { type: 'audio/wav' });}};this.recording = true; // 录音状态标志// 初始化音频上下文(确保单例)audioContextRef.current = audioContextRef.current || new AudioContext();this.audioInput = audioContextRef.current.createMediaStreamSource(stream);this.recorderNode = audioContextRef.current.createScriptProcessor(4096, 1, 1);// 绑定音频处理回调(使用箭头函数确保 this 指向 Recorder 实例)this.recorderNode.onaudioprocess = (e) => {const inputBuffer = e.inputBuffer.getChannelData(0);this.audioData.input(inputBuffer);this.sendData();this.updateWaveform(inputBuffer);};}// 启动录音(连接音频节点)start() {this.audioInput.connect(this.recorderNode);this.recorderNode.connect(audioContextRef.current.destination);}// 停止录音(断开连接并标记状态)stop() {this.recording = false;this.recorderNode.disconnect();}// 发送数据到 WebSocketsendData() {if (!this.recording) return;const reader = new FileReader();reader.onload = (e) => {const rawData = e.target.result;const byteArray = new Int8Array(rawData);// 分包发送(每包 1024 字节)for (let i = 0; i < byteArray.length; i += 1024) {const chunk = byteArray.slice(i, i + 1024);ws.current.send(chunk);}};// 读取编码后的 PCM 数据reader.readAsArrayBuffer(this.audioData.encodePCM());this.audioData.clear(); // 清理缓存}// 更新音浪效果updateWaveform(inputBuffer) {const canvas = waveformCanvas.current;if (!canvas) return;const ctx = canvas.getContext('2d');const width = canvas.width;const height = canvas.height;ctx.clearRect(0, 0, width, height);ctx.fillStyle = '#106AE8';const numBars = 20;const barWidth = width / (numBars * 3);let x = 15;for (let i = 0; i < numBars; i++) {const sampleIndex = Math.floor(i * (inputBuffer.length / numBars));const barHeight = (Math.abs(inputBuffer[sampleIndex]) * height * 6) / 2;ctx.fillRect(x, height / 2 - barHeight, barWidth, barHeight * 2);x += barWidth + 4;}}}// 初始化 WebSocketconst initWebSocket = () => {ws.current = new WebSocket('ws://localhost:8081/ws/a'); // 替换为实际地址ws.current.binaryType = 'arraybuffer';ws.current.onopen = () => {ws.current.send(JSON.stringify(startData));console.log('WebSocket 连接成功');if (record.current) {record.current.start(); // 启动录音}};ws.current.onmessage = (msg) => {const res = JSON.parse(msg.data);setResText(res.text); // 更新识别文字};ws.current.onerror = (err) => {console.error('WebSocket 错误:', err);};};// 开始对讲const startIntercom = async () => {try {// 申请麦克风权限const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });record.current = new Recorder(mediaStream); // 创建 Recorder 实例initWebSocket(); // 初始化 WebSocket} catch (error) {console.error('麦克风权限申请失败:', error);}};// 关闭对讲const endIntercom = () => {if (ws.current) {ws.current.send(JSON.stringify(endData)); // 发送结束标志record.current?.stop(); // 停止录音}};// 组件卸载清理useEffect(() => {return () => {if (ws.current) {ws.current.close(); // 关闭 WebSocket}if (audioContextRef.current) {audioContextRef.current.close(); // 关闭音频上下文}};}, []);return (<div className="mainContent"><button onClick={startIntercom}>开始对讲</button><button onClick={endIntercom}>关闭对讲</button><div>语音识别的文字为:{resText || '--'}</div><canvas ref={waveformCanvas} width="200" height="20" /></div>);
};export default AudioWebSocket;

B项目的代码:

WebSocketConfig:(项目路径:src/main/java/com/tyler/config/WebSocketConfig.java)

package com.tyler.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class WebSocketConfig {// 注册 ServerEndpointExporter,用于扫描所有 @ServerEndpoint 注解的类@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

WebSocketClient:(项目路径:src/main/java/com/tyler/voice/WebSocketClient.java)

package com.tyler.voice;import jakarta.websocket.Endpoint;
import jakarta.websocket.EndpointConfig;
import jakarta.websocket.Session;
import org.glassfish.tyrus.client.ClientManager;import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;public class WebSocketClient {private String cServerUrl;private Session cSession;private CountDownLatch connectLatch = new CountDownLatch(1);public WebSocketClient(String cServerUrl) {this.cServerUrl = cServerUrl;}// 连接public void connect() {new Thread(() -> {ClientManager client = ClientManager.createClient();try {client.connectToServer(new Endpoint() {@Overridepublic void onOpen(Session session, EndpointConfig config){cSession = session;connectLatch.countDown();System.out.println("已连接到 C项目 WebSocket 服务");}}, new URI(cServerUrl));connectLatch.await(); // 等待连接建立} catch (Exception e) {System.out.println("连接 C项目失败:"+e.getMessage());}}).start();}// 发送文本消息(新增方法)public void sendText(String text) {if (cSession != null && cSession.isOpen()) {try {cSession.getBasicRemote().sendText(text); // 直接发送文本} catch (IOException e) {System.out.println("向 C项目发送文本失败:"+e.getMessage());}}}// 保留原发送二进制数据的方法(用于音频数据)public void send(byte[] data) {if (cSession != null && cSession.isOpen()) {try {cSession.getBasicRemote().sendBinary(ByteBuffer.wrap(data));} catch (IOException e) {System.out.println("向 C项目发送数据失败:"+e.getMessage());}}}// 断开与C项目的连接public void disconnect() {if (cSession != null) {try {cSession.close();} catch (IOException e) {System.out.println("断开 C项目连接失败:"+e.getMessage());}}}}

WebSocketServer:(项目路径:src/main/java/com/tyler/voice/WebSocketServer.java)

package com.tyler.voice;import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Component
@ServerEndpoint(value = "/ws/a")
public class WebSocketServer {private static final ConcurrentHashMap<String, Session> aSessions = new ConcurrentHashMap<>();private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);private WebSocketClient cClient = new WebSocketClient("ws://localhost:8082/ws/c");@OnOpenpublic void onOpen(Session session) {String sessionId = session.getId();aSessions.put(sessionId, session);System.out.println("A项目连接已建立,SessionID: " + sessionId);cClient.connect(); // 连接 C 项目}@OnMessagepublic void onMessage(Session session, String controlMsg) {System.out.println("收到控制消息:" + controlMsg);try {JSONObject jsonObject = new JSONObject();jsonObject.put("text", "----------------已经收到数据了。。。。。。。。。。。。。。。");session.getBasicRemote().sendText(JSON.toJSONString(jsonObject));} catch (IOException e) {System.out.println("发送数据到A项目失败");}cClient.sendText(controlMsg);}@OnMessagepublic void onMessage(Session session, byte[] audioData) {System.out.println("收到A项目语音数据,长度:" + audioData.length + " bytes");try {JSONObject jsonObject = new JSONObject();Random random = new Random();jsonObject.put("text", random.nextInt(10, 100));session.getBasicRemote().sendText(JSON.toJSONString(jsonObject));} catch (IOException e) {System.out.println("发送数据到A项目失败");}cClient.send(audioData);}@OnClosepublic void onClose(Session session) {String sessionId = session.getId();scheduler.schedule(() -> {cClient.disconnect();aSessions.remove(sessionId);System.out.println("A项目连接已关闭,SessionID: " + sessionId);}, 500, TimeUnit.MICROSECONDS);}@OnErrorpublic void onError(Session session, Throwable error) {// 忽略因连接关闭导致的 IOException (关键)if (!(error instanceof IOException && error.getMessage() != null && error.getMessage().contains("中止了一个已建立的连接"))) {System.out.println("A项目连接异常:"+error.getMessage());}}}

application.yml: (项目路径:src/main/resources/application.yml)

server:port: 8081

C 项目代码:

websocket-node.js:(项目路径:websocket-node.js)

const WebSocket = require('ws');
const fs = require('fs');
const { exec } = require('child_process');
const path = require('path');const wss = new WebSocket.Server({ port: 8082 });
const clientBuffers = new Map(); // 存储 { buffer: Buffer[], isEnd: boolean }// 生成唯一客户端 ID
const generateClientId = () => {return `client-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
};// 计算缓冲区总长度
const sumBufferLength = (clientId) => {return clientBuffers.get(clientId).buffer.reduce((acc, chunk) => acc + chunk.length, 0);
};// FFmpeg 转码(WAV → MP3)
const convertToMp3 = (wavPath, mp3Path, callback) => {const ffmpegCmd = `ffmpeg -i "${wavPath}" -vn -ab 128k -ar 44100 -y "${mp3Path}"`;exec(ffmpegCmd, (error, stdout, stderr) => {if (error) {callback(`转码失败: ${error.message}`);return;}callback(null, mp3Path);});
};wss.on('connection', (ws) => {const clientId = generateClientId();clientBuffers.set(clientId, { buffer: [], isEnd: false });console.log(`新客户端连接: ${clientId}`);ws.on('message', (data) => {let controlMsg = null;const isJson = data.toString().startsWith('{') && data.toString().endsWith('}'); // 快速判断是否可能是 JSONtry {if (isJson) {controlMsg = JSON.parse(data.toString());}} catch (error) {// 故意空着,让 controlMsg 保持 null}if (controlMsg !== null) {// 处理控制消息console.log(`[${clientId}] 收到控制消息:`, controlMsg);if (controlMsg.is_speaking === false) {clientBuffers.get(clientId).isEnd = true;processAudio(clientId);}} else {// 处理音频数据(支持 Buffer/ArrayBuffer/Blob)let bufferData;if (data instanceof Buffer) {bufferData = data;} else if (data instanceof Uint8Array) {bufferData = Buffer.from(data);} else if (data instanceof ArrayBuffer) {bufferData = Buffer.from(data);} else {console.warn(`[${clientId}] 不支持的数据类型:`, typeof data);return;}clientBuffers.get(clientId).buffer.push(bufferData);console.log(`[${clientId}] 收到音频数据块,当前总长度: ${sumBufferLength(clientId)}`);}});ws.on('close', () => {console.log(`[${clientId}] 连接关闭,处理剩余数据`);processAudio(clientId); // 连接关闭时处理残留数据});ws.on('error', (error) => {console.error(`[${clientId}] 连接错误:`, error);processAudio(clientId); // 错误时强制处理});
});// 处理音频数据的核心函数
const processAudio = (clientId) => {const clientData = clientBuffers.get(clientId);if (!clientData || clientData.buffer.length === 0 && !clientData.isEnd) {clientBuffers.delete(clientId);return;}const audioBuffer = Buffer.concat(clientData.buffer);clientBuffers.delete(clientId);if (audioBuffer.length === 0) {console.log(`[${clientId}] 无有效音频数据`);return;}// 保存临时 WAV 文件(补充 WAV 头)const wavBuffer = addWavHeader(audioBuffer);const tempWavPath = path.join(__dirname, 'temp', `${clientId}.wav`);const outputMp3Path = path.join(__dirname, 'output', `${clientId}.mp3`);fs.writeFile(tempWavPath, wavBuffer, (err) => {if (err) {console.error(`[${clientId}] 写入临时文件失败:`, err);return;}convertToMp3(tempWavPath, outputMp3Path, (error, mp3Path) => {if (error) {console.error(`[${clientId}] ${error}`);} else {console.log(`[${clientId}] MP3 保存成功: ${mp3Path}`);}// fs.unlink(tempWavPath, () => {}); // 清理临时文件});});
};// 补充 WAV 头(与 A 项目采样率一致:16000Hz, 16bit, 单声道)
const addWavHeader = (pcmBuffer) => {const sampleRate = 16000; // A 项目 Recorder 中设置的采样率const numChannels = 1; // 单声道const bitDepth = 16; // 16bit 采样// WAV 头总长度固定为 44 字节,后续是 PCM 数据const wavBuffer = Buffer.alloc(44 + pcmBuffer.length);// 写入 RIFF 头(4字节)wavBuffer.write('RIFF', 0);wavBuffer.writeUInt32LE(36 + pcmBuffer.length, 4); // 文件总大小(RIFF 头 + WAV 头 + PCM 数据)wavBuffer.write('WAVE', 8);// 写入 fmt 子块(24字节)wavBuffer.write('fmt ', 12);wavBuffer.writeUInt32LE(16, 16); // fmt 子块大小(固定为 16)wavBuffer.writeUInt16LE(1, 20); // 音频格式(PCM = 1)wavBuffer.writeUInt16LE(numChannels, 22); // 声道数wavBuffer.writeUInt32LE(sampleRate, 24); // 采样率wavBuffer.writeUInt32LE((sampleRate * numChannels * bitDepth) / 8, 28); // 字节率(采样率×声道数×位深/8)wavBuffer.writeUInt16LE((numChannels * bitDepth) / 8, 32); // 块对齐(声道数×位深/8)wavBuffer.writeUInt16LE(bitDepth, 34); // 位深// 写入 data 子块(8字节 + PCM 数据)wavBuffer.write('data', 36);wavBuffer.writeUInt32LE(pcmBuffer.length, 40); // PCM 数据大小// 写入 PCM 数据(关键修复:逐个字节复制)pcmBuffer.copy(wavBuffer, 44); // 将 PCM 数据复制到 WAV 缓冲区的 44 字节之后return wavBuffer;
};// 初始化临时目录
fs.mkdirSync(path.join(__dirname, 'temp'), { recursive: true });
fs.mkdirSync(path.join(__dirname, 'output'), { recursive: true });
console.log('C项目启动,端口: 8082');

相关文章:

使用WebSocket实现跨多个服务器传输音频及实时语音识别

下面我的项目信息&#xff1a; 项目架构&#xff1a; A项目&#xff08;Websocket客户端 / React前端&#xff09; > B项目&#xff08;Websocket客户端 / Java后端&#xff09;》C项目&#xff08;Websocket服务端 / Node.js 后端&#xff09; 项目功能&#xff1a; A项目…...

C++ QT图片查看器

private:QList<QString> fs;int i;void MainWindow::on_btnSlt_clicked() {QStringList files QFileDialog::getOpenFileNames(this,"选择图片",".","Images(*.png *.jpg *.bmp)");qDebug()<<files;ui->picList->clear();ui-…...

从AlphaGo到ChatGPT:AI技术如何一步步改变世界?

从AlphaGo到ChatGPT&#xff1a;AI技术如何一步步改变世界&#xff1f; 这里给大家分享一个人工智能学习网站。点击跳转到网站。 https://www.captainbed.cn/ccc 前言 在科技发展的历史长河中&#xff0c;人工智能&#xff08;AI&#xff09;技术无疑是最为璀璨的明珠之一。从…...

跨系统数据烟囱如何破局?豪森智源HSMES重构制造协同新范式‌

‌行业困局&#xff1a;万亿级数据资产沉睡在孤岛中‌ IDC最新报告显示&#xff0c;中国86%的制造企业存在5套以上独立信息系统&#xff0c;设备联网率不足42%的工厂每年因数据断点损失超千万利润。某新能源龙头企业CTO坦言&#xff1a;"ERP、MES、WMS系统各自为政&#…...

MySQL DBA数据运维管理经验分享:新手入门快速提升效率的新工具与技巧

MySQL DBA数据运维管理经验分享:新手入门快速提升效率的新工具与技巧 前言 作为一名数据库管理员(DBA),MySQL的运维管理是我们日常工作的核心。随着技术的不断发展,MySQL运维工具和最佳实践也在不断演进。本文将分享一些实用的MySQL DBA运维经验,并对比分析当前流行的运维…...

通义千问-langchain使用构建(二)

目录 序言xinference应用构建构建过程简单概述成效 chatchat应用构建过程成效 总结 序言 在昨天的使用langchain的基础上。又尝试了构建智能问答应用。 使用langchain chatchat这个开源包&#xff0c;构建了一下智能问答系统。 前置项&#xff0c;是使用了一下xinference框架&…...

无人机动力系统全解析:核心组件、工作原理与实用指南

无人机想要实现稳定飞行与灵活操控&#xff0c;离不开一套高效协同的动力系统。该系统以电机、电子调速器&#xff08;电调&#xff09;、电池和螺旋桨四大核心组件为基础&#xff0c;各部分精密配合&#xff0c;共同驱动无人机翱翔蓝天。接下来&#xff0c;本文将从基础原理入…...

【系统架构师】2025论文《WEB系统性能优化技术》

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本文分享【系统架构师】2025论文《系统可靠性设计》&#xff0c;期待与你一同探索、学习、进步&#xff0c;一起卷起来叭&#xff01; 目录 项目介绍背景介绍系统模块技术栈性能…...

华为Watch的ECG功能技术分析

华为Watch的ECG&#xff08;心电图&#xff09;功能通过硬件传感器、算法优化和医疗认证的结合&#xff0c;实现了对心脏电活动的监测和房颤&#xff08;AFib&#xff09;等心律失常的预警。以下是其技术实现方案的详细解析&#xff1a; 1. 硬件设计&#xff1a;单导联ECG传感…...

2025.05.14华为机考笔试题-第二题-200分

📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 02. 探险家的最佳路径 问题描述 A先生是一位探险家,他需要穿越一片危险的山地区域。这个区域可以用一个 k k k \times k...

FTP 工具 vs. 命令行 SCP/RSYNC

&#x1f539; FTP 工具 vs. 命令行 SCP/RSYNC 对比 场景FTP/SFTP 工具&#xff08;如 Cyberduck、FileZilla&#xff09;命令行&#xff08;scp/rsync&#xff09;适用人群新手、喜欢图形化操作习惯命令行、需要自动化脚本传输少量文件✅ 拖拽即可上传下载⚠️ 需手动输入命令…...

工业HMI触摸屏技术方案:基于EFISH-SCB-RK3588/SAIL-RK3588的国产化替代赛扬N100/N150全场景技术解析

一、核心硬件选型与国产化替代优势‌ ‌1. 算力与架构突破‌ ‌异构八核CPU‌&#xff1a;采用4Cortex-A76&#xff08;2.4GHz&#xff09;4Cortex-A55&#xff08;1.8GHz&#xff09;架构&#xff0c;支持实时控制逻辑、高帧率UI渲染、协议解析多任务并行处理&#xff0c;相…...

MySQL主从复制与读写分离

一、MySQL主从复制&#xff08;Replication&#xff09; 1. 核心原理 主库&#xff08;Master&#xff09;&#xff1a;处理写操作&#xff0c;并将数据变更记录到二进制日志&#xff08;Binary Log, binlog&#xff09;。 从库&#xff08;Slave&#xff09;&#xff1a;通过…...

VMware虚拟机 安装 CentOS 7

原文链接: VMware虚拟机 安装 CentOS 7 安装准备 软件: VMware Workstation Pro 17.6.3 镜像: CentOS-7.0-1406-x86_64-DVD.iso 我打包好放这了&#xff0c;VMware 和 CentOS7 &#xff0c;下载即可。 关于VMware Workstation Pro 17.6.3&#xff0c;傻瓜式安装即可。 CentO…...

.NET 8 kestrel 配置PEM,实现内网https

一、生成证书 mkcert 是一个简单的工具&#xff0c;用于制作本地信任的开发证书。它不需要配置。 mkcert官方仓库地址&#xff1a;GitHub - FiloSottile/mkcert: A simple zero-config tool to make locally trusted development certificates with any names youd like. 简…...

ERP系统如何做好工厂生产管理?4种ERP先进生产管理模式分享!

现代企业先进的生产管理方式有哪些&#xff1f;相信很多被生产效率问题困扰、谋求转型的企业都思考过这个问题。 放在以前&#xff0c;工厂靠老师傅经验、主管“凭感觉”安排生产&#xff0c;材料、设备、交期全靠人盯。那是因为20年前市场稳定&#xff0c;这套办法还行得通。…...

HttpServletRequest常用功能简介-笔记

javax.servlet.http.HttpServletRequest 是 ServletRequest 接口的子接口&#xff0c;专用于处理 HTTP 协议相关的请求。它提供了访问请求行、请求头、请求参数以及请求属性等方法。 1.请求行&#xff08;Request Line&#xff09; ✅ 功能说明 请求行包含客户端发送的 HTTP …...

基于面向对象设计的C++日期推算引擎:精准高效的时间运算实现与运算重载工程化实践

前引&#xff1a; 在软件开发中&#xff0c;时间与日期的处理是基础但极具挑战性的任务。传统的手工日期运算逻辑往往面临闰年规则、月份天数动态变化、时区转换等复杂场景的容错难题&#xff0c;且代码冗余度高、可维护性差。本文将深入探讨如何利用C的面向对象特性与成员函数…...

记一次bug排查(.exe链接mysql失败)-每天学习一点点

哎! 从来没有接手过的项目, .exe项目, 链接服务器失败 报错:"unable to connect to any of the specified mySQL hosts" 虽然不懂c的代码, 也没法反编译.exe项目, 还是像挑战尝试一下解决这个问题. 明面看就是链接mysql失败. 但是mysql对应的服务器地址是啥呢? …...

U盘启动CentOS安装与网卡驱动修复指南

遇到CentOS安装找不到盘符如何解决 1、进入安装页面&#xff0c;点击e进入修改 2、找到自己的U盘&#xff0c;修改完成后按下"ctrlx"执行继续后续安装 vmlinuz initrdinitrd.imginst.stage2hd:LABEL... quiet 改为&#xff08;具体的U盘启动需要根据实际情况进行查找…...

【物联网】基于树莓派的物联网开发【3】——最新镜像下载和烧录

使用场景 程序猫将带领大家全面了解树莓派&#xff0c;包括从0到1组装&#xff0c;散热片、外壳、风扇安装&#xff0c;官网最新镜像下载及烧录等一系列操作&#xff01; 树莓派必备配件 1、TF卡&#xff08;SD内存卡&#xff09;品牌闪迪&#xff1a;16G以上 2、type-c充电…...

K8S Gateway API 快速开始、胎教级教程

假设有如下三个节点的 K8S 集群&#xff1a; ​​ k8s31master 是控制节点 k8s31node1、k8s31node2 是工作节点 容器运行时是 containerd 一、Gateway 是什么 背景和目的 入口&#xff08;Ingress&#xff09;目前已停止更新。新的功能正在集成至网关 API 中。在 Kubernetes …...

js关于number类型的计算问题

js中关于number类型的计算 因&#xff1a;在js中&#xff0c;number类型是用于表示数值的基本类型&#xff0c;它遵循 IEEE 754 双精度浮点数&#xff08;64 位&#xff09;标准。这导致在某些计算中可能出现精度问题。 例&#xff1a;console.log(0.1 0.2); // 0.300000000…...

GPT-4o 遇强敌?英伟达 Eagle 2.5 视觉 AI 王者登场

前言&#xff1a; 在人工智能领域&#xff0c;视觉语言模型的竞争愈发激烈。GPT-4o 一直是该领域的佼佼者&#xff0c;但英伟达的 Eagle 2.5 横空出世&#xff0c;凭借其 80 亿参数的精简架构&#xff0c;在长上下文多模态任务中表现出色&#xff0c;尤其是在视频和高分辨率图像…...

2025最新ChatGPT Plus令牌(Token)限制完全指南:每日/每月用量详解与突破方法

2025最新ChatGPT Plus令牌(Token)限制完全解析&#xff1a;真相、误区与突破方案 对于支付$20/月订阅费的ChatGPT Plus用户来说&#xff0c;"我的ChatGPT Plus每月有多少token限制&#xff1f;"是一个普遍关心却常被误解的问题。随着越来越多人依赖AI工作和学习&…...

二叉树深搜:在算法森林中寻找路径

专栏&#xff1a;算法的魔法世界 个人主页&#xff1a;手握风云 目录 一、搜索算法 二、回溯算法 三、例题讲解 3.1. 计算布尔二叉树的值 3.2. 求根节点到叶节点数字之和 3.3. 二叉树剪枝 3.4. 验证二叉搜索树 3.5. 二叉搜索树中第 K 小的元素 3.6. 二叉树的所有路径 …...

力扣310.最小高度树(拓扑排序,无向图),力扣.加油站力扣.矩阵置零​​​力扣.二叉树中的最大路径和

目录 力扣310.最小高度树(拓扑排序&#xff0c;无向图) 力扣.加油站 力扣.矩阵置零​​​ 力扣.二叉树中的最大路径和 力扣310.最小高度树(拓扑排序&#xff0c;无向图) 当他是最小高度树时候&#xff0c;树根必定是中心节点&#xff0c;且中心节点一定一两个&#xff0c;且…...

Java并发编程:CAS操作

1. 简介 CAS 全称是 Compare-and-Swap&#xff0c;即比较加交换&#xff0c;假设我们要对内存中的某个地址进行 CAS 操作&#xff0c;该地址当前值是 V&#xff0c;我们给定预期值 A 和新值 B&#xff0c;如果 V A&#xff0c;那么就将该地址的值更新为 B 并返回 true&#xf…...

InforSuite RDS 与django结合

InforSuite RDS&#xff08;关系型数据库服务&#xff09;是浪潮提供的托管式数据库服务&#xff0c;通常支持 MySQL、PostgreSQL、SQL Server 等主流数据库。将 Django 与 InforSuite RDS 结合使用&#xff0c;主要涉及数据库配置、连接优化、安全策略等环节。以下是详细的整合…...

【匹配】Needleman–Wunsch

Needleman-Wunsch 文章目录 Needleman-Wunsch1. 算法介绍2. 公式及原理3. 伪代码 1. 算法介绍 背景与目标 Needleman–Wunsch 算法由 Saul B. Needleman 和 Christian D. Wunsch 于1970年提出&#xff0c;是用于生物序列&#xff08;如蛋白质或 DNA&#xff09;全局比对&#x…...

Spring Boot 拦截器:解锁5大实用场景

一、Spring Boot中拦截器是什么 在Spring Boot中&#xff0c;拦截器&#xff08;Interceptor&#xff09;是一种基于AOP&#xff08;面向切面编程&#xff09;思想的组件&#xff0c;用于在请求处理前后插入自定义逻辑&#xff0c;实现权限校验、日志记录、性能监控等非业务功能…...

DVWA靶场通关笔记-SQL注入(SQL Injection Medium级别)

目录 一、SQL Injection 二、代码审计&#xff08;Medium级别&#xff09; 1、源码分析 &#xff08;1&#xff09;index.php &#xff08;2&#xff09;Medium.php 2、渗透思路 &#xff08;1&#xff09;SQL安全问题分析 &#xff08;2&#xff09;SQL渗透思路 三、…...

西瓜书【机器学习(周志华)】目录

第一部分&#xff1a;基础概念 机器学习概述 1.1 人工智能与机器学习1.2 机器学习分类1.3 机器学习应用1.4 机器学习常用术语解释 模型的评估与选择 2.1 经验误差与过拟合2.2 评估方法2.3 性能度量2.4 偏差与方差 第二部分&#xff1a;核心算法 线性模型 3.1 什么是回归3.2 …...

【漫话机器学习系列】260.在前向神经网络中初始权重(Initializing Weights In Feedforward Neural Networks)

前向神经网络中的权重初始化策略详解 在神经网络模型中&#xff0c;“初始化”常常被认为只是模型训练前的一个小步骤&#xff0c;但它却可能决定了整个网络能否高效收敛&#xff0c;是否会出现梯度爆炸或消失的问题。今天&#xff0c;我们通过一张生动的手绘图&#xff0c;来…...

每日Prompt:磨砂玻璃后的虚实对比剪影

提示词 一张黑白照片&#xff0c;展示了一个[主体]在磨砂或半透明表面后的模糊剪影。其[部分]轮廓清晰&#xff0c;紧贴表面&#xff0c;与其余朦胧、模糊的身影形成鲜明对比。背景是柔和的灰色渐变色调&#xff0c;增强了神秘和艺术的氛围。...

2025年长三角+山东省赛+ 认证杯二阶段资料助攻说明

长三角高校数模B题 完整论文代码已经在售后群 网盘链接 发布 长三角更新时间轴 5.15 23:00 B站发布 完整论文讲解视频 5.16 18:00 j降重说明 5.17 22:00 无水印版本可视化无水印代码 其余时间 写手老师 售后群在线答疑 山东省助攻C道 认证杯二阶段助攻C题 山东省认证杯…...

课程11. 计算机视觉、自编码器和生成对抗网络 (GAN)

计算机视觉、自编码器和生成对抗网络&#xff08;GAN&#xff09; 自动编码器Vanilla自动编码器使用 AE 生成新对象. 变分 AE (VAE)AE 条件 GAN理论示例下载并准备数据GAN模型 额外知识 课程计划&#xff1a; 自动编码器&#xff1a; 自动编码器结构&#xff1b;使用自动编码器…...

机器学习第十二讲:特征选择 → 选最重要的考试科目做录取判断

机器学习第十二讲&#xff1a;特征选择 → 选最重要的考试科目做录取判断 资料取自《零基础学机器学习》。 查看总目录&#xff1a;学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章&#xff1a;DeepSeek R1本地与线上满血版部署&#xff1a;超详细手把手指南 一、学…...

Typescript学习教程,从入门到精通, TypeScript编程基础语法知识点及案例代码(3)

TypeScript编程基础语法知识点及案例代码 本文将详细介绍TypeScript编程的基础知识&#xff0c;包括注释、标识符、关键字、基础类型、变量、常量以及操作符。 1. 注释 语法知识点 TypeScript 支持三种类型的注释&#xff1a; 单行注释&#xff1a;使用 // 开始&#xff0…...

云原生数据库排障新挑战:AI驱动与分布式架构深度解析

云原生数据库排障新挑战&#xff1a;AI驱动与分布式架构深度解析 一、问题描述与快速解决方案 1. 2025年数据库故障新特征 随着云原生与AI技术的深度耦合&#xff0c;数据库故障呈现三大新特征&#xff1a; AI模型推理性能瓶颈&#xff1a;向量化查询响应时间突增&#xff0…...

用MCP往ppt文件里插入系统架构图

文章目录 一、技术架构解析1. Markdown解析模块(markdown_to_hierarchy)2. 动态布局引擎(give_hierarchy_positions)3. PPTX生成模块(generate_pptx)二、核心技术亮点1. 自适应布局算法2. MCP服务集成三、工程实践建议1. 性能优化方向2. 样式扩展方案3. 部署实践四、应用…...

ABC301——ABCD

A 统计比赛胜场 #include<bits/stdc.h> using namespace std;#define x first #define y secondtypedef long long LL; typedef pair<int, int> PII;const int N 2e5 10;int t, n, m, a[N]; string s;void solve() {cin >> n;int c1 0, c2 0;cin >…...

Rust 数据结构:Vector

Rust 数据结构&#xff1a;Vector Rust 数据结构&#xff1a;Vector创建数组更新数组插入元素删除元素 获取数组中的元素迭代数组中的值使用枚举存储多个类型删除一个数组会删除它的元素 Rust 数据结构&#xff1a;Vector vector 来自标准库&#xff0c;在内存中连续存储相同类…...

GpuGeek:为创新者提供灵活、快速、高效的云计算服务!

目录 一、前言 二、GpuGeek平台的显著优势 2.1 显卡资源充足&#xff1a;强大计算能力的基础 &#xff08;1&#xff09;多种GPU配置选择 &#xff08;2&#xff09;弹性扩展与资源管理 2.2 节点丰富&#xff1a;满足多种计算需求 &#xff08;1&#xff09;各种节点配置…...

国产化Word处理控件Spire.Doc教程:通过C# 删除 Word 文档中的超链接

Word 文档中的超链接是可点击的链接&#xff0c;允许读者导航到一个网站或另一个文档。虽然超链接可以提供有价值的补充信息&#xff0c;但有时也会分散注意力或造成不必要的困扰&#xff0c;因此可能会需要删除这些超链接。本文将介绍如何使用 Spire.Doc for .NET 通过 C# 删除…...

MySQL 开发的智能助手:通义灵码在 IntelliJ IDEA 中的应用

一、引言 MySQL 作为一款高度支持 SQL 标准的数据库&#xff0c;在众多应用程序中得到了广泛应用。 尽管大多数程序员具备一定的 SQL 编写能力&#xff0c;但在面对复杂的 SQL 语句或优化需求时&#xff0c;往往需要专业数据库开发工程师的协助。 通义灵码的出现为这一问题提…...

golang -- 认识channel底层结构

channel channel是golang中用来实现多个goroutine通信的管道&#xff08;goroutine之间的通信机制&#xff09;&#xff0c;底层是一个叫做hchan的结构体&#xff0c;定义在runtime包中 type hchan struct {qcount uint // 循环数组中的元素个数&#xff08;通道…...

使用PEFT库将原始模型与LoRA权重合并

使用PEFT库将原始模型与LoRA权重合并 步骤如下&#xff1a; 基础模型加载&#xff1a;需保持与LoRA训练时相同的模型配置merge_and_unload()&#xff1a;该方法会执行权重合并并移除LoRA层保存格式&#xff1a;合并后的模型保存为标准HuggingFace格式&#xff0c;可直接用于推…...

基于微信小程序的在线聊天功能实现:WebSocket通信实战

基于微信小程序的在线聊天功能实现&#xff1a;WebSocket通信实战 摘要 本文将详细介绍如何使用微信小程序结合WebSocket协议开发一个实时在线聊天功能。通过完整的代码示例和分步解析&#xff0c;涵盖界面布局、WebSocket连接管理、消息交互逻辑及服务端实现&#xff0c;适合…...

SaaS基于云计算、大数据的Java云HIS平台信息化系统源码

利用云计算、大数据等现代信息技术研发的医疗信息管理系统&#xff08;HIS&#xff09;实现了医院信息化从局域网向互联网转型&#xff0c;重新定义医疗卫生信息化建设的理念、构架、功能和运维体系。平台构建了以患者为中心的云架构、云服务、云运维的信息体系&#xff0c;实现…...