WebSocket原理详解

1.引言

平时我们打开网页,比如购物网站某宝。都是点一下列表商品,跳转一下网页就到了商品详情。从HTTP协议的角度来看,就是点一下网页上的某个按钮,前端发一次HTTP请求,网站返回一次HTTP响应。这种由客户端主动请求,服务器响应的方式也满足大部分网页的功能场景。但有没有发现,这种情况下,服务器从来就不会主动给客户端发一次消息。就像你喜欢的女生从来不会主动找你一样。

但如果现在,你在刷网页的时候右下角突然弹出一个小广告,提示你【一个人在家偷偷才能玩哦】。像这种看起来服务器主动发消息给客户端的场景,是怎么做到的呢?

1.1.使用HTTP不断轮询

其实问题的痛点在于,怎么样才能在用户不做任何操作的情况下,网页能收到消息并发生变更。

最常见的解决方案是,网页的前端代码里不断定时发HTTP请求到服务器,服务器收到请求后给客户端响应消息。

这其实时一种服务器推的形式。

它其实并不是服务器主动发消息到客户端,而是客户端自己不断偷偷请求服务器,只是用户无感知而已。

用这种方式的场景也有很多,最常见的就是扫码登录

比如某信公众号平台,登录页面二维码出现之后,前端网页根本不知道用户扫没扫,于是不断去向后端服务器询问,看有没有人扫过这个码。而且是以大概1到2秒的间隔去不断发出请求,这样可以保证用户在扫码后能在1到2s内得到及时的反馈,不至于等太久

但这样,会有两个比较明显的问题

  • 当你打开F12页面时,你会发现满屏的HTTP请求。虽然很小,但这其实也消耗带宽,同时也会增加下游服务器的负担。

  • 最坏情况下,用户在扫码后,需要等个1~2s,正好才触发下一次http请求,然后才跳转页面,用户会感到明显的卡顿

使用起来的体验就是,二维码出现后,手机扫一扫,然后在手机上点个确认,这时候卡顿等个1~2s,页面才跳转。

那么问题又来了,有没有更好的解决方案?

有,而且实现起来成本还非常低。

1.2.长轮询

我们知道,HTTP请求发出后,一般会给服务器留一定的时间做响应,比如3s,规定时间内没返回,就认为是超时。

如果我们的HTTP请求将超时设置的很大,比如30s,在这30s内只要服务器收到了扫码请求,就立马返回给客户端网页。如果超时,那就立马发起下一次请求。

这样就减少了HTTP请求的个数,并且由于大部分情况下,用户都会在某个30s的区间内做扫码操作,所以响应也是及时的。

比如,某度云网盘就是这么干的。所以你会发现一扫码,手机上点个确认,电脑端网页就秒跳转,体验很好。

真一举两得。

像这种发起一个请求,在较长时间内等待服务器响应的机制,就是所谓的长训轮机制。我们常用的消息队列RocketMQ中,消费者去取数据时,也用到了这种方式。

像这种,在用户不感知的情况下,服务器将数据推送给浏览器的技术,就是所谓的服务器推送技术,它还有个毫不沾边的英文名,comet技术,大家听过就好。

上面提到的两种解决方案,本质上,其实还是客户端主动去取数据。

对于像扫码登录这样的简单场景还能用用。

但如果是网页游戏呢,游戏一般会有大量的数据需要从服务器主动推送到客户端。

这就得说下websocket了。

2.websocket

2.1.概述

我们知道TCP连接的两端,同一时间里双方都可以主动向对方发送数据。这就是所谓的全双工

而现在使用最广泛的HTTP1.1,也是基于TCP协议的,同一时间里,客户端和服务器只能有一方主动发数据,这就是所谓的半双工

也就是说,好好的全双工TCP,被HTTP用成了半双工。

为什么?

这是由于HTTP协议设计之初,考虑的是看看网页文本的场景,能做到客户端发起请求再由服务器响应,就够了,根本就没考虑网页游戏这种,客户端和服务器之间都要互相主动发大量数据的场景。

所以为了更好的支持这样的场景,我们需要另外一个基于TCP的新协议

于是新的应用层协议websocket就被设计出来了。

大家别被这个名字给带偏了。虽然名字带了个socket,但其实socket和websocket之间,就跟雷峰和雷峰塔一样,二者接近毫无关系

2.2.websocket建立过程

我们平时刷网页,一般都是在浏览器上刷的,一会刷刷图文,这时候用的是HTTP协议,一会打开网页游戏,这时候就得切换成我们新介绍的websocket协议

为了兼容这些使用场景。浏览器在TCP三次握手建立连接之后,都统一使用HTTP协议先进行一次通信。

  • 如果此时是普通的HTTP请求,那后续双方就还是老样子继续用普通HTTP协议进行交互,这点没啥疑问。

  • 如果这时候是想建立websocket连接,就会在HTTP请求里带上一些特殊的header头

Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: T2a6wZlAwhgQNqruZ2YUyg==\r\n

这些header头的意思是,浏览器想升级协议(Connection: Upgrade),并且想升级成websocket协议(Upgrade: websocket)

同时带上一段随机生成的base64码(Sec-WebSocket-Key),发给服务器。

如果服务器正好支持升级成websocket协议。就会走websocket握手流程,同时根据客户端生成的base64码,用某个公开的算法变成另一段字符串,放在HTTP响应的 Sec-WebSocket-Accept 头里,同时带上101状态码,发回给浏览器。

HTTP/1.1 101 Switching Protocols\r\n
Sec-WebSocket-Accept: iBJKv/ALIW2DobfoA4dmr3JHBCY=\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n

http状态码=200(正常响应)的情况,大家见得多了。101确实不常见,它其实是指协议切换

之后,浏览器也用同样的公开算法base64码转成另一段字符串,如果这段字符串跟服务器传回来的字符串一致,那验证通过。

就这样经历了一来一回两次HTTP握手,websocket就建立完成了,后续双方就可以使用webscoket的数据格式进行通信了。

2.3.抓包分析

我们可以用wireshark抓个包,实际看下数据包的情况。

上面这张图,注意画了红框的第2445行报文,是websocket的第一次握手,意思是发起了一次带有特殊Header的HTTP请求。

上面这个图里画了红框的4714行报文,就是服务器在得到第一次握手后,响应的第二次握手,可以看到这也是个HTTP类型的报文,返回的状态码是101。同时可以看到返回的报文header中也带有各种websocket相关的信息,比如Sec-WebSocket-Accept

上面这张图就是全貌了,从截图上的注释可以看出,websocket和HTTP一样都是基于TCP的协议。经历了三次TCP握手之后,利用HTTP协议升级为websocket协议。

你在网上可能会看到一种说法:"websocket是基于HTTP的新协议",其实这并不对,因为websocket只有在建立连接时才用到了HTTP,升级完成之后就跟HTTP没有任何关系了

这就好像你喜欢的女生通过你要到了你大学室友的微信,然后他们自己就聊起来了。你能说这个女生是通过你去跟你室友沟通的吗?不能。你跟HTTP一样,都只是个工具人

这就有点"借壳生蛋"的那意思。

2.4.websocket的消息格式

上面提到在完成协议升级之后,两端就会用webscoket的数据格式进行通信。

数据包在websocket中被叫做

我们来看下它的数据格式长什么样子。

这里面字段很多,但我们只需要关注下面这几个。

opcode字段:这个是用来标志这是个什么类型的数据帧。比如。

  • 等于1时是指text类型(string)的数据包

  • 等于2是二进制数据类型([]byte)的数据包

  • 等于8是关闭连接的信号

payload字段:存放的是我们真正想要传输的数据的长度,单位是字节。比如你要发送的数据是字符串"111",那它的长度就是3

另外,可以看到,我们存放payload长度的字段有好几个,我们既可以用最前面的7bit, 也可以用后面的7+16bit或7+64bit。

那么问题就来了。

我们知道,在数据层面,大家都是01二进制流。我怎么知道什么情况下应该读7bit,什么情况下应该读7+16bit呢?

websocket会用最开始的7bit做标志位。不管接下来的数据有多大,都先读最先的7个bit,根据它的取值决定还要不要再读个16bit或64bit。

  • 如果最开始的7bit的值是 0~125,那么它就表示了 payload 全部长度,只读最开始的7个bit就完事了。

  • 如果是126(0x7E)。那它表示payload的长度范围在 126~65535 之间,接下来还需要再读16bit。这16bit会包含payload的真实长度。

  • 如果是127(0x7F)。那它表示payload的长度范围>=65536,接下来还需要再读64bit。这64bit会包含payload的长度。这能放2的64次方byte的数据,换算一下好多个TB,肯定够用了。

payload data字段:这里存放的就是真正要传输的数据,在知道了上面的payload长度后,就可以根据这个值去截取对应的数据。

大家有没有发现一个小细节,websocket的数据格式也是  数据头(内含payload长度) + payload data 的形式。

TCP协议本身就是全双工,但直接使用纯裸TCP去传输数据,会有粘包的"问题"。为了解决这个问题,上层协议一般会用消息头+消息体的格式去重新包装要发的数据。

消息头里一般含有消息体的长度,通过这个长度可以去截取真正的消息体。

HTTP协议和大部分RPC协议,以及我们今天介绍的websocket协议,都是这样设计的。

3.使用场景

websocket完美继承了TCP协议的全双工能力,并且还贴心的提供了解决粘包的方案。它适用于需要服务器和客户端(浏览器)频繁交互的大部分场景。比如网页/小程序游戏,网页聊天室,以及一些类似飞书这样的网页协同办公软件。

回到文章开头的问题,在使用websocket协议的网页游戏里,怪物移动以及攻击玩家的行为是服务器逻辑产生的,对玩家产生的伤害等数据,都需要由服务器主动发送给客户端,客户端获得数据后展示对应的效果。

4.总结

  • TCP协议本身是全双工的,但我们最常用的HTTP1.1,虽然是基于TCP的协议,但它是半双工的,对于大部分需要服务器主动推送数据到客户端的场景,都不太友好,因此我们需要使用支持全双工的websocket协议。

  • 在HTTP1.1里。只要客户端不问,服务端就不答。基于这样的特点,对于登录页面这样的简单场景,可以使用定时轮询或者长轮询的方式实现服务器推送(comet)的效果。

  • 对于客户端和服务端之间需要频繁交互的复杂场景,比如网页游戏,都可以考虑使用websocket协议。

  • websocket和socket几乎没有任何关系,只是叫法相似。

  • 正因为各个浏览器都支持HTTP协议,所以websocket会先利用HTTP协议加上一些特殊的header头进行握手升级操作,升级成功后就跟HTTP没有任何关系了,之后就用websocket的数据格式进行收发数据。


相关文章

  • 如何在 Nginx 中进行速率限制?

    http {server {# 其他配置...在这个例子中,当请求超过速率限制时,通过error_page指令将 503 错误映射到页面。这使得可以为用户提供更友好的自定义页面,以解释为什么请求被拒绝。在本文中,我们详细介绍了在 Nginx 中进行速率限制的方法。我们从基础开始,讨论了模块的使用以及指令的配置。随后,我们深入探讨了不同的速率限制策略,包括按 IP 进行限制、限制特定 URI 或请求类型,以及设定速率限制的阈值。在高级策略方面,我们讨论了如何使用。

  • 【Linux】信号保存与信号捕捉处理

    介绍信号的保存,理解信号在操作系统中的保存方式,理解系统中信号捕捉的处理过程以及介绍信号的其它知识!

  • 如何在 Linux 命令行中检查 CPU 使用率

    使用top来实时查看系统资源的使用情况,特别是 CPU 和内存的使用率。使用sar定期收集系统性能数据,以便后续分析历史性能趋势。使用mpstat来监控多核 CPU 的使用率,了解每个核心的性能状况。使用perf进行更深入的性能分析,包括函数调用图、指令和缓存的分析等。使用strace跟踪系统调用,解决程序执行中的异常情况。使用topvmstat和sar进行实时和历史性能监控,以便了解系统整体的健康状况。使用mpstatperf和strace进行更深入的 CPU 使用率分析,找出潜在的性能瓶颈。

  • 同样都是光纤接入设备,OLT和ONT到底有啥区别?

    你好,这里是网络技术联盟站,我是瑞哥。光纤通信系统在现代通信网络中扮演着至关重要的角色。为了实现高效、快速的光纤接入,OLT(Optical Line Terminal)和ONT(Optical Network Terminal)这两种关键设备发挥着不可替代的作用。本文将深入研究OLT和ONT之间的主要区别,解释它们各自的功能、硬件特点、通信协议以及应用场景。

  • 高清网络视频监控系统技术方案

    随着数字技术、计算机技术和网络技术的发展,基于互联网的各种业务呈几何指数规律飞速增长,远程视频监控作为基于互联网的一种全新业务也得到了飞速的发展。高清网络摄像机IPC,高清硬盘录像机NVR/DVR等设备得到很大的发展。

  • 什么是BGP路由属性?BGP13条选路规则你还记得吗?

    BGP路由属性是一组参数,用于进一步描述特定路由的属性。这些属性的存在使得BGP能够根据网络的需求对路由进行精细的控制。在BGP的路由更新消息中,这些属性提供了丰富的信息,从而让网络管理员能够实施各种策略,例如负载均衡、路径选择和拓扑优化。公认必须遵循公认可选可选过渡可选非过渡BGP路由属性作为BGP协议的核心组成部分,在互联网路由中扮演着关键的角色。本文详细探讨了BGP路由属性的分类和功能,以及它们在网络设计和优化中的重要性。公认必须遵循属性。

  • IntelliJ IDEA如何使用固定地址公网远程访问本地Mysql数据库

    IDEA作为Java开发最主力的工具,在开发过程中需要经常用到数据库,如Mysql数据库,但是在IDEA中只能连接本地数据库,有时候需要访问其他地方如家里或者公司的数据库,将无法访问,内网的局限性导致我们只能在同一网络访问,无法跨网络访问,所以,本例将介绍如何在异地也可以实现远程连接本地的数据库,这里我们需要用到一个工具,叫Cpolar.隧道创建成功后,点击左侧的状态——在线隧道列表,查看所生成的公网TCP协议的地址,该地址就是公网地址,我们可以在任何设备中的IDEA中都可以访问。

  • Inis博客系统本地部署结合内网穿透实现远程访问本地站点

    互联网技术总在不断进步,新技术和新软件也会来带更多功能,就以最常见的个人博客系统,也在不断推陈出新,发掘出更多功能和特色。从早期少量自定义和编辑功能,发展到现在的支持各种自定义模块和外观,以及更多更全的支持软件适配(包括php程序、数据库程序等)。今天,笔者就为大家介绍,如何在Ubuntu系统上部署新版Inis博客系统,并使用cpolar创建的内网穿透数据隧道,将Inis博客网站发布到公共互联网上。

  • 千兆网口、2.5G端口、5G端口有啥区别?如何选择?

    通过本文对千兆以太网、2.5G 以太网和5G 以太网的详细介绍,我们深入了解了它们的技术原理、优势、局限性以及在不同行业和应用场景中的应用。千兆以太网适用于一般用途,而2.5G网口和5G网口则更适用于对速度和性能有更高要求的专业场景。在做决策时,始终确保您的选择符合当前需求,并具备未来的可扩展性。

  • 文件传输协议:FTP、TFTP、SFTP和SCP

    你好,这里是网络技术联盟站。文件传输协议在计算机网络中扮演着关键的角色,为用户提供了在网络上共享和传输文件的标准化方式。本文将深入介绍四种常见的文件传输协议:FTP、TFTP、SFTP和SCP,探讨它们的工作原理、特点以及在不同场景下的应用。

  • 如何强制Linux用户在下次登录时更改密码?

    在Linux系统中,用户账户的密码信息存储在和文件中。其中,文件包含用户的基本信息,而文件包含用户的加密密码以及与密码相关的设置。PAM模块允许管理员定义在用户登录、更改密码等操作时执行的任务。在密码管理方面,我们关注的是模块。本文介绍了如何强制Linux用户在下次登录时更改密码,并详细讨论了密码管理的各个方面。通过配置密码过期策略、使用PAM模块增加密码复杂性要求、设置强制密码更改通知、批量更改用户密码以及自动化密码更改过程,管理员可以提高系统的安全性,降低密码被滥用的风险。

  • 计算机网络中,距离矢量协议和链路状态协议到底有啥区别?

    距离矢量协议和链路状态协议都在不同的情境下展现出各自的优势和劣势。在选择使用哪种协议时,需要考虑网络的规模、带宽利用率、安全性需求等因素。对于小型网络或对带宽敏感的场景,距离矢量协议可能更为合适。而在大型网络、需要更好安全性和更快收敛速度的情况下,链路状态协议可能更具优势。在实际应用中,有时候也会采用混合使用这两种协议的方式,以充分发挥它们各自的优势,提高网络的性能和可靠性。这使得在不同的网络环境中选择合适的路由协议成为一个复杂而重要的决策。境中的优劣势。

  • SCCP 和 MTP3 之间有啥区别?

    通过详细介绍 SCCP 和 MTP3,以及它们之间的比较和协同工作,我们深入了解了它们在信令传输协议栈中的角色和功能。在设计和管理电信网络时,对 SCCP 和 MTP3 的理解是至关重要的,因为它们直接影响信令的可靠性、灵活性和性能。这样的理解有助于工程师和网络管理员更好地优化网络配置,确保通信系统的稳定运行。

  • 什么是虚拟路由器?与传统物理路由器有啥区别?

    虚拟路由器(Virtual Router)是一种软件功能,它在x86服务器硬件上以虚拟机实例的形式部署,提供基于云的网络服务。运行在虚拟化平台上的虚拟路由器承担着多种功能,包括路由、交换、安全和VPN等。通过这种虚拟化的方式,用户可以在云环境中便捷地配置和管理网络,实现高度灵活性和可定制性,为用户提供了强大的网络通信服务。虚拟机技术是虚拟路由器中常用的虚拟化手段之一。通过使用虚拟机管理程序(如VMware、KVM等),虚拟路由器可以在一台物理服务器上同时运行多个虚拟实例。

  • 网络安全之道:DMZ与端口转发有啥区别?

    通过本文的介绍,我们深入了解了DMZ和端口转发这两个网络安全和应用配置中的重要概念。DMZ在网络中扮演了重要的缓冲带角色,既确保了对外服务的可用性,又有效地隔离了来自外部的潜在威胁。端口转发通过设置网络设备的规则,将外部网络的请求定向到内部网络的特定主机或服务。这一技术为远程访问、个人服务器搭建等提供了便利。结合DMZ和端口转发,可以实现更灵活的网络架构。DMZ中部署公共服务,通过端口转发映射外部请求,不仅提高了网络的可用性,也增强了网络的安全性。