springboot实现webocket长连接(一)
demo下载地址:多种websocket实现方式,其中有基于spring-websocekt,也有基于netty框架,即下即用。
需求说明:
长连接一般用于实时消息推送、聊天会话等场景,可以将一个复杂的消息分批实时推送,用户体验较好。
实现方式:
这里通过一种较简单的方式实现websocket。这里简单的意思是指前端可以通过浏览器原生对象websocket实现客户端,如果浏览器不支持的话,这个方式不适用了,需要改造一下。
废话不多说,上代码。
第一步:
引入依赖,只需要引入spring-boot-starter-websocket即可。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
第二步:
配置文件。
import com.example.wsdemo.websocket.example1.interceptor.Example1HandshakeInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import javax.annotation.Resource;
/**
* 网络套接字配置
*
* @author lukou
* @date 2023/04/12
*/
@Configuration
@EnableWebSocket
public class Example1WebSocketConfig implements WebSocketConfigurer {
@Resource
private WebSocketHandler webSocketHandler;
@Resource
private Example1HandshakeInterceptor example1HandshakeInterceptor;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 访问地址:ws://localhost:9000/example1/ws
registry.addHandler(webSocketHandler, "/example1/ws")
// 注册拦截器
.addInterceptors(example1HandshakeInterceptor)
// 设置跨域
.setAllowedOrigins("*");
}
}
第三步:
拦截器(看实际场景,不一定要有)和业务处理器。
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import java.io.IOException;
/**
* 网络套接字业务处理器
*
* @author lukou
* @date 2023/05/05
*/
@Component
@Slf4j
public class Example1WebSocketHandler extends AbstractWebSocketHandler {
/**
* 连接建立之后
*
* @param session 会话
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) {
log.info("oneSearch websocket is connected! session id: [{}]", session.getId());
}
/**
* 处理文字信息
*
* @param session 会话
* @param message 消息
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
log.info("session [{}] received message is [{}]", session.getId(), message.getPayload());
}
/**
* 处理异常错误
*
* @param session 会话
* @param exception 异常
*/
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws IOException {
log.error("session [{}] error!", session.getId(), exception);
}
/**
* 连接关闭后
*
* @param session 会话
* @param status 状态
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
log.info("session [{}] is closed! closeStatus is [{}]", session.getId(), status.toString());
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* websocket握手拦截器
*
* @author lukou
* @date 2023/04/19
*/
@Slf4j
@Component
public class Example1HandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) serverHttpRequest;
// 得到Http协议的请求对象
HttpServletRequest request = servletServerHttpRequest.getServletRequest();
// 可以通过获取请求头或者参数进行校验,例如请求头中的token
String token = request.getHeader("token");
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
}
测试:
方式1:
可以通过一些网站测试,比如 http://www.jsons.cn/websocket/
结果如下:
方式2:
通过前端代码测试
<!DOCTYPE html>
<html>
<head>
<title>WebSocket SpringBootDemo</title>
</head>
<body>
<div>默认用户id:xiaoyou001(后期可以根据业务逻辑替换)</div>
<br /><input id="text" type="text" />
<button onclick="send()">发送消息</button>
<br />
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
// 后台给的地址
websocket = new WebSocket("ws://localhost:9000/example1/ws");
}
else {
alert('当前浏览器不支持websocket哦!')
}
//连接发生错误的回调方法
websocket.onerror = function () {
setMessageInnerHTML("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
setMessageInnerHTML("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function () {
setMessageInnerHTML("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
}
//将消息显示在网页上
function setMessageInnerHTML(sendMessage) {
document.getElementById('message').innerHTML += sendMessage + '<br/>';
}
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send() {
var message = document.getElementById('text').value;//要发送的消息内容
if (message == "") {
alert("发送信息不能为空!")
return;
}
document.getElementById('message').innerHTML += ("发送消息,消息内容为---->>" + message + '<br/>');
websocket.send(message);
}
</script>
</html>
结果如下: