springboot + websocket 实现消息推送(服务器端)

1、websocket 推送  依赖引入

        
        
            org.springframework.boot
            spring-boot-starter-websocket
        

2、添加消息配置,新建WebSocketConfig .java文件

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @ClassName WebSocketConfig
 * @Description WebSocket 消息配置
 * @Author 孜然公子
 * @Date 2022年11月2日16:50:49
 */
@Configuration
public class WebSocketConfig {
    /**
     * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3、配置封装websocket组件,处理连接、消息发送相关。新建WebSocketServer.java文件。

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName WebSocketConfig
 * @Description WebSocket 消息配置 接口路径 ws://ip:port/webSocket/userId;
 * @Author 孜然公子
 * @Date 2022年11月2日16:50:49
 */
@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")
public class WebSocketServer {
    // 非活动连接保持时间
    private static final long sessionTimeout = 60000;

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private static Session session;

    // 用来存在线连接数
    private static final Map sessionPool = new ConcurrentHashMap<>();

    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") String userId) {
        try {
            session.setMaxIdleTimeout(sessionTimeout);
            this.session = session;
            synchronized (sessionPool) {
                if (sessionPool.containsKey(userId)) {
                    sessionPool.remove(userId);
                }
                sessionPool.put(userId, this);
            }
            // log.info("【websocket服务端】有新的连接,总数为:" + sessionPool.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        try {
            for (Map.Entry entry : sessionPool.entrySet()) {
                String mapKey = entry.getKey();
                if (entry.getValue() != null && entry.getValue().session.equals(session)) {
                    sessionPool.remove(mapKey);
                }
            }
            // log.info("【websocket服务端】连接断开,当前连接总数为:" + sessionPool.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        // log.info("【websocket服务端】收到客户端消息:" + message);
        if (("HeartBeat").equals(message)) {
            try {
                sendMessage(session, "心跳");
            } catch (Exception e) {
                for (Map.Entry entry : sessionPool.entrySet()) {
                    String mapKey = entry.getKey();
                    if (entry.getValue() != null && entry.getValue().session.equals(session)) {
                        sessionPool.remove(mapKey);
                    }
                }
            }
        }
    }

    /**
     * 发送错误时的处理
     */
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    /**
     * 此为广播消息
     */
    public void sendAllMessage(String message) {
        // log.info("【websocket服务端】广播消息:" + message);
        sessionPool.forEach((k, v) -> {
            try {
                if (v != null && v.session != null && v.session.isOpen()) {
                    sendMessage(v.session, message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 此为单点消息
     */
    public void sendOneMessage(String userId, String message) {
        WebSocketServer webSocketServer = sessionPool.get(userId);
        if (webSocketServer != null) {
            Session pSession = webSocketServer.session;
            if (pSession != null && pSession.isOpen()) {
                try {
                    // log.info("【websocket服务端】 单点消息:" + message);
                    sendMessage(pSession, message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 此为单点消息(多人)
     */
    public void sendMoreMessage(String[] userIds, String message) {
        for (String userId : userIds) {
            WebSocketServer webSocketServer = sessionPool.get(userId);
            if (webSocketServer != null) {
                Session pSession = webSocketServer.session;
                if (pSession != null && pSession.isOpen()) {
                    try {
                        // log.info("【websocket服务端】 单点消息:" + message);
                        sendMessage(pSession, message);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 此为单点消息(多人)
     */
    public void sendMoreMessage(List userIds, String message) {
        // System.out.println("【websocket服务端】 单点消息:" + message + " 目标人群:" + JSON.toJSONString(userIds));
        // System.out.println("【websocket服务端】 会话列表:" + JSON.toJSONString(sessionPool.keySet()));
        for (String userId : userIds) {
            WebSocketServer webSocketServer = sessionPool.get(userId);
            if (webSocketServer != null) {
                Session pSession = webSocketServer.session;
                if (pSession != null && pSession.isOpen()) {
                    try {
                        // System.out.println("【websocket服务端】 单点消息:" + userId + " " + message);
                        sendMessage(pSession, message);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 服务端群发消息-心跳包
     *
     * @param message 消息
     * @return int
     */
    public static synchronized int sendPing(String message) {
        if (sessionPool.size() <= 0) {
            return 0;
        }

        StringBuffer userIds = new StringBuffer();
        AtomicInteger count = new AtomicInteger();
        sessionPool.forEach((userId, server) -> {
            count.getAndIncrement();
            if (sessionPool.containsKey(userId)) {
                try {
                    if (server != null) {
                        Session pSession = server.session;
                        if (pSession != null && pSession.isOpen()) {
                            sendMessage(pSession, message);
                            if (count.equals(sessionPool.size() - 1)) {
                                userIds.append(userId);
                            } else {
                                userIds.append(userId).append(",");
                            }
                        } else {
                            sessionPool.remove(userId);
                        }
                    }
                } catch (Exception e) {
                    sessionPool.remove(userId);
                    // System.out.println("【websocket服务端】客户端心跳检测异常:" + e.getMessage());
                    log.info("【websocket服务端】客户端心跳检测异常移除: " + userId + ",心跳发送失败,已移除!");
                }
            } else {
                log.info("【websocket服务端】客户端心跳检测异常不存在: " + userId + ",不存在!");
            }
        });
        log.info("【websocket服务端】客户端心跳检测结果: " + userIds + "连接正在运行");
        return sessionPool.size();
    }

    /**
     * 发送消息
     *
     * @param session 会话连接
     * @param message 消息内容
     */
    public synchronized static void sendMessage(Session session, String message) throws IOException {
        session.getBasicRemote().sendText(message);
    }
}

 4、如果后台发送心跳检测消息,则创建WebSocketScheduleTask.java文件

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * @ClassName websocket 任务
 * @Description
 * @Author 孜然公子
 * @Date 2022/11/12 18:00
 */
@Component
@Slf4j
@EnableScheduling
public class WebSocketScheduleTask {
    /**
     * 每10s发送一次心跳给客户端
     */
    @Scheduled(cron = "0/10 * * * * ?")
    public void sendPingToClient() {
        log.info("【websocket服务端】websocket心跳开始发送");
        int num = 0;
        try {
            num = WebSocketServer.sendPing("心跳");
        } finally {
            log.info("【websocket服务端】websocket心跳检测结果,共【" + num + "】个连接");
        }
        log.info("【websocket服务端】websocket心跳发送结束");
    }
}

注:如果启动文件Application中已经添加注解@EnableScheduling,该文件中就不要再添加该注解了!!!

5、对外ws协议消息配置接口路径地址为 ws://ip:port/webSocket/userId

感谢浏览Thanks♪(・ω・)ノ

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