SpringBoot+WebSocket实现即时通讯功能(Spring方式)
作者:晚上睡不着_
今天给大家分享一个SpringBoot+WebSocket实现即时通讯功能(Spring方式),WebSocket是一种在单个TCP连接上进行全双工通信的协议,文章通过代码示例给大家介绍的非常详细,需要的朋友可以参考下
什么是websocket?
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
为什么有了HTTP协议还要WebSocket
HTTP协议采用的是客户端(浏览器)轮询的方式,即客户端发送请求,服务端做出响应,为了获取最新的数据,需要不断的轮询发出HTTP请求,占用大量带宽。
WebSocket采用了一些特殊的报头,使得浏览器和服务器只需要通过“握手”建立一条连接通道后,此链接保持活跃状态,之后的客户端和服务器的通信都使用这个连接,解决了Web实时性的问题,相比于HTTP有一下好处:
一个Web客户端只建立一个TCP连接
WebSocket服务端可以主动推送(push)数据到Web客户端
有更加轻量级的头,减少了数据传输量
特点
- 建立在TCP协议只上,服务端比较容易实现
- 于HTTP协议有良好的兼容性,默认端口也是80和443,握手阶段使用HTTP协议,因此握手时不容易屏蔽,能通过各种HTTP代理服务器
- 数据格式轻量,通信高效且节省带宽
- 支持传输文本数据和二进制数据
- 没有同源限制,客户端可以与任意服务器通信
- 也支持加密传输,WS+SSL,URL形如wss://
技术
- jdk8
- maven
- SpringBoot2.7.4
- thmeleaf
- websocket
实现(简易websocket聊天室)
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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>websocket</artifactId> <version>0.0.1-SNAPSHOT</version> <name>websocket</name> <description>websocket</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <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> <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> </plugin> </plugins> </build> </project>
websocketHandler.java
package com.example.websocket.handler; import org.springframework.stereotype.Component; import org.springframework.web.socket.*; import java.io.IOException; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @Component public class WebsocketHandler implements WebSocketHandler { private static final Map<String, WebSocketSession> SESSIONS = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { System.out.println("连接成功"+session.getId()); SESSIONS.put(session.getId(), session); System.out.println("当前在线人数:"+SESSIONS.size()); } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { System.out.println("接收消息"+session.getId()); String msg = message.getPayload().toString(); System.out.println(msg); // session.sendMessage(message); sendMessageToAllUsers(session.getId()+":"+msg); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { System.out.println("连接出错"+session.getId()); if (!session.isOpen()) { SESSIONS.remove(session.getId()); session.close(); } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { System.out.println("关闭连接"+session.getId()); if(!session.isOpen()){ SESSIONS.remove(session.getId()); System.out.println("当前在线人数:"+SESSIONS.size()); } } @Override public boolean supportsPartialMessages() { return false; } /** * sendMessageToUser:发给指定用户 * */ public void sendMessageToUser(String userId, String contents) { WebSocketSession session = SESSIONS.get(userId); if (session != null && session.isOpen()) { try { TextMessage message = new TextMessage(contents); session.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * sendMessageToAllUsers:发给所有的用户 * */ public void sendMessageToAllUsers(String contents) { Set<String> userIds = SESSIONS.keySet(); for (String userId : userIds) { this.sendMessageToUser(userId, contents); } } }
WebsocketInterceptor.java
package com.example.websocket.interceptor; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; import java.util.Map; @Component public class WebsocketInterceptor implements HandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { System.out.println("前置拦截"); return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { System.out.println("后置拦截"); } }
WebsocketConfig.java
package com.example.websocket.config; import com.example.websocket.handler.WebsocketHandler; import com.example.websocket.interceptor.WebsocketInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Component @EnableWebSocket public class WebsocketConfig implements WebSocketConfigurer { @Autowired private WebsocketHandler websocketHandler; @Autowired private WebsocketInterceptor websocketInterceptor; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(websocketHandler, "/ws").setAllowedOrigins("*").addInterceptors(websocketInterceptor); } }
WebsocketController.java
package com.example.websocket.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class WebsocketController { @GetMapping("/") public String init() { return "websocket"; } @GetMapping("/chat") public String chat() { return "chatRoom"; } }
WebsocketApplication.java
package com.example.websocket; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class WebsocketApplication { public static void main(String[] args) { SpringApplication.run(WebsocketApplication.class, args); } }
前端
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>My WebSocket Test</title> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="external nofollow" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous"> <!-- 可选的 Bootstrap 主题文件(一般不用引入) --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css" rel="external nofollow" integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous"> <!-- 最新的 Bootstrap 核心 JavaScript 文件 --> <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script> </head> <body> Welcome<br/> <div id="message"></div> <input id="text" type="text" /> <button class="btn btn-primary" onclick="send()">Send</button> <button class="btn btn-danger"onclick="closeWebSocket()">Close</button> </body> <script type="text/javascript"> var websocket = null; //判断当前浏览器是否支持WebSocket if('WebSocket' in window){ websocket = new WebSocket("ws://localhost:8080/ws"); } else{ alert('Not support websocket') } //连接发生错误的回调方法 websocket.onerror = function(){ setMessageInnerHTML("error"); }; //连接成功建立的回调方法 websocket.onopen = function(event){ setMessageInnerHTML("open"); } //接收到消息的回调方法 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 closeWebSocket(){ websocket.close(); } //发送消息 function send(){ var message = document.getElementById('text').value; websocket.send(message); message.value(""); } </script> </html>
测试
先启动服务
然后我们就可以愉快聊天了
结尾
到这我们的简单版的ws长连接聊天室就做好了,以上就是SpringBoot+WebSocket实现即时通讯功能(Spring方式)的详细内容,更多关于SpringBoot+WebSocket即时通讯的资料请关注脚本之家其它相关文章!