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/

结果如下:

spring boot长连接 springboot设置长连接_spring

方式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>

结果如下:

spring boot长连接 springboot设置长连接_spring boot长连接_02