WebSocket 介紹(二)

轉載自:https://www.cnblogs.com/stoneniqiu/p/5373993.html

這一章介紹如何用WebSocket API來控制協議和創建應用,運用http://websocket.org 提供的現有WebSocket服務器,我們可以收發消息、創建一些簡單的WebSocket應用。一步一步的學習使用WebSocket API,最後我們會討論瀏覽器的支持度和連通性。這一章的重點是WebSocket 協議在Web客戶端的應用,在稍後的章節會介紹WebSocket協議以及其使用環境。

綜述:

     正如第一章提到的,WebSocket包含網絡協議和API,讓你能夠在客戶端和服務端創建WebSocket連接,第三章會詳細討論協議的細節,我們先看一下API。

WebSocket API其實就是一個使用WebSocket協議的接口,通過它來建立全雙工通道來收發消息,簡單易學,要連接遠程服務器,只需要創建一個WebSocket對象實體,並傳入一個服務端的URL。在客戶端和服務端一開始握手的期間,http協議升級到WebSocket協議就建立了連接,底層都是TCP協議。一旦建立連接,通過WebSocket接口可以反覆的發送消息。在你的代碼裏面,你可以使用異步事件監聽連接生命週期的每個階段。

WebSocket API是純事件驅動,一旦建立全雙工連接,當服務端給客戶端發送數據或者資源,它能自動發送狀態改變的數據和通知。所以你不需要爲了狀態的更新而去輪訓Server,在客戶端監聽即可。在後續的章節我們會介紹更高級的協議,例如STOMP和XMPP,會學習不同的WebSocket API使用例子,然而現在,先仔細看看API。

 入門:

     首先,我們需要通過調用WebSocket構造函數來創建一個WebSocket連接,構造函數會返回一個WebSocket實例,可以用來監聽事件。這些事件會告訴你什麼時候連接建立,什麼時候消息到達,什麼時候連接關閉了,以及什麼時候發生了錯誤。WebSocket協議定義了兩種URL方案,WS和WSS分別代表了客戶端和服務端之間未加密和加密的通信。WS(WebSocket)類似於Http URL,而WSS(WebSocket Security)URL 表示連接是基於安全傳輸層(TLS/SSL)和https的連接是同樣的安全機制。

WebSocket的構造函數需要一個URL參數和一個可選的協議參數(一個或者多個協議的名字),協議的參數例如XMPP(Extensible Messaging and Presence Protocol)、SOAP(Simple Object Access Protocol)或者自定義協議。而URL參數需要以WS://或者WSS://開頭,例如:ws://www.websocket.org,如果URL有語法錯誤,構造函數會拋出異常。

// Create new WebSocket connection
var ws = new WebSocket("ws://www.websocket.org");
//測試了下鏈接不上。

     第二個參數是協議名稱,是可選的,服務端和客服端使用的協議必須一致,這樣收發消息彼此才能理解,你可以定義一個或多個客戶端使用的協議,服務端會選擇一個來使用,一個客服端和一個服務端之間只能有一個協議。當然都得基於WebSocket,WebSocket的重大好處之一就是基於WebSocket協議的廣泛使用,讓你的Web能夠擁有傳統桌面程序那樣的能力,這個我們將在第三章第六節學習到。

    言歸正傳,我們回到構造函數,在第一次握手之後,和協議的名稱一起,客戶端會發送一個Sec-WebSocket-Protocol 頭,服務端會選擇0個或一個協議,響應會帶上同樣的Sec-WebSocket-Protocol 頭,否則會關閉連接。通過協議協商(Protocol negotiation ),我們可以知道給定的WebSocket服務器所支持的協議和版本,然後應用選擇協議使用。

// Connecting to the server with one protocol called myProtocol
var ws = new WebSocket("ws://echo.websocket.org", "myProtocol");
//myProtocol 是假設的一個定義好的且符合標準的協議。

你可以傳遞一個協議的數組。

var echoSocket = new WebSocket("ws://echo.websocket.org", ["com.kaazing.echo","example.imaginary.protocol"])
//服務端會選擇其中一個使用
echoSocket.onopen = function(e) {
// Check the protocol chosen by the server
console.log(echoSocket.protocol);
}

輸出:com.kaazing.echo

協議這個參數有三種。

1.註冊協議:根據RFC6455(WebSocket 協議)和IANA被官方註冊的標準協議。例如 微軟的SOAP。

詳情可以參考:http://www.iana.org/assignments/websocket/websocket.xml 

 

看到兩個華爲的

2.開放協議:被廣泛使用的標註協議,例如XMPP和STOMP。但沒有被正式註冊。

3.自定義協議:自己編寫和使用的WebSocket的協議。

協議會再後續章節給出詳細介紹,下面先看事件、對象和方法以及實例。

WebSocket事件:

     WebSocket API是純事件驅動,通過監聽事件可以處理到來的數據和改變的鏈接狀態。客戶端不需要爲了更新數據而輪訓服務器。服務端發送數據後,消息和事件會異步到達。WebSocket編程遵循一個異步編程模型,只需要對WebSocket對象增加回調函數就可以監聽事件。你也可以使用addEventListener()方法來監聽。而一個WebSocket對象分四類不同事件。

open:

     一旦服務端響應WebSocket連接請求,就會觸發open事件。響應的回調函數稱爲onopen。

// Event handler for the WebSocket connection opening
ws.onopen = function(e) {
console.log("Connection open...");
};

     open事件觸發的時候,意味着協議握手結束,WebSocket已經準備好收發數據。如果你的應用收到open事件,就可以確定服務端已經處理了建立連接的請求,且同意和你的應用通信。

Message:

當消息被接受會觸發消息事件,響應的回調函數叫做onmessage。如下:

// 接受文本消息的事件處理實例:
ws.onmessage = function(e) {
if(typeof e.data === "string"){
console.log("String message received", e, e.data);
} else {
console.log("Other message received", e, e.data);
}
};

除了文本消息,WebSocket消息機制還能處理二進制數據,有Blob和ArrayBuffer兩種類型,在讀取到數據之前需要決定好數據的類型。

// 設置二進制數據類型爲blob(默認類型)
ws.binaryType = "blob";
// Event handler for receiving Blob messages
ws.onmessage = function(e) {
if(e.data instanceof Blob){
console.log("Blob message received", e.data);
var blob = new Blob(e.data);
}
};

 

//ArrayBuffer
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
if(e.data instanceof ArrayBuffer){
console.log("ArrayBuffer Message Received", + e.data);
// e.data即ArrayBuffer類型
var a = new Uint8Array(e.data);
}
};

Error

      如果發生意外的失敗會觸發error事件,相應的函數稱爲onerror,錯誤會導致連接關閉。如果你收到一個錯誤事件,那麼你很快會收到一個關閉事件,在關閉事件中也許會告訴你錯誤的原因。而對錯誤事件的處理比較適合做重連的邏輯。

//異常處理
ws.onerror = function(e) {
console.log("WebSocket Error: " , e);
//Custom function for handling errors
handleErrors(e);
};

Close

      不言而喻,當連接關閉的時候回觸發這個事件,對應onclose方法,連接關閉之後,服務端和客戶端就不能再收發消息。

WebSocket的規範其實還定義了ping和pong 架構(frames),可以用來做keep-alive,心跳,網絡狀態查詢,latency instrumentation(延遲儀表?),但是目前 WebSocket API還沒有公佈這些特性,儘管瀏覽器支持了ping,但不會觸發ping事件,相反,瀏覽器會自動響應pong,第八章會將更多關於ping和pong的細節。

 當然你可以調用close方法斷開與服務端的鏈接來觸發onclose事件,

ws.onclose = function(e) {
console.log("Connection closed", e);
};

      連接失敗和成功的關閉握手都會觸發關閉事件,WebSocket的對象的readyState屬性就代表連接的狀態(2代表正在關閉,3代表已經關閉)。關閉事件有三個屬性可以用來做異常處理和重獲: wasClean,code和reason。wasClean是一個bool值,代表連接是否乾淨的關閉。   如果是響應服務端的close事件,這個值爲true,如果是別的原因,比如因爲是底層TCP連接關閉,wasClean爲false。code和reason代表關閉連接時服務端發送的狀態,這兩個屬性和給入close方法的code和reason參數是對應的,稍後會描述細節。

 WebSocket 方法:

 WebSocket 對象有兩個方法:send()和close()

 send():

一旦在服務端和客戶端建立了全雙工的雙向連接,可以使用send方法去發送消息,

//發送一個文本消息
ws.send("Hello WebSocket!");

當連接是open的時候send()方法傳送數據,當連接關閉或獲取不到的時候回拋出異常。一個通常的錯誤是人們喜歡在連接open之前發送消息。如下所示:

// 這將不會工作
var ws = new WebSocket("ws://echo.websocket.org")
ws.send("Initial data");

正確的姿勢如下,應該等待open事件觸發後再發送消息。

var ws = new WebSocket("ws://echo.websocket.org")
ws.onopen = function(e) {
ws.send("Initial data");
}

如果想通過響應別的事件去發送消息,可以檢查readyState屬性的值爲open的時候來實現。

function myEventHandler(data) {
if (ws.readyState === WebSocket.OPEN) {
//open的時候即可發送
ws.send(data);
} else {
// Do something else in this case.
//Possibly ignore the data or enqueue it.
}
}

發送二進制數據:

// Send a Blob
var blob = new Blob("blob contents");
ws.send(blob);
// Send an ArrayBuffer
var a = new Uint8Array([8,6,7,5,3,0,9]);
ws.send(a.buffer);

     Blob對象和JavaScript File API一起使用的時候相當有用,可以發送或接受文件,大部分的多媒體文件,圖像,視頻和音頻文件。這一章末尾會結合File API提供讀取文件內容來發送WebSocket消息的實例代碼。

close()

使用close方法來關閉連接,如果連接以及關閉,這方法將什麼也不做。調用close方法只後,將不能發送數據。

ws.close();

close方法可以傳入兩個可選的參數,code(numerical)和reason(string),以告訴服務端爲什麼終止連接。第三章講到關閉握手的時候再詳細討論這兩個參數。

// 成功結束會話
ws.close(1000, "Closing normally");
//1000是狀態碼,代表正常結束。

WebSocket 屬性

WebSocket對象有三個屬性,readyState,bufferedAmount和Protocol。

readyState:

WebSocket對象通過只讀屬性readyState來傳達連接狀態,它會更加連接狀態自動改變。下表展示了readyState屬性的四個不同的值。

屬性

狀態

WebSocket.CONNECTING

0

連接正在進行,但還沒有建立

WebSocket.OPEN

1

連接已經建立,可以發送消息。

WebSocket.CLOSING

2

連接正在進行關閉握手

WebSocket.CLOSED

3

連接已經關閉或不能打開

瞭解當前連接的狀態有助於我們調試。

bufferedAmount:

     有時候需要檢查傳輸數據的大小,尤其是客戶端傳輸大量數據的時候。雖然send()方法會馬上執行,但數據並不是馬上傳輸。瀏覽器會緩存應用流出的數據,你可以使用bufferedAmount屬性檢查已經進入隊列但還未被傳輸的數據大小。這個值不包含協議框架、操作系統緩存和網絡軟件的開銷。

下面這個例子展示瞭如何使用bufferedAmount屬性每秒更新發送。如果網絡不能處理這個頻率,它會自適應。

// 10k
var THRESHOLD = 10240;
//建立連接
var ws = new WebSocket("ws://echo.websocket.org");
// Listen for the opening event
ws.onopen = function () {
setInterval( function() {
//緩存未滿的時候發送
if (ws.bufferedAmount < THRESHOLD) {
ws.send(getApplicationState());
}
}, 1000);
};
//使用bufferedAmount屬性發送數據可以避免網絡飽和。

protocol:

      在構造函數中,protocol參數讓服務端知道客戶端使用的WebSocket協議。而WebSocket對象的這個屬性就是指的最終服務端確定下來的協議名稱,當服務端沒有選擇客戶端提供的協議或者在連接握手結束之前,這個屬性都是空的。

完整實例

     現在我們已經過了一遍WebSocket的構造函數、事件、屬性和方法,接下來通過一個完整的實例來學習WebSocket API。實例使用“Echo”服務器:ws://echo.websocket.org,它能夠接受和返回發過去的數據。這樣有助於理解WebSocket API是如何和服務器交互的。

首先,我們先建立連接,讓頁面展示客戶端連接服務端的信息,然後發送、接受消息,最後關閉連接。

  <h2>Websocket Echo Client</h2>
    <div id="output"></div>

 

     // 初始化連接和事件
        function setup() {
            output = document.getElementById("output");
            ws = new WebSocket("ws://echo.websocket.org/echo");
            // 監聽open
            ws.onopen = function (e) {
                log("Connected");
                sendMessage("Hello WebSocket!");
            }
            // 監聽close
            ws.onclose = function (e) {
                log("Disconnected: " + e.reason);
            }
            //監聽errors
            ws.onerror = function (e) {
                log("Error ");
            }
            // 監聽 messages 
            ws.onmessage = function (e) {
                log("Message received: " + e.data);
                //收到消息後關閉
                ws.close();
            }
        }
        // 發送消息
        function sendMessage(msg) {
            ws.send(msg);
            log("Message sent");
        }
        // logging
        function log(s) {
            var p = document.createElement("p");
            p.style.wordWrap = "break-word";
            p.textContent = s;
            output.appendChild(p);
            // Also log information on the javascript console
            console.log(s);
        }
        // Start 
        setup();

判斷瀏覽器是否支持:

if (window.WebSocket){
console.log("This browser supports WebSocket!");
} else {
console.log("This browser does not support WebSocket.");
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章