написано впереди
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-протокол для взаимодействия с данными: фронтенд использует axios
HTTP-запросы, а бэкенд-интерфейс получает 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
- Для использования сопоставления запросов
@ServerEndpoint("xx")
- Когда запрос будет инициализирован, поместите этот запрос в контейнер всех коннекторов, чтобы при отправке запроса можно было найти каждое подключение, которое в данный момент подключено к системе, используя
@OnOpen
тег - Чтобы получить данные внешнего интерфейса
@OnMessage
, вы можете использовать тот жеRequestMapping()
метод аннотации @ для получения данных, например@PathParam("xxxx")
: Обратите внимание, что этот метод будет вызываться каждый раз, когда внешний интерфейс отправляет данные. но он не вызывается при инициализации - Данные, возвращаемые во внешний интерфейс, не используются напрямую
return
, но сеанс подключения, сохраненный в пуле подключений, используется для получения сеанса подключения, а возвращенная информация передается через сеанс. - Прием и возврат сообщения завершены, остальное доп. Чтобы закрыть соединение, нужно вызвать метод под
@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
- Используйте заголовок запроса websocket, чтобы содержать
- Бэкэнд получает заголовок запроса.
Бэкенд здесь относится к 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.
- Создайте проект vue с помощью npm
- Загрузите Element-ui и импортируйте его в файл main.js.
- Загрузите axios, создайте класс инструмента для инкапсуляции запроса axios, а затем импортируйте его в js-часть нужной страницы.
- настроить маршрутизацию
- удалить бесполезные страницы
- Затем проверьте, может ли проект нормально работать, успешно ли импортирован элемент и т. д.
Часть проекта, связанная с инициализацией проекта 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>
После снятия края для заполнения
На данный момент интерфейсный стиль завершен, за исключением части сообщения, а остальное — отладка внутреннего интерфейса.
серверная часть
Особенности дизайна:
- Авторизоваться
- Показать список пользователей, которые находятся в сети
- Живой чат
- Онлайн-напоминание, офлайн-напоминание
Создайте уникальное имя пользователя
блок-схема
Тип
сообщения Сообщения, отправляемые между пользователями, пользовательские онлайн-уведомления, пользовательские офлайн-уведомления, сообщения списка друзей, системные уведомления (некоторые ошибки и т. д.)
Отправитель тела сообщения
, получатель, тип сообщения, содержание сообщения
Резюме идей :
- Как обеспечить безопасность? Изначально планировалось, что каждый запрос несет заголовок запроса для проверки в перехватчике, как и http-запрос, но после тестирования выяснилось, что каждый раз, когда http-запрос представляет собой другое соединение, его нужно каждый раз проверять. ws может проверить только один раз при создании соединения. Токен возвращается при входе в систему, и сервер использует карту для сохранения записи токена. Перенесите этот токен, когда ws создает соединение, сервер ищет его на карте и определяет, является ли соединение законным, на основе того, найдено ли оно.
- Как определить отправителя сообщения? При отправке сообщения укажите способ отправки в теле запроса (может быть небезопасно)
- Как поместить пользовательское соединение в карту сеанса? После проверки токена ws заносим найденное имя пользователя в LocalThrad, а при закладывании в sessionMap в качестве ключа берем имя пользователя из LocalThread, а значением является подключенная сессия
- Что делает инициализация? Поместите соединение в карту сеанса во время инициализации, а затем верните список друзей через сеанс, потому что имя используется в качестве ключа в этом коде, поэтому вам нужно только пройти все ключи в карте сеанса. Затем отправьте системное сообщение всем пользователям о том, что пользователь уже находится в сети.
- Как различать разные типы сообщений? Настройка формата сообщения позволяет различать различные типы сообщений и отправлять системные сообщения, списки друзей и сообщения друзей в едином формате.
- Как обновляется список друзей в сети? Когда начальное соединение будет установлено, серверная часть отправит всех текущих онлайн-пользователей на это соединение, а интерфейс будет обновляться, когда система позже предложит вам перейти в онлайн или офлайн.
- Как бороться с исключениями и отключениями? Инкапсулируйте метод для уведомления других сеансов об отключении пользователя.В этом методе в соответствии с сеансом в списке параметров перейдите к sessionMap, чтобы найти ключ в соответствии со значением, используйте ключ как сообщение и установите тип сообщения для пользователя в автономном режиме для других пользователей, отправленных в сеансе. Внешний интерфейс получает сообщение об автономном типе пользователя, запрашивает и удаляет это имя из списка онлайн-друзей.
комментарий к коду
Код внутреннего ядра : (Поскольку место слишком длинное, здесь только комментарии, подробный код был загружен на gitee, вы можете посетить и просмотреть его самостоятельно)
На данный момент все функции внутреннего интерфейса были в принципе закончено, только фронтенд получает разные типы сообщений, и назначаю его на рендеринг
Здесь консольный тест успешно отправил и получил сообщения, а следующее — самое проблемное отображение сообщений в этом проекте.
Используя метод кругового массива, этот метод будет немного отличаться от обычных привычек чата, поэтому эта версия не будет изменена.Кроме того
, онлайн-список также использует тот же метод.
Онлайн-чат версии 1.0 завершен
исходный код
Полный исходный код был загружен в gitee, перейдите по ссылке https://gitee.com/wang-yongyan188/websocketchat.git .
Примечание . Поскольку токен запроса на создание ws хранится в локальном хранилище, даже если тот же браузер регистрирует в разные учетные записи. Это также один и тот же пользователь, поэтому для проверки используйте разные браузеры. Если вы тестируете только серверную часть, рекомендуется использовать почтальон для тестирования.
Все коды оригинальные.Блог и коды заняли два дня.Любые ошибки приветствуются.Если вы считаете, что это нормально, пожалуйста, ставьте лайк.
Цель этого проекта — понять протокол ws и научиться использовать Springboot для смешанной разработки двух протоколов.
Кодовая страница относительно проста, а функции несовершенны (регистрацию нужно вручную добавлять в базу данных, а общаться только с онлайн-пользователями и т. д.), если вам это нравится, будут запущены последующие действия, такие как: добавить друзей, напоминание о непрочитанных сообщениях, групповой чат, сохранение сообщений и т. д., а также в качестве небольшого проекта.