websocket系列:一简介

一 序

在读<<netty权威指南>>第11章websocket协议开发的时候,觉得作者对于这块介绍相对简单,对于相关知识不熟悉的,不适合上来就用netty来实现websocket。打算分为三步:1websocket入门介绍。2.Tomcat实现websocket。3.netty实现websocket

主要包含两部分:1websocket协议介绍,2 Tomcat实现简单websocket.

二 websocket

2.1 背景

由于历史原因,在创建一个具有双向通信机制的 web 应用程序时,需要利用到 HTTP 轮询的方式。围绕轮询产生了 “短轮询” 和 “长轮询”。在特定的时间间隔(比如1秒钟)由浏览器自动发出请求,将服务器的消息主动的拉回来,在这种情况下,我们需要不断的向服务器发送请求,然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源。

而最比较新的技术去做轮询的效果是Comet – 用了AJAX。但这种技术虽然可达到全双工通信,但依然需要发出请求(reuqest)。

2.2 websocket简介

Websocket是html5提出的一个协议规范,参考rfc6455。

websocket约定了一个通信的规范,通过一个握手的机制,客户端(浏览器)和服务器(webserver)之间能建立一个类似tcp的连接,从而方便c-s之间的通信。在websocket出现之前,web交互一般是基于http协议的短连接或者长连接。

WebSocket是为解决客户端与服务端实时通信而产生的技术。websocket协议本质上是一个基于tcp的协议,是先通过HTTP/HTTPS协议发起一条特殊的http请求进行握手后创建一个用于交换数据的TCP连接,此后服务端与客户端通过此TCP连接进行实时通信。


websocket协议参见https://tools.ietf.org/html/rfc6455

2.3 协议概览


  1. 打开握手
  2. 数据传递
  3. 关闭握手

在websocket协议发展过程中前前后后就出现了多个版本的握手协议,这里列举下。

基于flash的握手协议

使用场景是IE的多数版本,因为IE的多数版本不都不支持WebSocket协议,以及FF、CHROME等浏览器的低版本,还没有原生的支持WebSocket。此处,server唯一要做的,就是准备一个WebSocket-Location域给client,没有加密,可靠性很差。

基于md5加密方式的握手协议

 客户端请求:

  • GET /demo HTTP/1.1
    Host: example.com
    Connection: Upgrade
    Sec-WebSocket-Key2: 
    Upgrade: WebSocket
    Sec-WebSocket-Key1: 

    Origin: http://www.qixing318.com
    [8-byte security key]

服务端返回:

HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://www.qixing318.com
WebSocket-Location: ws://example.com/demo
[16-byte hash response]

其中 Sec-WebSocket-Key1,Sec-WebSocket-Key2 和 [8-byte security key] 这几个头信息是web server用来生成应答信息的来源,依据 draft-hixie-thewebsocketprotocol-76 草案的定义。

基于sha加密方式的握手协议
也是目前见的最多的一种方式,这里的版本号目前是需要13以上的版本。其中 server就是把客户端上报的key拼上一段GUID( “258EAFA5-E914-47DA-95CA-C5AB0DC85B11″),拿这个字符串做SHA-1 hash计算,然后再把得到的结果通过base64加密,最后再返回给客户端。

客户端发出的握手信息类似:

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

服务端回应的握手信息类式:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Protocol: chat

客户端的握手请求由 请求行(Request-Line) 开始。客户端的回应由 状态行(Status-Line) 开始。请求行和状态行的生产式见 RFC2616

首行之后的部分,都是没有顺序要求的 HTTP Headers。

一旦客户端和服务端都发送了它们的握手信息,握手过程就完成了,随后就开始数据传输部分。因为这是一个双向的通信,所以客户端和服务端都可以首先发出信息。

在数据传输时,客户端和服务器都使用 “消息 Message” 的概念去表示一个个数据单元,而消息又由一个个 “帧 frame” 组成。这里的帧并不是对应到具体的网络层上的帧。

和 TCP 以及 HTTP 之间的关系

WebSocket 是一个独立的基于 TCP 的协议,它与 HTTP 之间的唯一关系就是它的握手请求可以作为一个升级请求(Upgrade request)经由 HTTP 服务器解释(也就是可以使用 Nginx 反向代理一个 WebSocket。



2.4 握手过程

握手部分的设计目的就是兼容现有的基于 HTTP 的服务端组件(web 服务器软件)或者中间件(代理服务器软件)。这样一个端口就可以同时接受普通的 HTTP 请求或则 WebSocket 请求了。为了这个目的,WebSocket 客户端的握手是一个 HTTP 升级版的请求(HTTP Upgrade request):
       Host 头中的服务器名称可以让客户端标识出哪个站点是其需要访问的,也使得服务器得知哪个站点是客户端需要请求的。
  • Upgrade:WebSocket
    表示这是一个特殊的 HTTP 请求,请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket 协议。
  • 源标识 Origin可以预防在浏览器中运行的脚本,在未经 WebSocket 服务器允许的情况下,对其发送跨域的请求。
  • Sec-WebSocket-Key
    是一段浏览器base64加密的密钥,server端收到后需要提取Sec-WebSocket-Key 信息,然后加密。
  • Sec-WebSocket-Accept
    服务器端在接收到的Sec-WebSocket-Key密钥后追加一段神奇字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,并将结果进行sha-1哈希,然后再进行base64加密返回给客户端(就是Sec-WebSocket-Key)。如果加密算法错误,客户端在进行校检的时候会直接报错。如果握手成功,则客户端侧会出发onopen事件。 

    function encry($req)
    {
        $key = $this->getKey($req);
        $mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 
        # 将 SHA-1 加密后的字符串再进行一次 base64 加密
        return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
    }

  • Sec-WebSocket-Protocol
    表示客户端请求提供的可供选择的子协议,及服务器端选中的支持的子协议。
  • Sec-WebSocket-Version: 13
    客户端在握手时的请求中携带,这样的版本标识,表示这个是一个升级版本,现在的浏览器都是使用的这个版本。
  • HTTP/1.1 101 Switching Protocols
    101为服务器返回的状态码,所有非101的状态码都表示handshake并未完成。

连接初始的初始状态被定义为 “连接中 CONNECTING”。

一旦客户端的握手请求发送完成后,客户端必须等待服务端的握手响应,在此期间不可以向服务器传输任何数据。

客户端也必须按照服务端生成 |Sec-WebSocket-Accept| 头字段值的方式也生成一个字符串,与服务端回传的进行对比,如果不同就标记连接为失败的。

如果服务端的响应符合上述的描述的话,那么就说明 WebSocket 的连接已经建立了,并且连接的状态变为 “OPEN 状态”

2.5 数据帧

Websocket协议通过序列化的数据帧传输数据。数据封包协议中定义了opcode、payload length、Payload data等字段。其中要求:

  1. 客户端向服务器传输的数据帧必须进行掩码处理:服务器若接收到未经过掩码处理的数据帧,则必须主动关闭连接。
  2. 服务器向客户端传输的数据帧一定不能进行掩码处理。客户端若接收到经过掩码处理的数据帧,则必须主动关闭连接。

针对上情况,发现错误的一方可向对方发送close帧(状态码是1002,表示协议错误),以关闭连接。
具体数据帧格式如下图所示:


  • FIN
    标识是否为此消息的最后一个数据包,占 1 bit
  • RSV1, RSV2, RSV3: 用于扩展协议,一般为0,各占1bit
  • Opcode
    数据包类型(frame type),占4bits
    0x0:标识一个中间数据包
    0x1:标识一个text类型数据包
    0x2:标识一个binary类型数据包
    0x3-7:保留
    0x8:标识一个断开连接类型数据包
    0x9:标识一个ping类型数据包
    0xA:表示一个pong类型数据包
    0xB-F:保留
  • MASK:占1bits
    用于标识PayloadData是否经过掩码处理。如果是1,Masking-key域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。
  • Payload length
    Payload data的长度,占7bits,7+16bits,7+64bits:
    • 如果其值在0-125,则是payload的真实长度。
    • 如果值是126,则后面2个字节形成的16bits无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。
    • 如果值是127,则后面8个字节形成的64bits无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。

这里的长度表示遵循一个原则,用最少的字节表示长度(尽量减少不必要的传输)。举例说,payload真实长度是124,在0-125之间,必须用前7位表示;不允许长度1是126或127,然后长度2是124,这样违反原则。

  • Payload data
    应用层数据

    server解析client端的数据

    接收到客户端数据后的解析规则如下:

  • 1byte
    • 1bit: frame-fin,x0表示该message后续还有frame;x1表示是message的最后一个frame
    • 3bit: 分别是frame-rsv1、frame-rsv2和frame-rsv3,通常都是x0
    • 4bit: frame-opcode,x0表示是延续frame;x1表示文本frame;x2表示二进制frame;x3-7保留给非控制frame;x8表示关 闭连接;x9表示ping;xA表示pong;xB-F保留给控制frame
  • 2byte
    • 1bit: Mask,1表示该frame包含掩码;0表示无掩码
    • 7bit、7bit+2byte、7bit+8byte: 7bit取整数值,若在0-125之间,则是负载数据长度;若是126表示,后两个byte取无符号16位整数值,是负载长度;127表示后8个 byte,取64位无符号整数值,是负载长度
    • 3-6byte: 这里假定负载长度在0-125之间,并且Mask为1,则这4个byte是掩码
    • 7-end byte: 长度是上面取出的负载长度,包括扩展数据和应用数据两部分,通常没有扩展数据;若Mask为1,则此数据需要解码,解码规则为- 1-4byte掩码循环和数据byte做异或操作。

2.6 关闭握手

关闭握手的操作也很简单。任意一端都可以选择关闭握手过程。主动关闭的一方向另一方发送一个关闭类型的数据包,对方收到此数据包之后,再回复一个相同类型的数据包,关闭完成。关闭类型数据包遵守封包协议,Opcode为0x8,Payload data可以用于携带关闭原因或消息。

2.7websocket的事件响应

以上的Opening Handshake、Data Framing、Closing Handshake三个步骤其实分别对应了websocket的三个事件:


  • onopen 当接口打开时响应
  • onmessage 当收到信息时响应
  • onclose 当接口关闭时响应

任何程序语言的websocket api都至少要提供上面三个事件的api接口, 有的可能还提供的有onerror事件的处理机制。

websocket 在任何时候都会处于下面4种状态中的其中一种:

  • CONNECTING (0):表示还没建立连接;
  • OPEN (1): 已经建立连接,可以进行通讯;
  • CLOSING (2):通过关闭握手,正在关闭连接;
  • CLOSED (3):连接已经关闭或无法打开;
下一篇我们将用Tomcat实现websocket的demo。

参考:

http://www.jianshu.com/p/867274a5e054

https://www.cnblogs.com/lizhenghn/p/5155933.html

https://www.cnblogs.com/tinywan/p/5894403.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值