WebSocket —— 在线聊天室

文章目录

消息推送常用方式介绍

轮询

SSE(server-sent event)

websocket介绍

websocket API

前端API


后端API



实现在线聊天室

需求与最终效果展示


实现流程分析

消息格式

代码实现


HttpSession的保存传递

这二者这里可以视为一个东西!!!HttpSession通过这个来保存传递!

具体代码

依赖

pom 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

pojo

Result
java 复制代码
@Data
public class Result {

    private boolean flag;

    private String message;
}
User
java 复制代码
@Data
public class User {

    private String userId;

    private String username;

    private String password;

}
Message
java 复制代码
@Data
public class Message {
    private String toName;

    private String message;
}
ResultMessage
java 复制代码
@Data
public class ResultMessage {

    private boolean isSystem;

    private String fromName;

    private Object message;
}

Config

WebsocketConfig
java 复制代码
@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
java 复制代码
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
java 复制代码
/**
 * @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
java 复制代码
@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
java 复制代码
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

测试
过程分析



响应


相关推荐
安当加密3 小时前
构建高安全堡垒机登录体系:RADIUS + 动态口令实践
网络·安全
我言秋日胜春朝★6 小时前
【Linux网络编程】多路转接reactor——ET模式的epoll
linux·服务器·网络
_清浅6 小时前
计算机网络【第五章-传输层】
网络
焦思懿--19期--工职大7 小时前
VMWare和物理机之间文件传输
linux·服务器·网络·电脑
爱偷懒的。。8 小时前
基于 WebSocket 协议的实时弹幕通信机制分析-抖音
网络·python·websocket·网络协议·学习·js
全栈小59 小时前
【小程序】微信开发者工具上调用api接口可以,到了线上调用发现提示wx.request调用报错,原来是https协议问题
网络协议·小程序·https
心.c9 小时前
深入理解HTTP协议的本质
网络·网络协议·http
国科安芯10 小时前
核辐射检测仪中的抗辐照MCU芯片应用探索与挑战应对
网络·人工智能·单片机·嵌入式硬件·安全·fpga开发
不吃鱼的羊10 小时前
Communication Stack简介
网络