WebSocket技术基础介绍

WebSocket技术基础介绍

WebSocket的基础概念

WebSocket是一种在单个TCP连接上进行全双工通信的协议(全双工即双方可同时向对方发送消息)。
WebSocket是html5规范中的一个部分,它借鉴了socket这种思想,为web应用程序客户端和服务端之间(注意是客户端服务端)提供了一种全双工通信机制。
WebSocket通信协议于2011年被IETF定为标准RFC6455,并由RFC7936补充规范。
WebSocketAPI也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
在WebSocketAPI中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
HTML5定义了WebSocket协议的目的,就是能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
WebSocket提供两种数据传输:文本数据和二进制数据。这个协议是基于帧而不是流,文本和二进制都是帧。
为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应。这个过程通常称为“握手”(handshaking)。
WebSocket技术可以被用到许多的Web应用:游戏、股票应用、多人协作应用、与后端服务实时交互的用户接口等。

WebSocket与TCP和HTTP的关系:

NewImage

基本上WebSocket的目标是在约束的条件下提供尽可能接近原生的TCP的Web服务。WebSocket协议是独立的基于TCP的协议。WebSocket是独立的、创建在TCP上的协议。
WebSocket是不能够脱离http而单独存在的协议。他和HTTP的唯一关系是建立连接的握手操作的升级请求是基于HTTP服务器的。
WebSocket默认使用80端口进行连接,而基于TLS的WebSocket连接是基于443端口的。
WebSocket通过HTTP/1.1 协议的101状态码进行握手。
WebSocket建立连接是使用HTTP协议进行握手的,握手完成之后,WebSocket和HTTP就没有关系了,真正的数据传输时不需要Http协议参与的。

HTTP 协议有一个缺陷:通信只能由客户端发起,而且轮询的效率低,非常浪费资源。于是WebSocket技术应运而生。
WebSocket的优势是能支持客户端和服务器端的双向通信,而且协议的头部又没有HTTP的Header那么大。图中可以看到Polling里面客户端发送了好多Request,而下图,只有一个Upgrade,非常简洁高效。

1194012-b88b2623a2e4a8ea.png

WebSocket的安全模型:

WebSocket协议在Web页面与WebSocket服务器建立连接时使用基于web浏览器的同源策略模型。同源策略在web应用的安全模型中是一个重要概念。在这个策略下,web浏览器允许第一个页面的脚本访问第二个页面里的数据。
WebSocket服务器同样无法与其他协议尤其是HTTP建立连接。
WebSocket协议实现了严格约束的握手过程和限制数据不能在握手完成和建立连接之前插入数据进行传输。
WebSocket目前支持两种统一资源标志符WSWSS,类似于HTTP和HTTPS。
当建立的连接过多时,服务端可以拒绝和主机/IP地址建立的连接,同时服务端在负载过高时也可以主动断开占用资源的连接。
无论WebSocket协议是否使用了TLS,帧都需要添加掩码。客户端必须在它发送到服务器的所有帧中添加掩码(Mask)。服务端收到没有添加掩码的数据帧以后,必须立即关闭连接。

WebSocket的特点:

较少的控制开销服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小
更强的实时性由于协议是全双工的,所以服务器可以随时主动给客户端下发数据
保持连接状态与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议
更好的二进制支持相对HTTP,可以更轻松地处理二进制内容
可以支持扩展用户可以扩展协议、实现部分自定义的子协议
更好的压缩效果可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率

WebSocket和Socket技术的区别:

Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。基于WebSocket和基于Socket都可以开发出IM社交聊天类的app。

1194012-d35653654be833ae.jpg

WebSocket并不是简单地将socket这一概念在浏览器环境中的移植。
WebSocket采取的方式是让所有客户端连接服务端,服务器将不同客户端发送给自己的消息进行转发或者广播。而对于原始的socket,只要两端建立连接之后,就可以发送端对端的数据,不需要经过第三方的转发,这也是websocket不同于socket的一个重要特点。

 来自客户端的握手数据
WebSocket客户端的握手是一个基于HTTP的升级请求。

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: bar; baz=2
第一行必须客户端请求遵循HTTP请求行的格式,请求方法必须是GET,而且HTTP的版本至少需要1.1。
第二行必须它包含了一个主机(host)字段加上一个紧跟在":"之后的端口(port)字段(如果端口不存在则使用默认端口)。
第三行必须请求必须包含一个Upgradeheader字段,它的值必须包含"websocket"。
第四行必须请求必须包含一个Connectionheader字段,它的值必须包含"Upgrade"。
第五行必须这个字段的值必须是由一个随机生成的16字节的随机数通过base64编码得到的。
第六行必须如果这个请求来自浏览器,那么请求必须包含一个Originheader字段
第七行可选包含了一个或者多个客户端希望使用的用逗号分隔的根据权重排序的子协议。
第八行必须这个header字段的值必须为13
第九行可选WebSocket 服务端可以接受其中一些或者所有的客户端请求的扩展。

请求还可能包含其他字段,如 cookie 等。

服务器端握手响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
第一行必须状态码为101的状态行。服务端的第一行(leading line)遵循了HTTP状态行的格式。
第二行必须响应必须包含一个Upgradeheader字段,它的值必须包含"websocket"。
第三行必须响应必须包含一个Connectionheader字段,它的值必须包含"Upgrade"。
第四行必须通过和字符串拼接在一起进行SHA-1哈希运算,得到一个20字节的值,然后对这20字节进行base64编码。
第五行可选根据权重排序的子协议。

如果响应不包含 Sec-WebSocket-Protocol 中指定的子协议,客户端断开
如果响应 HTTP/1.1 101 Switching Protocols 状态码不是 101,客户端断开

客户端的简单示例:

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
}; 

ws.onerror = function(evt) {
  console.log("error!!!"); 
}; 

为了建立一个WebSocket连接,客户端需要建立一个连接并且发送一个定义的握手协议。
当客户端通过一系列的配置字段(主机(host)、端口(port)、资源名称(resource name)和安全标记(secure))以及一个可被使用的协议(protocol)和扩展(extensions)列表来建立一个WebSocket连接,它一定会通过发送一个握手协议,并且收到一个服务端的握手响应来建立一条连接。
一旦到服务端的连接被建立了(包括通过一个代理或者通过一个TLS加密通道),客户端必须发送一个开始握手的数据包给服务端。这个数据包由一个HTTP升级请求构成,包含一系列必须的和可选的header字段。一旦客户端的握手请求发送出去,那么客户端必须在发送后续数据前等待服务端的响应。

 服务器端的简单示例:

const WebSocket = require('ws');
 
const wss = new WebSocket.Server({ port: 8080});
 
wss.on('connection', function connection(ws) {
  ws.on('message', function(message){
    wss.clients.forEach(function(ws)){ // wss.clients 拿到所有的连接
          ws.send(message) // 发送消息
      })
  })
});

基础帧协议:

  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 ...                |
 +---------------------------------------------------------------+
基础帧bit位说明
FIN1 bit表示这是消息的最后一个片段。第一个片段也有可能是最后一个片段。
RSV1-33 bit必须设置为0,除非扩展了非0值含义的扩展。
Opcode4 bit分为控制帧和数据帧。控制帧是用于WebSocket的通信状态的。控制帧可以被插入到消息片段中进行传输。具体意义请参考下面的【控制帧和数据帧】。
Mask1 bit是否添加掩码。所有的从客户端发送到服务端的帧都需要设置这个bit位为1
Payload length7bit记录有效载荷长度。有效载荷长度 = 扩展数据长度 + 应用程序数据长度;单位为Byte。
Extended payload length7+16bit扩展数据长度
Extended payload length continued7+64bit扩展数据长度
Masking-key0/4byte客户端发送给服务端的数据都是经过掩码处理的,长度为 32bit;服务器给客户端的是0。
Payload Datax+ybyte扩展数据+应用数据

控制帧和数据帧

帧名帧类型操作码说明
文本数据帧0x1用UTF-8编码的文本数据。
二进制数据帧0x2任意的二进制数据,二进制数据的解析仅仅依靠应用层。
未使用数据帧0x3-0x7扩展数据,保留给将来的非控制帧
关闭控制帧0x8可能包含内容(body)(帧的“应用数据”部分)来表明连接关闭的原因。两个整型字节之后的可以是UTF-8编码的的数据值
心跳Ping控制帧0x9如果收到了一个心跳Ping帧,那么终端必须发送一个心跳Pong 帧作为回应,除非已经收到了一个关闭帧
心跳Pong控制帧0xA作为回应发送的Pong帧必须完整携带Ping帧中传递过来的“应用数据”字段。Pong帧可以被主动发送作为单项的心跳。
未使用控制帧0xB-0xF扩展数据,保留给将来的控制帧

WebSocket通讯详细分为三个步骤:打开握手,数据传递,关闭握手 

显示了WebSocket主要的三步 浏览器和 服务器端分别做了那些事情。 

NewImage

MQTT协议专注于网络、资源受限环境,建立之初不曾考虑WEB环境。HTML5 Websocket是建立在TCP基础上的双通道通信,和TCP通信方式很类似,适用于WEB浏览器环境。虽然MQTT基因层面选择了TCP作为通信通道,但我们添加个编解码方式,MQTT over Websocket也可以的。这样做的好处,MQTT的使用范畴被扩展到HTML5、桌面端浏览器、移动端WebApp、Hybrid等,多了一些想像空间。这样看来,无论是移动端,还是WEB端,MQTT都会有自己的使用空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的横打

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值