aboutsummaryrefslogtreecommitdiff
path: root/files/zh-tw/web/api/websockets_api/writing_websocket_client_applications/index.html
blob: e5abbd7c9e438459addbe1225be72b8757c24605 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
---
title: 製作 WebSocket 客戶端應用程式
slug: Web/API/WebSockets_API/Writing_WebSocket_client_applications
tags:
  - WebSockets
translation_of: Web/API/WebSockets_API/Writing_WebSocket_client_applications
original_slug: WebSockets/Writing_WebSocket_client_applications
---
<p>{{ draft() }}</p>

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

<div class="note"><strong>注:</strong>當我們的系統架構可以寄存 WebSocket 範例之後,我們會提供聊天/伺服器系統實例的幾個範例。</div>

<h2 id="哪裡有_WebSocket">哪裡有 WebSocket</h2>

<p>若 JavaScript 代碼的範疇是 {{ domxref("Window") }} 物件或是實作 {{ domxref("WorkerUtils") }} 的物件,則可使用 WebSocket API。也就是可以從 Web Workers 使用 WebSocket。</p>

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

<h2 id="建立一個_WebSocket_物件">建立一個 WebSocket 物件</h2>

<p>你必須建立一個 <a href="/zh_tw/WebSockets/WebSockets_reference/WebSocket" title="zh tw/WebSockets/WebSockets reference/WebSocket"><code>WebSocket</code></a> 物件才能讓瀏覽器/伺服器得以以 WebSocket 協定進行通訊,此物件在被建立之後會自動與伺服器連線。</p>

<div class="note"><strong>注:</strong>別忘記在 Firefox 6.0 中 <code>WebSocket</code> 物件仍有前輟,所以在這裡須改成 <code>MozWebSocket</code></div>

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

<pre>WebSocket WebSocket(
  in DOMString url,
  in optional DOMString protocols
);

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString[] protocols
);
</pre>

<dl>
 <dt><code>url</code></dt>
 <dd>連線用的 URL,WebSocket 伺服器會回應這個 URL。<br>
 <br>
 根據網際網路工程任務小組(Internet Engineering Task Force,IETF)定義之規範, URL 的協議類型必須是 <code>ws://</code> (非加密連線)或是 <code>wss://</code> (加密連線)</dd>
 <dt><code>protocols</code> {{ optional_inline() }}</dt>
 <dd>一個表示協定的字串或者是一個表示協定的字串構成的陣列。這些字串可以用來指定子協定,因此一個伺服器可以實作多個 WebSocket 子協定(舉例來說,你可以讓一個伺服器處理不同種類的互動情形,各情形以 <code>protocol</code> 分別)。若不指定協定字串則預設值為空字串。</dd>
</dl>

<p>此建構方法可能拋出以下例外:</p>

<dl>
 <dt><code>SECURITY_ERR</code></dt>
 <dd>連線使用的埠被阻擋。</dd>
</dl>

<h3 id="範例">範例</h3>

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

<pre>var mySocket = new WebSocket("<span class="plain">ws://www.example.com/socketserver</span>", "my-custom-protocol");
</pre>

<p>回傳之後,<code>mySocket</code><code>readyState</code> 會變成 <code>CONNECTING</code>。當連線已可以傳輸資料時 <code>readyState</code> 會變成 <code>OPEN</code></p>

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

<pre>var mySocket = new WebSocket("<span class="plain">ws://www.example.com/socketserver</span>", ["protocol1", "protocol2"]);
</pre>

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

<h2 id="傳資料給伺服器">傳資料給伺服器</h2>

<p>連線開啟之後即可開始傳資料給伺服器。呼叫 <code>WebSocket</code><a href="/zh_tw/WebSockets/WebSockets_reference/WebSocket#send()" title="zh tw/WebSockets/WebSockets reference/WebSocket#send()"><code>send()</code></a> 來發送訊息:</p>

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

<p>可以被傳送的內容包括字串、<a href="/en/DOM/Blob" title="en/DOM/Blob"><code>Blob</code></a> 或是 <a href="/zh_tw/JavaScript_typed_arrays/ArrayBuffer" title="zh tw/JavaScript typed arrays/ArrayBuffer"><code>ArrayBuffer</code></a></p>

<div class="note"><strong>注:</strong>Firefox 目前只支援字串傳送。</div>

<h3 id="用_JSON_傳輸物件">用 JSON 傳輸物件</h3>

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

<pre class="brush: 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 = "";
}
</pre>

<p>這份代碼先建立一個物件:<code>msg</code>,它包含伺服器處理訊息所需的種種資訊,然後呼叫 <a href="/en/JavaScript/Reference/Global_Objects/JSON/stringify" title="en/JavaScript/Reference/Global Objects/JSON/stringify"><code>JSON.stringify()</code></a> 使該物件轉換成 JSON 格式並呼叫 WebSocket 的 <a href="/zh_tw/WebSockets/WebSockets_reference/WebSocket#send()" title="zh tw/WebSockets/WebSockets reference/WebSocket#send()"><code>send()</code></a> 方法來傳輸資料至伺服器。</p>

<h2 id="從伺服器接收訊息">從伺服器接收訊息</h2>

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

<pre class="brush: js">mySocket.onmessage = function(e) {
  console.log(e.data);
}
</pre>

<h3 id="接收並解讀_JSON_物件">接收並解讀 JSON 物件</h3>

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

<ul>
 <li>登入握手</li>
 <li>訊息文字</li>
 <li>更新使用者清單</li>
</ul>

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

<pre class="brush: 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 = "&lt;b&gt;使用者 &lt;em&gt;" + msg.name + "&lt;/em&gt; 登入於 " + timeStr + "&lt;/b&gt;&lt;br&gt;";
      break;
    case "message":
      text = "(" + timeStr + ") &lt;b&gt;" + msg.name + "&lt;/b&gt;: " + msg.text + "&lt;br&gt;";
      break;
    case "rejectusername":
      text = "&lt;b&gt;由於你選取的名字已被人使用,你的使用者名稱已被設置為 &lt;em&gt;" + msg.name + "&lt;/em&gt;。";
      break;
    case "userlist":
      var ul = "";
      for (i=0; i &lt; msg.users.length; i++) {
        ul += msg.users[i] + "&lt;br&gt;";
      }
      document.getElementById("userlistbox").innerHTML = ul;
      break;
  }

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

<p>這裡我們使用 <a href="/en/JavaScript/Reference/Global_Objects/JSON/parse" title="en/JavaScript/Reference/Global Objects/JSON/parse"><code>JSON.parse()</code></a> 使 JSON 物件轉換成原來的物件,檢驗並根據內容採取行動。</p>

<h2 id="關閉連線">關閉連線</h2>

<p>當你想結束 WebSocket 連線的時候,呼叫 WebSocket 的 <a href="/zh_tw/WebSockets/WebSockets_reference/WebSocket#close()" title="zh tw/WebSockets/WebSockets reference/WebSocket#close()"><code>close()</code></a> 方法:</p>

<pre>mySocket.close();
</pre>

<h2 id="參考資料">參考資料</h2>

<p><a href="https://tools.ietf.org/html/draft-abarth-thewebsocketprotocol-01">IETF: The WebSocket protocol draft-abarth-thewebsocketprotocol-01</a></p>

<p> </p>

<p>{{ languages ( {"en": "en/WebSockets/Writing_WebSocket_client_applications"} ) }}</p>

<dl>
</dl>