zoukankan      html  css  js  c++  java
  • WebSocket

    本文源代码下载:spring_websocket

    手册文档下载:websocket.doc

    WebSocket_百度百科

    Spring WebSocket简单入门测试Demo(网页及时聊天)

      项目需要一个及时推送消息的功能,并且这个任务落在了我的头上,早有实现此功能的心思,这不机会就来了,网上看了下页面ajax定时请求这种方案的居多,通过同时介绍找到了websocket,百度百科了下非常满意,并轻松在网上找到了相关文章。下面简单介绍下实现,代码就不在这里贴了,Spring WebSocket简单入门测试Demo(网页及时聊天)这篇博文给的很全,下面只对实现过程中遇到的一些问题简单说明下

    问题1:建立链接失败

      这个问题是由于页面链接ip与地址栏中不一致所致,正确方式如下图

      

      

      如果页面写成127.0.0.1,地址栏为localhost会链接报错,相反也会报错

    问题2:多页面使用websocket如何区分?

      这个问题困扰了好久,试过通过访问链接、单独建立websocket等方式,均不能很好的解决问题,最后还是通过万能的“打debug”得到了解决思路,豁然开朗,原来如此简单。解决思路如下:

      1)在WebsocketConfigure中为每个页面添加单独的websocket管理 

    @Configuration
    @EnableWebSocket//开启websocket
    public class WebSocketConfig implements WebSocketConfigurer {
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            //页面1的websocket支持
            registry.addHandler(new WebSocketHander(), "/echo").addInterceptors(new HandshakeInterceptor()); //支持websocket 的访问链接
            registry.addHandler(new WebSocketHander(), "/sockjs/echo").addInterceptors(new HandshakeInterceptor()).withSockJS(); //不支持websocket的访问链接
    
            //页面2的websocket支持
            registry.addHandler(new WebSocketHander(), "/echo1").addInterceptors(new HandshakeInterceptor()); //支持websocket 的访问链接
            registry.addHandler(new WebSocketHander(), "/sockjs/echo1").addInterceptors(new HandshakeInterceptor()).withSockJS(); //不支持websocket的访问链接
        }
    }

      有原来的一个websocket变为两个,只需与各自的页面对应即可

      2)拦截器标识修改(粗体字为修改之处)

    public class HandshakeInterceptor implements org.springframework.web.socket.server.HandshakeInterceptor {
    
        //进入hander之前的拦截
        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse,
                                       WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
            String[] uri = request.getURI().toString().split("/");
            if (request instanceof ServletServerHttpRequest) {
                ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
                HttpSession session = servletRequest.getServletRequest().getSession(true);
                //保证地址栏的请求和websocket的请求地址统一就能获取到了
                User user = (User) session.getAttribute("now_user");
                if (session != null) {
                    //使用userName区分WebSocketHandler,以便定向发送消息
                    map.put("websocket_index", uri[uri.length - 1] + "_" + user.getUserName());
                }
            }
            return true;
        }
    
        public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse,
                                   WebSocketHandler webSocketHandler, Exception e) {
    
        }
    }

      3)消息发送方法代码修改(黑体字为修改内容)

    package com.controller.websocket;
    
    import org.apache.log4j.Logger;
    import org.springframework.web.socket.CloseStatus;
    import org.springframework.web.socket.TextMessage;
    import org.springframework.web.socket.WebSocketHandler;
    import org.springframework.web.socket.WebSocketMessage;
    import org.springframework.web.socket.WebSocketSession;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * Created by root on 16-10-26.
     */
    public class WebSocketHander implements WebSocketHandler {
    
        private static Logger log = Logger.getLogger(WebSocketHander.class);
        private static int count = 0;//统计建立管道数
        private static final ArrayList<WebSocketSession> users = new ArrayList<WebSocketSession>();
        private static final Map<String, String> map = new HashMap();
    
        //初次链接成功执行
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            log.debug("链接成功......");
            users.add(session);
            String key = (String) session.getAttributes().get("websocket_index");
            if (key != null) {
                //未读消息处理逻辑
                count++;
                map.put(key, session.getId());
                session.sendMessage(new TextMessage(count + ""));
            }
        }
    
        //接受消息处理消息
        public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
            sendMessageToUsers(new TextMessage(webSocketMessage.getPayload() + ""));
            //sendMessageToUser("123", new TextMessage(webSocketMessage.getPayload() + ""));
        }
    
        public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
            if (webSocketSession.isOpen()) {
                webSocketSession.close();
            }
            count--;
            log.debug("链接出错,关闭链接......");
            users.remove(webSocketSession);
        }
    
        //关闭或离开此页面管道关闭
        public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
            count--;
            log.debug("链接关闭......" + closeStatus.toString());
            users.remove(webSocketSession);
        }
    
        public boolean supportsPartialMessages() {
            return false;
        }
    
        /**
         * 给所有在线用户发送消息
         *
         * @param message
         */public void sendMessageToUsers1(String index, TextMessage message) {
            ArrayList<WebSocketSession> u = users;
            Map<String, String> m = map;
            for (WebSocketSession user : users) {
                try {
                    String[] str = user.getAttributes().get("websocket_index").toString().split("_");
                    if (user.isOpen() && str[0].equals(index)) {
                        user.sendMessage(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 给某个用户发送消息
         *
         * @param userName
         * @param message
         */public void sendMessageToUser1(String index, TextMessage message) {
            ArrayList<WebSocketSession> u = users;
            Map<String, String> m = map;
            for (WebSocketSession user : users) {
                if (user.getId().equals(map.get(index))) {
                    try {
                        if (user.isOpen()) {
                            user.sendMessage(message);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                }
            }
        }
    
        public static Map<String, String> getMap() {
            return map;
        }
    }

      4)后端发送消息控制层代码修改

    package com.controller.websocket;
    
    import com.entity.User;
    
    import org.apache.log4j.Logger;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.socket.TextMessage;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * websocket数据推送测试 Created by root on 16-10-27.
     */
    @Controller
    @RequestMapping(value = "/websocket")
    public class WebsocketController {
    
        private static Logger log = Logger.getLogger(WebsocketController.class);
    
        @Bean
        public WebSocketHander webSocketHandler() {
            return new WebSocketHander();
        }
    
        /**
         * 后台推送消息给指定用户
         *
         * @param request
         * @return
         */
        @RequestMapping("/auditing")
        @ResponseBody
        public String auditing(HttpServletRequest request, String index) {
            User user = (User) request.getSession().getAttribute("now_user");
            //webSocketHandler().sendMessageToUser1(index + "_" + user.getUserName(), new TextMessage(user.getUserName()));
            webSocketHandler().sendMessageToUsers1(index, new TextMessage(user.getUserName()));
            return "success";
        }
    
        /**
         * 打开此页面前端和后端正式建立管道,关闭或离开此页面管道关闭
         *
         * @return
         */
        @RequestMapping(value = "/websocket")
        public String websocket() {
            return "websocket";
        }
    
        @RequestMapping(value = "/websocket1")
        public String websocket1() {
            return "websocket1";
        }
    
    }

     到此问题二得到解决。

    问题3:同一页面,在同一浏览器的两个标签中打开,此时同一功能的管道打开了两个,如何在新的标签打开页面建立管道的时候关闭之前的?

      解决方案是在新标签打开该页面时,将之前页面的管道关闭,代码如下: 

    //初次链接成功执行
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            String key = (String) session.getAttributes().get("websocket_index");
            users.add(session);
            log.debug(key + " 链接成功......");
            if (key != null) {
                if (map.get(key) != null) {
                    for (WebSocketSession sess : users) {
                        if (sess.getId().equals(map.get(key))) sess.close();
                        break;
                    }
                }
                //未读消息处理逻辑
                count++;
                map.put(key, session.getId());
                session.sendMessage(new TextMessage(count + ""));
            }
        }

     问题4:刷新页面,之前管道未加载完的信息仍会在刷新后的页面加载(要求刷新之后之前的请求停止)?

      解决方案记录每个管道的WebSocketSession的id,保存到要发送消息的方法中,每次发送消息时检测用户的当前的管道id与记录的是否相等,相等则发送,否则停止该方法。

      具体通过在WebSocketHander中定义map记录每个用户的管道信息实现。

      private static final Map<String, String> map = new HashMap();

    最后给出下所需依赖
    <!--websocket-->
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
          <version>2.3.0</version>
        </dependency>
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-core</artifactId>
          <version>2.3.1</version>
        </dependency>
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.3.3</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-websocket</artifactId>
          <version>${org.springframework-version}</version>
        </dependency>
  • 相关阅读:
    【转载】SAP_ECC6.0_EHP4或SAP_ECC6.0_EHP5_基于Windows_Server_2008R2_和SQL_server_2008下的安装
    使用delphi 开发多层应用(二十四)KbmMW 的消息方式和创建WIB节点
    使用delphi 开发多层应用(二十三)KbmMW 的WIB
    实现KbmMw web server 支持https
    KbmMW 服务器架构简介
    Devexpress VCL Build v2014 vol 14.1.1 beta发布
    使用delphi 开发多层应用(二十二)使用kbmMW 的认证管理器
    KbmMW 4.50.00 测试版发布
    Basic4android v3.80 beta 发布
    KbmMW 认证管理器说明(转载)
  • 原文地址:https://www.cnblogs.com/sunjf/p/spring_websocket.html
Copyright © 2011-2022 走看看