前言
当涉及到在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、导入依赖
<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相关的依赖项以及其他常见的依赖项。让我为您解释一下这些依赖项的作用:
spring-boot-starter-websocket
: 这个依赖项提供了Spring WebSocket模块的基本功能,包括WebSocket协议的支持和相关的类库。
org.projectlombok:lombok
: 这是一个可选的依赖项,用于简化Java代码的编写。它可以通过注解自动生成getter、setter和其他常见的方法,减少样板代码的编写量。
spring-boot-starter-test
: 这个依赖项提供了Spring Boot应用程序的测试支持,包括自动化单元测试和集成测试等。接下来是
<build>
部分,其中包含了用于构建和打包应用程序的插件配置。具体来说:
spring-boot-maven-plugin
: 这个插件可以将Spring Boot应用程序打包为可执行的JAR文件,并提供了其他与构建和运行相关的配置选项。在这个示例中,还对插件进行了特定的配置。
<image>
元素指定了用于构建容器镜像的基础镜像,<excludes>
元素指定了在镜像构建过程中排除的依赖项,这里排除了lombok。这是一个基本的pom.xml文件示例,用于构建一个使用WebSocket的Spring Boot应用程序。您可以根据自己的需求进行修改和扩展。
三、配置 WebSocketConfig 配置类
@Configuration
public class WebsocketConfig {/*** 装配 ServerEndpointExporter* 会自动注册带有 @ServerEndpoint 注解的类* @return*/@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}}
这段代码展示了一个典型的WebSocket配置类,通常用于将WebSocket端点暴露给Spring容器。让我为您解释一下这段代码的作用:
@Configuration
: 这个注解表明这是一个配置类,它会在Spring应用程序启动时被加载,并且可以包含用@Bean注解标记的方法用于定义bean。
WebsocketConfig
: 这是配置类的类名,用于定义WebSocket相关的配置。
@Bean
: 这个注解用于将方法返回的对象注册为Spring应用程序上下文中的bean,使其可以被其他组件自动注入和使用。
serverEndpointExporter()
: 这是一个@Bean方法,用于创建并返回一个ServerEndpointExporter对象。ServerEndpointExporter是Spring提供的用于扫描带有@ServerEndpoint注解的类并注册其实例的类,从而将其暴露为WebSocket端点。总的来说,这段代码的作用是创建一个配置类,并在其中定义了一个@Bean方法用于注册ServerEndpointExporter,以便自动注册带有@ServerEndpoint注解的类作为WebSocket端点。这样做可以使得WebSocket端点能够被Spring容器正确管理并且与WebSocket通信进行交互。
四、编写实体类
1、User 实体类
@Data
public class User {private String userName;}
用于登录,保存登录的用户名称。
2、Message 实体类
@Data
public class Message {/*** 发送人*/private String sendUser;/*** 发送消息*/private String message;}
封装发送消息的内容和发送人。
五、完成登录及页面
1、登录后台代码
@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控制器,用于处理用户登录请求并将用户保存到会话中。让我为您解释一下这段代码的作用:
@Controller
: 这个注解标识了这个类是一个Spring MVC控制器,它可以处理来自客户端的HTTP请求并返回相应的视图或数据。
LoginController
: 这是控制器类的类名,用于处理用户登录请求。
@PostMapping("/user/login")
: 这个注解指定了处理POST类型的"/user/login"请求的方法,即当用户提交登录表单时,将调用这个方法进行处理。
public String login(User user, HttpSession session)
: 这个方法接收一个User对象和HttpSession对象作为参数,表示从用户提交的表单中获取用户信息,并将用户信息保存到会话中。
session.setAttribute("user",user);
: 这行代码将用户对象保存到会话中,以便在后续的WebSocket通信中可以使用用户信息进行身份验证和授权等操作。
return "redirect:/chat.html";
: 这行代码将HTTP响应重定向到聊天页面(chat.html),即当用户登录成功后,将跳转到聊天页面进行实时通信。
2、登录页面
<!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>
<form method="post" action="user/login">
: 这个标签定义了一个HTML表单,并指定了提交方法为POST,提交地址为"user/login",即当用户点击登录按钮时,将向"/user/login"地址发送POST请求。
Name:<input type="text" name="userName" />
: 这行代码创建了一个输入框,用于用户输入用户名。
<input type="submit" value="登录">
: 这行代码创建了一个提交按钮,用于用户提交登录表单。
3、 聊天页面
<!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、服务器端
@Slf4j
@Component
@ServerEndpoint(value = "/connect",configurator = HttpSessionConfigutator.class)
public class ChatServer {/*** 用户列表*/private static Map<String, Session> users = new HashMap<>();@OnOpenpublic void onOpen(Session session){// 获取登录的用户User user = (User) session.getUserProperties().get("user");// 保存到用户列表中users.put(user.getUserName(),session);}/*** 接收消息并群发* @param message* @param session*/@OnMessagepublic 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*/@OnClosepublic void onClose(Session session){User user = (User) session.getUserProperties().get("user");log.info(user.getUserName() + ":已离线");// 从用户列表移除这个用户users.remove(user.getUserName());}}
这段代码是一个基于Java的WebSocket服务器端实现,用于创建一个聊天服务器。让我为您解释一下这段代码的作用:
@Slf4j
:这是一个Lombok注解,用于自动生成日志记录器。
@Component
:这是一个Spring注解,将该类声明为一个可被Spring容器管理的组件。
@ServerEndpoint(value = "/connect",configurator = HttpSessionConfigutator.class)
:这是一个Java WebSocket注解,它将这个类声明为一个WebSocket端点,指定了连接的URL路径为"/connect",并且配置了一个HttpSessionConfigutator来支持获取HttpSession。
private static Map<String, Session> users = new HashMap<>()
:这是一个静态变量,用于存储用户列表。它使用用户名作为键,WebSocket会话对象Session作为值。
@OnOpen
:这是一个Java WebSocket注解,它表示当有新的WebSocket连接打开时,会调用这个方法。在这个方法中,通过从会话的UserProperties中获取登录的用户,然后将用户保存到用户列表中。
@OnMessage
:这是一个Java WebSocket注解,它表示当接收到WebSocket消息时,会调用这个方法。在这个方法中,首先获取发送人的用户对象,然后封装成消息对象Message。接着使用ObjectMapper将消息对象序列化为JSON字符串,然后通过遍历用户列表,将消息发送给每个在线用户。
@OnClose
:这是一个Java WebSocket注解,它表示当WebSocket连接关闭时,会调用这个方法。在这个方法中,首先获取离线用户的用户对象,然后从用户列表中移除该用户。总体来说,这段代码实现了一个简单的聊天服务器,通过WebSocket协议实现实时的消息传递功能。当有用户连接到服务器时,将其加入用户列表;当接收到用户发送的消息时,将消息群发给所有在线用户;当用户离线时,将其从用户列表中移除。
2、握手连接处理类
public class HttpSessionConfigutator extends Configurator {@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {//获取HttpSessionHttpSession 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、客户端代码实现
<!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>
<div id="content"></div>
:用于显示聊天内容的区域。<form id="f1">
:定义一个表单,用于输入消息内容。<input type="text" id="message"/>
:文本输入框,用于输入消息。<input type="button" value="发送"/>
:按钮,用于发送消息。let ws = new WebSocket('ws://localhost:8080/connect');
:创建WebSocket对象,连接到服务器的WebSocket地址。ws.onmessage = function(event) { ... }
:当接收到服务端的消息时,执行其中的代码。let data = $.parseJSON(event.data);
:将接收到的消息数据解析为JSON对象。$('#content').append(data.sendUser + ' : ' + data.message + '<br>')
:将发送者和消息内容追加到聊天内容区域。$(function(){ ... })
:当页面加载完成后执行其中的代码。$(':button').on('click', function(){ ... })
:当按钮被点击时执行其中的代码。let msg = $('#message').val();
:获取输入框中的消息内容。ws.send(msg);
:通过WebSocket发送消息给服务器。$('#message').val('');
:清空输入框的内容。
这段代码实现了一个简单的前端聊天室界面,通过WebSocket与后端服务器进行通信,实现了即时的消息收发功能。用户可以在输入框中输入消息,点击发送按钮后,消息会发送给服务器,并显示在聊天内容区域中。同时,页面会接收到其他用户发送的消息,并将其展示在聊天内容区域中。
4、运行效果
七、springboot整合WebSocket的好处
将Spring Boot与WebSocket整合的好处如下:
-
实时性:WebSocket提供了双向通信的能力,使得服务器能够主动推送消息给客户端,而不需要客户端不断地发送请求。这样可以实现实时的消息传递,适用于聊天室、即时通讯等场景。
-
减少网络开销:相比传统的轮询方式,WebSocket减少了不必要的网络开销。客户端只需要与服务器建立一次连接,之后就可以保持长连接,避免了频繁的请求和响应。
-
提高性能:通过减少网络开销和降低服务器负载,WebSocket可以提高系统的整体性能。相比使用HTTP协议进行轮询的方式,WebSocket在消息传输过程中的开销更小,更高效。
-
简化开发:Spring Boot框架提供了对WebSocket的良好支持,简化了WebSocket的开发流程。开发者可以通过注解和配置快速实现WebSocket功能,减少了繁琐的手动配置和编码工作。
-
跨平台支持:WebSocket是一种基于标准的通信协议,得到了广泛的支持。无论是Web端还是移动端,都可以方便地使用WebSocket进行通信,实现跨平台的即时通讯。
综上所述,Spring Boot整合WebSocket可以提供实时性、性能优化和开发简化等好处,适用于需要实时通信的应用场景。