当前位置:   article > 正文

Spring Boot集成WebSocket以及基本使用_websockethandler

websockethandler

WebSocket概述

WebSocket是一种在Web浏览器和Web服务器之间建立实时双向通信的技术,它可以使客户端和服务器之间进行实时交互。

WebSocket让客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

添加依赖

在pom.xml文件中添加以下依赖:

	 <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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

服务端

创建WebSocket处理类

创建一个类,并使用@Component注解将其声明为Spring Bean。在该类中,需要实现WebSocketHandler接口,并重写其中的方法来处理WebSocket消息

@Component
public class MyWebSocketHandler implements WebSocketHandler {

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //连接成功时调用该方法
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        //接收到消息时调用该方法
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //发生错误时调用该方法
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        //连接关闭时调用该方法
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

自定义WebSocketHandler

还可以自定义WebSocketHandler来处理 WebSocket 消息。自定义WebSocketHandler需要实现WebSocketHandler接口,并重写其中的方法。

还可以继承以下类实现相关接口:

TextWebSocketHandler: 文本内容

BinaryWebSocketHandler:二进制内容
  • 1
  • 2
  • 3
@Component
public class CustomizeWebSocketHandler extends TextWebSocketHandler {

    private Logger log = LoggerFactory.getLogger(CustomizeWebSocketHandler.class);
    private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("与客户端建立连接...");
        //连接成功时调用该方法
        System.out.println("WebSocket connected: " + session.getId());
        sessions.add(session);
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        // 获取客户端发送的消息
        System.out.println("客户端ID: " + session.getId() + " 发送消息: " + message.getPayload());
        for (WebSocketSession s : sessions) {
            if (s.isOpen()) {
                String response = "服务端响应: " + message.getPayload();
                // 发送消息给客户端
                s.sendMessage(new TextMessage(response));
                // 关闭连接
                // s.close(CloseStatus.NORMAL);
            }
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //发生错误时调用该方法
        System.err.println("WebSocket error: " + exception.getMessage());
        session.close(CloseStatus.SERVER_ERROR);
        log.error("连接异常", exception);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        //连接关闭时调用该方法
        System.out.println("WebSocket closed: " + session.getId());
        sessions.remove(session);
        super.afterConnectionClosed(session, closeStatus);
        log.info("与客户端断开连接...");
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

WebSocketSession: 代表每个客户端会话,提供了许多实用方法

配置WebSocket

在 Spring Boot 应用程序的配置类中,创建WebSocketServerConfigure配置类,使用@EnableWebSocket注解启用WebSocket支持。然后需要实现 WebSocketConfigurer接口,将自定义的CustomizeWebSocketHandler 注册到Spring Boot容器并设置拦截路径/webSocket,凡是该路径请求的连接将自动连接到WebSocket服务端。

@Configuration
@EnableWebSocket//开启WebSocket相关功能
public class WebSocketServerConfigure implements WebSocketConfigurer {

    @Autowired
    private CustomizeWebSocketHandler customizeWebSocketHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    	// 注册自定义customizeWebSocketHandler 使用sockJs
        registry.addHandler(customizeWebSocketHandler, "/sockJs").withSockJS();
        // 注册自定义customizeWebSocketHandler 使用ws
        registry.addHandler(customizeWebSocketHandler, "/ws").setAllowedOrigins("*");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

客户端

WebSocket

WebSocket是一种基于TCP协议的全双工通信协议,在Web应用中可以实现双向通信。浏览器可以使用WebSocket对象与服务端建立连接,并通过该连接来接收服务端发送的消息,从而实现页面代码对服务端的通知。

创建index.html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket客户端</title>
</head>
<body>
<div style="width:50%;margin:auto;border-style:solid;border-color:aqua;border-width: 1px">
    <input type="text" placeholder="请输入发送的内容" id="text"/>
    <input type="button" value="连接" class="btn btn-info" onclick="connect()"/>
    <input type="button" value="发送" class="btn btn-success" onclick="sent()"/>
    <input type="button" value="断开" class="btn btn-danger" onclick="disconnect()"/>
    <div id="log">
        <p>聊天记录:</p>
    </div>
</div>
<script type="text/javascript">
    let text = document.querySelector('#text');
    let logDiv = document.querySelector("#log");

    let ws = null;

    function connect() {
        ws = new WebSocket("ws://localhost:8080/ws");

        ws.onopen = function () {
            log('与服务端连接成功!');
        };

        ws.onmessage = function (event) {
            log('服务端:' + event.data);
        };

        ws.onclose = function () {
            log('与服务端断开连接!')
        }

    }

    function sent() {
        if (ws != null) {
            ws.send(text.value);
            log('客户端:' + text.value);
        } else {
            log('请先建立连接!')
        }
    }

    function disconnect() {
        if (ws != null) {
            ws.close();
            ws = null;
        } else {
            log('连接已断开!')
        }
    }

    function log(value) {
        let content = document.createElement('p');
        content.innerHTML = value;
        logDiv.appendChild(content);
        text.value = '';
    }
</script>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

跳转到index.html页面

@Controller
public class TestController {

    @RequestMapping("/index")
    public String index() {
        return "index";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

SockJS

SockJS是一个WebSocket的封装库,它提供了一种简单的方式来处理WebSocket在某些浏览器中无法正常工作的情况。通常,当WebSocket不可用时,SockJS会自动降级到使用HTTP流等其他技术进行实现。

SockJS: https://github.com/sockjs/sockjs-client

SockJS对象包含几个常用的实用方法:

onopen,和服务端讲了连接后的回调方法

onmessage,服务端返回消息时的回调方法

onclose,和服务端断开连接的回调方法

send,发送消息给服务端

close,断开和服务端的连接
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

引入SockJS库。在connect()方法中,通过new SockJS(/connect)和服务端建立Socket通信

<head>
    <meta charset="UTF-8">
    <title>WebSocket客户端</title>
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
</head>
  • 1
  • 2
  • 3
  • 4
  • 5

使用SockJS对象

let url = "http://localhost:8080/sockJs";
ws = new SockJS(url);
  • 1
  • 2

通信测试

在这里插入图片描述
WebSocket日志

2023-05-21 21:07:40.182  INFO 28400 --- [nio-8080-exec-2] c.y.demo.ws.CustomizeWebSocketHandler    : 与客户端建立连接...
WebSocket connected: 5442a838-e6f7-fdff-3eef-004732be1bb2
客户端ID: 5442a838-e6f7-fdff-3eef-004732be1bb2 发送消息: 你好
WebSocket closed: 5442a838-e6f7-fdff-3eef-004732be1bb2
2023-05-21 21:07:45.885  INFO 28400 --- [nio-8080-exec-7] c.y.demo.ws.CustomizeWebSocketHandler    : 与客户端断开连接...
  • 1
  • 2
  • 3
  • 4
  • 5

SockJS日志

2023-05-21 21:11:42.962  INFO 9980 --- [nio-8080-exec-3] c.y.demo.ws.CustomizeWebSocketHandler    : 与客户端建立连接...
WebSocket connected: dnf1qgco
客户端ID: dnf1qgco 发送消息: 123
WebSocket closed: dnf1qgco
2023-05-21 21:11:47.419  INFO 9980 --- [nio-8080-exec-5] c.y.demo.ws.CustomizeWebSocketHandler    : 与客户端断开连接...
  • 1
  • 2
  • 3
  • 4
  • 5

@ServerEndpoint注解

还可以使用@ServerEndpoint注解来创建WebSocket服务器端点。这个注解将类标记为一个WebSocket服务器端点,并定义了客户端连接的URL路径,例如:/ws/socket

@OnOpen、@OnClose和@OnMessage注解分别表示连接建立、连接关闭和接收消息时要执行的方法。

当客户端连接到服务器时,onOpen方法将被调用,并且将传递Session对象作为参数。通过Session对象,可以发送消息给客户端

当客户端发送消息时,onMessage方法将被调用,并且将传递消息和Session对象作为参数。可以在其中实现业务逻辑,例如广播消息给所有连接的客户端

当客户端断开连接时,onClose方法将被调用
  • 1
  • 2
  • 3
  • 4
  • 5

实现服务器端点

@Slf4j
@Component
@ServerEndpoint(value = "/ws/socket")
public class WebSocketServer {


    // 使用一个ConcurrentSkipListSet集合来保存所有连接的WebSocket客户端。这个集合是线程安全的,可以安全地从多个线程访问
    private static Map<String, Session> sessionPools = new ConcurrentHashMap<>();


    /**
     * 连接建立成功调用
     *
     * @param session 客户端与socket建立会话
     */
    @OnOpen
    public void onOpen(Session session) {
        log.info("有新的WebSocket连接:" + session.getId());
        // 添加新连接到集合中
        sessionPools.put(session.getId(), session);
    }

    /**
     * 获取所有在线用户列表
     *
     * @return
     */
    public List<String> getInLineAccountIds() {
        List<String> list = new ArrayList();
        list.addAll(sessionPools.keySet());
        return list;
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        log.info("接收到WebSocket消息:" + message);
        sendMessage(session, message);
    }


    /**
     * 关闭连接时调用
     *
     * @param session 关闭连接的客户端的姓名
     */
    @OnClose
    public void onClose(Session session) {
        sessionPools.remove(session.getId());
        // 从集合中删除断开的连接
        log.info("WebSocket连接已关闭:" + session.getId());
    }

    /**
     * 发生错误时候
     *
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        log.info("发生错误");
        sessionPools.remove(session.getId());
        throwable.printStackTrace();
    }


    /**
     * 发送消息
     *
     * @param session
     * @param message
     */
    private void sendMessage(Session session, String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 给指定用户发送消息
     *
     * @throws IOException
     */
    public void pushMessage(String sessionId, String msg) {
        //获取当前会话
        Session session = sessionPools.get(sessionId);
        if (null != session) {
            //获取消息体
            sendMessage(session, msg);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

开启WebSocket支持

/**
 * 开启WebSocket支持
 */
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

测试

建立WebSocket链接

 ws = new WebSocket("ws://localhost:8080/ws/socket");
  • 1

日志

2023-05-21 21:59:48.100  INFO 31752 --- [nio-8080-exec-2] cn.ybzy.demo.ws.WebSocketServer          : 有新的WebSocket连接:0
session = org.apache.tomcat.websocket.WsSession@7100dfaf
2023-05-21 22:00:21.287  INFO 31752 --- [nio-8080-exec-7] cn.ybzy.demo.ws.WebSocketServer          : 接收到WebSocket消息:你好
2023-05-21 22:00:24.706  INFO 31752 --- [nio-8080-exec-8] cn.ybzy.demo.ws.WebSocketServer          : WebSocket连接已关闭:0
  • 1
  • 2
  • 3
  • 4
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/241978
推荐阅读
相关标签
  

闽ICP备14008679号