从输入URL到渲染页面 -- 网络协议

本文是从输入URL到渲染页面专栏的第二篇文章--网络协议
咱们知道TCP/IP协议将网络协议分了四层
image.pnghtml

咱们重点说下应用层,网络层和传输层浏览器

数据在网络上是如何传输的?

网络层IP

数据想在互联网上进行传输,就要符合网际协议(Internet Protocol,简称 IP)标准。互联网上不一样的在线设备都有惟一的地址标识,用一个数字来表示。
类比咱们日常网购,用咱们的收件地址类比设备的惟一标识,咱们知道了这个收件地址,就能够往这个地址发送包裹。计算机的地址就称为 IP 地址,访问任何网站实际上只是你的计算机向另一台计算机请求信息。
若是要想把一个数据包从主机 A 发送给主机 B,那么在传输以前,数据包上会被附加上主机 B 的 IP 地址信息,这样在传输过程当中才能正确寻址。另外,数据包上还会附加上主机 A 的 IP 地址,有了这些信息主机 B 才能够回复信息给主机 A。这些附加的信息会被装进一个叫 IP 头的数据结构里。缓存

下面咱们一块儿来看下一个易于理解的数据包从主机 A 到主机 B 的简化传输过程(不是4层网络协议):服务器

  • 上层将数据包交给网络层;
  • 网络层再将 IP 头附加到数据包上,组成新的 IP 数据包,并交给底层;
  • 底层经过物理网络将数据包传输给主机 B;
  • 数据包被传输到主机 B 的网络层,在这里主机 B 拆开数据包的 IP 头信息,并将拆开来的数据部分交给上层;
  • 最终,含有信息的数据包就到达了主机 B 的上层了。

传输层UDP/TCP

上面咱们讨论的基于IP传输是很是底层的协议,只负责把数据包传送到对方电脑,可是对方电脑并不知道把数据包交给谁。所以,须要基于 IP 之上开发能和应用打交道的协议,也就是传输层,最多见的就是UDP和TCP协议。网络

增长了传输层,咱们就能够把前面的三层结构扩充为四层结构,以下图所示数据结构

image.png
下面咱们再一块儿来看增长了传输层的数据传输路线:并发

  • 上层将数据包交给传输层;
  • 传输层会在数据包前面附加上 UDP/TCP 头,组成新的数据包,再将新的数据包交给网络层;
  • 网络层再将 IP 头附加到数据包上,组成新的 IP 数据包,并交给底层;
  • 数据包被传输到主机 B 的网络层,在这里主机 B 拆开 IP 头信息,并将拆开来的数据部分交给传输层;
  • 在传输层,数据包中的 UDP/TCP 头会被拆开,并根据 UDP/TCP 中所提供的端口号,把数据部分交给上层的应用程序;
  • 最终,数据包就到了主机 B 上层应用程序这里。

那么在传输层使用UDP和TCP传输有什么区别呢,他们分别适合什么场景呢,下面咱们来看一下性能

UDP和TCP的对比

在使用 UDP 发送数据时,有各类因素会致使数据包出错,虽然 UDP 能够校验数据是否正确,可是对于错误的数据包,UDP 并不提供重发机制,只是丢弃当前的包,并且 UDP 在发送以后也没法知道是否能达到目的地。虽然说 UDP 不能保证数据可靠性,可是传输速度却很是快,因此 UDP 会应用在一些关注速度、但不那么严格要求数据完整性的领域,如在线视频、互动游戏等。优化

UDP缺点:网站

  • 数据包在传输过程当中容易丢失,且没有重发机制;
  • 大文件会被拆分红不少小的数据包来传输,这些小的数据包会通过不一样的路由,并在不一样的时间到达接收端,而 UDP 协议并不知道如何组装这些数据包,从而不能把这些数据包还原成完整的文件。

针对 UDP 的缺点,TCP头除了包含了目标端口和本机端口号外,还提供了用于排序的序列号,以便接收端经过序号来重排数据包。
另外,传输能够保证数据的可靠性,而且提供了重发机制。
那么TCP是怎么作到的呢,这就不得不提大名鼎鼎的“三次握手”和“四次挥手”了。

下面咱们看看一次完成的TCP传输过程是怎样的:

  • 首先,创建链接阶段(三次握手)。TCP 提供面向链接的通讯传输。面向链接是指在数据通讯开始以前先作好两端之间的准备工做。所谓三次握手,是指在创建一个 TCP 链接时,客户端和服务器总共要发送三个数据包以确认链接的创建。
  • 其次,传输数据阶段。在该阶段,接收端须要对每一个数据包进行确认操做,也就是接收端在接收到数据包以后,须要发送确认数据包给发送端。因此当发送端发送了一个数据包以后,在规定时间内没有接收到接收端反馈的确认消息,则判断为数据包丢失,并触发发送端的重发机制。一样,一个大的文件在传输过程当中会被拆分红不少小的数据包,这些数据包到达接收端后,接收端会按照 TCP 头中的序号为其排序,从而保证组成完整的数据。
  • 最后,断开链接阶段(四次挥手)。数据传输完毕以后,就要终止链接了。

下面是网上很经典的三次握手和四次挥手的示意图
image.png

  • client发起链接,发送一个SYN包表示创建链接,还有一个SEQ = X(随机数)
  • server收到后,对client应答,并发送链接请求。发送一个既包含syn又包含ack的包,此时ack=X+1,SEQ = Y(随机数)。
  • client接到server的应答后,看到ack = X+1就知道server端已经接受了我以前的请求。对server的链接请求作应答,此时ack=Y+1

image.png

  • 第一次client发出一个FIN包和一个seq=x,以后client进入FIN_WAIT_1阶段
  • server接收到以后回复一个ack=x+1(原理同上),和seq=y,表示收到了client的关闭请求,以后进入CLOSE_WAIT状态,client进入FIN_WAIT_2状态
  • 以后server处理完本身其余的package以后发送一个ack=x+1,和seq=y,此时server进入LAST_ACK状态,再也不回复消息
  • client接收到server的FIN包后回复一个ack=y+1以后进入TIME_WAIT状态,server接收到这个包以后直接进入CLOSED状态,client等待了两个msl(Maximum Segment Lifetime最大报文生存时间)以后没有收到应答,表明server正常关闭,便也进入CLOSED状态,关闭链接

到这里你应该就明白了,TCP 为了保证数据传输的可靠性,牺牲了数据包的传输速度

HTTP协议

接下来咱们看下应用层HTTP协议的发展历史

HTTP1时代

HTTP/0.9

首先咱们来看看诞生最先的 HTTP/0.9。他的出现主要用于学术交流,需求很简单——用来在网络之间传递 HTML 超文本的内容,因此被称为超文本传输协议。总体来看,它的实现也很简单,采用了基于请求响应的模式,从客户端发出请求,服务器返回数据。

  • 由于 HTTP 都是基于 TCP 协议的,因此客户端先要根据 IP 地址、端口和服务器创建 TCP 链接,而创建链接的过程就是 TCP 协议三次握手的过程。
  • 创建好链接以后,会发送一个 GET 请求行的信息,如GET /index.html用来获取 index.html。
  • 服务器接收请求信息以后,读取对应的 HTML 文件,并将数据以 ASCII 字符流返回给客户端。
  • HTML 文档传输完成后,断开链接。

总的来讲,当时的需求很简单,就是用来传输体积很小的 HTML 文件,因此 HTTP/0.9 的实现有如下三个特色。

  • 第一个是只有一个请求行,并无 HTTP 请求头和请求体,由于只须要一个请求行就能够完整表达客户端的需求了。
  • 第二个是服务器也没有返回头信息,这是由于服务器端并不须要告诉客户端太多信息,只须要返回数据就能够了。
  • 第三个是返回的文件内容是以 ASCII 字符流来传输的,由于都是 HTML 格式的文件,因此使用 ASCII 字节码来传输是最合适的。

HTTP/1.0

随着互联网的发展,只能传输html已经不能知足需求了。还包括了 JavaScript、CSS、图片、音频、视频等不一样类型的文件。所以支持多种类型的文件下载是 HTTP/1.0 的一个核心诉求,并且文件格式不只仅局限于 ASCII 编码,还有不少其余类型编码的文件。

为了让客户端和服务器能够更灵活的交流,HTTP/1.0 引入了请求头和响应头,它们都是觉得 Key-Value 形式保存的,在 HTTP 发送请求时,会带上请求头信息,服务器返回数据时,会先返回响应头信息。例以下面的代码就是请求头和响应头信息的部分信息:

accept: text/html  // 指望服务器返回 html 类型的文件
accept-encoding: gzip, deflate, br // 指望服务器能够采用 gzip、deflate 或者 br 其中的一种压缩方式
accept-Charset: ISO-8859-1,utf-8 // 表示指望返回的文件编码是 UTF-8 或者 ISO-8859-1
accept-language: zh-CN,zh // 指望页面的优先语言是中文
content-encoding: br // 表示服务器采用了 br 的压缩方法
content-type: text/html; charset=UTF-8 // 表示服务器返回的是 html 文件,而且该文件的编码类型是 UTF-8

这就是浏览器和服务器在1.0时代的一个交流方式,就好像两我的在对“暗号”同样。

HTTP/1.0除了对多文件提供良好的支持外,还引入了不少其余的特性,这些特性都是经过请求头和响应头来实现的。
下面咱们来看看新增的几个典型的特性:

  • 有的请求服务器可能没法处理,或者处理出错,这时候就须要告诉浏览器服务器最终处理该请求的状况,这就引入了状态码。状态码是经过响应行的方式来通知浏览器的。
  • 为了减轻服务器的压力,提供了 Cache 机制,用来缓存已经下载过的数据。
  • 服务器须要统计客户端的基础信息,好比 Windows 和 macOS 的用户数量分别是多少,因此请求头中还加入了用户代理的字段。

HTTP/1.1

虽然1.0已经能够应付绝大部分的场景,可是他仍是有如下几个缺陷:

  • 每进行一次 HTTP 通讯,都须要经历创建 TCP 链接、传输 HTTP 数据和断开 TCP 链接三个阶段 —— 增长了持久链接的方法(Connection: keep-alive),它的特色是在一个 TCP 链接上能够传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开链接,那么该 TCP 链接会一直保持。(目前浏览器中对于同一个域名,默认容许同时创建 6 个 TCP 持久链接)
  • 队头阻塞问题 —— 没有解决
  • 每一个域名绑定了一个惟一的 IP 地址,所以一个服务器只能支持一个域名。可是随着虚拟主机技术的发展,须要实如今一台物理主机上绑定多个虚拟主机,每一个虚拟主机都有本身的单独的域名,这些单独的域名都公用同一个 IP 地址 —— 请求头中增长了 Host 字段,用来表示当前的域名地址,这样服务器就能够根据不一样的 Host 值作不一样的处理。
  • 须要在响应头中设置完整的数据大小,如Content-Length: 901,这样浏览器就能够根据设置的数据大小来接收数据。不过随着服务器端的技术发展,不少页面的内容都是动态生成的,所以在传输数据以前并不知道最终的数据大小,这就致使了浏览器不知道什么时候会接收完全部的文件数据 —— 经过引入 Chunk transfer 机制来解决这个问题,服务器会将数据分割成若干个任意大小的数据块,每一个数据块发送时会附上上个数据块的长度,最后使用一个零长度的块做为发送数据完成的标志。这样就提供了对动态内容的支持。

HTTP2

虽然 HTTP/1.1 采起了不少优化资源加载速度的策略,也取得了必定的效果,可是 HTTP/1.1对带宽的利用率却并不理想,这也是 HTTP/1.1 的一个核心问题。(带宽是指每秒最大能发送或者接收的字节数。咱们把每秒能发送的最大字节数称为上行带宽,每秒可以接收的最大字节数称为下行带宽。)之因此会出现这个问题,主要是由如下三个缘由致使的。

  • TCP 的慢启动。一旦一个 TCP 链接创建以后,就进入了发送数据状态,刚开始 TCP 协议会采用一个很是慢的速度去发送数据,而后慢慢加快发送数据的速度,直到发送数据的速度达到一个理想状态,咱们把这个过程称为慢启动(相似汽车发动过程)。慢启动是 TCP 为了减小网络拥塞的一种策略,咱们是没有办法改变的。而之因此说慢启动会带来性能问题,是由于页面中经常使用的一些关键资源文件原本就不大,如 HTML 文件、CSS 文件和 JavaScript 文件,一般这些文件在 TCP 链接创建好以后就要发起请求的,但这个过程是慢启动,因此耗费的时间比正常的时间要多不少,这样就推迟了宝贵的首次渲染页面的时长了。
  • 同时开启了多个 TCP 链接,这些链接会竞争固定的带宽。系统同时创建了多条 TCP 链接,当带宽充足时,每条链接发送或者接收速度会慢慢向上增长;而一旦带宽不足时,这些 TCP 链接又会减慢发送或者接收的速度。好比一个页面有 200 个文件,使用了 3 个 CDN,那么加载该网页的时候就须要创建 6 * 3,也就是 18 个 TCP 链接来下载资源;在下载过程当中,当发现带宽不足的时候,各个 TCP 链接就须要动态减慢接收数据的速度。这样就会出现一个问题,由于有的 TCP 链接下载的是一些关键资源,如 CSS 文件、JavaScript 文件等,而有的 TCP 链接下载的是图片、视频等普通的资源文件,可是多条 TCP 链接之间又不能协商让哪些关键资源优先下载,这样就有可能影响那些关键资源的下载速度了。
  • 队头阻塞。咱们知道在 HTTP/1.1 中使用持久链接时,虽然能公用一个 TCP 管道,可是在一个管道中同一时刻只能处理一个请求,在当前的请求没有结束以前,其余的请求只能处于阻塞状态。

HTTP2推出了著名的多路复用技术来解决上面的三个问题。

  • 一个域名只使用一个 TCP 长链接和消除队头阻塞问题。
  • 将请求分红一帧一帧的数据去传输,这样请求能够并行。

加入了多路复用技术后请求是如何进行的呢?

  • 首先,浏览器准备好请求数据,包括了请求行、请求头等信息,若是是 POST 方法,那么还要有请求体。
  • 这些数据通过二进制分帧层处理以后,会被转换为一个个带有请求 ID 编号的帧,经过协议栈将这些帧发送给服务器。
  • 服务器接收到全部帧以后,会将全部相同 ID 的帧合并为一条完整的请求信息。
  • 而后服务器处理该条请求,并将处理的响应行、响应头和响应体分别发送至二进制分帧层。
  • 一样,二进制分帧层会将这些响应数据转换为一个个带有请求 ID 编号的帧,通过协议栈发送给浏览器。
  • 浏览器接收到响应帧以后,会根据 ID 编号将帧的数据提交给对应的请求。

经过上面的分析,咱们知道了多路复用是 HTTP/2 的最核心功能,它能实现资源的并行传输。多路复用技术是创建在二进制分帧层的基础之上。基于二进制分帧层,HTTP/2 还实现了不少其余功能,下面咱们就来简要了解下。

  1. 能够设置请求的优先级。咱们知道浏览器中有些数据是很是重要的,可是在发送请求时,重要的请求可能会晚于那些不怎么重要的请求,若是服务器按照请求的顺序来回复数据,那么这个重要的数据就有可能推迟好久才能送达浏览器,这对于用户体验来讲是很是不友好的。为了解决这个问题,HTTP/2 提供了请求优先级,能够在发送请求时,标上该请求的优先级,这样服务器接收到请求以后,会优先处理优先级高的请求。
  2. 服务器推送。HTTP/2 还能够直接将数据提早推送到浏览器。当用户请求一个 HTML 页面以后,服务器知道该 HTML 页面会引用的JavaScript 文件和 CSS 文件,那么在接收到 HTML 请求以后,附带将要使用的 CSS 文件和 JavaScript 文件一并发送给浏览器,这样当浏览器解析完 HTML 文件以后,就能直接拿到须要的 CSS 文件和 JavaScript 文件,这对首次打开页面的速度起到了相当重要的做用。
  3. 头部压缩。HTTP/2 对请求头和响应头进行了压缩。一方面,头信息使用gzip或compress压缩后再发送;另外一方面,客户端和服务器同时维护一张头信息表,全部字段都会存入这个表,生成一个索引号,之后就不发送一样字段了,只发送索引号,这样就提升速度了。
相关文章
相关标签/搜索