websocket 协议

WebSocket 协议和知识


WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

WebSocket协议是基于TCP的一种新的网络协议,和http协议一样属于应用层协议

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。


 

为什么会有 WebSocket

众所周知,Web应用的通信过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现。这种机制对于信息变化不是特别频繁的应用可以良好支撑,但对于实时要求高、海量并发的应用来说显得捉襟见肘,尤其在当前业界移动互联网蓬勃发展的趋势下,高并发与用户实时响应是Web应用经常面临的问题,比如金融证券的实时信息、Web导航应用中的地理位置获取、社交网络的实时消息推送等.

传统的请求-响应模式的Web开发在处理此类业务场景时,通常采用实时通讯方案。比如常见的轮询方案,其原理简单易懂,就是客户端以一定的时间间隔频繁请求的方式向服务器发送请求,来保持客户端和服务器端的数据同步。其问题也很明显:当客户端以固定频率向服务器端发送请求时,服务器端的数据可能并没有更新,带来很多无谓请求,浪费带宽,效率低下。

基于Flash,AdobeFlash通过自己的Socket实现完成数据交换,再利用Flash暴露出相应的接口给JavaScript调用,从而达到实时传输目的。此方式比轮询要高效,且因为Flash安装率高,应用场景广泛。然而,移动互联网终端上Flash的支持并不好:IOS系统中无法支持Flash,Android虽然支持Flash但实际的使用效果差强人意,且对移动设备的硬件配置要求较高。2012年Adobe官方宣布不再支持Android4.1+系统,宣告了Flash在移动终端上的死亡。

传统的Web模式在处理高并发及实时性需求的时候,会遇到难以逾越的瓶颈,需要一种高效节能的双向通信机制来保证数据的实时传输。在此背景下,基于HTML5规范的、有Web TCP之称的 WebSocket应运而生。早期HTML5并没有形成业界统一的规范,各个浏览器和应用服务器厂商有着各异的类似实现,如IBM的MQTT、Comet开源框架等。直到2014年,HTML5终于尘埃落地,正式落实为实际标准规范,各个应用服务器及浏览器厂商逐步开始统一,在 JavaEE7中也实现了WebSocket协议。至此无论是客户端还是服务端的WebSocket都已完备。用户可以查阅HTML5规范,熟悉新的HTML协议规范及WebSocket支持

WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

 

WebSocket 有什么优点


开销少、时时性高、二进制支持完善、支持扩展、压缩更优。

  • 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
  • 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
  • 保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有* 状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
  • 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
  • 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
  • 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。

握手是怎么回事?


WebSocket 是独立的、创建在 TCP 上的协议。

Websocket 通过HTTP/1.1 协议的101状态码进行握手。

为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。

 

WebSocket 协议规范


WebSocket 是一个通信协议,它规定了一些规范和标准。它的协议标准为 RFC 6455,具体的协议内容可以在tools.ietf.org中查看。

协议共有 14 个部分,其中包括协议背景与介绍、握手、设计理念、术语约定、双端要求、掩码以及连接关闭等内容。

 

双端交互流程


客户端与服务端交互流程如下:客户端 - 发起握手请求 - 服务器接到请求后返回信息 - 连接建立成功 - 消息互通

所以,要解决的第一个问题就是握手问题。

握手 - 客户端


关于握手标准,在协议中有说明:

The opening handshake is intended to be compatible with HTTP-based
server-side software and intermediaries, so that a single port can be
used by both HTTP clients talking to that server and WebSocket
clients talking to that server. To this end, the WebSocket client's
handshake is an HTTP Upgrade request:

    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


In compliance with [RFC2616], header fields in the handshake may be
sent by the client in any order, so the order in which different
header fields are received is not significant.

WebSocket 握手时使用的并不是 WebSocket 协议,而是 HTTP 协议,握手时发出的请求可以叫做升级请求。客户端在握手阶段通过:Upgrade: websocket        Connection: Upgrade  2个头域告知服务端,要求将通信的协议转换为 websocket。

其中 Sec-WebSocket-Version、Sec-WebSocket-Protocol 这两个头域表明通信版本和协议约定,

Sec-WebSocket-Key 则作为一个防止无端连接的保障(其实并没有什么保障作用,因为 key 的值完全由客户端控制,服务端并无验证机制),其他几个头域则与 HTTP协议的作用一致。

握手 - 服务端


刚才只是客户端发出一个 HTTP 请求,表明想要握手,服务端需要对信息进行验证,确认以后才算握手成功(连接建立成功,可以双向通信),然后服务端会给客户端回复:"小老弟你好,没有内鬼,连接达成!"

服务端需要回复什么内容呢?
Status Code: 101 Web Socket Protocol Handshake

Sec-WebSocket-Accept: T5ar3gbl3rZJcRmEmBT8vxKjdDo=

Upgrade: websocket

Connection: Upgrade

首先,服务端会给出状态码,

101 状态码表示服务器已经理解了客户端的请求,

并且回复 Connection 和 Upgrade 表示已经切换成 websocket 协议。

Sec-WebSocket-Accept 则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。

这样,客户端与服务端就完成了握手操作,达成一致,使用 WebSocket 协议进行通信。

 

你来我往 - 数据交流


双方握手成功并确认协议后,就可以互相发送信息了。它们的信息是如何发送的呢?难道是:

client: Hello, server boy
server: Hello, client girl


跟我们在微信和 QQ 中发信息是一样的吗?

虽然我们看到的信息是这样的,但是在传输过程中可不是这样子的。传输这部也有相应的规定:

In the WebSocket Protocol, data is transmitted using a sequence of
frames. To avoid confusing network intermediaries (such as
intercepting proxies) and for security reasons that are further
discussed in Section 10.3, a client MUST mask all frames that it
sends to the server (see Section 5.3 for further details). (Note
that masking is done whether or not the WebSocket Protocol is running
over TLS.) The server MUST close the connection upon receiving a
frame that is not masked. In this case, a server MAY send a Close
frame with a status code of 1002 (protocol error) as defined in
Section 7.4.1. A server MUST NOT mask any frames that it sends to
the client. A client MUST close a connection if it detects a masked
frame. In this case, it MAY use the status code 1002 (protocol
error) as defined in Section 7.4.1. (These rules might be relaxed in
a future specification.)

The base framing protocol defines a frame type with an opcode, a
payload length, and designated locations for "Extension data" and
"Application data", which together define the "Payload data".
Certain bits and opcodes are reserved for future expansion of the
protocol.

协议中规定传输时并不是直接使用 unicode 编码进行传输,而是使用帧(frame),数据帧协议定义了带有操作码的帧类型,有效载荷长度,以及“扩展数据”和的指定位置应用程序数据”,它们共同定义“有效载荷数据”。某些位和操作码保留用于将来的扩展协议

 

websocket协议报文头格式:

帧由以下几部分组成:
FIN、RSV1、RSV2、RSV3、opcode、MASK、Payload length、Masking-key、Payload-Data。

它们的含义和作用如下:

1.FIN: 占 1bit

0:不是消息的最后一个分片
1:是消息的最后一个分片


2.RSV1, RSV2, RSV3:各占 1bit

一般情况下全为 0。当客户端、服务端协商采用 WebSocket 扩展时,这三个标志位可以非 0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用 WebSocket 扩展,连接出错。

3.Opcode: 4bit

%x0:表示一个延续帧。当 Opcode 为 0 时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片;
 
%x1:表示这是一个文本帧(text frame);
 
%x2:表示这是一个二进制帧(binary frame);
 
%x3-7:保留的操作代码,用于后续定义的非控制帧;
 
%x8:表示连接断开;
 
%x9:表示这是一个心跳请求(ping);
 
%xA:表示这是一个心跳响应(pong);
 
%xB-F:保留的操作代码,用于后续定义的控制帧。


4.Mask: 1bit

表示是否要对数据载荷进行掩码异或操作。

0:否
1:是


5.Payload length: 7bit or (7 + 16)bit or (7 + 64)bit

表示数据载荷的长度。

0~126:数据的长度等于该值;
 
126:后续 2 个字节代表一个 16 位的无符号整数,该无符号整数的值为数据的长度;
 
127:后续 8 个字节代表一个 64 位的无符号整数(最高位为 0),该无符号整数的值为数据的长度。


6.Masking-key: 0 or 4bytes

当 Mask 为 1,则携带了 4 字节的 Masking-key;
 
当 Mask 为 0,则没有 Masking-key。
 
掩码算法:按位做循环异或运算,先对该位的索引取模来获得 Masking-key 中对应的值 x,然后对该位与 x 做异或,从而得到真实的 byte 数据。

注意:掩码的作用并不是为了防止数据泄密,而是为了防止早期版本的协议中存在的代理缓存污染攻击(proxy cache poisoning attacks)等问题。

7.Payload Data: 载荷数据

双端接收到数帧之后,就可以根据数据帧各个位置的值进行处理或信息提取。
 

掩码


这里要注意的是从客户端向服务端发送数据时,需要对数据进行掩码操作;

从服务端向客户端发送数据时,不需要对数据进行掩码操作。如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接。如果Mask是1,那么在Masking-key中会定义一个掩码键(masking key),并用这个掩码键来对数据载荷进行反掩码。所有客户端发送到服务端的数据帧,Mask都是1。

保持连接


刚才提到 WebSocket 协议是双向通信的,那么一旦连接上,就不会断开了吗?

事实上确实是这样,但是服务端不可能让所有的连接都一直保持,所以服务端通常会在一个定期的时间给客户端发送一个 ping 帧,而客户端收到 Ping 帧后则回复一个 Pong 帧,如果客户端不响应,那么服务端就会主动断开连接。

opcode 帧为 0x09 则代表这是一个 Ping ,为 0x0A 则代表这是一个 Pong。
 

 

webSocket和http1.1的区别

 

  • http 只支持客户端请求服务器响应,websocket 支持服务端请求客户端响应。http是半双工,websocket 是全双工。
  • 效率问题:服务器发送信息到客户端的时候不必再带有head的部分信息了。与http的长链接通信来比,这种方式不仅能降低服务器的压力,而且信息当中也减少了部分多余的信息。

 

http 是基于 tcp 的,当然是双工。
你应该理解双工的两种模式:半双工(http 1.0/1.1),全双工(http 2.0)。

半双工:同一时间内,链接上只能有一方发送数据,另一方接受数据。
http 1.0 是短连接模式,每个请求都要建立新的 tcp 连接,然后 C 发送,S 响应,断开,下一个请求重复此步骤。
http 1.1 是长连接模式,可以多路复用,建立 tcp 连接,资源1 C 发送,S响应,资源2 C发送 S响应,资源3 C发送 S响应,免去了要为每个资源都建立一次 tcp 的开销。

全双工:同一时间内,两端都可以发送或接受数据
http 2.0 资源1 C发送,不必等待 S响应就可以继续发送 资源2 的请求,可以并发的发送,一边发,一边收。

 

webSocket和http2.0的区别

 

HTTP/2 是什么

这篇文章讲的比较详细:

  • 它是 google 提出的开源协议,旨在提高网络传输效率

  • 它是二进制协议

  • 它采用多路复用解决 HTTP 1.1 的 head-of-line blocking (HOL Blocking)问题(较慢的请求阻塞其它请求的问题)

  • 它通过压缩 http 头提高效率

  • 它支持全双工,因此可以使用 Server Push 推送到客户端

与 websocket 的比较

  HTTP/2 WebSocket
Headers 头 Compressed (HPACK) 请求头部压缩 None 无
Binary 二进制 Yes Binary or Textual 二进制或文本都支持
Multiplexing多路复用 Yes Yes
Prioritization优先化 Yes No
Compression压缩 Yes Yes
Direction 方向 Client/Server + Server Push (Server Push只能浏览器消化,不支持API,也就是代码无法使用) Bidirectional 双向
Full-duplex全双工 Yes Yes

问题

  • HTTP/2 Server Push 不能被代码使用,所以还得配合SSE(Server sent event),无论从coder还是运维的角度来看,这混搭增加了复杂度。
  • IE对http2以及SSE都支持的不好
  • HTTP/2 连接不确定性会永远保持连接,而websocket有onclose事件,对代码友好

    HTTP/2 Servers are encouraged to maintain open connections for as long as possible but are permitted to terminate idle connections if necessary. When either endpoint chooses to close the transport-layer TCP connection, the terminating endpoint SHOULD first send a GOAWAY (Section 6.8) frame so that both endpoints can reliably determine whether previously sent frames have been processed and gracefully complete or terminate any necessary remaining tasks.

  • 多个tab页windows页可能共用一个HTTP/2连接,你无法知道Server Push来自哪一个
  • 由于多路复用,以前基于HTTP 1.1的网站提速技巧Domain sharding(由于浏览器限制同一域名最多连接数)将不再起作用。

实际实现状态

HTTP2 vs Websocket 显而易见,http2 在浏览器服务器上限制颇多,而 websocket 基本普及。

再来看看SSE, 支持程度仍然不如websocket。

结论:

HTTP/2 完全不能替代websocket,各有各的适用场景。我个人偏好,做app还是偏向于websocket,参看我的另外一博文介绍Meteor.

 

webSocket和socket的区别

 

就像Java和JavaScript,并没有什么太大的关系,但又不能说完全没关系。可以这么说:

命名方面,Socket是一个深入人心的概念,WebSocket借用了这一概念;
使用方面,完全两个东西。
总之,可以把WebSocket想象成HTTP,HTTP和Socket什么关系,WebSocket和Socket就是什么关系。

 

websocket 原理

http 短连接客户端请求服务器端的时候,客户端会启动一个端口来和服务器交互,服务器响应之后,端口就释放了。

http 长连接客户端请求服务器端的时候,客户端每次会启动一个新端口来和服务器交互,服务器响应之后,端口就释放了。

websocket客户端请求服务器的时候,客户端会启动一个端口来和服务器交互,服务器响应之后,端口不释放。并且可以监听服务器的请求。

WebSocket模式客户端与服务器请求响应模式如下图

这里写图片描述

上图对比可以看出,相对于传统HTTP每次请求-应答都需要客户端与服务端建立连接的模式,WebSocket是类似Socket的TCP长连接通讯模式。一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端中断连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。

相比HTTP长连接,WebSocket有以下特点:

  • 是真正的全双工方式,建立连接后客户端与服务器端是完全平等的,可以互相主动请求。而HTTP长连接基于HTTP,是传统的客户端对服务器发起请求的模式。
  • HTTP长连接中,每次数据交换除了真正的数据部分外,服务器和客户端还要大量交换HTTP header,信息交换效率很低。Websocket协议通过第一个request建立了TCP连接之后,之后交换的数据都不需要发送 HTTP header就能交换数据,这显然和原有的HTTP协议有区别所以它需要对服务器和客户端都进行升级才能实现(主流浏览器都已支持HTML5)。此外还有 multiplexing、不同的URL可以复用同一个WebSocket连接等功能。这些都是HTTP长连接不能做到的。

下面再通过客户端和服务端交互的报文对比WebSocket通讯与传统HTTP的不同点:

在客户端,new WebSocket实例化一个新的WebSocket客户端对象,请求类似 ws://yourdomain:port/path 的服务端WebSocket URL,客户端WebSocket对象会自动解析并识别为WebSocket请求,并连接服务端端口,执行双方握手过程,客户端发送数据格式类似:

GET /webfin/websocket/ HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: http://localhost:8080
Sec-WebSocket-Version: 13

可以看到,客户端发起的WebSocket连接报文类似传统HTTP报文,Upgrade:websocket参数值表明这是WebSocket类型请求,Sec-WebSocket-Key是WebSocket客户端发送的一个 base64编码的密文,要求服务端必须返回一个对应加密的Sec-WebSocket-Accept应答,否则客户端会抛出Error during WebSocket handshake错误,并关闭连接。

服务端收到报文后返回的数据格式类似:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=

Sec-WebSocket-Accept的值是服务端采用与客户端一致的密钥计算出来后返回客户端的,HTTP/1.1 101 Switching Protocols表示服务端接受WebSocket协议的客户端连接,经过这样的请求-响应处理后,两端的WebSocket连接握手成功, 后续就可以进行TCP通讯了。用户可以查阅WebSocket协议栈了解WebSocket客户端和服务端更详细的交互数据格式。

在开发方面,WebSocket API 也十分简单:只需要实例化 WebSocket,创建连接,然后服务端和客户端就可以相互发送和响应消息。在WebSocket 实现及案例分析部分可以看到详细的 WebSocket API 及代码实现

 

 

 

websocket  不同协议实现之间的区别

SpringWebSocket, Tomcat WebSocket, Jetty WebSocket, Java-WebSocket.jar 的区别:

 

    Tomcat和Jetty本身作为J2EE的容器而存在,所以Tomcat以及Jetty中对于Websocket协议的支持,都是基于Java自身所定义的接口进行的支持,各容器对WebSocket的具体实现方式不同,但常用的WebSocket接口,如@ServerPoint(定义一个WebSocket接口)等这样的应用层规范,由于是Java自身已经定义的接口规范,所以在无需了解具体的容器实现时,只需要关注Java自身对于WebSocket的实现即可;(当然在具体使用时,需要确认当前容器的版本是否支持WebSocket以及是否引入的有对应WebSocket jar等,毕竟容器才是对外socket协议的具体实现交互类,由于Tomcat以及Jetty本身的lib下是存在对应的WebSocket具体的实现jar的,所以项目中进行引用的时候,需要设置为非runtime运行时使用的jar,或者直接将对应的容器lib直接引入到项目的librares中即可)

    Java-WebSocket.jar具体是做什么?

    因为Java-WebSocket.jar是github上关于java websocket的项目start数量最多的一个项目,所以,初次使用或者不熟悉Java WebSocket的同学,一般都会直接按照Java-WebSocket的实例Demo进行socket协议的效果验证,结果在具体的J2EE web开发中,却会发现一些莫名的问题,比如:虽然我引用了Java-WebSocket.jar但最终服务跑起来后,感觉和他并没有神马关系;反而会出现很多Tomcat容器不兼容等的容器异常;那么此时则必须了解下,对应的Java-WebSocket是做什么滴了,Java-WebSocket是github上一个开源大神写的关于Java实现WebSocket的一个开源组件,使用它可以做到Java中使用WebSocket协议,但是!具体的J2EE项目中,Tomcat中所实现的WebSocket协议的具体实现,则跟当前的Java-WebSocket.jar没有一毛钱关系,换句话说,如果你肯定是基于容器去启动服务的情况下,那么要Java-webSocket.jar于不要这个Jar问题不大,因为Tomcat容器已经帮你实现了一套WebSocket的具体实现了,但是!如果你的服务

不是基于jetty,Tomcat等容器去启动的话,那么你还想实现WebSocket效果,此时的Java-WebSocket.jar则是最佳的选择,因为它可以帮你实现Main函数启动时定义WebSocket端口等一系列事情(具体可参考github地址:https://github.com/TooTallNate/Java-WebSocket)注意:Java-WebSocket.jar对socket协议的具体实现,当然也是基于Java自身的WebSocket API规范来实现的了;

    Spring-WebSocket是做什么?

    既然容器已经帮我们实现了关于WebSocket协议的具体实现,那么为什么我还要引入Spring-WebSocket?我要它做什么?yes,是的,如果你只是单纯的使用Tomcat所实现的WebSocket时,直接使用@ServerPoint定义WebSocket接口,然后直接使用,当然是没有问题的撒,但是!如果你的项目是基于Spring做的开发,比如你引入了SpringMVC,还引入了SpringSecurity,那么问你个问题,既然Spring已经帮你管理了Controller控制层的访问(基于请求拦截),也帮你做了SpringSecurity安全请求认证,那么,为什么你定义一个WebSocket接口,Spring就会直接帮你映射到这个WebSocket的控制器上吗?答案是:当然不会,因为SpringMVC默认是做基于Http的拦截的,如果你想使用WebSocket协议,那么你只需要引入Spring-WebSocket的jar包集就行,它会帮你查找被@ServerPoint所定义的socket接口类,然后将该类定义为Socket的实现类,当然具体的Socket协议的规范实现,还是容器帮你进行实现;除此之外,既然Socket接口也是归属于Spring管理的,那么针对Socket协议,Spring-Socket也帮你实现了一整套的安全规范,可以设置拦截,是否允许非指定的域名访问,等一系列效果;(建议深度使用Spring的项目可以引入Spring-Socket做一整套的控制,因为Spring Socket的确帮你实现了很多一整套的安全认证的功能,容器只是基于WebSocket的具体实现罢了,所以,各自分工不同,各个角色所做的事情,使用socket时,此处需要牢记,只有明白了各个角色所做的解耦合的事情后,出现异常问题,才更加方便和有思绪的进行排查;注:LZ所实现的WebSocket也是基于Spring socket的实现,网上也有一些基于Spring的项目,使用非spring-socket的实现,感兴趣的小伙伴可以试一下,Spring本身应该也是支持开启具体参数后,然后支持socket协议的控制层的直接传输,具体没有做过验证实现;不想重新搞的小伙伴直接按照上述的思路和角色开发,肯定是没毛病的;    祝各位宝宝春梦了无痕;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(websocket 协议)