WebSocket 介紹(二)-WebSocket API

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

綜述:java

     正如第一章提到的,WebSocket包含網絡協議和API,讓你可以在客戶端和服務端建立WebSocket鏈接,第三章會詳細討論協議的細節,咱們先看一下API。
WebSocket API其實就是一個使用WebSocket協議的接口,經過它來創建全雙工通道來收發消息,簡單易學,要鏈接遠程服務器,只須要建立一個WebSocket對象實體,並傳入一個服務端的URL。在客戶端和服務端一開始握手的期間,http協議升級到WebSocket協議就創建了鏈接,底層都是TCP協議。一旦創建鏈接,經過WebSocket接口能夠反覆的發送消息。在你的代碼裏面,你可使用異步事件監聽鏈接生命週期的每一個階段。
WebSocket API是純事件驅動,一旦創建全雙工鏈接,當服務端給客戶端發送數據或者資源,它能自動發送狀態改變的數據和通知。因此你不須要爲了狀態的更新而去輪訓Server,在客戶端監聽便可。在後續的章節咱們會介紹更高級的協議,例如STOMP和XMPP,會學習不一樣的WebSocket API使用例子,然而如今,先仔細看看API。

 入門:web

     首先,咱們須要經過調用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。
 
看到兩個華爲的
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()websocket

 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.");
}
相關文章
相關標籤/搜索