Протокол WebSocket понимает и реализует онлайн-чат

написано впереди

WebSocket, называемый ws.
Эта статья представляет ws, затем шаг за шагом использует протокол ws для разработки и тестирования внешнего интерфейса и, наконец, использует SpringBootи vueиспользует протокол ws для реализации небольшой демонстрации онлайн-чата (исходный код находится по адресу конец статьи).

Прочитав эту статью, вы сможете выполнить вот такую ​​небольшую демонстрацию (нужно использовать springboot, vue2 и element-ui)
вставьте сюда описание изображения
вставьте сюда описание изображения
вставьте сюда описание изображения
вставьте сюда описание изображения
вставьте сюда описание изображения

Введение в веб-сокет

вставьте сюда описание изображения
WebSocket — это тип протокола, который может обмениваться данными по соединению, HTML5предоставленному в начале, его можно просто понять как: сравнение протокола WebSocket и протокола HTTP:单个TCP全双工网络通信协议

вставьте сюда описание изображения

  • HTTP-протокол. HTTP-протокол — это высококачественный протокол связи прикладного уровня, использующий модель «запрос-ответ» 无状态(请求完成后,连接彻底断开,节省资源).无连接(客户端端向服务端请求数据,获取数据后即断开连接)单向(连接建立只能由客户端发起)
    • Особенности : Клиент что-то спрашивает у сервера, а сервер ему говорит.После того, как он это сказал, клиент бесследно исчезает без каких-либо проблем.Появится. Сервер в свете, а клиент в темноте.
    • Недостатки , клиент может что-то запросить у сервера, но сервер не может найти клиента для чего-то, т.к. соединение полностью разрывается после того, как клиент завершает сбор данных, и в руках дело может держаться только между client и 建立连接的特权只在客户端службу и сообщите ему, когда клиент воссоздает запрос. Когда что-то срочное невозможно связаться с клиентом, т.е.服务端无法向客户端推送数据
    • Решение . Позвольте клиенту часто устанавливать соединение с сервером, чтобы данные на сервере не хранились слишком долго.
      • Существующая проблема: Сколько времени занимает установка соединения с клиентом? Во-первых, частое установление потребляет много системных ресурсов, но сервер редко передает данные, во-вторых, как часто вы устанавливаете соединение? Если соединение будет устанавливаться слишком часто, клиент не сможет его выдержать, а если частота низкая, то данные сервера все равно придется удерживать какое-то время, иначе они все равно не будут доступны 实时. Частые визиты потребляют огромные ресурсы и разбрасывают большую сеть с небольшой выгодой.

Если бы только был протокол, по которому клиент мог бы отправлять данные на сервер, а сервер также мог бы отправлять данные клиенту 全双工通讯! Клиент этого протокола не может быть отключен от сервера, чтобы сервер мог найти клиента для продолжения отправки данных, хотя это и 有状态连接будет потреблять ресурсы, но это намного лучше, чем частые подключения протокола HTTP при определенных требованиях. Такое соглашение есть WebSocket协议. Подводя итог, webSocket поддерживает 持久соединения, так что и сервер, и клиент могут 实时общаться.协议

Сценарии применения WebSocket

  • Веб-приложения для своевременной связи: например, требующие обновления данных веб-страницы в режиме реального времени.实时数据展示网站
  • Игровое приложение: анимацию экрана можно обновлять в режиме реального времени.
  • Приложение чата: отправка сообщений может быть получена и отправлена ​​вовремя

вставьте сюда описание изображения
рекомендуемая статья

Реализация веб-сокета

HTTP-метод : когда мы пишем код, который разделяет переднюю и заднюю части, задняя часть пишет @RestControllerинтерфейс для получения запросов, а передняя часть пишет пакет axiosдля отправки запросов для получения внутренних данных. По сути, этот метод использует HTTP-протокол для взаимодействия с данными: фронтенд использует axiosHTTP-запросы, а бэкенд-интерфейс получает HTTP-запросы.
Метод WS :
Поскольку обычно используемые @RestControllerинтерфейсы и axiosинструменты запроса все для HTTP协议服务的, WSхотя взаимодействие данных и является этим процессом (бэкэнд указывает интерфейс, а фронтенд получает данные), он должен иметь свой собственный набор инструментов реализации. Используйте @ServerEndpointдля достижения аналогичных @RestControllerэффектов и используйте WebSocketобъекты для достижения аналогичных axiosэффектов. Конкретное использование будет представлено позже. Отношения между Endpoint и webScoket аналогичны отношениям Servletс Http.


Жизненный цикл жизненного
цикла заставляет меня понять, оригинальные слова - это websocket事件
интерфейс

let ws=new webSocket(ws://ip:端口号/资源名称)
Имя функции описывать
ws.onopen 连接триггеры при сборке
ws.onmessage 接收Срабатывает, когда сообщение поступает на сервер
ws.onerror 错误Запускается, когда происходит процесс коммуникации
ws.onclose 关闭срабатывает при подключении

при отправке данныхws.send()

задняя часть

Название аннотации описывать
@onClose Запускается, когда соединение закрыто
@onOpen срабатывает при инициализации
@onError срабатывает при возникновении ошибки
@onMessage Автоматически запускается после инициализации для получения данных, переданных внешним интерфейсом.

Краткий обзор процесса реализации ws
Сервер больше не отвечает только за ответ данных от клиента, как в прошлом, но также может активно передавать данные клиенту. Есть еще один контейнер коннектора.
вставьте сюда описание изображения
@RestController

  1. Для использования сопоставления запросов@ServerEndpoint("xx")
  2. Когда запрос будет инициализирован, поместите этот запрос в контейнер всех коннекторов, чтобы при отправке запроса можно было найти каждое подключение, которое в данный момент подключено к системе, используя @OnOpenтег
  3. Чтобы получить данные внешнего интерфейса @OnMessage, вы можете использовать тот же RequestMapping()метод аннотации @ для получения данных, например @PathParam("xxxx"): Обратите внимание, что этот метод будет вызываться каждый раз, когда внешний интерфейс отправляет данные. но он не вызывается при инициализации
  4. Данные, возвращаемые во внешний интерфейс, не используются напрямую return, но сеанс подключения, сохраненный в пуле подключений, используется для получения сеанса подключения, а возвращенная информация передается через сеанс.
  5. Прием и возврат сообщения завершены, остальное доп. Чтобы закрыть соединение, нужно вызвать метод под @OnCloseметкой и вызвать @OnErrorметод под меткой при возникновении ошибки.

webSocket имеет собственный метод получения параметров, который очень похож на метод HTTP Controller.

  • 路径Получение @PathParam("xxx")данных из параметров запроса работает @PathValue("xxx")так же, как

Методы под некоторыми аннотациями должны содержать определенные параметры

  • @OnErrorСписок методов под логотипом должен содержать Throwableобъекты, иначе при запуске будет выдано сообщение об ошибке
  • @OnMessageМетод, указанный в этой аннотации, вызывается автоматически, когда фронтенд отправляет сообщение бэкэнду.Обязательно, чтобы этот метод содержал параметры, иначе при его запуске будет сообщено об ошибке

вопрос?

  • Как звонить, когда бэкенд отправляет информацию на фронтенд?
    Есть два метода, которые могут вызываться автоматически в течение всего жизненного цикла при подключении, один — вызов отмеченного @OnOpenметода при инициализации, а другой — вызов @OnMessageметода при получении фронтенд-запроса (этот метод будет вызываться каждый раз интерфейс отправляет данные). Вы можете выбрать @OnMessageметод под меткой в ​​качестве вводного, чтобы бэкенд возвращал данные во фронтэнд, и вызывать метод возврата данных во фронтэнд в этом методе.

  • Как отправить эффект мгновенного обмена сообщениями ?

  • Эту проблему можно решить, решив проблему того, как серверная часть отправляет данные во внешний интерфейс. Когда A передает сообщение, отправленное B, на сервер через ws, в методе получения данных front-end автоматически вызывается метод возврата данных внешнего интерфейса.В этом методе содержимое сообщения и пункт назначения получен, а затем перейдите к контейнеру соединителя, чтобы найти место назначения.Подключите объект, а затем используйте данные, отправленные A, в качестве данных, возвращенных объекту подключения B. **Этот способ мышления требует, чтобы B находился в контейнере коннектора, то есть B должен оставаться подключенным**

  • Как сделать так, чтобы передний план всегда получал данные, сгенерированные самим бэкендом.
    Данные, сгенерированные самим бэкендом, означают, что передний план может быть запущен только один раз, а остальные данные непрерывно возвращаются во внешний интерфейс этим триггером. Вы можете вызвать метод, аналогичный прослушиванию очереди блокировки, когда она срабатывает в первый раз, и непрерывно помещать сгенерированные данные в очередь.Когда данные отслеживаются в методе, они будут извлечены и возвращены на передний план конец. Таким образом, слушатель вызывается первым запросом, а данные генерируются и отслеживаются, чтобы инициировать возврат данных.

  • Как интерфейс получает данные, возвращаемые сервером? Когда у бэкенда есть данные для возврата, будет запущен
    внешний объект , где вы можете назначить данные и отобразить страницу.onmessage()

передача параметра ws

  • 路径传参: Передать параметры через путь во время инициализации. Бэкенд использует его для использования в сопоставлении пути. Например: login/{xxx}, а затем используется в параметре метода @PathParam("xxx")для получения

  • 消息传参: использовать объект для отправки сообщения ws.send(“xxx”), вы можете преобразовать некоторый контент в форму JSON для отправки, а затем @OnMessageбэкэнд получает и преобразует его в следующие параметры метода

  • 请求头传参: Используйте заголовок запроса, потому что он был представлен выше, поэтому я не буду повторяться здесь. Заголовок запроса является ключевым здесь

    • Интерфейсный запрос на добавление заголовка запроса
      ws не такой гибкий, как HTTP-запрос, отсутствует API для передачи параметров, а заголовок запроса нельзя настроить. Так как передать?
      • Используйте заголовок запроса websocket, чтобы содержать Sec-WebSocket-Protocolэтот атрибут, вы можете присвоить значение атрибуту, и бэкэнд берет его из этого атрибута. Для этого назначения свойства не существует отдельного API, 只能在构造ws对象能够进行赋值.
      例如 :
      let ws = new WebSocket("ws://localhost:8888/xxxx", "请求参数1");
      
      后端获取请求头 Sec-WebSocket-Protocol  得到的就是 请求参数1
      
      let ws = new WebSocket("ws://localhost:8888/xxxx", ["请求参数1","请求参数2"]);
      
      后端获取请求头 Sec-WebSocket-Protocol  得到的就是 请求参数1   请求参数2
      
    • Бэкэнд получает заголовок запроса.
      Бэкенд здесь относится к SpringBoot. Бэкенд получает заголовок запроса @RequestHeaderнапрямую Вместо этого он получается в указанном методе для наследования.

    вставьте сюда описание изображения
    *

    @Configuration
    @Slf4j
    public class WebSocketConfig extends ServerEndpointConfig.Configurator {
          
          
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
          
          
            return new ServerEndpointExporter();
        }
    
        public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
          
          
    
            //获取请求头 请求头的名字是固定的
            List<String> list = request.getHeaders().get("Sec-WebSocket-Protocol");
    
    
            //当Sec-WebSocket-Protocol请求头不为空时,需要返回给前端相同的响应
            response.getHeaders().put("Sec-WebSocket-Protocol",list);
    
    
            super.modifyHandshake(sec, request, response);
        }
    }
    
    
    • Затем также @ServerEndpointукажите класс конфигурации в аннотации
      вставьте сюда описание изображения

    Примечание : Этот вид метода будет выполняться только при создании соединения и не будет выполняться при отправке сообщения, при возникновении ошибки или
    при отключении.Достаточно ли для проверки личности подтвердить одно и то же соединение только один раз? HTTP использует перехватчик для однократной проверки, потому что он часто проверяется из-за частых подключений? То есть перехватчик используется при работе в качестве http в этом методе?
    вставьте сюда описание изображения

пробный тест

бэкэнд-моделирование

Внутреннее использование springBoot, внешнее использование postmanдля отправки запроса ws

Подготовка серверной части: обратите внимание, что tomcat7протокол webSocket будет поддерживаться в будущем.

  • В дополнение к зависимостям от среды серверная часть должна импортировать ключевые зависимости WebSocket.

      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
      </dependency>
    
  • зависит от окружающей среды

     <parent>
       <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> 
     </parent>
    
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
    <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-websocket</artifactId>
     </dependency>
     <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <optional>true</optional>
     </dependency>
    
  • Вставьте объект ServerEndpointExporter в контейнер.
    Создайте класс конфигурации и вставьте объект в контейнер в классе.

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    
    @Configuration
    public class WebSocketConfig {
          
          
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
          
          
            return new ServerEndpointExporter();
        }
    }
    
    
  • Затем вы можете написать приложение, похожее на интерфейс

Подготовка передней части

  • Создайте соединение типа ws в postMan ( исходный метод HTTP использовать нельзя )
    вставьте сюда описание изображения

Первый тест : после того, как соединение установлено, интерфейс отправляет данные на сервер, сервер получает данные и отправляет данные на интерфейс; существует только одно соединение (не используйте подключенные контейнеры сеансов).


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint(value = "/wsserver/{username}")
@Component
@Slf4j
public class WebSocketServer {
    
    
    private Session session;
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session){
    
    
        this.session=session;
        log.info(username);
        log.info("连接创建初始化");
    }

    @OnMessage
    public void onMessage(Session session,String message) throws IOException, InterruptedException {
    
    
        log.info("接收到数据"+message);
        Thread.sleep(10000);

        for (int i=0;i<10;i++){
    
    
            this.session.getBasicRemote().sendText("来自后端的数据");
        }
    }
    @OnError
    public void onError(Session session, Throwable error) {
    
    
        log.error("发生错误");
        error.printStackTrace();
    }
    @OnClose
    public void onClose(){
    
    
        log.info("关闭连接");
    }
}


примечание

вставьте сюда описание изображения

Результаты теста
вставьте сюда описание изображения
Второй тест : Создайте два независимых соединения A и B соответственно. A отправляет данные B, B отправляет данные A, и отправитель должен быть указан в данных

Идея реализации:

  • указано в данных 发送方, 接收方и 数据. Укажите отправителя в параметре пути, задайте правила в формате данных и извлеките получателя и данные.
  • 标识Каждое подключение будет добавлять свою сумму в публичный кеш при инициализации session对象, чтобы было удобно находить подключенную сессию по получателю данных, а потом возвращать данные через сессию получателя
@ServerEndpoint(value = "/wsserver/{username}")
@Component
@Slf4j
public class WebSocketServer {
    
    
    public static final Map<String,Session> sessionMap=new ConcurrentHashMap<>();
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session){
    
    
        sessionMap.put(username,session);
        log.info("数据来自"+username);
        log.info("连接创建初始化");

        for (Map.Entry<String,Session> entry:sessionMap.entrySet()) {
    
    
            System.out.println(entry);
        }
    }

    @OnMessage
    public void onMessage(Session session,String message) throws IOException, InterruptedException {
    
    
        log.info("接收到数据"+message);
        String[] split = message.split(":");
        sessionMap.get(split[0]).getBasicRemote().sendText(split[1]);
    }
    @OnError
    public void onError(Session session, Throwable error) {
    
    
        log.error("发生错误");
        error.printStackTrace();
    }
    @OnClose
    public void onClose(){
    
    
        log.info("关闭连接");
    }
}


примечание
вставьте сюда описание изображения

Результаты теста
вставьте сюда описание изображения


Примечание . Каждый запрос соответствует объекту WebSocketServer, и каждое изменение будет вызывать метод, указанный в объекте, например: метод под @onOpen будет запускаться один раз при подключении каждого запроса, а метод под логотипом @onMessage будет вызываться один раз, когда каждый запрос отправляет данные …

титульная страница

Создайте проект vue, потому что он будет использоваться в последующих проектах. Нет необходимости создавать его здесь, пока есть среда, которая может запускать js, проще всего создать страницу .htmlи открыть ее в браузере.

Первый тест устанавливает соединение с сервером

Создайте wsобъект, используйте этот объект 向后端发送数据и 获取后端数据т. д. Метод создания очень прост, и соединение будет установлено при его создании (имеется в виду, когда выполнение достигает здесь, а не после выхода нового)

вставьте сюда описание изображения
После запуска страницы сервер сразу создаст соединение
вставьте сюда описание изображения

Во втором тесте A в почтальоне отправляет данные C при просмотре, а C получает данные и печатает их на странице;

На основе завершения внутреннего теста 2 выше

вставьте сюда описание изображения
Примечание : onmessage всегда прослушивает данные, отправляемые сервером.

Третий тест отправляет данные в A postMan на основе теста 2.

Используйте метод send("xxx") объекта wc

<template>
    <div>
        {
    
    {
    
    receptionData}}
        <el-input size="mini" v-model="data"></el-input>
        <el-button @click="toSendMsg()">发送</el-button>
    </div>
</template>
<script>
export default {
    
    
    data(){
    
    
        return{
    
    
            data:'',
            ws:undefined,
            receptionData:undefined
        }
    },
    created(){
    
    
      this.ws= new WebSocket('ws://localhost:8080/wsserver/C');
        this.ws.onmessage=(msg)=>{
    
    
            this.receptionData=msg.data
        }
    },
    methods:{
    
    
        toSendMsg(){
    
    
           this.ws.send("A:cccccc")
        }
    }
}
</script>

Результаты теста:
вставьте сюда описание изображения
Примечание . Когда интерфейсная веб-страница будет завершена, процесс закрытия серверной части будет запущен автоматически.

Кроме того, sc.close() некоторые интерфейсные обработки закрываются вручную, а sc.onerror()некоторые выполняются при возникновении исключений.Использование onmessageтакое же, как и при назначении им функции.

В ходе тестирования было обнаружено , что данные могут быть использованы только один раз, то есть при подключении нескольких клиентов только один клиент может получить данные с сервера. Поскольку разные клиентские подключения имеют разные сеансы подключения. Поскольку код на стороне сервера использует хранилище карт, ключ является уникальным идентификатором, а значение — сеансом. Когда пользователь входит в систему на нескольких клиентах, старое значение того же ключа в карте будет перезаписано последним значением, и только последняя открытая веб-страница сможет получать данные с сервера.



Разработка системы живого чата

Эта система является оригинальной, основанной на смешанной разработке протокола HTTP (например, вход в систему, регистрация) и протокола ws (например, чат в реальном времени), цель состоит в том, чтобы умело использовать протокол ws, поэтому он относительно опущен в других аспектах. и могут быть баги или ошибки Пожалуйста поправьте меня

внешний интерфейс

Внешний интерфейс использует библиотеку пакетов vue и Element ( 非必要), использует axios для отправки HTTP-запросов и использует объекты webSocket для отправки запросов ws.

  1. Создайте проект vue с помощью npm
  2. Загрузите Element-ui и импортируйте его в файл main.js.
  3. Загрузите axios, создайте класс инструмента для инкапсуляции запроса axios, а затем импортируйте его в js-часть нужной страницы.
  4. настроить маршрутизацию
  5. удалить бесполезные страницы
  6. Затем проверьте, может ли проект нормально работать, успешно ли импортирован элемент и т. д.

Часть проекта, связанная с инициализацией проекта Vue, не будет представлена ​​здесь по отдельности.
Макет стиля входа

вставьте сюда описание изображения

часть кода:

<template>
  <div class="wrapper">
    <div class="login_div">
   
        <span class="title"><h2> 在线聊天室</h2></span>
        <div class="form_div">
          <el-form ref="form" :model="form" label-width="80px">
            <el-form-item>
              <el-input v-model="form.name" size="mini" placeholder="输入姓名"></el-input>
          </el-form-item>
            <el-form-item>
              <el-input v-model="form.password" size="mini" placeholder="输入密码" type="password"></el-input>
            </el-form-item>
             <el-form-item>
              <a href="#" style="color:blue">还没有账号?点我去注册</a>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="submitForm()" size="small" style="width:100%">提交</el-button>
          </el-form-item>
        </el-form>
        </div>
    </div>
  </div>
</template>

<script>
import {
    
    login} from '../api/userApi'
export default {
    
    
 
  data() {
    
    
    return {
    
    
      form:{
    
    
        name:'',
        password:''

      },
      mock:{
    
    
      
      }
    }
  },
  methods: {
    
    
      submitForm(){
    
    
        login(this.form).then(res=>{
    
    
           
        })
      }
  }
   
}
</script>
<style scoped>
.wrapper {
    
    
  height: 100vh;
  background-image: linear-gradient(to bottom right, #FC466B, #3F5EFB);
  width: 100%;
  overflow: hidden;
  position: relative;
 
}
.login_div{
    
    
  width:500px;
  height: 300px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -70%);
  background-color:aliceblue;
  opacity:0.9;  
}
.title{
    
    
  text-align: center  ;
}
.form_div{
    
    
  margin-right: 100px;
  margin-top: 50px;
}
</style>

Макет страницы чата
Используйте css для рисования градиентного фона, а затем нарисуйте общий макет страницы.
вставьте сюда описание изображения

<template>
  <div style="min-height: 100vh" class="window_div">
      <div class="chat_div">
          <div class="chat_header">
            <span class="hrader_title">当前用户:xxx</span>
            <span class="hrader_friend">正在和xxx 聊天</span>
          </div>

          <div class="chat_left">
              <div class="message_div"></div>
               <div class="input_div"></div>
          </div>
          <div class="chat_right">
            <div class="friend_div">
              <div class="friend_list_title"></div>
              <div class="friend_list"></div>
            </div>
            <div class="sys_info_div">
              <div class="sys_info_title"></div>
              <div class="sys_info"></div>
            </div>
          </div>
      </div>
  <div>
    </div>
  </div>
</template>

<script>


export default {
    
    
}
</script>

<style scoped>
.window_div{
    
    
  background-image: linear-gradient(to bottom right, #b1b1c5, #3F5EFB);
  overflow: hidden;
  position: relative;
}
.chat_div{
    
    
  width: 60%;
  height: 65vh;
  border: 1px solid red;
  margin: 10vh auto;
}
.chat_header{
    
    
  display: flex;
  width: 100%;
  height: 10vh;
  border: 1px solid red;
}

.chat_left{
    
    
  width: 70%;
  height: 55vh;
  border: 1px solid red;
  float: left; 
}

.chat_right{
    
    
  width: 30%;
  height: 55vh;
  border: 1px solid red;
  float: right; 
}
.hrader_title{
    
    
  display: flexbox;
  line-height: 10vh;
  margin-left: 6%;
  color: white;
  width: 30%;
  border: 1px solid red;
}
.hrader_friend{
    
    
  display: inline-block;
  margin-top: 6vh;
  height: 2vh;
  color: white;
  width: 25%;

  font-size: 12px;
  border: 1px solid red;
}
.friend_list_title{
    
    
  width: 100%;
  height: 5vh;
  border: 1px solid red;
}
.friend_list{
    
    
  width: 100%;
  height: 20vh;
  border: 1px solid red;
}
.friend_div{
    
    
  width: 100%;
  height: 25vh;
  border: 1px solid black;
}
.sys_info_div{
    
    
  width: 100%;
  height: 30vh;
  border: 1px solid black;
}
.sys_info_title{
    
    
  width: 100%;
  height: 5vh;
  border: 1px solid red;
}
.sys_info{
    
    
  width: 100%;
  height: 25vh;
  border: 1px solid red;
}
.message_div{
    
    
  width: 100%;
  height: 40vh;
  border: 1px solid red;
}
.input_div{
    
    
  width: 100%;
  height: 15vh;
  border: 1px solid red;
}

</style>

После снятия края для заполнения
вставьте сюда описание изображения

На данный момент интерфейсный стиль завершен, за исключением части сообщения, а остальное — отладка внутреннего интерфейса.


серверная часть

Особенности дизайна:

  1. Авторизоваться
  2. Показать список пользователей, которые находятся в сети
  3. Живой чат
  4. Онлайн-напоминание, офлайн-напоминание

Создайте уникальное имя пользователя

блок-схема

вставьте сюда описание изображения

Тип
сообщения Сообщения, отправляемые между пользователями, пользовательские онлайн-уведомления, пользовательские офлайн-уведомления, сообщения списка друзей, системные уведомления (некоторые ошибки и т. д.)

вставьте сюда описание изображения

Отправитель тела сообщения
, получатель, тип сообщения, содержание сообщения
вставьте сюда описание изображения

Резюме идей :

  1. Как обеспечить безопасность? Изначально планировалось, что каждый запрос несет заголовок запроса для проверки в перехватчике, как и http-запрос, но после тестирования выяснилось, что каждый раз, когда http-запрос представляет собой другое соединение, его нужно каждый раз проверять. ws может проверить только один раз при создании соединения. Токен возвращается при входе в систему, и сервер использует карту для сохранения записи токена. Перенесите этот токен, когда ws создает соединение, сервер ищет его на карте и определяет, является ли соединение законным, на основе того, найдено ли оно.
  2. Как определить отправителя сообщения? При отправке сообщения укажите способ отправки в теле запроса (может быть небезопасно)
  3. Как поместить пользовательское соединение в карту сеанса? После проверки токена ws заносим найденное имя пользователя в LocalThrad, а при закладывании в sessionMap в качестве ключа берем имя пользователя из LocalThread, а значением является подключенная сессия
  4. Что делает инициализация? Поместите соединение в карту сеанса во время инициализации, а затем верните список друзей через сеанс, потому что имя используется в качестве ключа в этом коде, поэтому вам нужно только пройти все ключи в карте сеанса. Затем отправьте системное сообщение всем пользователям о том, что пользователь уже находится в сети.
  5. Как различать разные типы сообщений? Настройка формата сообщения позволяет различать различные типы сообщений и отправлять системные сообщения, списки друзей и сообщения друзей в едином формате.
  6. Как обновляется список друзей в сети? Когда начальное соединение будет установлено, серверная часть отправит всех текущих онлайн-пользователей на это соединение, а интерфейс будет обновляться, когда система позже предложит вам перейти в онлайн или офлайн.
  7. Как бороться с исключениями и отключениями? Инкапсулируйте метод для уведомления других сеансов об отключении пользователя.В этом методе в соответствии с сеансом в списке параметров перейдите к sessionMap, чтобы найти ключ в соответствии со значением, используйте ключ как сообщение и установите тип сообщения для пользователя в автономном режиме для других пользователей, отправленных в сеансе. Внешний интерфейс получает сообщение об автономном типе пользователя, запрашивает и удаляет это имя из списка онлайн-друзей.

комментарий к коду

Код внутреннего ядра : (Поскольку место слишком длинное, здесь только комментарии, подробный код был загружен на gitee, вы можете посетить и просмотреть его самостоятельно)
вставьте сюда описание изображения
вставьте сюда описание изображения
На данный момент все функции внутреннего интерфейса были в принципе закончено, только фронтенд получает разные типы сообщений, и назначаю его на рендеринг

вставьте сюда описание изображения
вставьте сюда описание изображения
вставьте сюда описание изображения
Здесь консольный тест успешно отправил и получил сообщения, а следующее — самое проблемное отображение сообщений в этом проекте.

Используя метод кругового массива, этот метод будет немного отличаться от обычных привычек чата, поэтому эта версия не будет изменена.Кроме того
вставьте сюда описание изображения
, онлайн-список также использует тот же метод.
вставьте сюда описание изображения

Онлайн-чат версии 1.0 завершен

исходный код

Полный исходный код был загружен в gitee, перейдите по ссылке https://gitee.com/wang-yongyan188/websocketchat.git .
Примечание . Поскольку токен запроса на создание ws хранится в локальном хранилище, даже если тот же браузер регистрирует в разные учетные записи. Это также один и тот же пользователь, поэтому для проверки используйте разные браузеры. Если вы тестируете только серверную часть, рекомендуется использовать почтальон для тестирования.
вставьте сюда описание изображения

Все коды оригинальные.Блог и коды заняли два дня.Любые ошибки приветствуются.Если вы считаете, что это нормально, пожалуйста, ставьте лайк.
Цель этого проекта — понять протокол ws и научиться использовать Springboot для смешанной разработки двух протоколов.
Кодовая страница относительно проста, а функции несовершенны (регистрацию нужно вручную добавлять в базу данных, а общаться только с онлайн-пользователями и т. д.), если вам это нравится, будут запущены последующие действия, такие как: добавить друзей, напоминание о непрочитанных сообщениях, групповой чат, сохранение сообщений и т. д., а также в качестве небольшого проекта.

Ich denke du magst

Origin blog.csdn.net/m0_52889702/article/details/128246831
Empfohlen
Rangfolge