WebSocket —— 在线聊天室
文章目录
- 消息推送常用方式介绍
- 轮询
- SSE(server-sent event)
- websocket介绍
- websocket API
- 前端API
- 后端API
- 实现在线聊天室
- 需求与最终效果展示
- 实现流程分析
- 消息格式
- 代码实现
- HttpSession的保存传递
- 具体代码
- 依赖
- pojo
- Result
- User
- Message
- ResultMessage
- Config
- WebsocketConfig
- GetHttpSessionConfig
- Controller
- UserController
- Websocket
- ChatEndpoint
- utils
- MessageUtils
- 测试
- 注意事项
- 用户登录与连接Websocket
- 测试
- 过程分析
- 响应
- 用户私聊
- 测试
- 过程分析
- 响应
- 用户下线,断开Websocket
- 测试
- 过程分析
- 响应
消息推送常用方式介绍
轮询
SSE(server-sent event)
websocket介绍
websocket API
前端API
后端API
实现在线聊天室
需求与最终效果展示
实现流程分析
消息格式
代码实现
HttpSession的保存传递
这二者这里可以视为一个东西!!!HttpSession通过这个来保存传递!
具体代码
依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
pojo
Result
@Data
public class Result {private boolean flag;private String message;
}
User
@Data
public class User {private String userId;private String username;private String password;}
Message
@Data
public class Message {private String toName;private String message;
}
ResultMessage
@Data
public class ResultMessage {private boolean isSystem;private String fromName;private Object message;
}
Config
WebsocketConfig
@Configuration
public class WebsocketConfig {/*** ServerEndpointExporter 的作用** 自动注册 WebSocket 端点* ServerEndpointExporter 会自动扫描 Spring 容器中所有带有 @ServerEndpoint 注解的类,并将它们注册为 WebSocket 端点。* 这意味着你不需要手动在 Spring 容器中注册这些端点,ServerEndpointExporter 会自动处理。** 支持 Spring 的依赖注入* 通过 ServerEndpointExporter,Spring 容器中的 WebSocket 端点可以像其他 Spring Bean 一样使用依赖注入(DI)。* 例如,你可以在 WebSocket 端点类中注入其他 Spring Bean,如服务层(Service)或工具类(Utility)。** 简化 WebSocket 配置* 使用 ServerEndpointExporter 可以简化 WebSocket 的配置,避免手动编写大量的配置代码。* 它提供了一种声明式的方式来配置 WebSocket 端点,使得代码更加简洁和易于维护。* @return*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
GetHttpSessionConfig
public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {//获取HttpSession对象HttpSession httpSession = (HttpSession)request.getHttpSession();//将httpSession对象保存起来sec.getUserProperties().put(HttpSession.class.getName(),httpSession);}}
Controller
UserController
/*** @CrossOrigin 是 Spring Framework 提供的一个注解,用于解决跨域资源共享(Cross-Origin Resource Sharing, CORS)问题。* 可以应用于 Spring MVC 控制器类或方法上,用于配置允许跨域请求的规则。* 它支持多种配置选项,包括允许的域、HTTP 方法、请求头等。*/
@CrossOrigin
@RestController
@RequestMapping("/user")
public class UserController {/**** @param user* @param session 在 Spring MVC 中,HttpSession 是通过 Spring 的 @Controller 方法参数自动注入的。当你在方法签名中声明一个 HttpSession 参数时,* Spring 会自动从当前的 HTTP 请求中获取对应的 HttpSession 对象,并将其传递给方法。** HttpSession 的生命周期* 创建:当第一个请求到达服务器时,如果请求中没有 JSESSIONID(表示没有已存在的会话),Spring MVC 会创建一个新的 HttpSession。* 存储:HttpSession 对象存储在服务器端,与客户端通过 JSESSIONID 进行关联。* 访问:在后续的请求中,客户端会通过 JSESSIONID 来标识当前会话,服务器会根据 JSESSIONID 找到对应的 HttpSession。* 销毁:当用户关闭浏览器或会话超时后,HttpSession 会被销毁。* @return*/@PostMapping("/login")public Result login(@RequestBody User user, HttpSession session) {Result result = new Result();if (user != null && "123".equals(user.getPassword())) {result.setFlag(true);//将数据存储到session对象中session.setAttribute("user", user.getUsername());} else {result.setFlag(false);result.setMessage("登录失败");}return result;}}
Websocket
ChatEndpoint
@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfig.class)
@Component
public class ChatEndpoint {private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();private HttpSession httpSession;/*** 建立websocket连接后,被调用* @param session*/@OnOpenpublic void onOpen(Session session, EndpointConfig config) {//将session进行保存this.httpSession = (HttpSession)config.getUserProperties().get(HttpSession.class.getName());String username = (String) this.httpSession.getAttribute("user");onlineUsers.put(username, session);//广播消息,需要将登录的所有用户推送给所有的用户String message = MessageUtils.getMessage(true, null, getFriends());broadcastAllUsers(message);}public Set getFriends() {return onlineUsers.keySet();}private void broadcastAllUsers(String message) {try {//遍历map集合Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();for (Map.Entry<String, Session> entry : entries) {//获取到所有用户对应的session对象Session session = entry.getValue();//发送消息session.getBasicRemote().sendText(message);}} catch (Exception e) {throw new RuntimeException(e);}}/*** 浏览器发送消息到服务端,该方法被调用* @param message*/@OnMessagepublic void onMessage (String message) {try {//将消息推送给指定的用户Message msg = JSONObject.parseObject(message, Message.class);//获取 消息接收方的用户名String toName = msg.getToName();String mess = msg.getMessage();//获取 消息接收方的 session对象Session session = onlineUsers.get(toName);String username = (String) this.httpSession.getAttribute("user");session.getBasicRemote().sendText(MessageUtils.getMessage(false, (String)httpSession.getAttribute("user"), mess));} catch (Exception e) {throw new RuntimeException(e);}}/*** 断开websocket连接时被调用* @param session*/@OnClosepublic void onClose(Session session) {//从onlineUsers中剔除当前用户的session对象String username = (String) this.httpSession.getAttribute("user");onlineUsers.remove(username);//通知其他用户,当前用户下线了broadcastAllUsers(MessageUtils.getMessage(true, null, getFriends()));}
}
utils
MessageUtils
public class MessageUtils {public static String getMessage(boolean isSystemMessage, String fromName, Object message) {ResultMessage result = new ResultMessage();result.setSystem(isSystemMessage);result.setMessage(message);if (fromName != null) {result.setFromName(fromName);}return JSON.toJSONString(result);}
}
测试
注意事项
用户登录与连接Websocket
测试
过程分析
广播时沟通传递JSON格式数据,因此message记得从实体类转为JSON
获取HttpSession对象后要保存起来成为成员变量,方便后续使用
以及要将其中username信息和对应的Session映射保存起来,方便后续使用!!!
响应
用户私聊
测试
过程分析
响应
用户下线,断开Websocket
测试
过程分析
响应