利用webSocket进行服务器推送,并发起桌面通知

来源:互联网 发布:java jasperreports 编辑:程序博客网 时间:2024/06/04 19:51

需求:定时读取数据,判断后利用websocket 发送消息给特定用户,并利用html5的Notification 发起桌面通知。

pom.xml 中添加相关Maven依赖:

<!-- websockt-->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-websocket</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-messaging</artifactId>            <version>${spring.version}</version>        </dependency>

编写WebSocket相关类。在WebSocketConfig中配置处理器(systemWebSocketHandler)以及拦截器(WebSocketHandshakeInterceptor)

@Configuration@EnableWebMvc@EnableWebSocketpublic class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {    @Override    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {        webSocketHandlerRegistry.addHandler(systemWebSocketHandler(),"/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor());        webSocketHandlerRegistry.addHandler(systemWebSocketHandler(),"/sockjs/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor())                .setAllowedOrigins("*").withSockJS();    }    @Bean    public WebSocketHandler systemWebSocketHandler(){        return new SystemWebSocketHandler();    }}

下面是 SystemWebSocketHandler 的配置

public class SystemWebSocketHandler implements WebSocketHandler {    private static final Logger logger;    private static final HashSet<WebSocketSession> users;    static {        users = new HashSet<>();        logger = LoggerFactory.getLogger(SystemWebSocketHandler.class);    }//    @Autowired//    IWebSocketService webSocketService;    @Override    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {        users.add(webSocketSession);    }    @Override    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {    }    @Override    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {        if (webSocketSession.isOpen()) {            webSocketSession.close();        }        logger.debug("websocket连接关闭......");        users.remove(webSocketSession);    }    @Override    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {        logger.debug("websocket 连接关闭......");        users.remove(webSocketSession);    }    @Override    public boolean supportsPartialMessages() {        return false;    }    /**     * 向所有用户推送消息     *     * @param message     */    public void sendMessageToUsers(TextMessage message) {        for (WebSocketSession user : users) {            try {                if (user.isOpen()) {//                    synchronized(user) {                    user.sendMessage(message);//                    }                }            } catch (IOException e) {                e.printStackTrace();            }        }    }    /**     * 指定用户推送消息     *     * @param roleId     * @param message     */    public void sendMessageToUser(int roleId, TextMessage message) {        for (WebSocketSession user : users) {            LoginUser loginUser = (LoginUser) user.getAttributes().get("user");            if (loginUser.getRoleId() == roleId || loginUser.getRoleId() == 0) {                try {                    if (user.isOpen()) {                        synchronized (loginUser) {                            user.sendMessage(message);                        }                    }                } catch (IOException e) {                    e.printStackTrace();                }                break;            }        }    }    /**     * 查看是否存在未处理的举报     */    public void doTask() {        boolean hasinfo = false;        /**        逻辑处理代码段        **/        if (hasinfo) {            sendMessageToUser(2, new TextMessage("推送内容"));        }    }}

WebSocketHandshakeInterceptor 的配置(每次建立连接都会进行握手,会被此拦截器拦截)

public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {    private static Logger logger = LoggerFactory.getLogger(HandshakeInterceptor.class);    @Override    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {        if (request instanceof ServletServerHttpRequest) {            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;            HttpSession session = servletRequest.getServletRequest().getSession(false);            if (session != null) {                //使用userName区分WebSocketHandler,以便定向发送消息               LoginUser user = (LoginUser) session.getAttribute("login");                attributes.put("user",user);            }        }        return true;    }    @Override    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {    }}

定时进行任务处理

@Scheduled(fixedRate = 1000 * 60 * 60 * 2)  // 每隔2小时执行一次    public void scheduleMethod() {        new SystemWebSocketHandler().doTask();    }

定时任务的成功执行,需要在配置文件中启动定时器,使@Schedued 生效

    <!-- 启动定时器 -->    <task:annotation-driven/>

进行到这一步后台的功能基本完成,接下来需要在js 中连接webSocket 并接收消息。

var ip = window.location.host;var websocket;    if ('WebSocket' in window) {        websocket = new WebSocket("ws://" + ip + "/xxx/webSocketServer");    } else if ('MozWebSocket' in window) {        websocket = new MozWebSocket("ws://" + ip + "/xxx/webSocketServer");    } else {        websocket = new SockJS("http://" + ip + "/xxx/sockjs/webSocketServer");    }    websocket.onopen = function (evnt) {        console.info("websocket已连接")    };    websocket.onmessage = function (evnt) {        console.info("后台传来消息")        //发送桌面消息(这里是发送消息相关代码)    };    websocket.onerror = function (evnt) {        console.info("websocket连接出错")    };    websocket.onclose = function (evnt) {        console.info("websocket连接已关闭")    }

为了实现Notification,首先要检查并申请通知权限

window.addEventListener('load', function () {        // 首先,让我们检查我们是否有权限发出通知        // 如果没有,我们就请求获得权限        if (window.Notification && Notification.permission !== "granted") {            Notification.requestPermission(function (status) {                if (Notification.permission !== status) {                    Notification.permission = status;                }            });        }    })

以下为实现通知部分的逻辑

 // 如果用户同意就创建一个通知        if (window.Notification && Notification.permission === "granted") {            webnotify()        }        // 如果用户没有选择是否显示通知        // 注:因为在 Chrome 中我们无法确定 permission 属性是否有值,因此        // 检查该属性的值是否是 "default" 是不安全的。        else if (window.Notification && Notification.permission !== "denied") {            Notification.requestPermission(function (status) {                if (Notification.permission !== status) {                    Notification.permission = status;                }                // 如果用户同意了                if (status === "granted") {                    webnotify()                }                // 否则,我们可以让步的使用常规模态的 alert                else {                   mynotify()                }            });        }        // 如果用户拒绝接受通知        else {            // 我们可以让步的使用常规模态的 alert            mynotify()        }

mynotify()是自定义的通知实现,此处不贴出实现代码。下面是webnotify()方法的实现。

function webnotify(){        var notify = new Notification(            "通知",            {                dir: 'auto',                lang: 'zh-CN',                icon: '???.png',//通知的缩略图,//icon 支持ico、png、jpg、jpeg格式                body: "" //通知的具体内容            }        );        notify.onclick = function () {            //如果通知消息被点击,通知窗口将被激活            window.focus();            window.open("http://?");//打开指定url            notify.close();        },            notify.onerror = function () {                console.log("HTML5桌面消息出错!!!");            };        notify.onshow = function () {            setTimeout(function () {                notify.close();            }, 20000)        };        notify.onclose = function () {            console.log("HTML5桌面消息关闭!!!");        };    }

至此需求已初步实现,细节处可以慢慢修改。

0 0