01-websocket初识
springboot 整合 websocket示例
一 、websocket介绍
当今互联网应用程序越来越注重实时性和双向通信,而传统的HTTP协议在这方面的表现相对较弱。WebSocket(Web套接字)是一种在单个TCP连接上实现全双工通信的协议,它允许在浏览器和服务器之间建立持久性的、实时的通信连接,而无需不断发起新的HTTP请求。
特点:
-
全双工通信: WebSocket允许在同一个连接上实现双向的实时通信,服务器和客户端可以同时发送和接收数据,而不需要等待对方的响应。
-
持久连接: 传统的HTTP请求-响应模式需要在每次通信时重新建立连接,而WebSocket连接在建立后会一直保持开放状态,从而减少了连接建立的开销。
-
低延迟: WebSocket的持久性连接和全双工通信特性使得数据能够更快地传输,从而降低了通信的延迟。
-
轻量级: WebSocket的协议头相对较小,减少了数据传输的开销。
-
跨域支持: 通过CORS(跨源资源共享)机制,WebSocket允许不同域名下的网页与服务器进行实时通信。
工作原理:
-
握手阶段: WebSocket的连接始于一个HTTP请求,这个请求称为WebSocket握手。客户端向服务器发起一个带有特殊头部的HTTP请求,请求表明客户端希望升级连接为WebSocket连接。如果服务器支持WebSocket,它会响应一个HTTP 101状态码,表示握手成功,此后连接升级为WebSocket连接。
-
数据传输: 握手成功后,WebSocket连接会一直保持开放状态。在任何一方需要发送数据时,数据会被封装为WebSocket帧,并通过已建立的连接进行传输。帧可以是文本数据或二进制数据。
-
关闭连接: 要关闭WebSocket连接,任意一方可以发送一个关闭帧。收到关闭帧后,另一方也会发送一个关闭帧作为确认。连接会在双方都确认关闭后断开。
使用场景:
WebSocket适用于需要实时更新数据的应用场景,例如:
- 即时聊天应用
- 实时协作工具
- 实时数据可视化
- 在线游戏
- 实时通知和推送服务
总之,WebSocket是一种能够提供低延迟、实时、双向通信的协议,为现代Web应用程序提供了强大的实时性能。
二、示例代码
首先是依赖部分,主要包括springBoot、spring-web-socket
<parent> <artifactId>spring-boot-starter-parent</artifactId> <groupId>org.springframework.boot</groupId> <version>2.7.3</version> </parent>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</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-web</artifactId> </dependency>
然后是java代码部分,这里模拟最简单的,一个配置类提供 ServerEndpointExporter,
一个组件类,实现websocket的握手和接发消息,以及处理异常
配置类的代码
@Configuration public class WebSocketConfig implements WebMvcConfigurer { @Bean public ServerEndpointExporter serverEndpointExporter (){ return new ServerEndpointExporter(); } }
组件类的代码,主要有3个方法,建立连接、断开连接以及接收消息及转发消息的方法,最后还有一个出现错误时的处理方法
import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.example.config.MessageConfigurator; import org.example.domain.entity.UserSession; import org.example.domain.vo.AbstractMessage; import org.example.domain.vo.PersonMessage; import org.example.domain.vo.SystemMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * 类描述 * * @author cjia * @date 2023/8/28 下午 10:50 */ @ServerEndpoint(value = "/message/{username}") @Component public class GMessageListener { public static ConcurrentMap<String, UserSession> sessions = new ConcurrentHashMap<>(); private static Logger logger = LoggerFactory.getLogger(GMessageListener.class) ; private String username ; /** * 建立连接时触发的方法 * * @param session 会话 * @param config 配置 * @param username 用户名 */ @OnOpen public void onOpen(Session session, EndpointConfig config, @PathParam("username") String username){ UserSession userSession = new UserSession(session.getId(), username, session) ; this.username = username ; sessions.put(username, userSession) ; logger.info("【{}】用户进入, 当前连接数:{}", username + ":" + session.getId(), sessions.size()) ; } /** * 关闭连接时触发的方法 * * @param session 会话 * @param reason 原因 */ @OnClose public void onClose(Session session, CloseReason reason){ UserSession userSession = sessions.remove(this.username) ; if (userSession != null) { logger.info("用户【{}】, 断开连接, 当前连接数:{}", username, sessions.size()) ; } } /** * 接收消息触发的方法 * * @param session 会话 * @param message 消息 * @throws IOException ioexception */ @OnMessage public void onMessage(Session session, String message) throws IOException { //根据实际的情况书写逻辑,我这里传递的是json字符串 // { "type":"system", "content":"测试" } logger.info("接受到消息:" + message); JSONObject jsonObject = JSONObject.parseObject(message); logger.info("解析的内容:{}", jsonObject.get("type")); logger.info("解析的内容:{}", jsonObject.get("content")); SystemMessage systemMessage = jsonObject.toJavaObject(SystemMessage.class); logger.info("解析之后转换:{}", systemMessage); session.getBasicRemote().sendText("消息收到: "+ message); } /** * 出现错误时触发的方法 * * @param session 会话 * @param error 错误 */ @OnError public void onError(Session session, Throwable error) { logger.error(error.getMessage()) ; } }
三、使用apiPost7建立websocket连接
控制台部分日志输出
2023-08-31 22:33:32.169 INFO 13476 --- [nio-8090-exec-1] org.example.service.GMessageListener : 【1:0】用户进入, 当前连接数:1 2023-08-31 22:33:34.061 INFO 13476 --- [nio-8090-exec-2] org.example.service.GMessageListener : 接受到消息:{ "type":"system", "content":"测试" } 2023-08-31 22:33:34.094 INFO 13476 --- [nio-8090-exec-2] org.example.service.GMessageListener : 解析的内容:system 2023-08-31 22:33:34.094 INFO 13476 --- [nio-8090-exec-2] org.example.service.GMessageListener : 解析的内容:测试 2023-08-31 22:33:34.099 INFO 13476 --- [nio-8090-exec-2] org.example.service.GMessageListener : 解析之后转换:SystemMessage() 2023-08-31 22:33:35.757 INFO 13476 --- [nio-8090-exec-3] org.example.service.GMessageListener : 接受到消息:{ "type":"system", "content":"测试" } 2023-08-31 22:33:35.757 INFO 13476 --- [nio-8090-exec-3] org.example.service.GMessageListener : 解析的内容:system 2023-08-31 22:33:35.757 INFO 13476 --- [nio-8090-exec-3] org.example.service.GMessageListener : 解析的内容:测试 2023-08-31 22:33:35.757 INFO 13476 --- [nio-8090-exec-3] org.example.service.GMessageListener : 解析之后转换:SystemMessage() 2023-08 2023-08-31 22:48:53.786 INFO 13476 --- [nio-8090-exec-9] org.example.service.GMessageListener : 【2:1】用户进入, 当前连接数:2
项目仓库地址
gitee
github
git clone git@github.com:cwithinj/java-note.git
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具