zoukankan      html  css  js  c++  java
  • Java使用WebSocket

    网页端的消息推送,一般有以下方式:

    轮询方式:客户端定时向服务端发送ajax请求,服务器接收到请求后马上返回消息并关闭连接。
    优点:后端程序编写比较容易。
    缺点:TCP的建立和关闭操作浪费时间和带宽,请求中有大半是无用,浪费带宽和服务器资源。
    实例:适于小型应用。

    长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
    优点:在无消息的情况下不会频繁的请求,耗费资源小。
    缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。
    实例:WebQQ、Hi网页版、Facebook IM。

    长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。
    优点:消息即时到达,不发无用请求;管理起来也相对方便。
    缺点:服务器维护一个长连接会增加开销,当客户端越来越多的时候,server压力大!
    实例:Gmail聊天

    Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。
    优点:实现真正的即时通信,而不是伪即时。
    缺点:客户端必须安装Flash插件,移动端支持不好,IOS系统中没有flash的存在;非HTTP协议,无法自动穿越防火墙。
    实例:网络互动游戏。

    webSocket:HTML5 WebSocket设计出来的目的就是取代轮询和长连接,使客户端浏览器具备像C/S框架下桌面系统的即时通讯能力,实现了浏览器和服务器全双工通信,建立在TCP之上,虽然WebSocket和HTTP一样通过TCP来传输数据,但WebSocket可以主动的向对方发送或接收数据,就像Socket一样;并且WebSocket需要类似TCP的客户端和服务端通过握手连接,连接成功后才能互相通信。
    优点:双向通信、事件驱动、异步、使用ws或wss协议的客户端能够真正实现意义上的推送功能。
    缺点:少部分浏览器不支持。
    示例:社交聊天(微信、QQ)、弹幕、多玩家玩游戏、协同编辑、股票基金实时报价、体育实况更新、视频会议/聊天、基于位置的应用、在线教育、智能家居等高实时性的场景。

    Java实现Web Socket有两种方式:

    1、Tomcat WebSocket @ServerEndpoint

    需要Tomcat7,Java7以上的支持

    package com.Socket;  
      
    import java.io.IOException;  
    import java.util.Map;  
    import java.util.concurrent.ConcurrentHashMap;  
    import javax.websocket.*;  
    import javax.websocket.server.PathParam;  
    import javax.websocket.server.ServerEndpoint;  
    import net.sf.json.JSONObject;  
      
    @ServerEndpoint("/websocket/{username}")  
    public class WebSocket {  
      
        private static int onlineCount = 0;  
        private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();  
        private Session session;  
        private String username;  
          
        @OnOpen  
        public void onOpen(@PathParam("username") String username, Session session) throws IOException {  
      
            this.username = username;  
            this.session = session;  
              
            addOnlineCount();  
            clients.put(username, this);  
            System.out.println("已连接");  
        }  
      
        @OnClose  
        public void onClose() throws IOException {  
            clients.remove(username);  
            subOnlineCount();  
        }  
      
        @OnMessage  
        public void onMessage(String message) throws IOException {  
      
            JSONObject jsonTo = JSONObject.fromObject(message);  
              
            if (!jsonTo.get("To").equals("All")){  
                sendMessageTo("给一个人", jsonTo.get("To").toString());  
            }else{  
                sendMessageAll("给所有人");  
            }  
        }  
      
        @OnError  
        public void onError(Session session, Throwable error) {  
            error.printStackTrace();  
        }  
      
        public void sendMessageTo(String message, String To) throws IOException {  
            // session.getBasicRemote().sendText(message);  
            //session.getAsyncRemote().sendText(message);  
            for (WebSocket item : clients.values()) {  
                if (item.username.equals(To) )  
                    item.session.getAsyncRemote().sendText(message);  
            }  
        }  
          
        public void sendMessageAll(String message) throws IOException {  
            for (WebSocket item : clients.values()) {  
                item.session.getAsyncRemote().sendText(message);  
            }  
        }  
      
        public static synchronized int getOnlineCount() {  
            return onlineCount;  
        }  
      
        public static synchronized void addOnlineCount() {  
            WebSocket.onlineCount++;  
        }  
      
        public static synchronized void subOnlineCount() {  
            WebSocket.onlineCount--;  
        }  
      
        public static synchronized Map<String, WebSocket> getClients() {  
            return clients;  
        }  
    }  
    var websocket = null;  
    var username = localStorage.getItem("name");  
      
    //判断当前浏览器是否支持WebSocket  
    if ('WebSocket' in window) {  
        websocket = new WebSocket("ws://" + document.location.host + "/WebChat/websocket/" + username + "/"+ _img);  
    } else {  
        alert('当前浏览器 Not support websocket')  
    }  
      
    //连接发生错误的回调方法  
    websocket.onerror = function() {  
        setMessageInnerHTML("WebSocket连接发生错误");  
    };  
      
    //连接成功建立的回调方法  
    websocket.onopen = function() {  
        setMessageInnerHTML("WebSocket连接成功");  
    }  
      
    //接收到消息的回调方法  
    websocket.onmessage = function(event) {  
        setMessageInnerHTML(event.data);  
    }  
      
    //连接关闭的回调方法  
    websocket.onclose = function() {  
        setMessageInnerHTML("WebSocket连接关闭");  
    }  
      
    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。  
    window.onbeforeunload = function() {  
        closeWebSocket();  
    }  
      
    //关闭WebSocket连接  
    function closeWebSocket() {  
        websocket.close();  
    }  

    2、Spring WebSocket

    实现配置类

    import org.springframework.context.annotation.Bean;  
    import org.springframework.context.annotation.Configuration;  
    import org.springframework.web.socket.config.annotation.EnableWebSocket;  
    import org.springframework.web.socket.config.annotation.WebSocketConfigurer;  
    import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;  
    import org.springframework.web.socket.handler.TextWebSocketHandler;  
      
    @Configuration  
    @EnableWebSocket  
    public class WebSocketConfig implements WebSocketConfigurer {  
        @Override  
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {  
            registry.addHandler(chatMessageHandler(),"/webSocketServer").addInterceptors(new ChatHandshakeInterceptor());  
            registry.addHandler(chatMessageHandler(), "/sockjs/webSocketServer").addInterceptors(new ChatHandshakeInterceptor()).withSockJS();  
        }  
       
        @Bean  
        public TextWebSocketHandler chatMessageHandler(){  
            return new ChatMessageHandler();  
        }  
      
    }  
    import java.util.Map;  
    import org.apache.shiro.SecurityUtils;  
    import org.springframework.http.server.ServerHttpRequest;  
    import org.springframework.http.server.ServerHttpResponse;  
    import org.springframework.web.socket.WebSocketHandler;  
    import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;  
      
    public class ChatHandshakeInterceptor extends HttpSessionHandshakeInterceptor {  
      
        @Override  
        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,  
                Map<String, Object> attributes) throws Exception {  
            System.out.println("Before Handshake");  
            /* 
             * if (request instanceof ServletServerHttpRequest) { 
             * ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) 
             * request; HttpSession session = 
             * servletRequest.getServletRequest().getSession(false); if (session != 
             * null) { //使用userName区分WebSocketHandler,以便定向发送消息 String userName = 
             * (String) session.getAttribute(Constants.SESSION_USERNAME); if 
             * (userName==null) { userName="default-system"; } 
             * attributes.put(Constants.WEBSOCKET_USERNAME,userName); 
             *  
             * } } 
             */  
      
            //使用userName区分WebSocketHandler,以便定向发送消息(使用shiro获取session,或是使用上面的方式)  
            String userName = (String) SecurityUtils.getSubject().getSession().getAttribute(Constants.SESSION_USERNAME);  
            if (userName == null) {  
                userName = "default-system";  
            }  
            attributes.put(Constants.WEBSOCKET_USERNAME, userName);  
      
            return super.beforeHandshake(request, response, wsHandler, attributes);  
        }  
      
        @Override  
        public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,  
                Exception ex) {  
            System.out.println("After Handshake");  
            super.afterHandshake(request, response, wsHandler, ex);  
        }  
      
    }  
    import java.io.IOException;  
    import java.util.ArrayList;  
    import org.apache.log4j.Logger;  
    import org.springframework.web.socket.CloseStatus;  
    import org.springframework.web.socket.TextMessage;  
    import org.springframework.web.socket.WebSocketSession;  
    import org.springframework.web.socket.handler.TextWebSocketHandler;  
      
    public class ChatMessageHandler extends TextWebSocketHandler {  
      
        private static final ArrayList<WebSocketSession> users;// 这个会出现性能问题,最好用Map来存储,key用userid  
        private static Logger logger = Logger.getLogger(ChatMessageHandler.class);  
      
        static {  
            users = new ArrayList<WebSocketSession>();  
        }  
      
        /** 
         * 连接成功时候,会触发UI上onopen方法 
         */  
        @Override  
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {  
            System.out.println("connect to the websocket success......");  
            users.add(session);  
            // 这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户  
            // TextMessage returnMessage = new TextMessage("你将收到的离线");  
            // session.sendMessage(returnMessage);  
        }  
      
        /** 
         * 在UI在用js调用websocket.send()时候,会调用该方法 
         */  
        @Override  
        protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {  
            sendMessageToUsers(message);  
            //super.handleTextMessage(session, message);  
        }  
      
        /** 
         * 给某个用户发送消息 
         * 
         * @param userName 
         * @param message 
         */  
        public void sendMessageToUser(String userName, TextMessage message) {  
            for (WebSocketSession user : users) {  
                if (user.getAttributes().get(Constants.WEBSOCKET_USERNAME).equals(userName)) {  
                    try {  
                        if (user.isOpen()) {  
                            user.sendMessage(message);  
                        }  
                    } catch (IOException e) {  
                        e.printStackTrace();  
                    }  
                    break;  
                }  
            }  
        }  
      
        /** 
         * 给所有在线用户发送消息 
         * 
         * @param message 
         */  
        public void sendMessageToUsers(TextMessage message) {  
            for (WebSocketSession user : users) {  
                try {  
                    if (user.isOpen()) {  
                        user.sendMessage(message);  
                    }  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
      
        @Override  
        public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {  
            if (session.isOpen()) {  
                session.close();  
            }  
            logger.debug("websocket connection closed......");  
            users.remove(session);  
        }  
      
        @Override  
        public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {  
            logger.debug("websocket connection closed......");  
            users.remove(session);  
        }  
      
        @Override  
        public boolean supportsPartialMessages() {  
            return false;  
        }  
      
    }  
    <script type="text/javascript" src="http://localhost:8080/Bank/js/sockjs-0.3.min.js"></script>  
        <script>  
            var websocket;  
          
            if ('WebSocket' in window) {  
                websocket = new WebSocket("ws://" + document.location.host + "/Bank/webSocketServer");  
            } else if ('MozWebSocket' in window) {  
                websocket = new MozWebSocket("ws://" + document.location.host + "/Bank/webSocketServer");  
            } else {  
                websocket = new SockJS("http://" + document.location.host + "/Bank/sockjs/webSocketServer");  
            }  
          
            websocket.onopen = function(evnt) {};  
            websocket.onmessage = function(evnt) {  
                $("#test").html("(<font color='red'>" + evnt.data + "</font>)")  
            };  
          
            websocket.onerror = function(evnt) {};  
            websocket.onclose = function(evnt) {}  
          
            $('#btn').on('click', function() {  
                if (websocket.readyState == websocket.OPEN) {  
                    var msg = $('#id').val();  
                    //调用后台handleTextMessage方法  
                    websocket.send(msg);  
                } else {  
                    alert("连接失败!");  
                }  
            });  
        </script>  

     ps: 导入socketjs时要使用地址全称,并且连接使用的是http而不是websocket的ws

    可以使用Web Socket高性能的实现站内信需求!

  • 相关阅读:
    209. Minimum Size Subarray Sum
    208. Implement Trie (Prefix Tree)
    207. Course Schedule
    206. Reverse Linked List
    205. Isomorphic Strings
    204. Count Primes
    203. Remove Linked List Elements
    201. Bitwise AND of Numbers Range
    199. Binary Tree Right Side View
    ArcGIS API for JavaScript 4.2学习笔记[8] 2D与3D视图同步
  • 原文地址:https://www.cnblogs.com/ijavanese/p/9630122.html
Copyright © 2011-2022 走看看