Skip to content

Commit

Permalink
feat: 富文本交流 & 视频交流
Browse files Browse the repository at this point in the history
  • Loading branch information
liuyueyi committed Dec 19, 2023
1 parent fe7ad5a commit de603fb
Show file tree
Hide file tree
Showing 7 changed files with 989 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.git.hui.boot.chat.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.websocket.server.ServerEndpointConfig;

/**
* 解决注入其他类的问题
*/
public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware {

private static volatile BeanFactory context;

@Override
public <T> T getEndpointInstance(Class<T> clazz) {
if (context == null) {
return null;
}
return context.getBean(clazz);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
MyEndpointConfigure.context = applicationContext;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import java.util.concurrent.Executors;

/**
* Created by @author yihui in 10:39 19/4/18.
* @author YiHui
* @date 2023/11/24
*/
@Configuration
@EnableWebSocketMessageBroker
Expand Down Expand Up @@ -41,7 +45,7 @@ public void configureMessageBroker(MessageBrokerRegistry registry) {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// Endpoint指定了客户端建立连接时的请求地址
registry.addEndpoint("/ws/chat/{channel}")
registry.addEndpoint("/ws/chat/{channel}", "/video/{target}")
// 用于设置连接的用户身份识别
.setHandshakeHandler(new AuthHandshakeHandler())
// 设置拦截器,从cookie中识别出登录用户
Expand All @@ -61,6 +65,7 @@ public AuthHandshakeInterceptor authHandshakeInterceptor() {
*/
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.taskExecutor().corePoolSize(4).maxPoolSize(4).queueCapacity(100).keepAliveSeconds(60);
registration.interceptors(new SocketInChannelInterceptor());
}

Expand All @@ -73,4 +78,19 @@ public void configureClientInboundChannel(ChannelRegistration registration) {
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.interceptors(new SocketOutChannelInterceptor());
}


/**
* 用途:扫描并注册所有携带@ServerEndpoint注解的实例。 @ServerEndpoint("/websocket")
* PS:如果使用外部容器 则无需提供ServerEndpointExporter。
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}

@Bean
public MyEndpointConfigure myEndpointConfigure() {
return new MyEndpointConfigure();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.git.hui.boot.chat.rest;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;

import javax.websocket.Session;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

/**
* @author YiHui
* @date 2023/12/12
*/
@Slf4j
@Controller
public class VideoController {
@MessageMapping("video/{target}")
public void videoChat(String message, @DestinationVariable("target") String target, SimpMessageHeaderAccessor headerAccessor) {
log.info("接收到请求参数: {}", message);
try {
//jackson
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

//JSON字符串转 HashMap
HashMap hashMap = mapper.readValue(message, HashMap.class);

//消息类型
String type = (String) hashMap.get("type");

//to user
String toUser = (String) hashMap.get("toUser");
String fromUser = (String) hashMap.get("fromUser");

//msg
String msg = (String) hashMap.get("msg");

//sdp
String sdp = (String) hashMap.get("sdp");

//ice
Map iceCandidate = (Map) hashMap.get("iceCandidate");

HashMap<String, Object> map = new HashMap<>();
map.put("type", type);

//对方挂断
if ("hangup".equals(type)) {
map.put("fromUser", fromUser);
map.put("msg", "对方挂断!");
}

//视频通话请求
if ("call_start".equals(type)) {
map.put("fromUser", fromUser);
map.put("msg", "1");
}

//视频通话请求回应
if ("call_back".equals(type)) {
map.put("fromUser", toUser);
map.put("msg", msg);
}

//offer
if ("offer".equals(type)) {
map.put("fromUser", toUser);
map.put("sdp", sdp);
}

//answer
if ("answer".equals(type)) {
map.put("fromUser", toUser);
map.put("sdp", sdp);
}

//ice
if ("_ice".equals(type)) {
map.put("fromUser", toUser);
map.put("iceCandidate", iceCandidate);
}

WsAnswerHelper.publish(target, "/topic/video", mapper.writeValueAsString(map));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package com.git.hui.boot.chat.rest;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.git.hui.boot.chat.config.MyEndpointConfigure;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* WebRTC + WebSocket
*/
@Slf4j
@Component
@ServerEndpoint(value = "/webrtc/{username}", configurator = MyEndpointConfigure.class)
public class WebRtcWSServer {

/**
* 连接集合
*/
private static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();

/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("username") String username, @PathParam("publicKey") String publicKey) {
sessionMap.put(username, session);
}

/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session) {
for (Map.Entry<String, Session> entry : sessionMap.entrySet()) {
if (entry.getValue() == session) {
sessionMap.remove(entry.getKey());
break;
}
}
}

/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}

/**
* 服务器接收到客户端消息时调用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
try {
//jackson
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

//JSON字符串转 HashMap
HashMap hashMap = mapper.readValue(message, HashMap.class);

//消息类型
String type = (String) hashMap.get("type");

//to user
String toUser = (String) hashMap.get("toUser");
Session toUserSession = sessionMap.get(toUser);
String fromUser = (String) hashMap.get("fromUser");

//msg
String msg = (String) hashMap.get("msg");

//sdp
String sdp = (String) hashMap.get("sdp");

//ice
Map iceCandidate = (Map) hashMap.get("iceCandidate");

HashMap<String, Object> map = new HashMap<>();
map.put("type", type);

//呼叫的用户不在线
if (toUserSession == null) {
toUserSession = session;
map.put("type", "call_back");
map.put("fromUser", "系统消息");
map.put("msg", "Sorry,呼叫的用户不在线!");

send(toUserSession, mapper.writeValueAsString(map));
return;
}

//对方挂断
if ("hangup".equals(type)) {
map.put("fromUser", fromUser);
map.put("msg", "对方挂断!");
}

//视频通话请求
if ("call_start".equals(type)) {
map.put("fromUser", fromUser);
map.put("msg", "1");
}

//视频通话请求回应
if ("call_back".equals(type)) {
map.put("fromUser", toUser);
map.put("msg", msg);
}

//offer
if ("offer".equals(type)) {
map.put("fromUser", toUser);
map.put("sdp", sdp);
}

//answer
if ("answer".equals(type)) {
map.put("fromUser", toUser);
map.put("sdp", sdp);
}

//ice
if ("_ice".equals(type)) {
map.put("fromUser", toUser);
map.put("iceCandidate", iceCandidate);
}

send(toUserSession, mapper.writeValueAsString(map));
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 封装一个send方法,发送消息到前端
*/
private void send(Session session, String message) {
try {
System.out.println(message);

session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ <h5 class="card-title" style="padding-top: 1em">聊天信息</h5>
const channel = $(`#${topicId}`).val();
const msg = $(`#${msgId}`).html();

// 富文本传输,主要是通过base64的方式进行交互
console.log("input msg:", channel, msg);
stompClient.send('/app/msg/' + channel, {}, msg);
console.log("消息发送完成!");
Expand Down
Loading

0 comments on commit de603fb

Please sign in to comment.