为什么有HTTP协议,还要有websocket协议?

  • Post author:
  • Post category:其他


平时我们打开网页,比如购物网站某宝。都是点一下

列表商品

,跳转一下网页就到了

商品详情

从HTTP协议的角度来看,就是点一下网页上的某个按钮,

前端发一次HTTP请求,网站返回一次HTTP响应

这种由客户端主动请求,服务器响应的方式也满足大部分网页的功能场景。

但有没有发现,这种情况下,服务器从来就不会

主动

给客户端发一次消息。

就像你喜欢的女生从来不会主动找你一样。

但如果现在,你在刷网页的时候

右下角

突然弹出一个

小广告

,提示你【

一个人在家偷偷才能玩哦

】。


求知,好学,勤奋

,这些刻在你DNA里的东西都动起来了。

你点开后发现。

长相平平无奇的古某提示你”道士9条狗,全服横着走”。

影帝某辉老师跟你说”系兄弟就来砍我”。

来都来了,你就选了个角色进到了游戏界面里。

这时候,上来就是一个小怪,从远处走来,然后疯狂拿木棒子抽你。


你全程没点任何一次鼠标

。服务器就自动将怪物的移动数据和攻击数据源源不断发给你了。

这…太暖心了。

感动之余,问题就来了,

像这种

看起来服务器主动发消息给客户端的场景

,是怎么做到的?

在真正回答这个问题之前,我们先来聊下一些相关的知识背景。



使用HTTP不断轮询

其实问题的痛点在于,

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

最常见的解决方案是,

网页的前端代码里不断定时发HTTP请求到服务器,服务器收到请求后给客户端响应消息。

这其实时一种



服务器推的形式。

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

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

扫码登录

比如某信公众号平台,登录页面二维码出现之后,

前端

网页根本不知道用户扫没扫,于是不断去向

后端

服务器询问,看有没有人扫过这个码。而且是以大概1到2秒的间隔去不断发出请求,这样可以保证用户在扫码后能在1到2s内得到及时的反馈,不至于

等太久

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

  • 当你打开F12页面时,你会发现满屏的HTTP请求。虽然很小,但这其实也消耗带宽,同时也会增加下游服务器的负担。
  • 最坏情况下,用户在扫码后,需要等个1~2s,正好才触发下一次http请求,然后才跳转页面,用户会感到

    明显的卡顿

使用起来的体验就是,二维码出现后,手机扫一扫,然后在手机上点个确认,这时候

卡顿等个1~2s

,页面才跳转。

那么问题又来了,

有没有更好的解决方案?

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



长轮询

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

如果我们的HTTP请求

将超时设置的很大

,比如30s,

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

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

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

秒跳转

,体验很好。

真一举两得。

像这种发起一个请求,在较长时间内等待服务器响应的机制,就是所谓的

长训轮机制

。我们常用的消息队列RocketMQ中,消费者去取数据时,也用到了这种方式。

像这种,在用户不感知的情况下,服务器将数据推送给浏览器的技术,就是所谓的

服务器推送

技术,它还有个毫不沾边的英文名,

comet

技术,大家听过就好。

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

对于像扫码登录这样的

简单场景

还能用用。

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

这就得说下

websocket

了。



websocket是什么

我们知道TCP连接的两端,

同一时间里



双方

都可以

主动

向对方发送数据。这就是所谓的

全双工

而现在使用最广泛的

HTTP1.1

,也是基于TCP协议的,

同一时间里

,客户端和服务器

只能有一方主动

发数据,这就是所谓的

半双工

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

为什么?

这是由于HTTP协议设计之初,考虑的是看看网页文本的场景,能做到

客户端发起请求再由服务器响应

,就够了,根本就没考虑网页游戏这种,客户端和服务器之间都要互相主动发大量数据的场景。

所以为了更好的支持这样的场景,我们需要另外一个

基于TCP的新协议

于是新的应用层协议

websocket

就被设计出来了。

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

毫无关系



怎么建立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的数据格式进行通信了。



websocket抓包

我们可以用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一样,都只是个

工具人

这就有点”

借壳生蛋

“的那意思。



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

的形式。

之前写的

《既然有HTTP协议,为什么还要有RPC》

提到过,TCP协议本身就是全双工,但直接使用

纯裸TCP

去传输数据,会有

粘包

的”问题”。为了解决这个问题,上层协议一般会用

消息头+消息体

的格式去重新包装要发的数据。



消息头

里一般含有

消息体的长度

,通过这个长度可以去截取真正的消息体。

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



websocket的使用场景

websocket完美继承了TCP协议的

全双工

能力,并且还贴心的提供了解决粘包的方案。它适用于

需要服务器和客户端(浏览器)频繁交互

的大部分场景。比如网页/小程序游戏,网页聊天室,以及一些类似飞书这样的网页协同办公软件。

回到文章开头的问题,在使用websocket协议的网页游戏里,怪物移动以及攻击玩家的行为是

服务器逻辑

产生的,对玩家产生的伤害等数据,都需要由

服务器主动发送给客户端

,客户端获得数据后展示对应的效果。



总结

  • TCP协议本身是

    全双工

    的,但我们最常用的HTTP1.1,虽然是基于TCP的协议,但它是

    半双工

    的,对于大部分需要服务器主动推送数据到客户端的场景,都不太友好,因此我们需要使用支持全双工的websocket协议。
  • 在HTTP1.1里。只要客户端不问,服务端就不答。基于这样的特点,对于登录页面这样的简单场景,可以使用

    定时轮询或者长轮询

    的方式实现

    服务器推送

    (comet)的效果。
  • 对于客户端和服务端之间需要频繁交互的复杂场景,比如网页游戏,都可以考虑使用websocket协议。
  • websocket和socket几乎没有任何关系,只是叫法相似。
  • 正因为各个浏览器都支持HTTP协议,所以websocket会先利用HTTP协议加上一些特殊的header头进行握手升级操作,升级成功后就跟HTTP没有任何关系了,之后就用websocket的数据格式进行收发数据。



最后

最近原创更文的阅读量稳步下跌,思前想后,夜里辗转反侧。

我有个不成熟的请求。


离开广东好长时间了,好久没人叫我靓仔了。

大家可以在

评论区

里,叫我一靓仔吗?

我这么善良质朴的愿望,能被满足吗?

如果实在叫不出口的话,能帮我

点下关注

和右下角的

点赞+收藏

吗?



别说了,一起在知识的海洋里呛水吧

作者:小白debug

链接:https://juejin.cn/post/7144161126652051464



最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

在这里插入图片描述

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。



一、架构师筑基必备技能

1、深入理解Java泛型

2、注解深入浅出

3、并发编程

4、数据传输与序列化

5、Java虚拟机原理

6、高效IO

……

在这里插入图片描述



二、Android百大框架源码解析

1.Retrofit 2.0源码解析

2.Okhttp3源码解析

3.ButterKnife源码解析

4.MPAndroidChart 源码解析

5.Glide源码解析

6.Leakcanary 源码解析

7.Universal-lmage-Loader源码解析

8.EventBus 3.0源码解析

9.zxing源码分析

10.Picasso源码解析

11.LottieAndroid使用详解及源码解析

12.Fresco 源码分析——图片加载流程

在这里插入图片描述



三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述



四、高级kotlin强化实战

1、Kotlin入门教程

2、Kotlin 实战避坑指南

3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述



五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用

2.Android之PullToRefresh控件源码解析

3.Android-PullToRefresh下拉刷新库基本用法

4.LoadSir-高效易用的加载反馈页管理框架

5.Android通用LoadingView加载框架详解

6.MPAndroidChart实现LineChart(折线图)

7.hellocharts-android使用指南

8.SmartTable使用指南

9.开源项目android-uitableview介绍

10.ExcelPanel 使用指南

11.Android开源项目SlidingMenu深切解析

12.MaterialDrawer使用指南

在这里插入图片描述



六、NDK模块开发

1、NDK 模块开发

2、JNI 模块

3、Native 开发工具

4、Linux 编程

5、底层图片处理

6、音视频开发

7、机器学习

在这里插入图片描述



七、Flutter技术进阶

1、Flutter跨平台开发概述

2、Windows中Flutter开发环境搭建

3、编写你的第一个Flutter APP

4、Flutter开发环境搭建和调试

5、Dart语法篇之基础语法(一)

6、Dart语法篇之集合的使用与源码解析(二)

7、Dart语法篇之集合操作符函数与源码分析(三)



在这里插入图片描述



八、微信小程序开发

1、小程序概述及入门

2、小程序UI开发

3、API操作

4、购物商场项目实战……

在这里插入图片描述



全套视频资料:


一、面试合集


在这里插入图片描述


二、源码解析合集

在这里插入图片描述


三、开源框架合集

在这里插入图片描述


欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓



版权声明:本文为weixin_47933729原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。