WebSocket的使用

由於最近學習微信小程序做聊天時,有幸涉及到WebSocket協議;在這裏我就把我認爲最好理解的方法給大家呈現下前輩的文章;

聲明參考引用的文章有:

     http://blog.csdn.net/wwd0501/article/details/54582912

     https://www.cnblogs.com/xdp-gacl/p/5193279.html

     https://www.cnblogs.com/winkey4986/p/5478332.html

首先需要了解啥什麼是WebSocket

定義:是HTML5一種新的協議。它實現了瀏覽器與服務器全雙工通信(full-duplex)。一開始的握手需要藉助HTTP請求完成。

      原理:WebSocket同HTTP一樣也是應用層的協議,但是它是一種雙向通信協議,是建立在TCP之上的。

目的:即時通訊,替代輪詢

優點:輪詢技術會導致過多不必要的請求,浪費流量和服務器資源,每一次請求、應答,都浪費了一定流量在相同的頭部信息上然而WebSocket的出現可以彌補這一缺點。在WebSocket中,只需要服務器和瀏覽器通過HTTP協議進行一個握手的動作,然後單獨建立一條TCP的通信通道進行數據的傳送。

注意:WebSocket和Socket沒有半毛錢關係;是兩個不完全不一樣的東西;Socket是位於應用層和傳輸控制層之間的一組接口;(建立網絡通信連接至少要一對端口號(socket)。socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網絡開發所用的接口);然而,WebSocket是應用層的協議。

隨着H5以及微信公衆號,小程序的快速發展;用戶的消息等待時間急劇減短;響應速度也隨之提高,這時及時消息也成爲了程序的必須,之前的輪詢機制太過浪費,所以WebSocket必定被重視;實現web端消息即時通訊的代碼:

前端代碼:

<%@ page language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>Java後端WebSocket的Tomcat實現</title>
</head>
<body>
    Welcome<br/><input id="text" type="text"/>
    <button onclick="send()">發送消息</button>
    <hr/>
    <button onclick="openWebSocket()">開啓WebSocket連接</button>
    <button onclick="closeWebSocket()">關閉WebSocket連接</button>
    <hr/>
    <div id="message"></div>
</body>

<script type="text/javascript">
(function(window){
openWebSocket_m()
})(this);
    var websocket = null;
    function openWebSocket_m(){
    //判斷當前瀏覽器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://localhost:8080/WX_auth/websocket/李");
    }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();
    }

    //將消息顯示在網頁上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }
    }
    //開啓WebSocket連接
    function openWebSocket() {
    openWebSocket_m();
    }
    //關閉WebSocket連接
    function closeWebSocket() {
        websocket.close();
    }

    //發送消息
    function send() {
        var message = ‘{To:All,message:’+document.getElementById('text').value+'}';
        websocket.send(message);
    }
</script>
</html>

後端代碼:


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 com.alibaba.fastjson.JSONObject;  

/**
 * @ServerEndpoint 註解是一個類層次的註解,它的功能主要是將目前的類定義成一個websocket服務器端,
 * 註解的值將被用於監聽用戶連接的終端訪問URL地址,客戶端可以通過這個URL來連接到WebSocket服務器端
 */
@ServerEndpoint("/websocket/{username}")  
public class WebSocket {  
   //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。
    private static int onlineCount = 0;  
    //concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。若要實現服務端與單一客戶端通信的話,可以使用Map來存放,其中Key可以爲用戶標識( 這裏就使用的Map實現一對一和一對多)

//private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();

private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();  
  //與某個客戶端的連接會話,需要通過它來給客戶端發送數據
    private Session session;

//單個連接的名稱,及其session的關鍵字
    private String username;  
      /**
     * 連接建立成功調用的方法
     * @param session  可選的參數。session爲與某個客戶端的連接會話,需要通過它來給客戶端發送數據
     */
    @OnOpen  
    public void onOpen(@PathParam("username") String username, Session session) throws IOException {  
        this.username = username;  
        this.session = session;  
        addOnlineCount();  //在線數加1
        clients.put(username, this);  //將webSocket的連接存在map中,通過用戶名來指定對應的session
        System.out.println(username+"已連接"+session.getId()+"在線人數"+ getOnlineCount());  
    }  
    /**
     * 連接關閉調用的方法
     */
    @OnClose  
    public void onClose() throws IOException {  
        clients.remove(username);  
        subOnlineCount();  //在線數減1
        System.out.println("有一連接關閉!當前在線人數爲" + getOnlineCount());
    }  
   /**
     * 收到客戶端消息後調用的方法
     * @param message 客戶端發送過來的消息
     * @param session 可選的參數
     */
    @OnMessage  
    public void onMessage(String message) throws IOException {  //message是服務器收到的消息json串;裏面包括髮給說,發的什麼

    System.out.println("來自客戶端的消息:" + message);
        JSONObject jsonTo = JSONObject.parseObject(message);  
        if (!jsonTo.get("To").equals("All")){  //一對一發
            sendMessageTo("給一個人說:"+jsonTo.get("message ").toString(), jsonTo.get("To").toString());  
        }else{  //羣發
            sendMessageAll("給所有人說:"+jsonTo.get("message ").toString());  
        }  
    }  
   /**
     * 發生錯誤時調用
     * @param session
     * @param error
     */
    @OnError  
    public void onError(Session session, Throwable error) {  
        error.printStackTrace();  
    }  
   /**
     * 發給某一個人。
     * @param message
     * @throws IOException
     */
    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(To+‘---’+message);  
        }  
    }  
     //羣發
    public void sendMessageAll(String message) throws IOException {  
        for (WebSocket item : clients.values()) {  
            item.session.getAsyncRemote().sendText(message);  
        }  
    }  
     //保證onlineCount的線程安全,使用同步鎖
    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;  
    }  
}  

最後給大家說下如何後臺主動推送消息給前端(消息推送,而非即時通訊)

第一步: 
將之前的WebSocket端點設置成靜態公共

 public static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();  


第二步: 

在服務端的某個方法中,調用該Set集合

        //羣發消息
        for(MyWebSocket item: MyWebSocket.webSocketSet){ 
                try {
                  if(message!=null&&!"".equals(message)){
                      item.sendMessage(message);
                  }
                } catch (IOException e) {
                    e.printStackTrace();
                    continue;
                }
            }
或者:直接是其類中方法onMessage

   @RequestMapping(value = "/login1", method = RequestMethod.GET)
    public void login1(ModelMap modelMap,HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String message="你好-----";
        //羣發消息
        WebSocketTest.onMessage( message, WebSocketTest.session);
    }

結果:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章