Java使用WebSocket

網頁端的消息推送,通常有如下方式:javascript

輪詢方式:客戶端定時向服務端發送ajax請求,服務器接收到請求後立刻返回消息並關閉鏈接。
優勢:後端程序編寫比較容易。
缺點:TCP的創建和關閉操做浪費時間和帶寬,請求中有大半是無用,浪費帶寬和服務器資源。
實例:適於小型應用。html

長輪詢:客戶端向服務器發送Ajax請求,服務器接到請求後hold住鏈接,直到有新消息才返回響應信息並關閉鏈接,客戶端處理完響應信息後再向服務器發送新的請求。
優勢:在無消息的狀況下不會頻繁的請求,耗費資源小。
缺點:服務器hold鏈接會消耗資源,返回數據順序無保證,難於管理維護。
實例:WebQQ、Hi網頁版、Facebook IM。java

長鏈接:在頁面裏嵌入一個隱蔵iframe,將這個隱蔵iframe的src屬性設爲對一個長鏈接的請求或是採用xhr請求,服務器端就能源源不斷地往客戶端輸入數據。
優勢:消息即時到達,不發無用請求;管理起來也相對方便。
缺點:服務器維護一個長鏈接會增長開銷,當客戶端愈來愈多的時候,server壓力大!
實例:Gmail聊天web

Flash Socket:在頁面中內嵌入一個使用了Socket類的 Flash 程序JavaScript經過調用此Flash程序提供的Socket接口與服務器端的Socket接口進行通訊,JavaScript在收到服務器端傳送的信息後控制頁面的顯示。
優勢:實現真正的即時通訊,而不是僞即時。
缺點:客戶端必須安裝Flash插件,移動端支持很差,IOS系統中沒有flash的存在;非HTTP協議,沒法自動穿越防火牆。
實例:網絡互動遊戲。ajax

webSocket:HTML5 WebSocket設計出來的目的就是取代輪詢和長鏈接,使客戶端瀏覽器具有像C/S框架下桌面系統的即時通信能力,實現了瀏覽器和服務器全雙工通訊,創建在TCP之上,雖然WebSocket和HTTP同樣經過TCP來傳輸數據,但WebSocket能夠主動的向對方發送或接收數據,就像Socket同樣;而且WebSocket須要相似TCP的客戶端和服務端經過握手鍊接,鏈接成功後才能互相通訊。
優勢:雙向通訊、事件驅動、異步、使用ws或wss協議的客戶端可以真正實現意義上的推送功能。
缺點:少部分瀏覽器不支持。
示例:社交聊天(微信、QQ)、彈幕、多玩家玩遊戲、協同編輯、股票基金實時報價、體育實況更新、視頻會議/聊天、基於位置的應用、在線教育、智能家居等高實時性的場景。

Java實現Web Socket有兩種方式:spring

一、Tomcat WebSocket @ServerEndpointapache

須要Tomcat7,Java7以上的支持json

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();  
}  

 

二、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高性能的實現站內信需求!

相關文章
相關標籤/搜索