消息推送常用方式介绍

轮询

在这里插入图片描述

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
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
GetHttpSessionConfig
public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {

    @Override
    public 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
     */
    @OnOpen
    public 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
     */
    @OnMessage
    public 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
     */
    @OnClose
    public 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

测试

在这里插入图片描述

过程分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

响应

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐