William's Blog

WebSocket协议学习笔记

协议产生背景

  http协议是一个单向、无连接、无状态的协议。因为协议的特性,在客户端需要即时获取服务端变动的业务场景下,http协议通信效率低下。为了弥补http协议这方面的缺陷,人们设计出了WebSocket协议。该协议建立在http协议的基础上,对http协议的功能进行了扩展。图1展示了WebSocket协议和http协议的关系。

图1 WebSocket协议与http协议的关系

建立WebSocket通信

客户端

  HTML5将WebSocket协议标准化,目前各大主流浏览器均已支持WebSocket协议。浏览器提供了一个WebSocket对象用来创建和管理WebSocket连接。客户端可以利用这一对象与服务器进行WebSocket通信。一个简单的客户端建立WebSocket通信的代码如下:

// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:3000');

// Connection opened
socket.addEventListener('open', () => {
    alert('websocket协议握手成功!!!');
});

// Listen for messages
socket.addEventListener('message', (event) => {
    alert('收到新消息!!!');
});

// Connection closed
socket.addEventListener('close', () => {
    alert('停止websocket协议!!!');
});

服务端

  目前,针对不同的服务器环境均有对应的WebSocket框架可以使用,详情可见维基百科。适用于Node环境的WebSocket框架主要有以下几类:

  以Node环境下的ws框架为例,一个简单的服务器端实现WebSocket协议的示例代码如下:

// import http module
const http = require('http');

// import ws
const websocket = require('ws');

// create http server
const server = http.createServer(app.callback());

// create websocket instance
const websocketServer = new websocket.Server({server});

// listen for connection
websocketServer.on('connection', (ws, req) => {
    // listen for messages
    ws.on('message', (msg) => {
        console.log("收到了新消息!!!");
        ws.send('您好,我已经收到了你的消息');
    });
});

WebSocket协议握手

  WebSocket协议使用了http协议的握手通道,由http协议升级到WebSocket协议时的http报文示例如下:

  • request

websocket协议握手时http请求报文

  • response

websocket协议握手时http响应报文

  上述http报文中与WebSocket协议握手阶段相关的字段解释如下:

表1 WebSocket协议握手阶段http报文首部相关字段解释
字段名称 含义
Connection: Upgrade 表示要升级协议。
Upgrade:websocket 表示要升级到WebSocket协议。
Sec-WebSocket-Extensions 仅用在WebSocket连接握手阶段。它先由客户端(浏览器)发送到服务器,然后再由服务器传回客户端,以便协商连接过程中的协议层扩展集。可能在HTTP请求中出现多次(等价于出现一次,但有多个值),但是最多只能在HTTP响应中出现一次。
Sec-WebSocket-Key Base64编码字符串,与服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。
Sec-WebSocket-Version 表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Version首部字段,里面包含服务端支持的版本号。
Sec-WebSocket-Accept Base64编码字符串,根据客户端请求首部的Sec-WebSocket-Key计算出来。

  Sec-WebSocket-Accept字段计算公式:

  1. 将Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。
  2. 通过SHA1计算出摘要,并转成base64字符串。伪代码:toBase64(sha1(Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11))。

参考文献

  1. WebSocket 教程
  2. 知乎:WebSocket 是什么原理?为什么可以实现持久连接?
  3. WebSocket:5分钟从入门到精通
  4. 如何理解HTTP协议的 “无连接,无状态” 特点?
  5. WebSocket协议详解及应用(三)-WebSocket握手协议之服务器响应

William

本博客作者 William 现任职于北京贝壳找房,从事web前端开发相关工作。
您可以通过Email与他取得联系