WebSockets 是一种基于 ws 协议的技术,它使得建立全双工连接成为可能。websocket 常见于浏览器中,但是这个协议不受使用平台的限制。
WebSockets 的可用性
WebSocket API 在 JavaScript 中的可用域(scope)包括 DOM Window
对象,和任何实现了 WorkerUtils
的对象。也就是说,你可以在 Web Workers 中使用这些 API。
创建一个 WebSocket 对象
要通过 WebSocket 协议通讯,你需要先创建一个 WebSocket
对象,然后它会自动尝试连接服务器。
WebSocket 构造函数需要一个必填参数,以及一个可选参数:
WebSocket WebSocket( in DOMString url, in optional DOMString protocols ); WebSocket WebSocket( in DOMString url, in optional DOMString[] protocols );
-
url
- 你要连接到的 URL;此 URL 应为 WebSocket 服务器响应的URL。
-
protocols
可选 - 单个协议名称或字符串数组。数组中的字符串用于指定子协议,这样便可在一个服务器下实现多个 WebSocket 协议(例如,对于不同的协议,使用不同的数据处理方法)。如果不提供此参数,则默认为空字符串。
构造函数会抛出以下异常:
-
SECURITY_ERR
- 用于连接的端口被屏蔽。
连接错误
如果连接时出错,会有一个叫 "error" 的事件会被发送到 WebSocket
对象(并调用 onerror
程序),随后 CloseEvent
事件也会发送到 WebSocket
对象(并调用 onclose
程序),同时提供连接关闭的原因。
对于 Firefox 11 而言,可以在 Mozilla 控制台中看到错误信息的描述。此外,关闭代码可以参阅 RFC 6455, Section 7.4 中关于 CloseEvent
的部分。
例子
这是一个创建 WebSocket 并连接到 ws://www.example.com/socketserver 服务器的简单例子。其中指定了一个自定义协议
"protocolOne" ,不过这个可以忽略。
var exampleSocket = new WebSocket("ws://www.example.com/socketserver", "protocolOne");
赋值之后, exampleSocket
.readyState
会变成 CONNECTING。一旦连接建立,并可传输数据,这里的
readyState
会变成 OPEN
。
如果你想创建一个能灵活的使用多种协议的连接,只需要提供一个协议名的数组即可:
var exampleSocket = new WebSocket("ws://www.example.com/socketserver", ["protocolOne", "protocolTwo"]);
一旦连接建立(也就是说 readyState
变成 OPEN),
exampleSocket.protocol
将会告诉你服务器选择了哪一种协议。
上面例子中用 ws
替换了 http,类似的可用
wss
替换 https
。建立一个 WebSocket 连接需要依赖 HTTP 升级机制,因此在通过地址 ws://www.example.com
或者 wss://www.example.com
访问 HTTP 服务器时会带上协议升级请求。
发送数据到服务器
建立连接之后便能传输数据到服务器了。要发送一条信息,只需要调用 WebSocket
对象的 send()
方法即可。
exampleSocket.send("Here's some text that the server is urgently awaiting!");
你可以将数据作为字符串、 Blob
或者 ArrayBuffer 来发送。
建立连接的过程是异步的,而且可能会出错,因此刚刚连接就调用 send()
可能会失败。 我们可以设置 onopen
回调函数来确定什么时候连接成功。
exampleSocket.onopen = function (event) { exampleSocket.send("亲爱的服务器!我连上你啦!"); };
使用 JSON 来传输对象
你可以很方便地利用 JSON 发送比较复杂的数据到服务器。例如这个和服务器交互的聊天程序就使用了一个协议,其中利用了 JSON 封装的数据包:
// 通过服务器向全体发言 function sendText() { // 创建一个 msg 对象,其中含有服务器需要处理的数据。 var msg = { type: "message", text: document.getElementById("text").value, id: clientID, date: Date.now() }; // 将其作为 JSON 格式字符串发送。 exampleSocket.send(JSON.stringify(msg)); // 清空文本输入框 document.getElementById("text").value = ""; }
从服务器接收信息
WebSockets 是事件驱动 API。当有消息到来,会触发 "message" 事件,调用 onmessage
函数。要侦听服务器发来的消息,就像这样即可:
exampleSocket.onmessage = function (event) { console.log(event.data); }
接收并读出 JSON 对象
假定我们的程序就是上面 使用 JSON 来传输对象 提到的聊天程序。其中会遇到多种类型的消息,例如:
- 登录完成
- 收到文字消息
- 更新成员列表
这就是处理上述多种类型的消息的一个例子:
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>用户 <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> 。</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); } };
我们使用了 JSON.parse()
将 JSON 还原成对象,然后根据还原得到的对象做相应的操作。
文本数据格式
从 WebSocket 连接收到的文本都是 UTF-8 格式。
在 Gecko 9.0 (Firefox 9.0 / Thunderbird 9.0 / SeaMonkey 2.6) 之前的版本上,UTF-8 文本里的某些非文本字符会导致连接断开,但是现在的 Gecko 已经解决了这个问题。
关闭连接
用完了 WebSocket 连接之后,使用方法 close() 来关闭它:
exampleSocket.close();
在关闭连接之前,可以考虑检查一下 bufferedAmount
以确保所有要传送的数据都传完了。
安全问题
不要将 WebSockets 用于混合内容环境!不要在 HTTPS 安全页面下创建非安全的 WebSocket 连接,反之亦然。有些浏览器对此是强行禁止的,例如 Firefox 8 和后续版本。