SpringBoot和Vue2集成WebSocket,实现聊天室功能

SpringBoot和Vue2集成WebSocket,实现聊天室功能

1.加入依赖
2.后端建立socket服务端
3.前端建立客户端

后端


        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-websocketartifactId>
        dependency>
		<dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.76version>
        dependency>
// 配置开启WebSocket
@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
/**
 * @author websocket服务
 */
@ServerEndpoint(value = "/imserver/{userId}")
@Component
public class WebSocketServer {


    private static UserService userService;

    private static RedisTemplate redisTemplate;

    public static void setUserService(ApplicationContext context){
        userService = context.getBean(UserServiceImpl.class);
        redisTemplate = (RedisTemplate) context.getBean("redisTemplate");
    }


    private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
    /**
     * 记录当前在线连接数
     */
    public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
    /**
     * 连接建立成功调用的方法
     */
    // 当前用户
    private UserVo userVo;
    // 连接上服务端触发的方法
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        if (StringUtils.hasText(userId)){
            // 加入新用户
            if (sessionMap.containsKey(userId)){
                sessionMap.remove(userId);
            }
            sessionMap.put(userId, session);
            this.userVo = userService.findById(Long.valueOf(userId));
            // 统计所有在线用户
            List<UserVo> list = new LinkedList<>();
            sessionMap.forEach((userId1,session1) -> {
                UserVo userVo = userService.findById(Long.valueOf(userId1));
                list.add(userVo);
            });
            try {
                // 发送给所有在线的用户,更新在线人数
                sendAllMessage(JSON.toJSONString(list));
            } catch (Exception e) {
                e.printStackTrace();
            }
            log.info("有新用户加入,userId={}, 当前在线人数为:{}", userId, sessionMap.size());
        }

    }
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("userId") String userId) {
        sessionMap.remove(userId);
        // 统计所有在线用户
        List<UserVo> list = new LinkedList<>();
        sessionMap.forEach((userId1,session1) -> {
            UserVo userVo = userService.findById(Long.valueOf(userId1));
            list.add(userVo);
        });
        sendAllMessage(JSON.toJSONString(list));
        log.info("有一连接关闭,移除userId={}的用户session, 当前在线人数为:{}", userId, sessionMap.size());
    }
    /**
     * 收到客户端消息后调用的方法
     * 后台收到客户端发送过来的消息
     * onMessage 是一个消息的中转站
     * 接受 浏览器端 socket.send 发送过来的 json数据
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("userId") String userId) {
        userVo = userService.findById(Long.valueOf(userId));
        log.info("服务端收到用户username={},id={}的消息:{}", userVo.getNickname(),userId, message);
        // 解析消息
        JSONObject jsonObject1 = JSON.parseObject(message);
        String toUserId = jsonObject1.getString("toUserId");
        String text = jsonObject1.getString("text");
        // 判断是给指定人发,还是群发
        if (StringUtils.hasText(toUserId)){
            // {"to": "admin", "text": "聊天文本"}
            Session toSession = sessionMap.get(toUserId); // 根据 to用户名来获取 session,再通过session发送消息文本
            if (toSession != null) {
                // 服务器端 再把消息组装一下,组装后的消息包含发送人和发送的文本内容
                // {"from": "zhang", "text": "hello"}
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("fromUser",userVo);
                jsonObject.put("toUser",userService.findById(Long.valueOf(toUserId)));
                jsonObject.put("text",text);
                this.sendMessage(jsonObject.toJSONString(), toSession);
                log.info("发送给用户userId={},消息:{}", toUserId, jsonObject.toJSONString());
            } else {
                log.info("发送失败,未找到用户username={}的session", toUserId);
            }
        }else{
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("fromUser",userVo);
            jsonObject.put("text",text);
            this.sendAllMessage(jsonObject.toJSONString());
            // 将消息存入redis
            redisTemplate.opsForList().rightPush("messageList",jsonObject.toJSONString());
            redisTemplate.expire("messageList",60*60, TimeUnit.SECONDS); // 过期时间

            log.info("发送给所有用户,消息:{}", toUserId, jsonObject.toJSONString());

        }
    }
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }
    /**
     * 服务端发送消息给客户端
     */
    private void sendMessage(String message, Session toSession) {
        try {
            log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
            toSession.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }
    /**
     * 服务端发送消息给所有客户端
     */
    private void sendAllMessage(String message) {
        try {
            for (Session session : sessionMap.values()) {
                log.info("服务端给客户端[{}]发送消息{}", session.getId(), message);
                session.getBasicRemote().sendText(message);

            }
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }
}

// WebSocket服务类无法进行bean的注入,所以要自己调用ApplicationContext获取bean再注入

@SpringBootApplication
public class BlogApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(BlogApplication.class, args);
        WebSocketServer.setUserService(applicationContext);
    }
}

前端






springboot集成websocket实现聊天室的功能。如有不足之处,还望大家斧正。

你可能感兴趣的:(spring,boot,websocket,java,vue,redis)