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高性能的实现站内信需求!

  • 相关阅读:
    NET Attribute
    net core HttpClient
    Nuget服务器
    JS-防抖节流
    Codeforces,Topcoder,SGU,Timus,ProjectEuler
    并发编程,高速缓存,原子操作,指令重排序
    C编译器的编译过程主要分成四步: (1) 预处理 (2) 编译 (3) 汇编 (4) 连接
    C#--Distinct
    PageRank算法的思想
    ML.NET 发布0.11版本:.NET中的机器学习,为TensorFlow和ONNX添加了新功能
  • 原文地址:https://www.cnblogs.com/ijavanese/p/9630122.html
Copyright © 2011-2022 走看看