一、前言

WebSocket是一种新型的通信协议,它可以在客户端和服务端之间实现双向通信,具有低延迟、高效性等特点,适用于实时通信场景。它是一种基于TCP协议实现的全双工通信协议,使用它可以实现实时通信,不必担心HTTP协议的短连接问题。SpringBoot可以很方便的集成WebSocket实现高效实时的消息推送。

二、特点

1.WebSocket是HTML5的一种新的协议,本质上它是独立的、创建在TCP上的一种协议。

2.通过HTTP/1.1协议的101状态码进行握手。

3.为了达成WebSocket连接,需要浏览器发出请求后服务器进行回应,这个过程也称为握手。

4.WebSocket是一种持久化的协议。

5.WebSocket实现了浏览器和服务器之间全双工通信,能够更好的节省服务器资源和带宽,以此达到实时通讯的目的。

三、前端相关API

1.建立连接

使用new WebSocket(url)创建WebSocket连接。

var url = "ws://mywebsocket";
var websocket = new WebSocket(url);

2.推送消息

通过send方法向服务器发送消息。

websocket.send("hello");

3.接收服务器传过来的数据

websocket.onmessage = function (resp) {
     var data = resp.data;
}

4.监听WebSocket打开事件

websocket.onopen = function (event){
            // 开始通信事件
}

5.监听WebSocket关闭事件

websocket.onclose = function (event){
            // 监听关闭事件
}

6.监听WebSocket关闭事件

websocket.onclose = function (event){
            // 监听关闭事件
        }

7.WebSocket状态

我们可以通过readyState属性来获取WebSocket对象的状态。

CONNECTION(0):表示正在连接。

OPEN(1):表示已建立连接。

CLOSING(2):表示正在关闭连接。

CLOSED(3):表示已关闭连接。


四、SpringBoot集成WebSocket

1.添加依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

2.添加WebSocket配置类

package com.example.nettydemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author qx
 * @date 2023/12/25
 * @des WebSocket配置
 */
@Configuration
public class WebSocketConfig {

    /**
     * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3.创建WebSocketServer类,用来进行服务端和客户端之间的交互

package com.example.nettydemo.websocket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author qx
 * @date 2023/12/25
 * @des WebSocket核心处理类
 */
@Component
@ServerEndpoint("/myWebsocket")
@Slf4j
public class WebSocketServer {


    private static List<Session> sessionList;

    @OnOpen
    public void onOpen(Session session) {
        sessionList = new ArrayList<>();
        sessionList.add(session);
        log.info("websocket建立连接成功");
    }

    @OnMessage
    public void onMessage(String message) {
        log.info("接收到客户端的数据:{}", message);
    }

    @OnError
    public void onError(Throwable error) {
        log.info("连接客户端失败:{}", error.getMessage());
    }

    @OnClose
    public void onClose() {
        log.info("websocket断开连接");
    }

    /**
     * 实现服务端发送消息
     *
     * @param msg 消息内容
     * @throws IOException
     */
    public void sendMsg(String msg) throws IOException {
        sessionList.get(0).getBasicRemote().sendText(msg);
    }
}

我们用到了几个注解,分别解释一下它们的含义。

注解

作用

@ServerEndpoint

创建一个服务端的URL,客户端通过这个URL来连接服务端

@OnOpen

打开连接触发事件

@OnMessage

收到接收消息事件

@OnClose

关闭连接触发事件

@OnError

处理发生错误的事件


4.创建控制层

package com.example.nettydemo.controller;

import com.example.nettydemo.websocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;

/**
 * @author qx
 * @date 2023/12/25
 * @des
 */
@RestController
@RequestMapping("/websocket")
public class WebSocketController {

    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 跳转WebSocket通信页面
     */
    @GetMapping("/page")
    public ModelAndView toPage() {
        return new ModelAndView("webSocket");
    }

    /**
     * 服务端发送消息
     *
     * @param msg 消息内容
     * @throws IOException
     */
    @GetMapping("/send")
    public void sendData(String msg) throws IOException {
        webSocketServer.sendMsg(msg);
    }

}

5.创建前端页面

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>WebSocket测试</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<body>
<input type="text" id="msg" placeholder="请填写数据"/>
<button onclick="sendData()">发送数据</button>
<script>
    var websocket;
    if (typeof (WebSocket) === 'undefined') {
        alert("您的浏览器不支持WebSocket");
    } else {
        // 8090是服务器的端口
        websocket = new WebSocket("ws://localhost:8090/myWebsocket");

        websocket.onopen = function () {
            console.log("webSocket已连接")
        }
        websocket.onmessage = function (event) {
            console.log("接收到服务器的消息:" + event.data)
        }

        websocket.onclose = function () {
            console.log("WebSocket已关闭")
        }
        websocket.onerror = function () {
            console.log("WebSocket发生错误")
        }
    }

    function sendData() {
        if (websocket != null) {
            websocket.send($("#msg").val());
        } else {
            alert("您的浏览器不支持WebSocket");
        }
    }
</script>
</body>

</html>

6.测试

我们打开WebSocket测试页面,控制台显示已成功连接的日志。

SpringBoot集成WebSocket实现消息推送_消息推送

接着我们发送数据给服务端,点击发送按钮后,服务端控制台日志显示收到来自客户端的数据。

SpringBoot集成WebSocket实现消息推送_WebSocket_02

2023-12-25 10:48:50.735  INFO 4660 --- [nio-8090-exec-4] c.e.nettydemo.websocket.WebSocketServer  : 接收到客户端的数据:我是客户端数据

最后我们调用服务端发送消息给客户端的方法。

SpringBoot集成WebSocket实现消息推送_WebSocket_03

在客户端的控制台日志上我们看到了来自服务端的消息。

SpringBoot集成WebSocket实现消息推送_消息推送_04