springMVC+websockt实现

2018-11-21  本文已影响0人  请让我想一想

场景还原:即时通讯,类似微信qq这种效果

代码实现:

环境:

  1. Tomcat 7+
  2. Maven+SpringMVC+Mybatis

代码实现:

流程梳理:前端请求后端,建立socket连接,成功后保持连接状态,
前端代码:

 $(document).ready(function () {
        // 指定websocket路径
        var websocket;
        if ('WebSocket' in window) {
        // Spring-websocket 自己项目名称
            websocket = new WebSocket("ws://localhost:8080/Spring-websocket/ws?uid=" +${sessionScope.uid});
        } else if ('MozWebSocket' in window) {
            websocket = new MozWebSocket("ws://localhost:8080/Spring-websocket/ws" +${sessionScope.uid});
        } else {
            websocket = new SockJS("http://localhost:8080/Spring-websocket/ws/sockjs" +${sessionScope.uid});
        }
      // 上面执行完成,就已经与后端建立好了socket连接

      
        // 后端有消息通知会触发这个事件
        websocket.onmessage = function (event) {
            var data = JSON.parse(event.data);
            if (data.from > 0 || data.from == -1) {//用户或者群消息
                // 接收服务端的实时消息并添加到HTML页面中
                $("#log-container").append("<div class='bg-info'><label class='text-danger'>" + data.fromName + "&nbsp;" + data.date + "</label><div class='text-success'>" + data.text + "</div></div><br>");
                // 滚动条滚动到最低部
                scrollToBottom();
            } else if (data.from == 0) {//上线消息
                if (data.text != "${sessionScope.username}") {
                    $("#users").append('<a href="#" onclick="talk(this)" class="list-group-item">' + data.text + '</a>');
                    alert(data.text + "上线了");
                }
            } else if (data.from == -2) {//下线消息
                if (data.text != "${sessionScope.username}") {
                    $("#users > a").remove(":contains('" + data.text + "')");
                    alert(data.text + "下线了");
                }
            }
        };
      
        $("#send").click(function () {
            $.post("getuid", {"username": $("body").data("to")}, function (d) {
                var v = $("#myinfo").val();

                if (v == "") {
                    return;
                } else {
                    var data = {};
                    data["from"] = "${sessionScope.uid}";
                    data["fromName"] = "${sessionScope.username}";
                    data["to"] = d.uid;
                    data["text"] = v;
                    // 发送socket信息,首先会被socket核心类拦截,在里面会根据参数获取指定通知的用户,发送通知
                    websocket.send(JSON.stringify(data));
                    $("#log-container").append("<div class='bg-success'><label class='text-info'>我&nbsp;" + new Date() + "</label><div class='text-info'>" + v + "</div></div><br>");
                    scrollToBottom();
                    $("#myinfo").val("");
                }
            });

        });

    });

后端代码:3个核心类





/**
 * Socket建立连接(握手)和断开
 */
public class HandShake implements HandshakeInterceptor {

    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        System.out.println("Websocket:用户[ID:" + ((ServletServerHttpRequest) request).getServletRequest().getSession(false).getAttribute("uid") + "]已经建立连接");
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            // 标记用户
            Long uid = (Long) session.getAttribute("uid");
            if(uid!=null){
                attributes.put("uid", uid);
            }else{
                return false;
            }
        }
        return true;
    }

    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
    System.out.println("after hand");
    
    }

}




/**
 * Socket处理器
 */
@Component
public class MyWebSocketHandler implements WebSocketHandler {
    //用于保存HttpSession与WebSocketSession的映射关系
    public static final Map<Long, WebSocketSession> userSocketSessionMap;

    @Autowired
    LoginService loginservice;
    
    static {
        userSocketSessionMap = new ConcurrentHashMap<Long, WebSocketSession>();
    }
    
    /**
     * 建立连接后,把登录用户的id写入WebSocketSession
     */
    public void afterConnectionEstablished(WebSocketSession session)
            throws Exception {
        Long uid = (Long) session.getAttributes().get("uid");
        String username=loginservice.getnamebyid(uid);
        if (userSocketSessionMap.get(uid) == null) {
            userSocketSessionMap.put(uid, session);
            Message msg = new Message();
            msg.setFrom(0L);//0表示上线消息
            msg.setText(username);
            this.broadcast(new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
        }
    }

    /**
     * 消息处理,在客户端通过Websocket API发送的消息会经过这里,然后进行相应的处理
     */
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
            if(message.getPayloadLength()==0)
                return;
            Message msg=new Gson().fromJson(message.getPayload().toString(),Message.class);
            msg.setDate(new Date());
            sendMessageToUser(msg.getTo(), new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
    }

    /**
     * 消息传输错误处理
     */
    public void handleTransportError(WebSocketSession session,
            Throwable exception) throws Exception {
        if (session.isOpen()) {
            session.close();
        }
        Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();
        // 移除当前抛出异常用户的Socket会话
        while (it.hasNext()) {
            Entry<Long, WebSocketSession> entry = it.next();
            if (entry.getValue().getId().equals(session.getId())) {
                userSocketSessionMap.remove(entry.getKey());
                System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
                String username=loginservice.getnamebyid(entry.getKey());
                Message msg = new Message();
                msg.setFrom(-2L);
                msg.setText(username);
                this.broadcast(new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
                break;
            }
        }
    }

    /**
     * 关闭连接后
     */
    public void afterConnectionClosed(WebSocketSession session,CloseStatus closeStatus) throws Exception {
        System.out.println("Websocket:" + session.getId() + "已经关闭");
        Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();
        // 移除当前用户的Socket会话
        while (it.hasNext()) {
            Entry<Long, WebSocketSession> entry = it.next();
            if (entry.getValue().getId().equals(session.getId())) {
                userSocketSessionMap.remove(entry.getKey());
                System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
                String username=loginservice.getnamebyid(entry.getKey());
                Message msg = new Message();
                msg.setFrom(-2L);//下线消息,用-2表示
                msg.setText(username);
                this.broadcast(new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
                break;
            }
        }
    }

    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 给所有在线用户发送消息
     * @param message
     * @throws IOException
     */
    public void broadcast(final TextMessage message) throws IOException {
        Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();

        //多线程群发
        while (it.hasNext()) {

            final Entry<Long, WebSocketSession> entry = it.next();

            if (entry.getValue().isOpen()) {
                // entry.getValue().sendMessage(message);
                new Thread(new Runnable() {

                    public void run() {
                        try {
                            if (entry.getValue().isOpen()) {
                                entry.getValue().sendMessage(message);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                }).start();
            }

        }
    }

    /**
     * 给某个用户发送消息
     * 
     * @param userName
     * @param message
     * @throws IOException
     */
    public void sendMessageToUser(Long uid, TextMessage message) throws IOException {
        WebSocketSession session = userSocketSessionMap.get(uid);
        if (session != null && session.isOpen()) {
            session.sendMessage(message);
        }
    }

}


/**
 * WebScoket配置处理器
 */
@Component
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {

    @Resource
    MyWebSocketHandler handler;

    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(handler, "/ws").addInterceptors(new HandShake());

        registry.addHandler(handler, "/ws/sockjs").addInterceptors(new HandShake()).withSockJS();
    }

}


spring-mvc.xml配置

// 配置websocket 3个核心配置类的包,其他配置按自己项目定义即可
<context:component-scan base-package="controller,service.impl,websocket" />

结束:这只是部分socket代码抽取,参考了码云项目

源码可直接跳转:https://gitee.com/shenzhanwang/Spring-websocket.git
感谢这个作者的无私奉献.

上一篇下一篇

猜你喜欢

热点阅读