blob: 16ca1dcc5cf436f068c8d098a28ea4f1d8aa7638 (
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
181
182
183
184
185
186
|
---
title: 编写WebSocket客户端应用
slug: Web/API/WebSockets_API/Writing_WebSocket_client_applications
tags:
- WebSocket
- 指南
translation_of: Web/API/WebSockets_API/Writing_WebSocket_client_applications
---
<p>WebSocket客户端应用程序使用WebSocket API通过WebSocket协议与WebSocket服务器通信。</p>
<p>{{AvailableInWorkers}}</p>
<div class="warning">
<p>本文中的示例代码片段来自我们的 WebSocket 聊天应用示例,<a href="https://github.com/mdn/samples-server/tree/master/s/websocket-chat">源代码在此处</a>,then <a href="https://mdn-samples.mozilla.org/s/websocket-chat">也可以在这里试一试</a>。现在示例中有一个 bug,使用不安全的 WebSockets 连接需要更新使用安全的 WebSocket,我们将很快修复。</p>
</div>
<h2 id="创建_WebSocket_对象">创建 WebSocket 对象</h2>
<p>为了使用 WebSocket 协议通信,你需要创建一个 <a href="/en/WebSockets/WebSockets_reference/WebSocket" title="en/WebSockets/WebSockets reference/WebSocket"><code>WebSocket</code></a> 对象;这将会自动地尝试建立与服务器的连接。</p>
<p>WebSocket 构造函数接受一个必要参数和一个可选参数:</p>
<pre class="syntaxbox">WebSocket WebSocket(
in DOMString url,
in optional DOMString protocols
);
</pre>
<dl>
<dt><code>url</code></dt>
<dd>要连接的URL;这应当是 WebSocket 服务器会响应的URL。</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>
<dl>
</dl>
<h3 id="连接错误">连接错误</h3>
<p>如果尝试连接过程中发生错误,那么首先一个名为 "error" 的事件会被发送给 <a href="/en/WebSockets/WebSockets_reference/WebSocket" title="WebSocket"><code>WebSocket</code></a> 对象(然后调用其<code>onerror</code> handler),然后 <a href="/en/WebSockets/WebSockets_reference/CloseEvent" title="CloseEvent"><code>CloseEvent</code></a> 被发送给<a href="/en/WebSockets/WebSockets_reference/WebSocket" title="WebSocket"><code>WebSocket</code></a> (然后调用其 <code>onclose</code> handler)以说明连接关闭的原因。</p>
<p>在 Firefox 11 中,通常会从 Mozilla 平台的控制台中收到一个描述性的错误信息,以及一个通过 <code><a href="/en/WebSockets/WebSockets_reference/CloseEvent" title="CloseEvent">CloseEvent</a></code> 在 <a class="external" href="http://tools.ietf.org/html/rfc6455#section-7.4" title="RFC 6455 Section 7.4">RFC 6455, Section 7.4</a> 中定义的错误代码。</p>
<h3 id="示例">示例</h3>
<p>本例创建了一个新的 WebSocket,连接到地址为 <code><span class="nowiki">ws://www.example.com/socketserver</span></code> 的服务器。请求中命名了一个自定义的协议 "protocolOne",这一部分可以省略。</p>
<pre class="brush: js">var exampleSocket = new WebSocket("ws://www.example.com/socketserver", "protocolOne");
</pre>
<p>返回后,<code>exampleSocket.readyState</code> 参数为 <code>CONNECTING</code>。一旦连接可以传送数据,<code>readyState</code> 就会变成 <code>OPEN</code> 。</p>
<p>如果你想建立一个支持协议可选的连接,你可以指定协议的列表:</p>
<pre class="brush: js">var exampleSocket = new WebSocket("ws://www.example.com/socketserver", ["protocolOne", "protocolTwo"]);
</pre>
<p>一旦连接建立了(也就是说 <code>readyState</code> 是 <code>OPEN</code>) <code>exampleSocket.protocol</code> 就会告诉你服务器选择了哪个协议。</p>
<p>上面的例子中 <code>ws</code> 替代了 <code>http</code>,同样地 <code>wss 也会替代https</code>. 建立WebSocket链接有赖于 <a href="/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism">HTTP Upgrade mechanism</a>, 所以当我们使用 <code><span class="nowiki">ws://www.example.com</span></code>或者 <code><span class="nowiki">wss://www.example.com</span></code>来访问HTTP服务器的时候协议会隐式地升级。</p>
<h2 id="向服务器发送数据">向服务器发送数据</h2>
<p>一旦你的连接打开完成,你就可以向服务器发送数据了。对每一个要发送的消息使用 <code>WebSocket</code> 对象的 <a href="/en/WebSockets/WebSockets_reference/WebSocket#send()" title="en/WebSockets/WebSockets reference/WebSocket#send()"><code>send()</code></a> 方法:</p>
<pre class="brush: js">exampleSocket.send("Here's some text that the server is urgently awaiting!");
</pre>
<p>你可以把数据作为字符串,{{ domxref("Blob") }},或者<a href="/en/JavaScript_typed_arrays/ArrayBuffer" title="en/JavaScript typed arrays/ArrayBuffer"><code>ArrayBuffer</code></a>来发送。</p>
<div class="note"><strong>注意:</strong> 在版本11之前,Firefox只支持以字符串的形式发送数据。</div>
<p>因为连接的建立是异步的,而且容易失败,所以不能保证刚创建WebSocket对象时使用 <code>send()</code> 方法会成功。我们至少可以确定企图在链接建立起来之后立马发送数据,可以通过注册 <code>onopen</code> 事件处理器解决:</p>
<pre class="brush: js">exampleSocket.onopen = function (event) {
exampleSocket.send("Here's some text that the server is urgently awaiting!");
};
</pre>
<h3 id="使用_JSON_发送对象">使用 JSON 发送对象</h3>
<p>你可以方便地使用<a href="/en/JSON" title="en/JSON">JSON</a> 来向服务器发送复杂一些的数据。例如一个聊天程序与服务器交互的协议可以通过封装在JSON里的数据来实现:</p>
<pre class="brush: js">// 服务器向所有用户发送文本
function sendText() {
// 构造一个 msg 对象, 包含了服务器处理所需的数据
var msg = {
type: "message",
text: document.getElementById("text").value,
id: clientID,
date: Date.now()
};
// 把 msg 对象作为JSON格式字符串发送
exampleSocket.send(JSON.stringify(msg));
// 清空文本输入元素,为接收下一条消息做好准备。
document.getElementById("text").value = "";
}
</pre>
<h2 id="接收服务器发送的消息">接收服务器发送的消息</h2>
<p>WebSockets 是一个基于事件的 API;收到消息的时候,一个 "message" 消息会被发送到 <code>onmessage</code> 函数。为了开始监听传入数据,可以进行如下操作:</p>
<pre class="brush: js">exampleSocket.onmessage = function (event) {
console.log(event.data);
}
</pre>
<h3 id="接受与解析_JSON_对象">接受与解析 JSON 对象</h3>
<p>考虑在 <a href="#使用_json_发送对象">使用 JSON 发送对象</a>中提到的聊天应用客户端。客户端会收到各种类型的数据包,比如:</p>
<ul>
<li>登陆握手</li>
<li>消息文本</li>
<li>用户列表更新</li>
</ul>
<p>解析这些收到的消息的代码可能是这样的:</p>
<pre><code>exampleSocket.onmessage = function(event) {
var f = document.getElementById("chatbox").contentDocument;
var text = "";
var msg = JSON.parse(event.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>User <em>" + msg.name + "</em> signed in at " + timeStr + "</b><br>";
break;
case "message":
text = "(" + timeStr + ") <b>" + msg.name + "</b>: " + msg.text + "<br>";
break;
case "rejectusername":
text = "<b>Your username has been set to <em>" + msg.name + "</em> because the name you chose is in use.</b><br>"
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);
}
};</code></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>
<h3 id="文本数据的格式">文本数据的格式</h3>
<p>通过WebSocket连接收到的文本是 UTF-8 格式的。</p>
<p>在 Gecko 9.0 {{ geckoRelease("9.0") }} 之前,一部分有效的 UTF-8 文本中的非字符将导致连接被中断。现在 Gecko 已经允许这些值。</p>
<h2 id="关闭连接">关闭连接</h2>
<p>当你不需要再用 WebSocket 连接了,调用 WebSocket <a href="/en/WebSockets/WebSockets_reference/WebSocket#close()" title="en/WebSockets/WebSockets reference/WebSocket#close()"><code>close()</code></a>方法:</p>
<pre class="brush: js">exampleSocket.close();
</pre>
<p>关闭连接前最好检查一下 socket 的 <code>bufferedAmount</code> 属性,以防还有数据要传输。</p>
<h2 id="安全方面的考虑">安全方面的考虑</h2>
<p>WebSocket 不应当用于混合的上下文环境;也就是说,不应该在用HTTPS加载的页面中打开非安全版本的WebSocket,反之亦然。而实际上一些浏览器也明确禁止这一行为,包括 Firefox 8 及更高版本。</p>
|