本文记录说明springboot websocket示例及实战,你将学习到
三种websocket开发方式
以及通过实战演示在不同业务场景的技术选择
使用环境:
Spring Boot WebSocket是Spring框架的一部分,用于实现WebSocket通信协议的支持。WebSocket是一种双向通信协议,允许服务器与客户端之间进行实时、低延迟的数据交换,适用于实时聊天、实时通知、在线协作和实时数据展示等场景。Spring Boot WebSocket提供了使用WebSocket的简化和高效的方法,让开发者能够轻松地实现WebSocket通信。
以下是Spring Boot WebSocket的主要特点和概念:
步骤说明
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
@Autowired
private EchoWebSocketHandler echoWebSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 配置原生WebSocket处理器
registry.addHandler(echoWebSocketHandler, "/websocket")
.setAllowedOrigins("*"); //允许跨域
}
@Bean
public EchoWebSocketHandler echoWebSocketHandler() {
return new EchoWebSocketHandler();
}
}
public class EchoWebSocketHandler implements WebSocketHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(EchoWebSocketHandler.class);
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 连接建立时的处理逻辑
LOGGER.info("[websocket-echo] session:{} ConnectionEstablished", session.getId());
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws IOException {
String payload = (String) message.getPayload();
LOGGER.info("[websocket-echo] session:{} receive:{}", session.getId(), payload);
session.sendMessage(new TextMessage(payload));
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
// 处理传输错误
}
// 连接关闭时的处理逻辑
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
LOGGER.info("[websocket-echo] WebSocketSession[{}] close all ssh connection", session.getId());
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}
TODO 说明下WebSocket 对象作用
DOCTYPE html>
<html>
<head>
<title>原始WebSocket示例title>
head>
<body>
<h1>测试默认websocket 接发消息h1>
<div id="messages">div>
<input type="text" id="message" placeholder="输入消息">
<button onclick="sendMessage()">发送button>
<script>
var socket = new WebSocket("ws://localhost:9090/websocket");
socket.onopen = function(event) {
console.log("WebSocket连接已打开");
};
socket.onmessage = function(event) {
var messages = document.getElementById("messages");
messages.innerHTML += ""
+ event.data + "";
};
function sendMessage() {
var messageInput = document.getElementById("message");
var message = messageInput.value;
socket.send(message);
messageInput.value = "";
}
script>
body>
html>
为了区别原生WebSocket处理器,以示区别,再注册一个接口/websocket-sockjs. 并且开启.withSockJS()
@Configuration
@EnableWebSocket
@Import({WebSocketStompConfiguration.class})
public class WebSocketConfiguration implements WebSocketConfigurer {
@Autowired
private EchoWebSocketHandler echoWebSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 配置原生WebSocket处理器器
registry.addHandler(echoWebSocketHandler, "/websocket")
.setAllowedOrigins("*");
//WebSocket connection to 'ws://localhost:9090/websocket' failed: Error during WebSocket handshake: Unexpected response code: 200
//.withSockJS();
//.withSockJS()的作用是声明我们想要使用 SockJS 功能,如果WebSocket不可用的话,会使用 SockJS。(前端需要使用sockjs库)
registry.addHandler(echoWebSocketHandler, "/websocket-sockjs")
.setAllowedOrigins("*")
.withSockJS();
}
@Bean
public EchoWebSocketHandler echoWebSocketHandler() {
return new EchoWebSocketHandler();
}
}
使用sockjs需要引入前端sockjs的库: https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js
DOCTYPE html>
<html>
<head>
<title>原始WebSocket示例title>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js">script>
head>
<body>
<h1>测试websocket sockjs接发消息h1>
<div id="messages">div>
<input type="text" id="message" placeholder="输入消息">
<button onclick="sendMessage()">发送button>
<script>
// var socket = new WebSocket("ws://localhost:9090/websocket");//原始写法
var socket = new SockJS('http://localhost:9090/websocket-sockjs');// 依赖sockjs库
socket.onopen = function(event) {
console.log("WebSocket连接已打开");
};
// 监听websockt消息回调
socket.onmessage = function(event) {
var messages = document.getElementById("messages");
messages.innerHTML += ""
+ event.data + "";
};
// 定义连接关闭时的回调函数
socket.onclose = function () {
console.log('WebSocket 连接已关闭');
};
// 定义连接错误时的回调函数
socket.onerror = function (e) {
console.error('WebSocket 连接出错:', e);
};
function sendMessage() {
var messageInput = document.getElementById("message");
var message = messageInput.value;
socket.send(message);
messageInput.value = "";
}
script>
body>
html>
步骤说明
@EnableWebSocketMessageBroker //在 WebSocket 上启用 STOMP
public class WebSocketStompConfiguration {
@Bean
@ConditionalOnMissingBean
public WebSocketMessageBrokerConfigurer brokerConfigurer() {
return new WebSocketMessageBrokerConfigurer() {
/**
* stomp 协议,一种格式比较简单且被广泛支持的通信协议,spring4提供了以stomp协议为基础的websocket通信实现。
* spring 的websocket实现,实际上是一个简易版的消息队列(而且是主题-订阅模式的)
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// "/stomp-websocket",客户端需要注册这个端点进行链接,
// .withSockJS()的作用是声明我们想要使用 SockJS 功能,如果WebSocket不可用的话,会使用 SockJS。
registry.addEndpoint("/websocket-stomp")
.setAllowedOrigins("*")
.withSockJS();
}
};
}
}
@Controller
public class TestWebSocketStompController {
private static final Logger LOGGER = LoggerFactory.getLogger(TestWebSocketStompController.class);
@Autowired
SimpMessagingTemplate messagingTemplate;
@GetMapping("/websocket/print/{msg}")
@ResponseBody
public String print(@PathVariable String msg) {
messagingTemplate.convertAndSend("/topic/print", msg);
return msg;
}
@MessageMapping("/app/{destination}") // 使用 {destination} 占位符来捕获动态的目的地
@SendTo("/topic/print") // 服务器广播消息的目的地
public String handleMessage(@DestinationVariable String destination, String message) {
// 处理接收到的消息
LOGGER.info("Dynamic destination:[{}] Received message:{}", destination, message);
// 根据动态目的地执行不同的操作
if ("destination1".equals(destination)) {
// 处理目的地1的操作
} else if ("destination2".equals(destination)) {
// 处理目的地2的操作
} else {
// 直接转发到对应订阅地址
messagingTemplate.convertAndSend("/topic/" + destination, "copy:" + message);
}
// 可以根据需要添加更多的条件
// 返回响应消息
return message;
}
}
@Configuration
@EnableWebSocket
@Import({WebSocketStompConfiguration.class})
public class WebSocketConfiguration implements WebSocketConfigurer {
@Autowired
private EchoWebSocketHandler echoWebSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 配置原生WebSocket处理器
registry.addHandler(echoWebSocketHandler, "/websocket")
.setAllowedOrigins("*");
//WebSocket connection to 'ws://localhost:9090/websocket' failed: Error during WebSocket handshake: Unexpected response code: 200
//.withSockJS();
//.withSockJS()的作用是声明我们想要使用 SockJS 功能,如果WebSocket不可用的话,会使用 SockJS。(前端需要使用sockjs库)
registry.addHandler(echoWebSocketHandler, "/websocket-sockjs")
.setAllowedOrigins("*")
.withSockJS();
}
@Bean
public EchoWebSocketHandler echoWebSocketHandler() {
return new EchoWebSocketHandler();
}
}
前端依赖sockjs及stomp.min.js的库
DOCTYPE html>
<html>
<head>
<title>WebSocket Exampletitle>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.5.2/sockjs.min.js">script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js">script>
head>
<body>
<h1>测试websocket stomp协议接发消息h1>
<h2>发送示例h2>
<input type="text" id="sendDestination" placeholder="Enter send destination">
<input type="text" id="message" placeholder="Enter your message">
<button onclick="sendMessage()">Send Messagebutton>
<hr>
<h2>订阅示例h2>
<input type="text" id="listenDestination" placeholder="Enter listen destination">
<button onclick="subscribeToDestination()">Subscribebutton>
<div id="messages">div>
<script>
var stompClient = null;
var listenDestination = null;
function connect() {
var socket = new SockJS('/websocket-stomp'); // WebSocket端点
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('stomp Connected: ' + frame);
});
}
function sendMessage() {
var sendDestination = document.getElementById('sendDestination').value; // 获取发送目的地
var message = document.getElementById('message').value;
stompClient.send('/app/' + sendDestination, {}, message); // 发送消息到指定的目的地
}
function subscribeToDestination() {
listenDestination = document.getElementById('listenDestination').value; // 获取监听目的地
stompClient.subscribe('/topic/' + listenDestination, function (message) {
displayMessage(message.body);
});
}
function displayMessage(message) {
var messagesDiv = document.getElementById('messages');
var p = document.createElement('p');
p.appendChild(document.createTextNode(message));
messagesDiv.appendChild(p);
}
connect();
script>
body>
html>
如果你期望WebSocket连接之间是隔离的,不需要共享数据,那么使用原始的WebSocket通信是一个合适的选择。原始WebSocket通信提供了一种简单而轻量级的方式,每个连接都是独立的,不会共享会话数据。
STOMP协议通常用于需要高级消息传递功能和消息广播的场景,其中不同连接之间需要共享数据。如果你不需要这种复杂性,原始WebSocket通信足以满足需求,而且更容易理解和维护。
因此,你可以根据你的应用程序需求选择使用原始WebSocket通信或STOMP协议。如果只需要简单的双向通信并希望保持连接之间的隔离,原始WebSocket通信通常是一个更直接的选择
springboot实现webssh功能, 使用xterm(前端) + websocket + jsch技术实现。后端主要实现websocket消息与jsch命令收发即可。还在开发中, 篇幅关系,实现过程就不写了。有需要点赞或留言,开源后再通知。
web在线查看springboot后台日志,源码参考:https://github.com/easycode8/easy-log 的easy-log-web模块, 代码量很少。