websocket

  • 浏览器发起http请求初始化连接;
  • 服务器响应,从http协议切换到websocket协议;
  • websocket使用自定义协议而非http协议;
  • 优点:客户端和服务器可以发送很少的数据;
  • 缺点:定义协议的时间比定义js API要长;
  • 浏览器支持情况:所有主流浏览器都支持;
  • 同源策略不适用于websocket;
  • 网络连接包含长连接和基于TCP套接字的双向消息交换;
  • 不信任的客户端脚本访问TCP套接字是不安全的;
  • WebSocket API定义了一种安全方案:客户端和服务器端创建双向的套接字类型的连接;
  • 客户端和服务器的通信通过TCP套接字长连接实现的,遵循WebSocket定义的规则;
  • WebSocket协议,实现让web服务器能同时处理同一端口上的HTTP连接和WebSocket连接;

API

实例化WebSocket对象并传入提供连接的URL:

let socket = new WebSocket("ws://xx");

readyState属性表示当前状态:

  • OPENING(0): 连接正在建立;
  • OPEN(1): 连接已经建立;
  • CLOSING(2): 连接正在关闭;
  • CLOSE(3): 连接已经关闭。

WebSocket没有readyStateChange事件。

任何时候都可以调用close()方法关闭WebSocket连接,调用close()后,readyState变为2,关闭后变为3。

发送和接收数据

通过连接发送和接收数据。要向服务器发送数据调用send()方法并传入一个字符串、ArrayBuffer或Blob。服务器向客户端发送消息时,或触发message事件,可以通过event.data属性访问到有效载荷,event.data返回的数据可能是ArrayBuffer, Blob,这由WebSocket的binaryType属性决定,该属性可能是arraybuffer, blob。如下示例:

let socket = new WebSocket("ws://xx");
let stringData = "Hello";
socket.send(stringData);
socket.onmessage = function(event){
    let data = event.data;
}

其它事件

WebSocket在连接生命周期有可能触发3个其它事件。

  • open:连接已经建立时触发;
  • error:发生错误时触发;
  • close:连接关闭时触发;
    这些事件中只有close事件event对象上有额外信息,wasClean是否干净的关闭,code是来自服务端的数值状态码,reason是来自服务端的信息。如下示例:
socket.onopen = function () {};
socket.onerror = function () {};
socket.onclose = function (event) {};

协议

WebSocket协议和HTTP协议都是应用层协议,都是基于TCP协议。WebSocket建立连接时需要借助HTTP协议,连接建立好后双方痛信就和HTTP协议无关了。

  • Upgrade:websocket:通知服务器切换到websocket协议;
  • Sec-WebSocket-key:xxx:客户端发送的base64密文,要求server返回对应的加密应答Sec-WebSocket-Accept,否则客户端抛出错误Erro during WebSocket handshake,并关闭连接;
  • server收到报文,如支持WebSocket协议,返回HTTP/1.1 101 WebSocket Protocol Handshake
  • 握手成功后,进行TCP通讯。

其它

  • 网络应用中客户端和服务端都能主动发送消息使用的技术是socket,socket是一组接口,方便开发者使用底层的协议,而WebSocket是一种协议,包含标准的API;
  • Socket.IO是封装了WebSocket的js框架,不仅支持WebSocket协议,还支持多种轮询通讯机制。
  • nodejs服务端实现方案:ws

聊天室Demo

输入用户名进入聊天室,聊天室页面有消息记录、发送消息文本框和发送按钮。示例代码:

(function () {
  const oMessage = document.getElementById("message");
  const oSend = document.getElementById("send");
  const socket = new WebSocket("ws://localhost:8000");
  function init() {
    bindEvent();
  }
  function bindEvent() {
    oSend.addEventListener("click", handleSend);
    socket.addEventListener("open", handleOpen);
    socket.addEventListener("close", handleClose);
    socket.addEventListener("error", handleError);
    socket.addEventListener("message", handleMessage);
  }
  function handleOpen(e) {
    console.log("client open");
  }
  function handleClose(e) {
    console.log("client close");
  }
  function handleError(e) {
    console.log("client error");
  }
  function handleMessage(e) {
    console.log("client message");
    const oList = document.getElementById("list");
    const message = JSON.parse(e.data);
    oList.appendChild(createMessage(message));
  }
  function createMessage(data) {
    const { username, time, message } = data;
    const oItem = document.createElement("li");
    oItem.innerHTML = `
    <p>
      <span>${username}:</span>
      <i>${time}</i>
    </p>
    <p>消息:${message}</p>
    `;
    return oItem;
  }
  function handleSend() {
    const message = oMessage.value.trim();
    const username = window.localStorage.getItem("username");
    if (message) {
      socket.send(
        JSON.stringify({
          username,
          time: new Date().getTime(),
          message,
        })
      );
      oMessage.value = "";
    } else {
      alert("请输入消息");
    }
  }
  init();
})();

服务端通过ws实现WebSocket,示例代码:

const ws = require("ws");
const server = new ws.Server({ port: 8000 });

function init() {
  bindEvent();
}

function bindEvent() {
  server.on("connection", handleConnection);
  server.on("error", handleError);
  server.on("close", handleClose);
}

function handleConnection(ws) {
  console.log("server connection");

  ws.on("message", handleMessage);
}
function handleError() {
  console.log("server error");
}
function handleClose() {
  console.log("server close");
}
function handleMessage(message) {
  console.log("server message");
  server.clients.forEach((client) => {
    client.send(message);
  });
}

init();

参考

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容