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>
  • 相关阅读:
    Eclipse护眼技巧
    Maven搭建SSM框架(Spring+SpringMVC+MyBatis)
    Spring之各jar包作用
    Maven新建web项目jsp报错
    js金额转大写(万元为单位)
    linux常用指令
    ie8下数组不支持indexOf方法解决方法
    string,stringBuffer,stringBuilder的比较
    input限制输入
    spring boot Mybatis --maven
  • 原文地址:https://www.cnblogs.com/sunjf/p/spring_websocket.html
Copyright © 2011-2022 走看看