01-websocket初识

springboot 整合 websocket示例

一 、websocket介绍

当今互联网应用程序越来越注重实时性和双向通信,而传统的HTTP协议在这方面的表现相对较弱。WebSocket(Web套接字)是一种在单个TCP连接上实现全双工通信的协议,它允许在浏览器和服务器之间建立持久性的、实时的通信连接,而无需不断发起新的HTTP请求。

特点:

  1. 全双工通信: WebSocket允许在同一个连接上实现双向的实时通信,服务器和客户端可以同时发送和接收数据,而不需要等待对方的响应。

  2. 持久连接: 传统的HTTP请求-响应模式需要在每次通信时重新建立连接,而WebSocket连接在建立后会一直保持开放状态,从而减少了连接建立的开销。

  3. 低延迟: WebSocket的持久性连接和全双工通信特性使得数据能够更快地传输,从而降低了通信的延迟。

  4. 轻量级: WebSocket的协议头相对较小,减少了数据传输的开销。

  5. 跨域支持: 通过CORS(跨源资源共享)机制,WebSocket允许不同域名下的网页与服务器进行实时通信。

工作原理:

  1. 握手阶段: WebSocket的连接始于一个HTTP请求,这个请求称为WebSocket握手。客户端向服务器发起一个带有特殊头部的HTTP请求,请求表明客户端希望升级连接为WebSocket连接。如果服务器支持WebSocket,它会响应一个HTTP 101状态码,表示握手成功,此后连接升级为WebSocket连接。

  2. 数据传输: 握手成功后,WebSocket连接会一直保持开放状态。在任何一方需要发送数据时,数据会被封装为WebSocket帧,并通过已建立的连接进行传输。帧可以是文本数据或二进制数据。

  3. 关闭连接: 要关闭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连接

img

控制台部分日志输出

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

git clone https://gitee.com/JiaSir2022/java-note.git

github

git clone git@github.com:cwithinj/java-note.git

posted @   书画三千里  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示