Files
OneKeyCallWebRTCSignaling2/src/main/java/com/ttstd/signaling/handler/WebRtcSignalHandler.java
2026-01-21 11:19:43 +08:00

120 lines
4.9 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.ttstd.signaling.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ttstd.signaling.entity.RtcSignalMessage;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* WebRTC核心信令处理器
* 继承TextWebSocketHandler处理文本类型的WebSocket消息WebRTC信令都是JSON文本
*/
@Component
public class WebRtcSignalHandler extends TextWebSocketHandler {
/**
* 核心维护在线用户的WebSocket会话映射
* key: 用户IDuserId
* value: 该用户的WebSocket连接会话
* ConcurrentHashMap线程安全解决多用户并发连接的线程安全问题
*/
public static final Map<String, WebSocketSession> ONLINE_USER_SESSIONS = new ConcurrentHashMap<>();
// JSON序列化工具用于将消息实体与JSON字符串互转
private final ObjectMapper objectMapper = new ObjectMapper();
/**
* 客户端成功建立WebSocket连接后触发
* 约定客户端连接时通过请求参数传递userId例如ws://localhost:8080/ws/rtc?userId=user1001
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) {
// 获取连接的用户ID
String userId = getUserIdFromSession(session);
if (userId != null && !userId.isEmpty()) {
// 将用户会话存入在线列表
ONLINE_USER_SESSIONS.put(userId, session);
System.out.println("用户[" + userId + "] 上线,当前在线人数:" + ONLINE_USER_SESSIONS.size());
}
}
/**
* 核心方法:接收客户端发送的信令消息,并完成【精准中转】
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception {
// 1. 获取发送方用户ID
String fromUserId = getUserIdFromSession(session);
if (fromUserId == null || !ONLINE_USER_SESSIONS.containsKey(fromUserId)) {
sendErrorMessage(session, "当前用户未登录或连接已失效");
return;
}
// 2. 解析前端发送的信令消息JSON -> 转为实体类
RtcSignalMessage signalMessage = objectMapper.readValue(textMessage.getPayload(), RtcSignalMessage.class);
String toUserId = signalMessage.getToUserId(); // 目标用户ID
String signalType = signalMessage.getSignalType(); // 信令类型offer/answer/ice-candidate
// 3. 核心逻辑:查询目标用户的连接会话,存在则中转消息,不存在则返回错误
WebSocketSession targetSession = ONLINE_USER_SESSIONS.get(toUserId);
if (targetSession != null && targetSession.isOpen()) {
// 将信令消息原样转发给目标用户
targetSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(signalMessage)));
System.out.println("信令中转成功:[" + fromUserId + "] -> [" + toUserId + "] 类型:" + signalType);
} else {
// 目标用户不在线/连接已关闭
sendErrorMessage(session, "目标用户[" + toUserId + "] 不在线或连接已断开");
}
}
/**
* 客户端断开WebSocket连接后触发
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
String userId = getUserIdFromSession(session);
if (userId != null) {
ONLINE_USER_SESSIONS.remove(userId);
System.out.println("用户[" + userId + "] 下线,当前在线人数:" + ONLINE_USER_SESSIONS.size());
}
}
/**
* 处理WebSocket连接异常
*/
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) {
String userId = getUserIdFromSession(session);
if (userId != null) {
ONLINE_USER_SESSIONS.remove(userId);
System.out.println("用户[" + userId + "] 连接异常,已移除会话,异常信息:" + exception.getMessage());
}
}
// ========== 私有工具方法 ==========
/**
* 从WebSocket会话中获取请求参数里的userId
*/
private String getUserIdFromSession(WebSocketSession session) {
try {
return session.getUri().getQuery().split("=")[1];
} catch (Exception e) {
return null;
}
}
/**
* 向客户端发送错误提示消息
*/
private void sendErrorMessage(WebSocketSession session, String errorMsg) throws IOException {
RtcSignalMessage errorMessage = new RtcSignalMessage(null, null, "error", errorMsg);
session.sendMessage(new TextMessage(objectMapper.writeValueAsString(errorMessage)));
}
}