製作 WebSocket 客戶端應用程式

WebSocket 是一種讓瀏覽器與伺服器進行一段互動通訊的技術。使用這項技術的 Webapp 可以直接進行即時通訊而不需要不斷對資料更改進行輪詢(polling)。

备注: 當我們的系統架構可以寄存 WebSocket 範例之後,我們會提供聊天/伺服器系統實例的幾個範例。

哪裡有 WebSocket

若 JavaScript 代碼的範疇是 Window 物件或是實作 WorkerUtils 的物件,則可使用 WebSocket API。也就是可以從 Web Workers 使用 WebSocket。

备注: WebSockets API(與底層協定)的開發還在進展中,且目前不同瀏覽器(甚至瀏覽器的不同版本)有很多兼容問題。

建立一個 WebSocket 物件

你必須建立一個 WebSocket 物件才能讓瀏覽器/伺服器得以以 WebSocket 協定進行通訊,此物件在被建立之後會自動與伺服器連線。

备注: 別忘記在 Firefox 6.0 中 WebSocket 物件仍有前輟,所以在這裡須改成 MozWebSocket

WebSocket 的建構方法有一個必要參數與一個選擇參數:

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString protocols
);

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString[] protocols
);
url

連線用的 URL,WebSocket 伺服器會回應這個 URL。

根據網際網路工程任務小組(Internet Engineering Task Force,IETF)定義之規範, URL 的協議類型必須是 ws:// (非加密連線)或是 wss:// (加密連線)

protocols 選擇性

一個表示協定的字串或者是一個表示協定的字串構成的陣列。這些字串可以用來指定子協定,因此一個伺服器可以實作多個 WebSocket 子協定(舉例來說,你可以讓一個伺服器處理不同種類的互動情形,各情形以 protocol 分別)。若不指定協定字串則預設值為空字串。

此建構方法可能拋出以下例外:

SECURITY_ERR

連線使用的埠被阻擋。

範例

此簡單範例建立了一個新的 WebSocket,連到位於 http://www.example.com/socketserver 的伺服器。指定的子協定是 "my-custom-protocol"。

var mySocket = new WebSocket("ws://www.example.com/socketserver", "my-custom-protocol");

回傳之後,mySocketreadyState 會變成 CONNECTING。當連線已可以傳輸資料時 readyState 會變成 OPEN

要建立一個連線但不指定單一個特定協定,可以指定一個協定構成的陣列:

var mySocket = new WebSocket("ws://www.example.com/socketserver", ["protocol1", "protocol2"]);

當連線建立的時候(也就是 readyState 變成而 OPEN 的時候),protocol 屬性會回傳伺服器選擇的協定。

傳資料給伺服器

連線開啟之後即可開始傳資料給伺服器。呼叫 WebSocketsend() 來發送訊息:

mySocket.send("這是伺服器正迫切需要的文字!");

可以被傳送的內容包括字串、Blob 或是 ArrayBuffer

备注: Firefox 目前只支援字串傳送。

用 JSON 傳輸物件

有一個很方便的方法是用 JSON 傳送複雜的資料給伺服器,舉例來說,聊天程式可以設計一種協定,這個協定傳送以 JSON 封裝的資料封包:

js
// 透過伺服器傳送文字給所有使用者

function sendText() {
  var msg = {
    type: "message",
    text: document.getElementById("text").value,
    id: clientID,
    date: Date.now(),
  };

  mySocket.send(JSON.stringify(msg));
  document.getElementById("text").value = "";
}

這份代碼先建立一個物件:msg,它包含伺服器處理訊息所需的種種資訊,然後呼叫 JSON.stringify() 使該物件轉換成 JSON 格式並呼叫 WebSocket 的 send() 方法來傳輸資料至伺服器。

從伺服器接收訊息

WebSockets 是一個事件驅動 API,當瀏覽器收到訊息時,一個「message」事件被傳入 onmessage 函數。使用以下方法開始傾聽傳入資料:

js
mySocket.onmessage = function (e) {
  console.log(e.data);
};

接收並解讀 JSON 物件

考慮先前在「用 JSON 傳輸物件」談起的聊天應用程式。客戶端可能收到的資料封包有幾種:

  • 登入握手
  • 訊息文字
  • 更新使用者清單

用來解讀傳入訊息的代碼可能像是:

js
connection.onmessage = function (evt) {
  var f = document.getElementById("chatbox").contentDocument;
  var text = "";
  var msg = JSON.parse(evt.data);
  var time = new Date(msg.date);
  var timeStr = time.toLocaleTimeString();

  switch (msg.type) {
    case "id":
      clientID = msg.id;
      setUsername();
      break;
    case "username":
      text =
        "<b>使用者 <em>" + msg.name + "</em> 登入於 " + timeStr + "</b><br>";
      break;
    case "message":
      text = "(" + timeStr + ") <b>" + msg.name + "</b>: " + msg.text + "<br>";
      break;
    case "rejectusername":
      text =
        "<b>由於你選取的名字已被人使用,你的使用者名稱已被設置為 <em>" +
        msg.name +
        "</em>。";
      break;
    case "userlist":
      var ul = "";
      for (i = 0; i < msg.users.length; i++) {
        ul += msg.users[i] + "<br>";
      }
      document.getElementById("userlistbox").innerHTML = ul;
      break;
  }

  if (text.length) {
    f.write(text);
    document.getElementById("chatbox").contentWindow.scrollByPages(1);
  }
};

這裡我們使用 JSON.parse() 使 JSON 物件轉換成原來的物件,檢驗並根據內容採取行動。

關閉連線

當你想結束 WebSocket 連線的時候,呼叫 WebSocket 的 close() 方法:

mySocket.close();

參考資料