SpringBoot : ch07 整合websocket

前言

当涉及到在Spring Boot应用程序中整合WebSocket时,我们可以使用Spring框架提供的功能来实现实时双向通信。WebSocket是一种在Web浏览器和服务器之间进行全双工通信的协议,它允许服务器主动向客户端发送消息,而不需要客户端发起请求。

在本博客中,我们将探讨如何在Spring Boot应用程序中使用WebSocket来建立实时通信。我们将通过一个简单的示例来演示如何配置和使用WebSocket,以及如何处理客户端与服务器之间的消息传递。

首先,我们将介绍WebSocket的基本概念和工作原理,然后引入Spring WebSocket模块,并展示如何配置和启用WebSocket支持。接下来,我们将创建一个WebSocket控制器,处理客户端连接和消息的收发。最后,我们将创建一个前端页面来展示实时通信的效果。

一、WebSocket的基本概念和工作原理

WebSocket是一种在Web浏览器和服务器之间进行全双工通信的协议,它允许服务器主动向客户端发送消息,而不需要客户端发起请求。相比于传统的基于HTTP的轮询技术,WebSocket可以实现更加高效、实时的双向通信。

WebSocket的基本概念和工作原理如下:

1.握手阶段

在WebSocket连接建立之前,需要进行一次握手过程。客户端向服务器发送一个HTTP请求,请求头中包含了Upgrade和Connection字段,表明客户端希望将当前的HTTP连接升级为WebSocket连接。服务器收到请求后,验证请求头信息并响应一个HTTP 101 Switching Protocols响应,表示升级成功,此时WebSocket连接建立完成。

2.数据传输阶段

WebSocket连接建立后,客户端和服务器之间可以进行双向通信。客户端和服务器都可以随时向对方发送消息,并且不需要进行额外的请求和响应。通信过程中,数据以二进制或文本的形式进行传输。

3.断开连接阶段

当客户端或服务器想要关闭WebSocket连接时,可以发送一个特殊的关闭帧,表示结束通信。接收到关闭帧后,对方也会发送一个关闭帧进行确认。之后,WebSocket连接会被断开,通信终止。

总的来说,WebSocket的工作原理可以概括为以下几个步骤:握手阶段建立连接、数据传输阶段双向通信、断开连接阶段结束通信。WebSocket的优点在于它可以实现低延迟、高效的实时双向通信,适用于需要及时响应和交互的应用场景,如在线游戏、在线聊天等。

二、前期准备

1、新建项目,结构如下
2、导入依赖
XML 复制代码
  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <image>
                        <builder>paketobuildpacks/builder-jammy-base:latest</builder>
                    </image>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

这是一个典型的Spring Boot项目的pom.xml文件示例,其中包含了与WebSocket相关的依赖项以及其他常见的依赖项。让我为您解释一下这些依赖项的作用:

  1. spring-boot-starter-websocket: 这个依赖项提供了Spring WebSocket模块的基本功能,包括WebSocket协议的支持和相关的类库。

  2. org.projectlombok:lombok: 这是一个可选的依赖项,用于简化Java代码的编写。它可以通过注解自动生成getter、setter和其他常见的方法,减少样板代码的编写量。

  3. spring-boot-starter-test: 这个依赖项提供了Spring Boot应用程序的测试支持,包括自动化单元测试和集成测试等。

接下来是<build>部分,其中包含了用于构建和打包应用程序的插件配置。具体来说:

  1. spring-boot-maven-plugin: 这个插件可以将Spring Boot应用程序打包为可执行的JAR文件,并提供了其他与构建和运行相关的配置选项。

在这个示例中,还对插件进行了特定的配置。<image>元素指定了用于构建容器镜像的基础镜像,<excludes>元素指定了在镜像构建过程中排除的依赖项,这里排除了lombok。

这是一个基本的pom.xml文件示例,用于构建一个使用WebSocket的Spring Boot应用程序。您可以根据自己的需求进行修改和扩展。

三、配置 WebSocketConfig 配置类

java 复制代码
@Configuration
public class WebsocketConfig {

    /**
     * 装配 ServerEndpointExporter
     * 会自动注册带有 @ServerEndpoint 注解的类
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }

}

这段代码展示了一个典型的WebSocket配置类,通常用于将WebSocket端点暴露给Spring容器。让我为您解释一下这段代码的作用:

  1. @Configuration: 这个注解表明这是一个配置类,它会在Spring应用程序启动时被加载,并且可以包含用@Bean注解标记的方法用于定义bean。

  2. WebsocketConfig: 这是配置类的类名,用于定义WebSocket相关的配置。

  3. @Bean: 这个注解用于将方法返回的对象注册为Spring应用程序上下文中的bean,使其可以被其他组件自动注入和使用。

  4. serverEndpointExporter(): 这是一个@Bean方法,用于创建并返回一个ServerEndpointExporter对象。ServerEndpointExporter是Spring提供的用于扫描带有@ServerEndpoint注解的类并注册其实例的类,从而将其暴露为WebSocket端点。

总的来说,这段代码的作用是创建一个配置类,并在其中定义了一个@Bean方法用于注册ServerEndpointExporter,以便自动注册带有@ServerEndpoint注解的类作为WebSocket端点。这样做可以使得WebSocket端点能够被Spring容器正确管理并且与WebSocket通信进行交互。

四、编写实体类

1、User 实体类
java 复制代码
@Data
public class User {

    private String userName;

}

用于登录,保存登录的用户名称。

2、Message 实体类
java 复制代码
@Data
public class Message {

    /**
     * 发送人
     */
    private String sendUser;

    /**
     * 发送消息
     */
    private String message;

}

封装发送消息的内容和发送人。

五、完成登录及页面

1、登录后台代码
java 复制代码
@Controller
public class LoginController {

    /**
     * 登录并将用户保存到会话中,重定向到聊天页面
     * @param user
     * @param session
     * @return
     */
    @PostMapping("/user/login")
    public String login(User user, HttpSession session){
        session.setAttribute("user",user);
        return "redirect:/chat.html";
    }

}

这段代码展示了一个简单的Spring MVC控制器,用于处理用户登录请求并将用户保存到会话中。让我为您解释一下这段代码的作用:

  1. @Controller: 这个注解标识了这个类是一个Spring MVC控制器,它可以处理来自客户端的HTTP请求并返回相应的视图或数据。

  2. LoginController: 这是控制器类的类名,用于处理用户登录请求。

  3. @PostMapping("/user/login"): 这个注解指定了处理POST类型的"/user/login"请求的方法,即当用户提交登录表单时,将调用这个方法进行处理。

  4. public String login(User user, HttpSession session): 这个方法接收一个User对象和HttpSession对象作为参数,表示从用户提交的表单中获取用户信息,并将用户信息保存到会话中。

  5. session.setAttribute("user",user);: 这行代码将用户对象保存到会话中,以便在后续的WebSocket通信中可以使用用户信息进行身份验证和授权等操作。

  6. return "redirect:/chat.html";: 这行代码将HTTP响应重定向到聊天页面(chat.html),即当用户登录成功后,将跳转到聊天页面进行实时通信。

2、登录页面
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户登录</h1>

<form method="post" action="user/login">
    Name:<input type="text" name="userName" />
    <input type="submit" value="登录">
</form>

</body>
</html>
  1. <form method="post" action="user/login">: 这个标签定义了一个HTML表单,并指定了提交方法为POST,提交地址为"user/login",即当用户点击登录按钮时,将向"/user/login"地址发送POST请求。

  2. Name:<input type="text" name="userName" />: 这行代码创建了一个输入框,用于用户输入用户名。

  3. <input type="submit" value="登录">: 这行代码创建了一个提交按钮,用于用户提交登录表单。

3、 聊天页面
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/JQuery文件.txt.js"></script>
</head>
<body>
<h2>聊天室</h2>
<div id="content"></div>
<form id="f1">
    <input type="text" id="message"/>
    <input type="button" value="发送"/>
</form>


</body>
</html>
4、效果

六、实现聊天功能

1、服务器端
java 复制代码
@Slf4j
@Component
@ServerEndpoint(value = "/connect",configurator = HttpSessionConfigutator.class)
public class ChatServer {

    /**
     * 用户列表
     */
    private static Map<String, Session> users = new HashMap<>();

    @OnOpen
    public void onOpen(Session session){
        // 获取登录的用户
        User user = (User) session.getUserProperties().get("user");
        // 保存到用户列表中
        users.put(user.getUserName(),session);
    }

    /**
     * 接收消息并群发
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message,Session session) throws JsonProcessingException {
        // 获取发送人
        User sendUser = (User) session.getUserProperties().get("user");
        // 封装消息对象
        Message message1 = new Message();
        message1.setSendUser(sendUser.getUserName());
        message1.setMessage(message);

        // 序列化成 JSON 字符串
        String json = new ObjectMapper().writeValueAsString(message1);
        // 群发
        users.forEach((userName,sess) -> {
            try {
                sess.getBasicRemote().sendText(json);
            } catch (IOException e) {
                throw new RuntimeException("发送失败",e);
            }
        });
    }

    /**
     * 用户离线移除在线用户
     * @param session
     */
    @OnClose
    public void onClose(Session session){
        User user = (User) session.getUserProperties().get("user");
        log.info(user.getUserName() + ":已离线");
        // 从用户列表移除这个用户
        users.remove(user.getUserName());
    }


}

这段代码是一个基于Java的WebSocket服务器端实现,用于创建一个聊天服务器。让我为您解释一下这段代码的作用:

  1. @Slf4j:这是一个Lombok注解,用于自动生成日志记录器。

  2. @Component:这是一个Spring注解,将该类声明为一个可被Spring容器管理的组件。

  3. @ServerEndpoint(value = "/connect",configurator = HttpSessionConfigutator.class):这是一个Java WebSocket注解,它将这个类声明为一个WebSocket端点,指定了连接的URL路径为"/connect",并且配置了一个HttpSessionConfigutator来支持获取HttpSession。

  4. private static Map<String, Session> users = new HashMap<>():这是一个静态变量,用于存储用户列表。它使用用户名作为键,WebSocket会话对象Session作为值。

  5. @OnOpen:这是一个Java WebSocket注解,它表示当有新的WebSocket连接打开时,会调用这个方法。在这个方法中,通过从会话的UserProperties中获取登录的用户,然后将用户保存到用户列表中。

  6. @OnMessage:这是一个Java WebSocket注解,它表示当接收到WebSocket消息时,会调用这个方法。在这个方法中,首先获取发送人的用户对象,然后封装成消息对象Message。接着使用ObjectMapper将消息对象序列化为JSON字符串,然后通过遍历用户列表,将消息发送给每个在线用户。

  7. @OnClose:这是一个Java WebSocket注解,它表示当WebSocket连接关闭时,会调用这个方法。在这个方法中,首先获取离线用户的用户对象,然后从用户列表中移除该用户。

总体来说,这段代码实现了一个简单的聊天服务器,通过WebSocket协议实现实时的消息传递功能。当有用户连接到服务器时,将其加入用户列表;当接收到用户发送的消息时,将消息群发给所有在线用户;当用户离线时,将其从用户列表中移除。

2、握手连接处理类
java 复制代码
public class HttpSessionConfigutator extends Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        //获取HttpSession
        HttpSession session = (HttpSession) request.getHttpSession();
        //获取登录用户的信息
        User user = (User) session.getAttribute("user");
        //再将用户信息保存到websocket的session的属性中
        sec.getUserProperties().put("user", user);
    }

}

这个类是一个自定义的WebSocket配置器,它实现了javax.websocket.server.ServerEndpointConfig.Configurator接口。它的作用是在WebSocket握手期间修改握手请求和响应,以便将用户信息从HttpSession传递到WebSocket的会话中。

具体来说,这个类重写了modifyHandshake方法,在WebSocket握手期间被调用。在这个方法中,它首先通过request.getHttpSession()方法获取到当前的HttpSession对象,然后从HttpSession中获取登录用户的信息。接着,它将用户信息保存到WebSocket会话的属性中,使用sec.getUserProperties().put("user", user)语句将用户信息存储在user键下。

通过这样的操作,WebSocket会话就可以在握手成功后获取到用户信息,并且可以在后续的处理过程中使用该信息。这种方式可以实现将用户身份信息从HttpSession传递到WebSocket会话的目的,方便在WebSocket处理逻辑中使用用户信息。

3、客户端代码实现
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/JQuery文件.txt.js"></script>
</head>
<body>
<h2>聊天室</h2>
<div id="content"></div>
<form id="f1">
    <input type="text" id="message"/>
    <input type="button" value="发送"/>
</form>
<script>
    //实例化ws对象
    let ws = new WebSocket('ws://localhost:8080/connect');
    //接收服务端的消息
    ws.onmessage = function(event) {
        //先将json字符串转换为json对象
        let data = $.parseJSON(event.data);
        //将消息追加到内容区
        $('#content').append(data.sendUser + ' : ' + data.message + '<br>')
    }
    //发送消息
    $(function(){
        $(':button').on('click', function(){
            let msg = $('#message').val();
            //通过ws发送
            ws.send(msg);
            //清空发送框
            $('#message').val('');
        });
    })
</script>

</body>
</html>
  1. <div id="content"></div>:用于显示聊天内容的区域。
  2. <form id="f1">:定义一个表单,用于输入消息内容。
  3. <input type="text" id="message"/>:文本输入框,用于输入消息。
  4. <input type="button" value="发送"/>:按钮,用于发送消息。
  5. let ws = new WebSocket('ws://localhost:8080/connect');:创建WebSocket对象,连接到服务器的WebSocket地址。
  6. ws.onmessage = function(event) { ... }:当接收到服务端的消息时,执行其中的代码。
  7. let data = $.parseJSON(event.data);:将接收到的消息数据解析为JSON对象。
  8. $('#content').append(data.sendUser + ' : ' + data.message + '<br>'):将发送者和消息内容追加到聊天内容区域。
  9. $(function(){ ... }):当页面加载完成后执行其中的代码。
  10. $(':button').on('click', function(){ ... }):当按钮被点击时执行其中的代码。
  11. let msg = $('#message').val();:获取输入框中的消息内容。
  12. ws.send(msg);:通过WebSocket发送消息给服务器。
  13. $('#message').val('');:清空输入框的内容。

这段代码实现了一个简单的前端聊天室界面,通过WebSocket与后端服务器进行通信,实现了即时的消息收发功能。用户可以在输入框中输入消息,点击发送按钮后,消息会发送给服务器,并显示在聊天内容区域中。同时,页面会接收到其他用户发送的消息,并将其展示在聊天内容区域中。

4、运行效果

七、springboot整合WebSocket的好处

将Spring Boot与WebSocket整合的好处如下:

  1. 实时性:WebSocket提供了双向通信的能力,使得服务器能够主动推送消息给客户端,而不需要客户端不断地发送请求。这样可以实现实时的消息传递,适用于聊天室、即时通讯等场景。

  2. 减少网络开销:相比传统的轮询方式,WebSocket减少了不必要的网络开销。客户端只需要与服务器建立一次连接,之后就可以保持长连接,避免了频繁的请求和响应。

  3. 提高性能:通过减少网络开销和降低服务器负载,WebSocket可以提高系统的整体性能。相比使用HTTP协议进行轮询的方式,WebSocket在消息传输过程中的开销更小,更高效。

  4. 简化开发:Spring Boot框架提供了对WebSocket的良好支持,简化了WebSocket的开发流程。开发者可以通过注解和配置快速实现WebSocket功能,减少了繁琐的手动配置和编码工作。

  5. 跨平台支持:WebSocket是一种基于标准的通信协议,得到了广泛的支持。无论是Web端还是移动端,都可以方便地使用WebSocket进行通信,实现跨平台的即时通讯。

综上所述,Spring Boot整合WebSocket可以提供实时性、性能优化和开发简化等好处,适用于需要实时通信的应用场景。

相关推荐
小_太_阳1 小时前
Scala_【1】概述
开发语言·后端·scala·intellij-idea
智慧老师1 小时前
Spring基础分析13-Spring Security框架
java·后端·spring
lxyzcm1 小时前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
搬码后生仔2 小时前
asp.net core webapi项目中 在生产环境中 进不去swagger
chrome·后端·asp.net
迷糊的『迷』2 小时前
vue-axios+springboot实现文件流下载
vue.js·spring boot
凡人的AI工具箱2 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite
Lx3523 小时前
Pandas数据重命名:列名与索引为标题
后端·python·pandas
小池先生3 小时前
springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失
java·spring boot·后端
百罹鸟4 小时前
【vue高频面试题—场景篇】:实现一个实时更新的倒计时组件,如何确保倒计时在页面切换时能够正常暂停和恢复?
vue.js·后端·面试
苹果醋34 小时前
2020重新出发,MySql基础,MySql表数据操作
java·运维·spring boot·mysql·nginx