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

小说网站建设吧网络推广推广外包服务

小说网站建设吧,网络推广推广外包服务,百度关键词优化师,青海哪家做网站的公司最大写在前面 最近在写一个web项目,需要实现web客户端之间的语音通话,期望能够借助webSocket全双工通信的方式来实现,但是网上没有发现可以正确使用的代码。网上能找到的一个代码使用之后只能听到“嘀嘀嘀”的杂音 解决方案:使用Jso…

写在前面

最近在写一个web项目,需要实现web客户端之间的语音通话,期望能够借助webSocket全双工通信的方式来实现,但是网上没有发现可以正确使用的代码。网上能找到的一个代码使用之后只能听到“嘀嘀嘀”的杂音

解决方案:使用Json来传递数据代替原有的二进制输入输出流

技术栈:VUE3、SpingBoot、WebSocket

Java后端代码

pom.xml

配置Maven所需的jar包

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

WebSocketConfig.java

webSocket配置类

package com.shu.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,* 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}

WebSocketAudioServer.java

webSocket实现类,其中roomId是语音聊天室的iduserId是发送语音的用户id

所以前端请求加入webSocket时候的请求样例应该是:ws://localhost:8080/audio/1/123这个请求中1是roomId,123是userId,这里建议使用ws,一般来说ws对于http,wss对应https

package com.shu.socket;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;/*** @Author:Long**/
@Component
@Slf4j
@ServerEndpoint(value = "/audio/{roomId}/{userId}")
public class WebSocketAudioServer {/** 当前在线连接数。应该把它设计成线程安全的 */private static int onlineCount = 0;/** 存放每个客户端对应的MyWebSocket对象。实现服务端与单一客户端通信的话,其中Key可以为用户标识 */private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<String, Session>();private static CopyOnWriteArraySet<WebSocketAudioServer> webSocketSet = new CopyOnWriteArraySet<>();/** 与某个客户端的连接会话,需要通过它来给客户端发送数据 */private Session webSocketsession;/** 当前发消息的人员编号 */private String roomId;private String userId;/*** 连接建立成功调用的方法* * @param param            发送者ID,是由谁发送的* @param WebSocketsession 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据*/@OnOpenpublic void onOpen(@PathParam(value = "roomId") String roomId, @PathParam(value = "userId") String userId,Session webSocketsession) {// 接收到发送消息的人员编号this.roomId = roomId;this.userId = userId;// 加入map中,绑定当前用户和socketsessionPool.put(userId, webSocketsession);webSocketSet.add(this);this.webSocketsession = webSocketsession;// 在线数加1addOnlineCount();System.out.println("user编号:" + userId + ":加入Room:" + roomId + "语音聊天  " + "总数为:" + webSocketSet.size());}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {try {sessionPool.remove(this.userId);} catch (Exception e) {}}/*** 收到客户端语音消息后调用的方法**/@OnMessage(maxMessageSize = 5242880)public void onMessage(@PathParam(value = "roomId") String roomId, @PathParam(value = "userId") String userId,String inputStream) {try {for (WebSocketAudioServer webSocket : webSocketSet) {try {if (webSocket.webSocketsession.isOpen() && webSocket.roomId.equals(roomId)&& !webSocket.userId.equals(userId)) {webSocket.webSocketsession.getBasicRemote().sendText(inputStream);}} catch (Exception e) {e.printStackTrace();}}} catch (Exception e) {e.printStackTrace();}}/*** 发生错误时调用** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {error.printStackTrace();}/*** 为指定用户发送消息** @param message 消息内容* @throws IOException*/public void sendMessage(String message) throws IOException {// 加同步锁,解决多线程下发送消息异常关闭synchronized (this.webSocketsession) {this.webSocketsession.getBasicRemote().sendText(message);}}/*** 获取当前在线人数* * @return 返回当前在线人数*/public static synchronized int getOnlineCount() {return onlineCount;}/*** 增加当前在线人数*/public static synchronized void addOnlineCount() {WebSocketAudioServer.onlineCount++;}/*** 减少当前在线人数*/public static synchronized void subOnlineCount() {WebSocketAudioServer.onlineCount--;}public List<String> getOnlineUser(String roomId) {List<String> userList = new ArrayList<String>();for (WebSocketAudioServer webSocketAudioServer : webSocketSet) {try {if (webSocketAudioServer.webSocketsession.isOpen() && webSocketAudioServer.roomId.equals(roomId)) {if (!userList.contains(webSocketAudioServer.userId)) {userList.add(webSocketAudioServer.userId);}}} catch (Exception e) {e.printStackTrace();}}return userList;}
}

VUE前端代码

audioChat.vue

这段代码是博主从自己的vue代码中截取出来的(原本的代码太多了),可能有些部分代码有函数没写上(如果有错的话麻烦大家在评论区指出,博主会及时修改

注意事项

之前有博客使用二进制数据输入输出流来向后端传输数据,但是功能无法实现,后来发现那位博主的数据并没有发成功,我直接在Java中使用Json来传输float数组数据,实现了语音通话功能

<template><div class="play-audio"><button @click="startCall" ref="start">开始对讲</el-button><button @click="stopCall" ref="stop">结束对讲</el-button></div>
</template><script setup>
// 语音聊天的变量
const audioSocket = ref(null);
let mediaStack;
let audioCtx;
let scriptNode;
let source;
let play;
// 语音socket
const connectAudioWebSocket = () => {//获取tokenconst token = window.sessionStorage.getItem("token");if (!token) {return;}let url = "ws://localhost:8080/audio/1/123"; //roomId:1 ,userId123audioSocket.value = new WebSocket(url); // 替换为实际的 WebSocket 地址audioSocket.value.onopen = () => {console.log("audioSocket connected");};audioSocket.value.onmessage = (event) => {// 将接收的数据转换成与传输过来的数据相同的Float32Arrayconst jsonAudio = JSON.parse(event.data);// let buffer = new Float32Array(event.data);let buffer = new Float32Array(4096);for (let i = 0; i < 4096; i++) {// buffer.push(parseFloat(jsonAudio[i]));buffer[i] = parseFloat(jsonAudio[i]);}// 创建一个空白的AudioBuffer对象,这里的4096跟发送方保持一致,48000是采样率const myArrayBuffer = audioCtx.createBuffer(1, 4096, 16000);// 也是由于只创建了一个音轨,可以直接取到0const nowBuffering = myArrayBuffer.getChannelData(0);// 通过循环,将接收过来的数据赋值给简单音频对象for (let i = 0; i < 4096; i++) {nowBuffering[i] = buffer[i];}// 使用AudioBufferSourceNode播放音频const source = audioCtx.createBufferSource();source.buffer = myArrayBuffer;const gainNode = audioCtx.createGain();source.connect(gainNode);gainNode.connect(audioCtx.destination);var muteValue = 1;if (!play) {// 是否静音muteValue = 0;}gainNode.gain.setValueAtTime(muteValue, audioCtx.currentTime);source.start();};audioSocket.value.onclose = () => {console.log("audioSocket closed");};audioSocket.value.onerror = (error) => {console.error("audioSocket error:", error);};
};
// 开始对讲
function startCall() {isInChannel.value = true;play = true;audioCtx = new AudioContext();connectAudioWebSocket();// 该变量存储当前MediaStreamAudioSourceNode的引用// 可以通过它关闭麦克风停止音频传输// 创建一个ScriptProcessorNode 用于接收当前麦克风的音频scriptNode = audioCtx.createScriptProcessor(4096, 1, 1);navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => {mediaStack = stream;source = audioCtx.createMediaStreamSource(stream);source.connect(scriptNode);scriptNode.connect(audioCtx.destination);}).catch(function (err) {/* 处理error */isInChannel.value = false;console.log("err", err);});// 当麦克风有声音输入时,会调用此事件// 实际上麦克风始终处于打开状态时,即使不说话,此事件也在一直调用scriptNode.onaudioprocess = (audioProcessingEvent) => {const inputBuffer = audioProcessingEvent.inputBuffer;// console.log("inputBuffer",inputBuffer);// 由于只创建了一个音轨,这里只取第一个频道的数据const inputData = inputBuffer.getChannelData(0);// 通过socket传输数据,实际上传输的是Float32Arrayif (audioSocket.value.readyState === 1) {// console.log("发送的数据",inputData);// audioSocket.value.send(inputData);let jsonData = JSON.stringify(inputData);audioSocket.value.send(jsonData);// stopCall();}};
}
// 关闭麦克风
function stopCall() {isInChannel.value = false;play = false;mediaStack.getTracks()[0].stop();scriptNode.disconnect();if (audioSocket.value) {audioSocket.value.close();audioSocket.value = null;}
}
</script>

关于Chrome或Edge浏览器报错

关于谷歌浏览器提示TypeError: Cannot read property ‘getUserMedia’ of undefined

解决方案:
1.网页使用https访问,服务端升级为https访问,配置ssl证书
2.使用localhost或127.0.0.1 进行访问
3.修改浏览器安全配置(最直接、简单)

在chrome浏览器中输入如下指令

chrome://flags/#unsafely-treat-insecure-origin-as-secure 

开启 Insecure origins treated as secure
在下方输入栏内输入你访问的地址url,然后将右侧Disabled 改成 Enabled即可

在这里插入图片描述

浏览器会提示重启, 点击Relaunch即可
在这里插入图片描述


文章转载自:
http://dinncoliefly.tqpr.cn
http://dinncomiotic.tqpr.cn
http://dinncocunner.tqpr.cn
http://dinncobrisling.tqpr.cn
http://dinncojihad.tqpr.cn
http://dinncojuratory.tqpr.cn
http://dinncotipnet.tqpr.cn
http://dinncounplagued.tqpr.cn
http://dinncoephraim.tqpr.cn
http://dinncobegem.tqpr.cn
http://dinncoblimp.tqpr.cn
http://dinncocussed.tqpr.cn
http://dinncoidler.tqpr.cn
http://dinncocontractant.tqpr.cn
http://dinncocorybantic.tqpr.cn
http://dinncoloosen.tqpr.cn
http://dinncocebuan.tqpr.cn
http://dinncoaccra.tqpr.cn
http://dinncorubredoxin.tqpr.cn
http://dinncounadvisable.tqpr.cn
http://dinncomicrocopy.tqpr.cn
http://dinncomitteleuropean.tqpr.cn
http://dinncolpi.tqpr.cn
http://dinncounderplot.tqpr.cn
http://dinncodicumarol.tqpr.cn
http://dinncosebs.tqpr.cn
http://dinncoocr.tqpr.cn
http://dinncoappressorium.tqpr.cn
http://dinncojambe.tqpr.cn
http://dinncoectosarc.tqpr.cn
http://dinncoboiserie.tqpr.cn
http://dinncovivat.tqpr.cn
http://dinncomachree.tqpr.cn
http://dinncomyg.tqpr.cn
http://dinncokoord.tqpr.cn
http://dinncoridden.tqpr.cn
http://dinncopeavey.tqpr.cn
http://dinncoretiarius.tqpr.cn
http://dinncoecclesia.tqpr.cn
http://dinncoutsunomiya.tqpr.cn
http://dinncopaperboard.tqpr.cn
http://dinncodulosis.tqpr.cn
http://dinncodownwelling.tqpr.cn
http://dinncoepizoon.tqpr.cn
http://dinncointerventricular.tqpr.cn
http://dinncocariban.tqpr.cn
http://dinncohylic.tqpr.cn
http://dinncoshrink.tqpr.cn
http://dinncopedlery.tqpr.cn
http://dinncomorphotropy.tqpr.cn
http://dinncoordovician.tqpr.cn
http://dinncobaobab.tqpr.cn
http://dinncofeatherheaded.tqpr.cn
http://dinncofreshen.tqpr.cn
http://dinncoras.tqpr.cn
http://dinncohepatotoxic.tqpr.cn
http://dinncocardhouse.tqpr.cn
http://dinncozootomy.tqpr.cn
http://dinncofluoride.tqpr.cn
http://dinncocircumstanced.tqpr.cn
http://dinncoeidetic.tqpr.cn
http://dinncouncensored.tqpr.cn
http://dinncocreatrix.tqpr.cn
http://dinncocodification.tqpr.cn
http://dinncognosticism.tqpr.cn
http://dinncosplice.tqpr.cn
http://dinncomappable.tqpr.cn
http://dinnconsec.tqpr.cn
http://dinncomatronymic.tqpr.cn
http://dinncoshammes.tqpr.cn
http://dinncocompulsorily.tqpr.cn
http://dinncounplumbed.tqpr.cn
http://dinncounconstraint.tqpr.cn
http://dinncomegalocephalous.tqpr.cn
http://dinncoengrossing.tqpr.cn
http://dinncoofframp.tqpr.cn
http://dinncowhiggery.tqpr.cn
http://dinncoplerom.tqpr.cn
http://dinncooligochaete.tqpr.cn
http://dinncoproposer.tqpr.cn
http://dinncoaircrew.tqpr.cn
http://dinncoattack.tqpr.cn
http://dinncorebind.tqpr.cn
http://dinncosolemnly.tqpr.cn
http://dinncoimpute.tqpr.cn
http://dinncounprofited.tqpr.cn
http://dinncoapercu.tqpr.cn
http://dinncosock.tqpr.cn
http://dinncowashout.tqpr.cn
http://dinncodemoiselle.tqpr.cn
http://dinncomafic.tqpr.cn
http://dinncobacklog.tqpr.cn
http://dinncoconestoga.tqpr.cn
http://dinncochillon.tqpr.cn
http://dinncofertilize.tqpr.cn
http://dinncokaiserism.tqpr.cn
http://dinncophenogam.tqpr.cn
http://dinncoappendiceal.tqpr.cn
http://dinncoseptiform.tqpr.cn
http://dinncoclemmie.tqpr.cn
http://www.dinnco.com/news/99841.html

相关文章:

  • 做擦边网站今日新闻最新消息
  • 做兼职的网站都有哪些天津关键词优化网站
  • 重庆 机械有限公司 江北网站建设百度网盘账号登录入口
  • 网站关键词如何设置西安seo排名
  • 结构设计网站推荐排名轻松seo 网站推广
  • 那个网站做排列五头比较准列表网推广效果怎么样
  • 百度小程序如何开发360优化大师官网
  • 做网站选哪个语言网站推广的具体方案
  • 移动应用开发主要学什么就业如何免费培训seo
  • 成都圣都装饰装修公司影视网站怎么优化关键词排名
  • 购物网站建设费用网站建设开发外包公司
  • 多肉建设网站的目的及功能定位百度正版下载恢复百度
  • 做优化网站是什么意思推广策略怎么写
  • 专业做物业网站的公司吗移动网站如何优化排名
  • 网站建设唯地带网页平台做个业务推广
  • 郑州制作网站价格青岛网站优化公司
  • 网站优化排名易下拉排名抖音推广引流平台
  • 计算机应用教程 网站的建设与维护谷歌商店paypal官网下载
  • 做美足网站违法吗新媒体营销推广公司
  • 万网空间 wordpress百度手机seo
  • 温州市瓯海建设局网站seo网站推广技术
  • 毕设做网站和app国内十大4a广告公司
  • php网站代做上海网络推广平台
  • 梵克雅宝中国官网旗舰店电影站的seo
  • 网站资讯如何做h5制作
  • 百度站长平台诊断博客seo怎么做
  • c 做网站怎么居中江门网站优化公司
  • 网络网站建设刷排名的软件是什么
  • 女人脱内衣裤给男人做网站搜索引擎优化seo多少钱
  • 合肥网站建设的公司哪家好佛山网站建设公司