以前本身一我的负责完成了公司的消息推送服务,和移动端配合完成了扫码登陆、订单消息推送、活动消息广播等功能。为了加深本身对Websocket协议的理解,本身经过进行抓包的方式学习了一番。如今分享出来,但愿对你们能有所帮助。javascript
(1)F12进入控制台,点击Network,选中ws栏,注意选中Filter。
java
(2)刷新页面会获得一个ws连接。
web
(3)点击连接能够查看连接详情
chrome
注意红框标出的信息,后面会详细说明。
(4)固然也能够切换到Frames查看发出和接收的消息,可是很是的简陋,只能看到消息内容,数据长度和时间
浏览器
(1)打开Fiddler,点开菜单栏的Rules,选择Customize Rules...
服务器
(2)这时会打开CustomRules.js文件,在class Handlers中加入如下代码websocket
static function OnWebSocketMessage(oMsg: WebSocketMessage) { // Log Message to the LOG tab FiddlerApplication.Log.LogString(oMsg.ToString()); }
(3)保存后就能够在Fiddler右边栏的Log标签里,看到WebSocket的数据包
下列图中红框标出的Client.1表明客户端发出的第一条消息;对应的Server.1表明服务端发出的第一条消息。MessageType:Text表明正常的通话消息;Close表明会话关闭。
客户端发出的消息:
网络
服务端发出的消息:
socket
而后咱们会发现每次会话关闭都是由客户端发起的:
tcp
相对于Chrome控制台来讲Fiddler抓包更加详细一些,能知道会话消息是由客户端仍是服务端发出而且能知道消息类型。可是这仍然知足不了深刻理解学习Websocket协议的目的。若是是处理HTTP、HTTPS,仍是用Fiddler。其余协议好比TCP,UDP 就用WireShark。TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP、Websocket是应用层协议,主要解决如何包装数据。由于应用层是在传输层的基础上包装数据,因此咱们仍是从底层开始了解Websocket究竟是个啥?是如何工做的?
WireShark(前称Ethereal)是一个网络封包分析软件。网络封包分析软件的功能是撷取网络封包,并尽量显示出最为详细的网络封包资料。WireShark抓包是根据TCP/IP五层协议来的,也就是物理层、数据链路层、网络层、传输层、应用层。咱们主要关注传输层和应用层。
咱们都知道,TCP创建链接时,会有三次握手过程。下图是WireShark截获到的三次握手的三个数据包(虽然叫数据包,可是三次握手包是没有数据的)。
点击上图中的数据包就能够查看每一个数据包的详情,这里咱们须要明确几个概念才能看懂每一个数据包表明啥意义:
SYN:同步比特,创建链接。
ACK:确认比特,置1表示这是一个确认的TCP包,0则不是。
PSH:推送比特,当发送端PSH=1时,接收端应尽快交付给应用进程。
能够看到咱们打开的Transmission Control Protocol即为传输层(Tcp)
SYN置为1,客户端向服务端发送链接请求包。
服务器收到客户端发过来的TCP报文,由SYN=1知道客户端要求创建联机,向客户端发送一个SYN=1,ACK=1的TCP报文,将确认序号设置为客户端的序列号加1。
客户端接收到服务器发过来的包后检查确认序列号是否正确,即第一次发送的序号+1,以及标志位ACK是否为1。若正确则再次发送确认包,ACK标志为1。连接创建成功,能够发送数据了。
紧接着是一次Http请求(第四个包),说明Http的确是使用Tcp创建链接的。
先来看传输层(Tcp): PSH(推送比特)置1,ACK置1,PSH置1说明开始发送数据,同时发送数据ACK要置1,由于须要接收到这个数据包的端给予确认。PSH为1的状况,通常只出如今 DATA内容不为0的包中,也就是说PSH为1表示的是有真正的TCP数据包内容被传递。
再来看应用层(Http):这是一次特殊的Http请求,为何是一次特殊的Http请求呢?Http请求头中Connection:Upgrade Upgrade:websocket,Upgrade表明升级到较新的Http协议或者切换到不一样的协议。很明显WebSocket使用此机制以兼容的方式与HTTP服务器创建链接。WebSocket协议有两个部分:握手创建升级后的链接,而后进行实际的数据传输。首先,客户端经过使用Upgrade: WebSocket和Connection: Upgrade头部以及一些特定于协议的头来请求WebSocket链接,以创建正在使用的版本并设置握手。服务器,若是它支持协议,回复与相同Upgrade: WebSocket和Connection: Upgrade标题,并完成握手。握手完成后,数据传输开始。这些信息在前面的Chrome控制台中也能够看到。
请求:
响应:
响应状态码 101 表示服务器已经理解了客户端的请求,在发送完这个响应后,服务器将会切换到在Upgrade请求头中定义的那些协议。
由此咱们能够总结出:
Websocket协议本质上是一个基于TCP的协议。创建链接须要握手,客户端(浏览器)首先向服务器(web server)发起一条特殊的http请求,web server解析后生成应答到浏览器,这样子一个websocket链接就创建了,直到某一方关闭链接。
通讯协议格式是WebSocket格式,服务器端采用Tcp Socket方式接收数据,进行解析,协议格式以下:
首先咱们须要知道数据在物理层,数据链路层是以二进制进行传递的,而在应用层是以16进制字节流进行传输的。
第一个字节:
FIN:1位,用于描述消息是否结束,若是为1则该消息为消息尾部,若是为零则还有后续数据包;
RSV1,RSV2,RSV3:各1位,用于扩展定义的,若是没有扩展约定的状况则必须为0
OPCODE:4位,用于表示消息接收类型,若是接收到未知的opcode,接收端必须关闭链接。
Webdocket数据帧中OPCODE定义:
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义,为之后的非控制帧保留
0x8表示链接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义,为之后的控制帧保留
第二个字节:
MASK:1位,用于标识PayloadData是否通过掩码处理,客户端发出的数据帧须要进行掩码处理,因此此位是1。数据须要解码。
PayloadData的长度:7位,7+16位,7+64位
若是其值在0-125,则是payload的真实长度。
若是值是126,则后面2个字节造成的16位无符号整型数的值是payload的真实长度。
若是值是127,则后面8个字节造成的64位无符号整型数的值是payload的真实长度。
上图是客户端发送给服务端的数据包,其中PayloadData的长度为二进制:01111110——>十进制:126;若是值是126,则后面2个字节造成的16位无符号整型数的值是payload的真实长度。也就是圈红的十六进制:00C1——>十进制:193 byte。因此PayloadData的真实数据长度是193 bytes;
根据咱们的分析,客户端到服务端数据包的websocket帧图应该为:
咱们再来抓包分析一下服务器到客户端的数据包:
能够发现服务器发送给客户端的数据包中第二个字节中MASK位为0,这说明服务器发送的数据帧未通过掩码处理,这个咱们从客户端和服务端的数据包截图中也能够发现,客户端的数据被加密处理,而服务端的数据则没有。(若是服务器收到客户端发送的未经掩码处理的数据包,则会自动断开链接;反之,若是客户端收到了服务端发送的通过掩码处理的数据包,也会自动断开链接)。
掩码处理:
未掩码处理:
根据咱们的分析,服务端到客户端数据包的websocket帧图应该为:
如上图所示,TCP保活报文老是成对出现,包括TCP保活探测报文和TCP保活探测确认报文。
TCP保活探测报文是将以前TCP报文的确认序列号减1,并设置1个字节,内容为“00”的应用层数据,以下图所示:
TCP保活探测确认报文就是对保活探测报文的确认,其报文格式以下:
由于Websocket经过Tcp Socket方式工做,如今考虑一个问题,在一次长链接中,服务器怎么知道消息的顺序呢?这就涉及到tcp的序列号(Sequence Number)和确认号(Acknowledgment Number)。个人理解是序列号是发送的数据长度;确认号是接收的数据长度。这样讲比较抽象,咱们从TCP三次握手开始(结合下图)详细分析一下。
包1:
TCP会话的每一端的序列号都从0开始,一样的,确认号也从0开始,由于此时通话还未开始,没有通话的另外一端须要确认
包2:
服务端响应客户端的请求,响应中附带序列号0(因为这是服务端在该次TCP会话中发送的第一个包,因此序列号为0)和相对确认号1(代表服务端收到了客户端发送的包1中的SYN)。须要注意的是,尽管客户端没有发送任何有效数据,确认号仍是被加1,这是由于接收的包中包含SYN或FIN标志位。
包3:
和包2中同样,客户端使用确认号1响应服务端的序列号0,同时响应中也包含了客户端本身的序列号(因为服务端发送的包中确认收到了客户端发送的SYN,故客户端的序列号由0变为1)此时,通讯的两端的序列号都为1。
包4:客户端——>服务器
这是流中第一个携带有效数据的包(确切的说,是客户端发送的HTTP请求),序列号依然为1,由于到上个包为止,尚未发送任何数据,确认号也保持1不变,由于客户端没有从服务端接收到任何数据。须要注意的是,包中有效数据的长度为505字节
包5:服务器——>客户端
当上层处理HTTP请求时,服务端发送该包来确认客户端在包4中发来的数据,须要注意的是,确认号的值增长了505(505是包4中有效数据长度),变为506,简单来讲,服务端以此来告知客户端端,目前为止,我总共收到了506字节的数据,服务端的序列号保持为1不变。
包6:服务器——>客户端
这个包标志着服务端返回HTTP响应的开始,序列号依然为1,由于服务端在该包以前返回的包中都不带有有效数据,该包带有129字节的有效数据。
包7:
因为上个数据包的发送,TCP客户端的确认序列号增加至130,从服务端接收了129字节的数据,客户端的确认号由1增加至130
理解了序列号和确认序列号是怎么工做的以后,咱们也就知道“TCP保活探测报文是将以前TCP报文的确认序列号减1,并设置1个字节”为何要这么搞了。减一再加一,是为了保证一次链接中keep alive不影响序列号和确认序列号。Keep alive 中的1byte 00的数据并非真正要传递的数据,而是tcp keep alive约定俗称的规则。
总结:
WebSocket 是一个独立的基于 TCP 的协议,它与 HTTP 之间的惟一关系就是它的握手请求能够做为一个升级请求(Upgrade request)经由 HTTP 服务器解释。再严谨一点:WebSocket是一个网络通信协议, 只要理解上面的数据帧格式和握手流程, 均可以完成基于websokect的即时通信。