web网页端使用webSocket实现语音通话功能(SpringBoot+VUE)

写在前面

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

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

技术栈:VUE3、SpingBoot、WebSocket

Java后端代码

pom.xml

配置Maven所需的jar包

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-websocketartifactId>
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
     */
    @Bean
    public 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为与某个客户端的连接会话,需要通过它来给客户端发送数据
	 */
	@OnOpen
	public void onOpen(@PathParam(value = "roomId") String roomId, @PathParam(value = "userId") String userId,
			Session webSocketsession) {
		// 接收到发送消息的人员编号
		this.roomId = roomId;
		this.userId = userId;
		// 加入map中,绑定当前用户和socket
		sessionPool.put(userId, webSocketsession);
		webSocketSet.add(this);
		this.webSocketsession = webSocketsession;
		// 在线数加1
		addOnlineCount();
		System.out.println("user编号:" + userId + ":加入Room:" + roomId + "语音聊天  " + "总数为:" + webSocketSet.size());
	}

	/**
	 * 连接关闭调用的方法
	 */
	@OnClose
	public 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
	 */
	@OnError
	public 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数组数据,实现了语音通话功能




关于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即可

web网页端使用webSocket实现语音通话功能(SpringBoot+VUE)_第1张图片

浏览器会提示重启, 点击Relaunch即可
web网页端使用webSocket实现语音通话功能(SpringBoot+VUE)_第2张图片

你可能感兴趣的:(前端,websocket,spring,boot,vue)