热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WebSocket协议初探

 公司项目使用WebSocket作为主要的请求方式,知其然也要知其所以然,会用也需要知道它的基本原理,所以写此文章分享下自己的浅见,文章主要包括以下内容:WebSocket是什么W

 

公司项目使用WebSocket作为主要的请求方式,知其然也要知其所以然,会用也需要知道它的基本原理,所以写此文章分享下自己的浅见,文章主要包括以下内容:



  • WebSocket是什么

  • WebSocket和Socket区别

  • 建立连接

  • 数据帧格式

  • 发送数据

聊天Demo代码: github.com/madaoCN/Web… 包含tornado写的 Server 和 Client 脚本 和 简单ws使用实例的iOS代码


WebSocket是什么

WebSocket是一种在单个 TCP 连接上进行 全双工 通信的协议,WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。WebSocket协议在2011年由 IETF 标准化为 RFC 6455


1. 优势



  • 全双工,服务器可以主动推送数据给客户端,持久连接,实时性强

  • 省流量,协议控制的数据包头部较小,只需要进行一次完整的http握手,后续升级为WebSocket进行通信,而HTTP的header一般有几十字节,而且每次通信都需要携带完整的头部

  • 不仅可以发送文本,也可以发送二进制,对二进制数据比较友好


WebSocket和Socket联系

Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口, 而WebSocket和Http一样是属于应用层协议。

d7717b58f75b4f77bd17bed13a7e6ffa.png

当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。


建立连接

Websocket 握手过程复用了HTTP协议的信道,然后对服务进行升级,后续就使用Websocket协议进行通信,所以只需要一次 HTTP 握手,服务端就能一直与客户端保持通信,直到关闭连接。

一、升级请求

GET / HTTP/1.1Host: 192.168.1.250:6767Sec-WebSocket-Version: 13Upgrade: websocketSec-WebSocket-Key: cNtBvwgrxXtqDppb/0mcMw==Connection: UpgradeOrigin: http://192.168.1.250:6767

与HTTP报文不太一样的主要是 Connection: Upgrade : 标识要升级协议 Upgrade: websocket : 升级到 Websocket 协议 Sec-WebSocket-Version: 13 : 标明WebSocket协议的版本号 Sec-WebSocket-Key: cNtBvwgrxXtqDppb/0mcMw== 随机生成的 ,与服务端响应 Sec-WebSocket-Accept 字段对应,提供基本的防护,防止恶意或者无意的连接,

二、服务端响应报文

HTTP/1.1 101 Switching ProtocolsUpgrade: websocketSec-Websocket-Accept: hutW70GFRNI1vI45roqiU0Lu33A=Server: TornadoServer/5.0.2Connection: Upgrade

Sec-Websocket-Accept : 是根据客户端 Sec-WebSocket-Key 计算而来的

计算方法为:

258EAFA5-E914-47DA-95CA-C5AB0DC85B11

简单的python代码验证

#coding=utf8import hashlibimport base64if __name__ == "__main__": sec_key = "cNtBvwgrxXtqDppb/0mcMw==" static_key = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" contact_key = sec_key + static_key sha1_rt = hashlib.sha1(contact_key).digest() print sec_key print base64.encodestring(sha1_rt)

a5aaeab8680e61dec1069d5000b60f8d.png

三、抓包验证

使用WireShark对WebSocket连接过程抓包

442c807e74aa0f32a4d3dae6f889d7d8.png

很直观的可以看到 HTTP三次握手 -> 升级协议 -> 使用WebSocket通信 的过程

6cefb3bc28c82a73b33e4bcd8057b7c9.png

abdc19d5e618129a6ceea2afde9eb873.png

Websocket升级协议报文也与上文一致,大家可以自行运行Demo代码,然后使用WireShark进行抓包验证请求过程和报文


数据帧格式

如果我们数据帧格式都不清楚的话,更遑论说了解WebSocket协议了


数据帧概览

数据帧(frame)是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 ... | +---------------------------------------------------------------+

引用自 RFC 6455 - Base Framing Protocol 这个一章节


数据帧格式



  • FIN1bit 表明是消息的最后一个数据帧,数据帧有可能是第一个,同时也是最后一个

  • RSV1, RSV2, RSV3每个1 bit 一般都设置为0,如果客户端和服务端采用了拓展,那么就可以为非0值,非0值的含义由拓展来决定,如果收到了非0的值,而且未采用拓展协商,那么接收终端就应该断开WebSocket连接

  • 操作码4个bit 决定了后续的载荷数据(Payload data)的解析方式,如果收到了未知的操作码,那么接收终端就应该断开WebSocket连接,具体的操作码如下:

* %x0 : 表明了这个一个持续帧(continuation frame),当操作码为0时,说明使用了数据分片,该帧为数据分片中的一帧* %x1 :表明了这是一个文本帧(text frame)* %x2 :表明了这是一个二进制帧(binary frame)* %x3-7 :保留操作码,用于后续定义的非控制帧further non-control frames)* %x8 :表明了这是一个关闭操作码* %x9 :表明了这是一个ping操作码* %xA :表明了这是一个pong操作码* %xB-F :保留操作码, 用于后续定义的控制帧( further control frames)


  • Mask 4个bit

表明是否要对载荷数据(Payload data)进行掩码操作,如果被置为1,那么 Masking-key 会定义一个 32位,4个字节的值,用于对载荷数据(Payload data)的反掩码操作,具体掩码的算法在后续内容中进行说明

值得注意的是,只有在客户端向服务端发送数据时,Mask才为1,而服务端向客户端发送数据时不需要进行掩码操作



  • Masking-key 0 或者4个bit

客户端向服务端发送的数据都进行了掩码操作,客户端必须为发送的每一个frame选择新的掩码 (随机生成),要求是这个掩码无法被提供数据的终端应用(即客户端)预测 备注:掩码长度不计入载荷数据(Payload data)的长度



  • Payload length 7 bits 或 7+16 bits 或 7+64 bits 长度为0 - 125 : 那么它就是载荷(playload)的长度 长度为126 : 那么接下来的2个字节(16位无符号整型)就是载荷(playload)的长度 长度为127 : 那么接下来的8个字节(64位无符号整型)就是载荷(playload)的长度

另外,Payload length采用了网络字节序,也就是大端(big endian数据的高字节保存在内存的低地址中),大小端具体详情请看百度百科 大小端模式



  • Payload data(x+y) 字节 载荷数据包括了应用数据(x)和拓展数据(y) 应用数据 y 字节: 如果存在拓展数据的话,占据了拓展数据之后的帧位置 拓展数据 x字节: 除非客户端和服务端进行了协商,那么拓展数据应为0,否则拓展数据应当在握手过程中明确定义其长度,或者协商如何进行长度计算(如果存在拓展数据,那么它的长度应当也计入载荷长度中)

  • 掩码的算法

/**original-octet-i为原始数据的第i字节masking-key-octet-j为masking-key的第j个字节transformed-octet-i为掩码计算后的第i个字节*/Octet i of the transformed data ("transformed-octet-i") is the XOR ofoctet i of the original data ("original-octet-i") with octet at indexi modulo 4 of the masking key ("masking-key-octet-j"): j = i MOD 4 transformed-octet-i = original-octet-i XOR masking-key-octet-j

摘抄自 RFC : Client-to-Server Masking

也就是将原始数据和masking-key做异或操作(异或的位数 j = i mode 4),获得的就是转换后的结果


发送数据

WebSocket根据 opcode 操作码来区分操作类型, %x0 %x1 %x2 代表了数据交互帧



  • %x0 : 表明了这个一个持续帧(continuation frame),当操作码为0时,说明使用了数据分片,该帧为数据分片中的一帧

  • %x1 :表明了这是一个文本帧(text frame)

  • %x2 :表明了这是一个二进制帧(binary frame

特别的操作码 %x0 代表使用了数据分片,消息可能被切分成多个数据帧,笔者发现 tornado 和 SocketRocket 并没有实现数据分片,这里就暂不深入讨论,详情请参考 RFC: Fragmentation


存活心跳ping 和 pong

一旦建立与服务器的连接,客户端和服务端都可以发起一个ping请求,当接收到一个ping请求,那么接收端必须要尽快回复pong请求,通过这种方式,来确认对方是否存活,确保客户端、服务端之间的TCP通道保持连接没有断开

ping, pong操作码分别为 %x9 %xA


断开连接

客户端和服务端都可以通过发送带有特殊控制序列 %x8 的数据帧来发起断开连接,一旦某一端收到该帧,需要也响应一个断开帧,之后主动断开的那端便可以关闭连接了,之前提到过Websocket 握手过程复用了HTTP协议的信道,那么很自然的,断开连接期间也经历了四次挥手的过程


Sec-WebSocket-Key/Accept的作用

Sec-WebSocket-Key/Sec-WebSocket-Accept 的算法都是公开的,而且也不复杂,仅仅是提供一些基础防护,防止一些意外链接,和恶意链接

[ WebSocket协议:5分钟从入门到精通 ]( www.cnblogs.com/chyingp/p/w… )中已经写得很详细,这边就不做详细探究


数据掩码的作用

随着websocket协议被开发出来,一项针对代理服务器的攻击(污染那些广泛部署的缓存代理服务器)实验也开始进行。 一般形式的攻击是跟被攻击者控制的服务器建立连接,并构造一个类似WebSocket握手一样的UPGRADE请求,随后通过UPGRADE建立的连接发送看起来就像GET请求的frame去获取一个已知资源(在攻击场景中可能是一个点击跟踪脚本或广告服务网络中的资源) 之后远程服务器会返回某些东西,就像对于这个伪造GET请求的响应,并且这个响应会被很多广泛部署的网络中间设备缓存,从而达到了污染缓存服务器的目的。对于这个攻击的产生的效应,可能一个用户被诱导访问受攻击者操控的服务器,攻击者就有可能污染这个用户以及其他共享相同缓存服务用户的缓存服务器,并跨域执行恶意脚本,破坏web安全模型

总结一下,掩码的作用很重要两点就是 防止攻击者获知网络链路中传输的原始数据避免缓存

 


原文链接:https://www.cnblogs.com/cuihongyu3503319/p/15224152.html



推荐阅读
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • Python实现变声器功能(萝莉音御姐音)的方法及步骤
    本文介绍了使用Python实现变声器功能(萝莉音御姐音)的方法及步骤。首先登录百度AL开发平台,选择语音合成,创建应用并填写应用信息,获取Appid、API Key和Secret Key。然后安装pythonsdk,可以通过pip install baidu-aip或python setup.py install进行安装。最后,书写代码实现变声器功能,使用AipSpeech库进行语音合成,可以设置音量等参数。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
author-avatar
单纯的猪猪zhu
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有