Что такое веб-сокет?
Протокол WebSocket — это новый сетевой протокол, основанный на TCP. Он реализует полнодуплексную связь между браузером и сервером, позволяя серверу активно отправлять информацию клиенту.
Зачем нужны веб-сокеты?
Все мы знаем, что в прошлом клиент хотел знать ход обработки сервера, и было необходимо использовать Ajax для непрерывного опроса, чтобы браузер отправлял запрос на сервер каждые несколько секунд, что ставило много задач. давления на сервер. Другим методом опроса является использование длинного опроса, который аналогичен телефонному звонку.Если сообщение не получено, он никогда не повесит трубку.То есть, после того, как клиент инициирует соединение, если нет сообщения, он будет не возвращать ответ клиенту. , фаза подключения всегда блокируется.
И WebSocket решает эти проблемы HTTP. Когда сервер завершает обновление протокола ( HTTP -> WebSocket ), сервер может активно передавать информацию клиенту, что решает проблему задержки синхронизации, вызванной опросом. Поскольку для WebSocket требуется только одно рукопожатие HTTP, сервер может поддерживать связь с клиентом до тех пор, пока соединение не будет закрыто, что избавляет сервер от необходимости многократно анализировать протокол HTTP и снижает накладные расходы на ресурсы.
Теперь интегрируйте WebSocket через SpringBoot, чтобы реализовать интерфейсную и внутреннюю связь.
Интегрируйте WebSocket для реализации внешнего и внутреннего взаимодействия.
Схема структуры кода проекта
импорт зависимостей
Поддержка WebSocket в SpringBoot2.0 просто великолепна, и есть пакеты, которые можно внедрить напрямую.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Настроить WebSocketConfig
Включить поддержку WebSocket также очень просто, внедрив объект ServerEndpointExporter в контейнер.
package com.tuhu.websocketsample.configuration;
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();
}
}
Веб-сокет-сервер Веб-сокет-сервер
Поскольку WebSocket аналогичен форме клиентского сервера (использующего протокол ws), то WebSocketServer здесь фактически эквивалентен контроллеру протокола ws. Просто включите @ServerEndpoint("/websocket") и @Component напрямую, а затем реализуйте в нем @OnOpen , @onClose , @onMessage и другие методы
package com.tuhu.websocketsample.controller;
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;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/websocket/{sid}")
@Slf4j
public class WebSocketServer {
/**
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
*/
private static int onlineCount = 0;
/**
* concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
*/
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收sid
*/
private String sid="";
/**
* 连接建立成功调用的方法
**/
@OnOpen
public void onOpen(Session session,@PathParam("sid") String sid) {
this.session = session;
//加入set中
webSocketSet.add(this);
//在线数加1
addOnlineCount();
log.info("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());
this.sid=sid;
try {
sendMessage("连接成功");
} catch (IOException e) {
log.error("websocket IO异常");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
//从set中删除
webSocketSet.remove(this);
//在线数减1
subOnlineCount();
log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息
**/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来自窗口"+sid+"的信息:"+message);
//群发消息
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 群发自定义消息
* */
public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
log.info("推送消息到窗口"+sid+",推送内容:"+message);
for (WebSocketServer item : webSocketSet) {
try {
//这里可以设定只推送给这个sid的,为null则全部推送
if(sid==null) {
item.sendMessage(message);
}else if(item.sid.equals(sid)){
item.sendMessage(message);
}
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
сообщение
Что касается отправки новой информации, вы можете написать метод в своем собственном контроллере для вызова WebSocketServer.sendInfo().
package com.tuhu.websocketsample.controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
@RestController
@RequestMapping("/checkcenter")
public class CheckCenterController {
/**
* 页面请求
* @param cid
* @return
*/
@GetMapping("/socket/{cid}")
public ModelAndView socket(@PathVariable String cid) {
ModelAndView mav=new ModelAndView("/socket");
mav.addObject("cid", cid);
return mav;
}
/**
* 推送数据接口
* @param cid
* @param message
* @return
*/
@ResponseBody
@RequestMapping("/socket/push/{cid}")
public String pushToWeb(@PathVariable String cid,String message) {
try {
WebSocketServer.sendInfo(message,cid);
} catch (IOException e) {
e.printStackTrace();
return "error:"+cid+"#"+e.getMessage();
}
return "success:"+cid;
}
}
Страница инициирует запрос сокета
Затем используйте код js для вызова сокета на странице.Конечно, слишком старые браузеры не будут работать.Как правило, новые браузеры или Google Chrome не проблема. Еще один момент, помните, что протокол ws. Откройте соединение прямо в консоли браузера.
var socket;
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else{
console.log("您的浏览器支持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
socket = new WebSocket("ws://localhost:8080/websocket/20");
//打开事件
socket.onopen = function() {
console.log("Socket 已打开");
//socket.send("这是来自客户端的消息" + location.href + new Date());
};
//获得消息事件
socket.onmessage = function(msg) {
console.log(msg.data);
//发现消息进入 开始处理前端触发逻辑
};
//关闭事件
socket.onclose = function() {
console.log("Socket已关闭");
};
//发生了错误事件
socket.onerror = function() {
alert("Socket发生了错误");
//此时可以尝试刷新页面
}
//离开页面时,关闭socket
//jquery1.8中已经被废弃,3.0中已经移除
// $(window).unload(function(){
// socket.close();
//});
}
текущий результат
Теперь соединение можно открывать в браузере, и сервер может отправлять сообщения в браузер, вызывая интерфейс с клиента.
Теперь откройте две страницы и откройте два соединения:
- socket = новый WebSocket("ws://localhost:8080/websocket/20");
- socket = new WebSocket("ws://localhost:8080/websocket/22") ;
Отправьте данные во внешний интерфейс:
- http://localhost:8080/checkcenter/socket/push/20?message=Hello
- http://localhost:8080/checkcenter/socket/push/22?message=HelloWorld
Вы можете видеть, что сервер передал сообщение клиенту.
И клиент также получил сообщение
Сначала откройте страницу, укажите cid, включите прием сокета, а затем вызовите метод пуша информации, инкапсулированной Контроллером только что на другой странице, в сокет этого cid, и тогда сообщение можно будет пушить на фронтенд.
следовать за
ошибка serverEndpointExporter
org.springframework.beans.factory.BeanCreationException: ошибка при создании bean-компонента с именем serverEndpointExporter, определенным в ресурсе пути к классу [com/xxx/WebSocketConfig.class]: не удалось вызвать метод инициализации; вложенным исключением является java.lang.IllegalStateException: javax. websocket.server.ServerContainer недоступен
Если развертывание Tomcat продолжает сообщать об этой ошибке, удалите внедрение @Bean ServerEndpointExporter в WebSocketConfig.
ServerEndpointExporter — это стандартная реализация, официально предоставленная Spring для сканирования классов конфигурации ServerEndpointConfig и экземпляров аннотаций @ServerEndpoint. Правила использования также просты:
1. Если вы используете встроенный контейнер по умолчанию, такой как Tomcat, вы должны вручную указать ServerEndpointExporter в контексте.
2. Если вы используете внешний контейнер для развертывания военного пакета, вам не нужно предоставлять ServerEndpointExporter, потому что SpringBoot по умолчанию передает поведение сканирования сервера во внешний контейнер для обработки, поэтому при развертывании в сети вы должны учитывать код, внедренный в bean-компонент в WebSocketConfig.