1. WebSocket介绍
WebSocket 是基于 TCP 的一种新的网络协议。
它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输。
HTTP协议和WebSocket协议对比:
- HTTP是短连接
- WebSocket是长连接
- HTTP通信是单向的,基于请求响应模式
- WebSocket支持双向通信
- HTTP和WebSocket底层都是TCP连接
![websocket入门教程 图片[1]-websocket入门教程-不念博客](https://www.bunian.cn/wp-content/uploads/2023/11/image-113.png)
思考:既然WebSocket支持双向通信,功能看似比HTTP强大,那么我们是不是可以基于WebSocket开发所有的业务功能?
WebSocket缺点:
服务器长期维护长连接需要一定的成本 各个浏览器支持程度不一 WebSocket 是长连接,受网络限制比较大,需要处理好重连
结论:WebSocket并不能完全取代HTTP,它只适合在特定的场景下使用
WebSocket应用场景:
- 视频弹幕
- 网页聊天
- 体育实况更新
- 股票基金报价实时更新
2. SpringBoot结合WebSocket入门案例
需求:实现浏览器与服务器全双工通信。浏览器既可以向服务器发送消息,服务器也可主动向浏览器推送消息。
结果展示:
![websocket入门教程 图片[2]-websocket入门教程-不念博客](https://www.bunian.cn/wp-content/uploads/2023/11/image-114.png)
2.1. 实现步骤
入门案例实现步骤如下:
- 1.直接使用websocket.html页面作为WebSocket客户端
- 2.导入WebSocket的maven坐标
- 3.导入WebSocket服务端组件WebSocketServer,用于和客户端通信
- 4.导入配置类WebSocketConfiguration,注册WebSocket的服务端组件
- 5.导入定时任务类WebSocketTask,定时向客户端推送数据
2.2. 代码实现
创建WebSocket-Demo,并导入对应依赖到pom.xml
- 完整项目结果
![websocket入门教程 图片[3]-websocket入门教程-不念博客](https://www.bunian.cn/wp-content/uploads/2023/11/image-115.png)
- pom.xml 依赖
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.7.15</version></parent><groupId>com.zbbmeta</groupId><artifactId>WebSocket-Demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency></dependencies></project><?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>spring-boot-starter-parent</artifactId> <groupId>org.springframework.boot</groupId> <version>2.7.15</version> </parent> <groupId>com.zbbmeta</groupId> <artifactId>WebSocket-Demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> </dependencies> </project><?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>spring-boot-starter-parent</artifactId> <groupId>org.springframework.boot</groupId> <version>2.7.15</version> </parent> <groupId>com.zbbmeta</groupId> <artifactId>WebSocket-Demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> </dependencies> </project>
【步骤一】:创建项目引导类
在java目录下创建com.zbbmeta
包,在其包下创建WebSocketApplication
引导类
package com.zbbmeta;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.scheduling.annotation.EnableScheduling;/*** @author 不念博客* @description: TODO*/@SpringBootApplication@EnableSchedulingpublic class WebSocketApplication {public static void main(String[] args) {SpringApplication.run(WebSocketApplication.class,args);}}package com.zbbmeta; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; /** * @author 不念博客 * @description: TODO */ @SpringBootApplication @EnableScheduling public class WebSocketApplication { public static void main(String[] args) { SpringApplication.run(WebSocketApplication.class,args); } }package com.zbbmeta; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; /** * @author 不念博客 * @description: TODO */ @SpringBootApplication @EnableScheduling public class WebSocketApplication { public static void main(String[] args) { SpringApplication.run(WebSocketApplication.class,args); } }
【步骤二】:配置文件
在resources
目录下创建application.yml
文件,内容如下
server:port: 8078server: port: 8078server: port: 8078
【步骤三】: 定义websocket.html页面
在resources
目录下创建static
,并创建websocket.html
页面,内容如下:
<!DOCTYPE HTML><html><head><meta charset="UTF-8"><title>WebSocket Demo</title></head><body><input id="text" type="text" /><button onclick="send()">发送消息</button><button onclick="closeWebSocket()">关闭连接</button><div id="message"></div></body><script type="text/javascript">var websocket = null;var clientId = Math.random().toString(36).substr(2);//判断当前浏览器是否支持WebSocketif('WebSocket' in window){//连接WebSocket节点websocket = new WebSocket("ws://localhost:8078/ws/"+clientId);}else{alert('Not support websocket')}//连接发生错误的回调方法websocket.onerror = function(){setMessageInnerHTML("error");};//连接成功建立的回调方法websocket.onopen = function(){setMessageInnerHTML("连接成功");}//接收到消息的回调方法websocket.onmessage = function(event){setMessageInnerHTML(event.data);}//连接关闭的回调方法websocket.onclose = function(){setMessageInnerHTML("close");}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function(){websocket.close();}//将消息显示在网页上function setMessageInnerHTML(innerHTML){document.getElementById('message').innerHTML += innerHTML + '<br/>';}//发送消息function send(){websocket.onopen()var message = document.getElementById('text').value;websocket.send(message);}//关闭连接function closeWebSocket() {websocket.close();}</script></html><!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>WebSocket Demo</title> </head> <body> <input id="text" type="text" /> <button onclick="send()">发送消息</button> <button onclick="closeWebSocket()">关闭连接</button> <div id="message"> </div> </body> <script type="text/javascript"> var websocket = null; var clientId = Math.random().toString(36).substr(2); //判断当前浏览器是否支持WebSocket if('WebSocket' in window){ //连接WebSocket节点 websocket = new WebSocket("ws://localhost:8078/ws/"+clientId); } else{ alert('Not support websocket') } //连接发生错误的回调方法 websocket.onerror = function(){ setMessageInnerHTML("error"); }; //连接成功建立的回调方法 websocket.onopen = function(){ setMessageInnerHTML("连接成功"); } //接收到消息的回调方法 websocket.onmessage = function(event){ setMessageInnerHTML(event.data); } //连接关闭的回调方法 websocket.onclose = function(){ setMessageInnerHTML("close"); } //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function(){ websocket.close(); } //将消息显示在网页上 function setMessageInnerHTML(innerHTML){ document.getElementById('message').innerHTML += innerHTML + '<br/>'; } //发送消息 function send(){ websocket.onopen() var message = document.getElementById('text').value; websocket.send(message); } //关闭连接 function closeWebSocket() { websocket.close(); } </script> </html><!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>WebSocket Demo</title> </head> <body> <input id="text" type="text" /> <button onclick="send()">发送消息</button> <button onclick="closeWebSocket()">关闭连接</button> <div id="message"> </div> </body> <script type="text/javascript"> var websocket = null; var clientId = Math.random().toString(36).substr(2); //判断当前浏览器是否支持WebSocket if('WebSocket' in window){ //连接WebSocket节点 websocket = new WebSocket("ws://localhost:8078/ws/"+clientId); } else{ alert('Not support websocket') } //连接发生错误的回调方法 websocket.onerror = function(){ setMessageInnerHTML("error"); }; //连接成功建立的回调方法 websocket.onopen = function(){ setMessageInnerHTML("连接成功"); } //接收到消息的回调方法 websocket.onmessage = function(event){ setMessageInnerHTML(event.data); } //连接关闭的回调方法 websocket.onclose = function(){ setMessageInnerHTML("close"); } //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function(){ websocket.close(); } //将消息显示在网页上 function setMessageInnerHTML(innerHTML){ document.getElementById('message').innerHTML += innerHTML + '<br/>'; } //发送消息 function send(){ websocket.onopen() var message = document.getElementById('text').value; websocket.send(message); } //关闭连接 function closeWebSocket() { websocket.close(); } </script> </html>
【步骤四】: 导入websocket依赖坐标
在pom.xml文件中添加websocket依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
【步骤五】: 定义WebSocket服务端组件
在com.zbbmeta.websocket
包下创建WebSocketServer
服务组件类
package com.zbbmeta.websocket;import org.springframework.stereotype.Component;import javax.websocket.OnClose;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.util.Collection;import java.util.HashMap;import java.util.Map;/*** @author springboot葵花宝典* @description: TODO*/@Component@ServerEndpoint("/ws/{sid}")public class WebSocketServer {//存放会话对象private static Map<String, Session> sessionMap = new HashMap();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {System.out.println("客户端:" + sid + "建立连接");sessionMap.put(sid, session);}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, @PathParam("sid") String sid) {System.out.println("收到来自客户端:" + sid + "的信息:" + message);}/*** 连接关闭调用的方法** @param sid*/@OnClosepublic void onClose(@PathParam("sid") String sid) {System.out.println("连接断开:" + sid);sessionMap.remove(sid);}/*** 群发** @param message*/public void sendToAllClient(String message) {Collection<Session> sessions = sessionMap.values();for (Session session : sessions) {try {//服务器向客户端发送消息session.getBasicRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}}package com.zbbmeta.websocket; import org.springframework.stereotype.Component; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * @author springboot葵花宝典 * @description: TODO */ @Component @ServerEndpoint("/ws/{sid}") public class WebSocketServer { //存放会话对象 private static Map<String, Session> sessionMap = new HashMap(); /** * 连接建立成功调用的方法 */ @OnOpen public void onOpen(Session session, @PathParam("sid") String sid) { System.out.println("客户端:" + sid + "建立连接"); sessionMap.put(sid, session); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息 */ @OnMessage public void onMessage(String message, @PathParam("sid") String sid) { System.out.println("收到来自客户端:" + sid + "的信息:" + message); } /** * 连接关闭调用的方法 * * @param sid */ @OnClose public void onClose(@PathParam("sid") String sid) { System.out.println("连接断开:" + sid); sessionMap.remove(sid); } /** * 群发 * * @param message */ public void sendToAllClient(String message) { Collection<Session> sessions = sessionMap.values(); for (Session session : sessions) { try { //服务器向客户端发送消息 session.getBasicRemote().sendText(message); } catch (Exception e) { e.printStackTrace(); } } } }package com.zbbmeta.websocket; import org.springframework.stereotype.Component; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * @author springboot葵花宝典 * @description: TODO */ @Component @ServerEndpoint("/ws/{sid}") public class WebSocketServer { //存放会话对象 private static Map<String, Session> sessionMap = new HashMap(); /** * 连接建立成功调用的方法 */ @OnOpen public void onOpen(Session session, @PathParam("sid") String sid) { System.out.println("客户端:" + sid + "建立连接"); sessionMap.put(sid, session); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息 */ @OnMessage public void onMessage(String message, @PathParam("sid") String sid) { System.out.println("收到来自客户端:" + sid + "的信息:" + message); } /** * 连接关闭调用的方法 * * @param sid */ @OnClose public void onClose(@PathParam("sid") String sid) { System.out.println("连接断开:" + sid); sessionMap.remove(sid); } /** * 群发 * * @param message */ public void sendToAllClient(String message) { Collection<Session> sessions = sessionMap.values(); for (Session session : sessions) { try { //服务器向客户端发送消息 session.getBasicRemote().sendText(message); } catch (Exception e) { e.printStackTrace(); } } } }
【步骤六】: 定义配置类,注册WebSocket的服务端组件
在com.zbbmeta.config
包下,创建WebSocketConfiguration
配置类
package com.zbbmeta.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @author 不念博客* @description: TODO*/@Configurationpublic class WebSocketConfiguration {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}package com.zbbmeta.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * @author 不念博客 * @description: TODO */ @Configuration public class WebSocketConfiguration { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }package com.zbbmeta.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * @author 不念博客 * @description: TODO */ @Configuration public class WebSocketConfiguration { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
【步骤七】: 定义定时任务类,定时向所有客户端推送数据
在com.zbbmeta.task
包下,创建WebSocketTask
类,使用Spring-Task进行定时发送
package com.zbbmeta.task;import com.zbbmeta.websocket.WebSocketServer;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;/*** @author springboot葵花宝典* @description: TODO*/@Componentpublic class WebSocketTask {@Autowiredprivate WebSocketServer webSocketServer;/*** 通过WebSocket每隔5秒向客户端发送消息*/@Scheduled(cron = "0/5 * * * * ?")public void sendMessageToClient() {webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));}}package com.zbbmeta.task; import com.zbbmeta.websocket.WebSocketServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** * @author springboot葵花宝典 * @description: TODO */ @Component public class WebSocketTask { @Autowired private WebSocketServer webSocketServer; /** * 通过WebSocket每隔5秒向客户端发送消息 */ @Scheduled(cron = "0/5 * * * * ?") public void sendMessageToClient() { webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now())); } }package com.zbbmeta.task; import com.zbbmeta.websocket.WebSocketServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** * @author springboot葵花宝典 * @description: TODO */ @Component public class WebSocketTask { @Autowired private WebSocketServer webSocketServer; /** * 通过WebSocket每隔5秒向客户端发送消息 */ @Scheduled(cron = "0/5 * * * * ?") public void sendMessageToClient() { webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now())); } }
2.3. 功能测试
启动服务,浏览器,打开websocket.html页面浏览器结果
![websocket入门教程 图片[4]-websocket入门教程-不念博客](https://www.bunian.cn/wp-content/uploads/2023/11/image-116.png)
© 版权声明
本站文章由不念博客原创,未经允许严禁转载!
THE END