编写 WebSocket 客户端程序

WebSockets 是一种基于 ws 协议的技术,它使得建立全双工连接成为可能。websocket 常见于浏览器中,但是这个协议不受使用平台的限制。

注: 我们准备了一个聊天服务器程序用于测试,一旦服务器配置好就能使用。

WebSockets 的可用性

WebSocket API 在 JavaScript 中的可用域(scope)包括 DOM Window 对象,和任何实现了 WorkerUtils 的对象。也就是说,你可以在 Web Workers 中使用这些 API。

注: WebSockets 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 来发送。

注: 在第 11 版之前,Firefox 仅支持字符串方式。

建立连接的过程是异步的,而且可能会出错,因此刚刚连接就调用 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 和后续版本。

文档标签和贡献者

 此页面的贡献者: laobubu
 最后编辑者: laobubu,