1 背景

WebSocket protocol 是HTML5一种新的协议。它是实现了浏览器与服务器全双工通信(full-duplex)。
这篇文章介绍了关于Websocket 协议的相关信息,供大家参考。

现很多网站为了实现即时通讯,所用的技术都是轮询(polling)。
轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP request,
然后由服务器返回最新的数据给客服端的浏览器。

这种传统的HTTP request 的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求,
然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽。
而最比较新的技术去做轮询的效果是Comet – 用了AJAX。
但这种技术虽然可达到全双工通信,但依然需要发出请求。

在 WebSocket API,浏览器和服务器只需要要做一个握手的动作,
然后,浏览器和服务器之间就形成了一条快速通道。
两者之间就直接可以数据互相传送,改变了原有的B/S模式。

Fig-1

在这里是关于Websocket在 php 中的实现,
Websocket 业务模型:

Fig-2

浏览器端的websocket 请求一般是
// javacsript

// 创建WS对象
var ws = new WebSocket("ws://127.0.0.1:4000");

// 注册回调函数
ws.onopen = function(){
  console.log("succeed");
};
ws.onerror = function(){
  console.log(“error”);
};
ws.onmessage = function(e){
  console.log(e);
}

当 new 一个 websocket 对象之后,就会向服务器发送一个 get 请求:

Fig-3

这个请求是对某个服务器的端口发送的,
一般的话,会预先在服务器将一个socket 绑定到一个端口上,
客户端和服务器端在这个预定的端口上通信(这里绑定的就是 4000 端口,

默认情况下,websocke 使用 80 端口)。

然后,在服务器端的socket监听到这个packet之后就生成一个新的 socket,
将发送过来的数据中的 Sec-WebSocket-Key 解析出来,
然后按照把:

“Sec-WebSocket-Key”加上一个字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。
使用SHA-1加密,之后进行BASE-64编码,

将结果做为“Sec-WebSocket-Accept”头的值,返回给客户端。
客户端收到这个之后,就会将通信协议 upgrade 到 websocket 协议。

Fig-4 
然后就会在这个持久的通道下进行通信,
包括浏览器的询问,服务器的push,双方是在一个全双工的状态下相互通信。

切换后的websocket 协议中的 数据传输帧的格式(此时不再使用html协议) 官方文档给出的说明:

Fig-5

直接看这个,谁都会有点头大: 我花了一幅图来简单的解释这个 frame 的结构。

Fig-6

各字段的解释:
FIN            1bit 表示信息的最后一帧,flag,也就是标记符
RSV 1-3        1bit each 以后备用的 默认都为 0
Opcode         4bit 帧类型,
Mask           1bit 掩码,是否加密数据,默认必须置为1
Payload len    7bit 数据的长度,
               当这个7 bit的数据 == 126 时,后面的2 个字节也是表示数据长度,
               当它 == 127 时,后面的 8 个字节表示数据长度Masking-key 1 or 4 bit 掩码
               Payload data  playload len  bytes 数据

所以我们这里的代码,通过判断 Playload len的值,来用 substr 截取 Masking-key 和 PlayloadData。
根据掩码解析数据的方法是:
for( i = 0; i < data.length ; i++){
  orginalData += data[i]  ^  maskingKey[i mod 4];
}
在PHP中,当我们收到数据之后,按照这里的格式截取数据,
并将其按照这里的方法解析后就得到了浏览器发送过来的数据。 
当我们想把数据发送给浏览器时,也要按照这个格式组装frame。 
这里是我的方法:
function frame($s){
  $a = str_split($s, 125);
  if (count($a) == 1){
    return "\x81" . chr(strlen($a[0])) . $a[0];
  }
  $ns = "";
  foreach ($a as $o){
    $ns .= "\x81" . chr(strlen($o)) . $o;
  }
  return $ns;
}
强行将要发送的数据分割成 125 Byte / frame,这样 playload len 只需要 7 bits。
也就是直接将数据的长度的ascall码拼接上去,然后后面跟上要发送的数据。 
每一个 frame 前面加的 ‘\x81’ 用二进制就是: 1000 0001 1000 :
1 是 FIN
000 是三个备用的bit
0001 : 指的是 opcode 官方的解释:

Fig-7
可以设置 opcode的值,来告诉浏览器这个frame的数据属性。

2 协议概览

协议包含两部分:握手,数据传输。

客户端的握手如下:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin:
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

服务端的握手如下:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

客户端和服务端都发送了握手,并且成功,数据传输即可开始。

3 发起握手

发起握手是为了兼容基于HTTP的服务端程序,这样一个端口可以同时处理HTTP客户端和WebSocket客户端
因此WebSocket客户端握手是一个HTTP Upgrade请求:
        GET /chat HTTP/1.1
        Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
        Origin:
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13
握手中的域的顺序是任意的。

4 数据帧

4.1 概述

WebScoket协议中,数据以帧序列的形式传输。
考虑到数据安全性,客户端向服务器传输的数据帧必须进行掩码处理。
服务器若接收到未经过掩码处理的数据帧,则必须主动关闭连接。
服务器向客户端传输的数据帧一定不能进行掩码处理。
客户端若接收到经过掩码处理的数据帧,则必须主动关闭连接。
针对上情况,发现错误的一方可向对方发送close帧(状态码是1002,表示协议错误),以关闭连接。

4.2 帧协议

WebSocket数据帧结构如下图所示:
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+
 
FIN:1位
表示这是消息的最后一帧(结束帧),一个消息由一个或多个数据帧构成。若消息由一帧构成,起始帧即结束帧。
 
RSV1,RSV2,RSV3:各1位
MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. 
If a nonzero value is received and none of the negotiated extensions 
defines the meaning of such a nonzero value, 
the receiving endpoint MUST _Fail the WebSocket Connection_.
这里我翻译不好,大致意思是如果未定义扩展,各位是0;
如果定义了扩展,即为非0值。
如果接收的帧此处非0,扩展中却没有该值的定义,那么关闭连接。
 
OPCODE:4位
解释PayloadData,如果接收到未知的opcode,接收端必须关闭连接。
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义,为以后的非控制帧保留
0x8表示连接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义,为以后的控制帧保留
 
MASK:1位
用于标识PayloadData是否经过掩码处理。
如果是1,Masking-key域的数据即是掩码密钥,用于解码PayloadData。
客户端发出的数据帧需要进行掩码处理,所以此位是1。
 
Payload length:7位,7+16位,7+64位
PayloadData的长度(以字节为单位)。
如果其值在0-125,则是payload的真实长度。
如果值是126,则后面2个字节形成的16位无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。
如果值是127,则后面8个字节形成的64位无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。
长度表示遵循一个原则,用最少的字节表示长度(我理解是尽量减少不必要的传输)。
举例说,payload真实长度是124,在0-125之间,必须用前7位表示;
不允许长度1是126或127,然后长度2是124,这样违反原则。
Payload长度是ExtensionData长度与ApplicationData长度之和。
ExtensionData长度可能是0,这种情况下,Payload长度即是ApplicationData长度。
 
WebSocket协议规定数据通过帧序列传输。
客户端必须对其发送到服务器的所有帧进行掩码处理。
服务器一旦收到无掩码帧,将关闭连接。
服务器可能发送一个状态码是1002(表示协议错误)的Close帧。
而服务器发送客户端的数据帧不做掩码处理,一旦客户端发现经过掩码处理的帧,将关闭连接。
客户端可能使用状态码1002。
 
消息分片
分片目的是发送长度未知的消息。
如果不分片发送,即一帧,就需要缓存整个消息,计算其长度,构建frame并发送;
使用分片的话,可使用一个大小合适的buffer,用消息内容填充buffer,填满即发送出去。

分片规则:
1.一个未分片的消息只有一帧(FIN为1,opcode非0)
2.一个分片的消息由起始帧(FIN为0,opcode非0),
  若干(0个或多个)帧(FIN为0,opcode为0),结束帧(FIN为1,opcode为0)。

3.控制帧可以出现在分片消息中间,但控制帧本身不允许分片。
4.分片消息必须按次序逐帧发送。

5.如果未协商扩展的情况下,两个分片消息的帧之间不允许交错。
6.能够处理存在于分片消息帧之间的控制帧

7.发送端为非控制消息构建长度任意的分片
8.client和server兼容接收分片消息与非分片消息

9.控制帧不允许分片,中间媒介不允许改变分片结构(即为控制帧分片)
10.如果使用保留位,中间媒介不知道其值表示的含义,那么中间媒介不允许改变消息的分片结构

11.如果协商扩展,中间媒介不知道,那么中间媒介不允许改变消息的分片结构,
   同样地,如果中间媒介不了解一个连接的握手信息,也不允许改变该连接的消息的分片结构
12.由于上述规则,一个消息的所有分片是同一数据类型(由第一个分片的opcode定义)的数据。
   因为控制帧不允许分片,所以一个消息的所有分片的数据类型是文本、二进制、opcode保留类型中的一种。

需要注意的是,如果控制帧不允许夹杂在一个消息的分片之间,延迟会较大,
比如说当前正在传输一个较大的消息,此时的ping必须等待消息传输完成,才能发送出去,会导致较大的延迟。
为了避免类似问题,需要允许控制帧夹杂在消息分片之间。

控制帧

根据官方文档整理,官方文档参考

websocket协议详解相关推荐

  1. WebSocket协议详解及应用

    标签:websocket WebSocket协议详解及应用(七)-WebSocket协议关闭帧 本篇介绍WebSocket协议的关闭帧,包括客户端及服务器如何发送并处理关闭帧.关闭帧错误码及错误处理方 ...

  2. 计算机网络:WebSocket协议详解

    1. 概述 一直以来,网络在很大程度上都是围绕着HTTP的请求/响应模式而构建的.所有的HTTP通信都是由客户端控制的,这就需要用户进行互动或者定期轮询,以便从服务器加载数据.长期以来存在着各种技术让 ...

  3. websocket协议详解与抓包分析

    Websocket是一种用于H5浏览器的实时通讯协议,它实现了浏览器与服务器全双工通信(full-duplex). 可以做到数据的实时推送,适用于广泛的工作环境,例如客服系统.物联网数据传输系统. 通 ...

  4. IoT:MQTT协议详解

    IoT -- (七)MQTT协议详解 转自:https://blog.csdn.net/anxianfeng55555/article/details/80908795 MQTT是什么? MQTT(M ...

  5. 【计算机网络面试高频】-HTTP协议详解,HTTP协议常见问题

    4.HTTP协议详解有关问题 4.1 HTTP请求 一个HTTP请求报文由请求行.请求头部.空行和请求数据四个部分组成. 1.请求行 请求行中有请求方法字段.URL字段和HTTP协议版本3个字段组成. ...

  6. webSocket(与http请求区别、请求和返回示例、替代方法:ajax轮询、long poll、Flash、NodeJS实现WebSocket通信代码、原理、WebSocket构造函数详解)

    目录 简介 优点 请求示例 请求成功返回示例 替代方法 Ajax 轮询 long poll Flash NodeJS实现webSocket(ws模块) 服务端代码 客户端代码 在线网页客户端 运行结果 ...

  7. 【Java网络编程与IO流】Http协议详解以及面试有关问题

    HTTP协议详解以及面试有关题目 1 HTTP请求 一个HTTP请求报文由请求行.请求头部.空行和请求数据四个部分组成. 1.1 请求行 请求行中有请求方法字段.URL字段和HTTP协议版本3个字段组 ...

  8. Spring和WebSocket整合详解

    Spring和WebSocket整合详解 本篇编写日期较早,代码冗余较多,新博文:Spring和WebSocket整合并建立简单的Web聊天室 官方主页 Spring WebSocket 概述 Web ...

  9. Https完全协议详解

    转自Https协议详解 HTTP 的缺点 到现在为止,我们已了解到 HTTP 具有相当优秀和方便的一面,然而 HTTP 并非只有好的一面,事物皆具两面性,它也是有不足之处的.HTTP 主要有这些不足, ...

最新文章

  1. java struts 框架_java struts 框架编程
  2. git gui here如何汉化_你不知道的一些在Git使用中的奇技淫巧!
  3. 自定义线程池-线程池工作流程介绍
  4. HDU - 5592 ZYBs Premutation(线段树,逆序对)
  5. C#实现渐变颜色的Windows窗体控件
  6. html中 一条样式规则,css规则的基本格式是什么?
  7. 可逆矩阵的秩等于矩阵的阶数_矩阵论一些总结点
  8. 18000 Two String 暴力。——— 读题
  9. B/S应用中的ActiveX数字签名相关问题杂谈
  10. 如何用一句话证明你是程序员?
  11. SaaSBase:最受欢迎的跨境电商软件有哪些(上篇)
  12. 网页飘窗效果,jsp页面飘窗浮窗,html飘窗浮窗,点叉号关闭飘窗
  13. 纬度,经度和坐标系网格
  14. 从0写USB摄像头驱动程序
  15. WordPress 安装时常见的数据库的错误
  16. 数据挖掘机器学习及其他领域数据集汇总
  17. Wannafly挑战赛26 御坂网络
  18. 攻防世界pwn高手进阶(持续更新)
  19. ArrayList删除元素的细则
  20. 神经网络——机器学习基础

热门文章

  1. 效率神器工具,看这篇就够了!
  2. 浅谈最近流行的三起区块链51%算力攻击
  3. 在win10上去除移动硬盘的bitlocker
  4. 网上如何赚钱?教你利用知识变现来赚钱!
  5. excel制作跨职能流程图_excel制作流程图的方法步骤图
  6. 日语 “何” 的读音问题
  7. 计算机制图作品答辩,教师资格证考试小学信息技术《认识画图》答辩
  8. 订单能否按时交期?制造企业必须面对这几个问题
  9. php获取搜索记录,PHP写的获取各搜索蜘蛛爬行记录代码
  10. 什么是蜘蛛统计 有什么作用?