На базе видеочата также необходимо реализовать украшение, отключение проверки пульса и оптимизацию пропуска кадров. Бизнес-потребности на уровне предприятия, такие как автономное переподключение
WebSocket — это сетевой протокол, основанный на протоколе TCP.,Он реализует браузер и серверЗенсуко Коммуникация,Поддержка отправки информации друг другу между клиентом и сервером. До появления WebSockets,Если данные сервера меняются,клиент Если хочешь знать,Его можно получить только с сервера посредством запланированного опроса.,Этот метод значительно увеличивает частичное давление,После наличия WebSocket,Если данные сервера меняются,Можно немедленно уведомить клиента,клиент Для обмена не нужно проводить опрос,Снижает нагрузку на сервер. В настоящее время все основные браузеры уже поддерживают протокол WebSocket. WebSocket использует ws и wss в качестве идентификаторов ресурсов. TSL Есть 4 основных события:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<!-- websocket -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<!-- fastjson -->
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
@Slf4j
@RestControllerAdvice
public class WebExceptionAdvice {
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<Result> handleRuntimeException(HttpServletRequest request, RuntimeException e) {
log.error(e.toString(), e);
Result result = Result.fail(e.getMessage());
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;//500
if (e instanceof UnAuthorException) {
//Это код состояния, устанавливаемый, когда перехватчик сообщает об ошибке.
status = HttpStatus.UNAUTHORIZED;//401
}
ResponseEntity<Result> resultResponseEntity = new ResponseEntity<>(result, status);
log.error(resultResponseEntity.toString());
return resultResponseEntity;
}
}
public class UnAuthorException extends RuntimeException {
public UnAuthorException(String message) {
super(message);
}
}
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
//Добавляем перехватчик InterceptorRegistry registry Регистратор-перехватчик ignorePathPatterns исключает ненужные перехваченные пути
// Пока это не имеет отношения к Авторизоваться, перехватывать не надо. Функция перехватчика – только проверка статуса Авторизоваться.
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/index/**",
"/user/wechat/login",
"/user/zfb/login",
//...установите это самостоятельно здесь Страницы, которые вы не хотите блокировать Остальные перехвачены
).order(1);
// order — это последовательность настроек
// Обновить перехватчик токенов
registry.addInterceptor(new RefreshTokeninterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
}
}
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1. Определяем, нужен ли перехват (есть ли пользователь в ThreadLocal)
if (UserHolder.getUser() == null&&ListenerHolder.getListener()==null) {
System.out.println("Перехватчик сообщил об ошибке!!!");
//response.getHeader("erro");
throw new UnAuthorException("Пользователь не имеет Авторизоваться"); }
return true;
}
}
/*/**
*@author suze
*@date 2023-10-25
*@time 15:23
**/
public class RefreshTokeninterceptor implements HandlerInterceptor {
//И используется в MvcConfig LoginInterceptor Итак, нам нужно перейти в MvcConfig, чтобы внедрить
private StringRedisTemplate stringRedisTemplate;
//Потому что этот класс не Spring Он создается при загрузке, но это класс, созданный вручную, поэтому внедрение зависимостей невозможно внедрить с помощью аннотаций. Нам нужно вручную использовать конструктор для внедрения этой зависимости.
public RefreshTokeninterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token2=request.getHeader("token2");
String ListenerKey = LOGIN_LISTENER_KEY + token2;
//Здесь информация о слушателе вводится в функцию слушателя Авторизоваться
String LisStr = stringRedisTemplate.opsForValue().get(ListenerKey);
if(LisStr== null || LisStr.isEmpty()){
System.err.println("Токен прослушивателя пуст");
}
else {
Listener listener = JSON.parseObject(LisStr, Listener.class);
ListenerHolder.saveListener(listener);
stringRedisTemplate.expire(ListenerKey,15, TimeUnit.MINUTES);
return true;
}
//Получаем токен в заголовке запроса Подробности смотрите в авторизации во внешнем коде.
String token = request.getHeader("token");
if(StrUtil.isBlank(token)){//Определить, пуст ли он
System.err.println("токен пуст");
return true;
}
// Получить пользователей Redis на основе токена
String key =LOGIN_USER_KEY+token;
String userstr = stringRedisTemplate.opsForValue().get(key);
//System.err.println("Получить пользователей Redis на основе токена:"+userstr);
//Определяем, существует ли пользователь Если он не существует, проверьте, является ли он прослушивателем.
if(userstr== null || userstr.isEmpty()){
System.err.println("Пользователь пуст");
return true;
}
// Преобразование строки json запрашиваемого пользователя в объект пользователя.
User user = JSON.parseObject(userstr, User.class);
//существовать Сохраните информацию о пользователе в TheadLocal.
UserHolder.saveUser(user);
System.out.println("Сохранить пользователя"+user.getOpenId()+"Информация в TheadLocal");
//Обновляем срок действия токена
stringRedisTemplate.expire(key,15, TimeUnit.MINUTES);
//выпускать
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//удаляем пользователя
UserHolder.removeUser();
ListenerHolder.removeListener();
}
}
В соответствии с вашими потребностями я могу удалить некоторые части своего бизнеса. Ничего страшного, если я не удалю его. Его все равно можно использовать, но это немного медленно.
@ServerEndpoint(value = "/imserver/{userId}")
@Component
public class WebSocketServer {
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
/**
* Запишите текущее количество онлайн-соединений
*/
public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
//public static final Map<String, Session> UserMap = new ConcurrentHashMap<>();Здесь нет необходимости знать имя другого человека Так что не надо добавлять Нужно добавить еще
/**
* <<<<<<< HEAD
* установлен в статический режим Поделитесь картой сообщений ConcurrentMap — это потокобезопасная карта. HashMap небезопасен
*/
//MessageMap здесь хранит информацию о том, что пользователь находится в автономном режиме Коллекция сообщений, полученных, пока он был офлайн. Так что ключ здесь - это ключ получателя
private static ConcurrentMap<String, List<String>> messageMap = new ConcurrentHashMap<>();
/**
*
* Метод, вызываемый при успешном установлении соединения
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
sessionMap.put(userId, session);
// stringRedisTemplate.opsForList().
log.info("Присоединился новый пользователь, userId={}, Текущее количество людей онлайн: {}", userId, sessionMap.size());
JSONObject result = new JSONObject();
JSONArray array = new JSONArray();
result.set("users", array);
for (Object key : sessionMap.keySet()) {
JSONObject jsonObject = new JSONObject();
jsonObject.set("userId", key);
// {"userId": "aysgduiehfiuew", "userId": "admin"}
array.add(jsonObject);
}
//Здесь вы получаете карту истории пользователя userMessage
List<String> userMessage = messageMap.get(userId);
//Загрузка истории Этот процесс эквивалентен повторной отправке сообщения самому себе.
if (userMessage!=null) {
for (int i = userMessage.size() - 1; i >= 0; i--) {
String message = userMessage.get(i);
//Функция сеанса здесь — сообщить, кому отправлено sendMessage. Здесь вы хотите загрузить исторические сообщения, которые вы пропустили.
// Поэтому я отправляю записи истории себе. Итак, toSession заполняет ваш собственный сеанс.
this.sendMessage(message, session);
// Thread.sleep(10000);
}
messageMap.remove(userId);
}
// {"users": [{"userId": "zhang"},{ "userId": "admin"}]}
sendAllMessage(JSONUtil.toJsonStr(result)); // Отправлять сообщения всем клиентам в фоновом режиме
}
/**
* Сервер отправляет сообщение клиенту
*/
private void sendMessage(String message, Session toSession) {
try {
log.info("Сервер отправляет сообщение {} клиенту[{}]", toSession.getId(), message);
toSession.getBasicRemote().sendText(message);
//String from = JSONUtil.parseObj(message).getStr("from");
if (!messageMap.get(toSession.getId()).isEmpty()) {
List<String> list = messageMap.get(toSession.getId());
log.info("Сообщения, которые будут отправлены, продолжают храниться");
list.add(message);
//toSession — идентификатор отправителя
messageMap.put(toSession.getId(), list);
return;
} else {
List<String> list = new ArrayList<>();
//Коллекция офлайн-сообщений, отправленных этим пользователем
list.add(message);
messageMap.put(toSession.getId(), list);
log.info("Пользователь не сохраняет информацию онлайн");
return;
}
} catch (Exception e) {
log.error("Сервер отправляет сообщение клиентунеудача", e);
}
// {"users": [{"userId": "zhang"},{ "userId": "admin"}]}
}
/**
* Метод, вызываемый при закрытии соединения
*/
@OnClose
public void onClose(Session session, @PathParam("userId") String userId) {
sessionMap.remove(userId);
log.info("Соединение закрыто, удалите сеанс пользователя с именем пользователя={}, Текущее количество людей онлайн: {}", userId, sessionMap.size());
}
/**
* Метод, вызываемый после получения сообщения клиента
* Сообщение, отправленное клиентом, было получено в фоновом режиме.
* onMessage Это станция ретрансляции сообщений
* принимать Сторона браузера socket.send Отправил данные JSON
* @param message клиент Отправилинформация
*/
@OnMessage
public void onMessage(String message, Session session, @PathParam("userId") String userId) {
log.info("Сервер получил сообщение от пользователя username={}:{}", userId, message);
JSONObject obj = JSONUtil.parseObj(message);
String toUserId = obj.getStr("to"); // to указывает, какому пользователю оно отправлено, например admin
String text = obj.getStr("text"); // Текст отправленного сообщения hello
//Создаем массив Всегда кладите все затем ниже
//TODO Напишите здесь Метод истории кэша для обработки Кроме теста123 Это для сердцебиения Нет необходимости кэшировать
if(!toUserId.equals("test123")){
Session toSession = sessionMap.get(toUserId); // в соответствии с to идентификатор пользователя, который нужно получить сеанс, а затем отправить текст сообщения через сеанс
if (toSession != null) {
// серверная часть Снова соберите сообщение. Собранное сообщение содержит отправителя и отправленное текстовое содержимое.
// {"from": "zhang", "text": "hello"}
JSONObject jsonObject = new JSONObject();
jsonObject.set("from", userId); // from да zhang
jsonObject.set("text", text); // text Тот же текст, что и выше
this.sendMessage(jsonObject.toString(), toSession);
log.info("Отправлено пользователю username={}, сообщение: {}", toUserId, jsonObject.toString());
} else {
log.info("Отправка не удалась, сеанс для пользователя username={}", не найден. toUserId);
}
}
}
@OnError
public void onError(Session session, Throwable error) {
log.error("Произошла ошибка");
error.printStackTrace();
}
/**
* Сервер отправляет сообщение всем клиентам
*/
private void sendAllMessage(String message) {
try {
for (Session session : sessionMap.values()) {
log.info("Сервер отправляет сообщение {} клиенту[{}]", session.getId(), message);
session.getBasicRemote().sendText(message);
}
} catch (Exception e) {
log.error("Сервер отправляет сообщение клиентунеудача", e);
}
}
}