基于上篇的浏览器架构,我们知道了打开一个页面,为什么至少会有四个进程出现。接下来,我们一起来讨论一下,服务端的页面文件是如何被完整的送达到浏览器的,这一中间经历了哪些过程,以及如何确保页面文件能够安全的送达浏览器端。本文重点介绍在Web世界中TCP/IP是如何工作的。
相信作为前端开发人员来说,在衡量Web页面性能的时候都会听到一个重要的指标FP(First Paint),指的是从页面加载到首次开始绘制页面的时长。这个指标会直接影响用户的跳出率,更快的页面响应意味着更多的PV(页面浏览量或点击量,用户每一次对网站中的每个页面访问均被记录一个PV)。影响FP的一个重要的因素是网络加载速度
我们需要知道的是,不管使用http,还是使用webSocket,它们都是基于TCP/IP协议的,如果你对这些有着充分的了解,那么你就很清楚如何去优化Web性能,或者是你能够轻松的定位到Web问题。
网络七大层(OSI)
物理层:
负责物理媒介上传输比特流。以二进制数据形式在物理媒体上传输数据。
数据链数层:
负责将比特流转化为数据帧,并进行错误检测和纠正,确保数据传输的可靠性。(SLIP、PPP、ARP、RARP、MTU)
网络层:
将数据帧转发到目标地址,包括数据分段、传输控制和流量控制等,同时处理不同网之间的通信。(IP、ICMP、RIP)
传输层:
负责提供端到端的数据传输。包括数据分段、传输控制和流量控制等。(TCP、UDP)
会话层:
负责建立、管理和终止会话,解除或建立与别的接点的联系。
表示层:
负责数据格式的转换和加密/解密等功能,确保数据的可读性和完整性。
应用层:
负责提供各种网络应用服务,如文件传输、电子邮件、远程登录。(TFTP、HTTP、FTP、SMTP、DNS)
以一张图总结一下上面的七大层:
了解了上面的网络七大层之后,我们再来详细探究一下传输层中的TCP、UDP协议,这两个是最具有代表性的传输层协议。
TCP
TCP是面向连接的、可靠的、基于字节流的传输层通信协议。具有流量控制、拥塞控制等功能,可以保证数据传输的实时性和可靠性。
面向连接
所谓的面向连接,指的就是在发送数据之前需要两端建立连接。就是我们常说的“三次握手”,建立一个安全连接,为后面可靠传输打下基础。
下面说一下TCP几个核心的特点:
可靠传输
对于可靠传输,判断丢包,误码考靠的是TCP的段编号以及确认号。TCP为了保证报文传输的可靠,就给每个包一个序号(SYN),同时序号也保证了传送到接收端实体的包是按顺序接收的,然后接收端会返回一个确认号(ACK),如果发送端在规定的时间内没有收到确认,那么就可以判断对应的数据(丢失)将会被重传。(重传机制)
提供拥塞控制
当网络出现拥塞时,TCP能够减小向网络注入数据的速率和数量,缓解拥塞。
全双工通信
TCP运行通信双方的应用程序在任何时候都能发送数据,因为TCP连接的两端都设有缓存,用来临时存放双向通信的数据。当然,TCP可以立即发送一个数据段,也可以缓存一段时间以便下一下发送更多的数据段。
TCP 拥塞控制机制
为了更好的了解拥塞机制,你需要具备以下的知识概念:
拥塞窗口:用来表示发送方可以容忍的最大拥塞窗口大小,也就是网络中可以传输的最大数据包大小。当发送方接收到接收方发送的确认应答后,会根据确认应答中的(ACK)值来更新拥塞窗口大小。
状态变量:表示网络拥塞程度的变量。
TCP拥塞控制机制主要有以下四种机制:
慢启动
思路:开始的时候不要发送大量数据,而是先测试一下网络的拥塞程度,由小到大增加拥塞窗口的大小。
1、在开始发送的时候设置cwnd = 1(cwnd指的是拥塞窗口)
2、为了防止cwnd增长过大引起网络拥塞,设置一个慢开始门限(ssthresh 状态变量)
当 cwnd < ssthresh,使用慢开始算法
当 cwnd = ssthresh,即可使用慢开始算法,也可以使用拥塞避免算法
当 cwnd > ssthresh,使用拥塞避免算法
拥塞避免
思路:让拥塞窗口cwnd缓慢增大,即每经过一个返回时间就把发送方的拥塞控制窗口加一。
1、拥塞避免未必能够完全避免拥塞,是说在拥塞避免阶段将拥塞窗口控制为线性增长,使网络不容易出现阻塞。
2、无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现了拥塞,就把慢开始门限(ssthresh)设置为出现拥塞时发送窗口大小的一半。然后把拥塞窗口设置为1,执行慢开始算法。
注意:判断网络出现拥塞的根据就是没有收到确认,虽然没有收到确认可能是其他原因的分组丢失,都当作是拥塞来处理。
快速重传
快速重传要求接收方在收到一个失序的报文段后就立即发出重复确认(目的其实就是使发送方及早知道有报文段没有送达)。发送方只要连续收到三个重复确认就立即重传对方尚未接收到的报文段, 而不必继续等待重传计数器时间到期。
由于不用等待设置的重传计时器到期,能尽早重传未被确认的报文段,能提高整个网络的吞吐量
快速恢复
当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半。但是接下去并不执行慢开始算法。
考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在任务网络可能没有出现拥塞,此时不执行慢开始算法,而是将cwnd设置为ssthresh的大小,然后执行拥塞避免算法。
TCP流量控制机制
想要理解流量控制机制,需要知道什么是滑动窗口。
滑动窗口: 滑动窗口是一个缓冲区,用于存储 TCP 连接中的数据包。当客户端发送数据包时,服务器会将其存储在滑动窗口中,然后逐步发送出去。发送方会根据滑动窗口的大小来确定发送数据的速度,而接收方会根据滑动窗口的大小来确定接收数据的速度。
滑动窗口的大小通常是固定的,但是可以通过 TCP 选项来调整。窗口的大小决定了 TCP 连接中数据包发送和接收速率,同时也决定了数据包的丢包率和延迟时间。如果窗口的大小比较小,则 TCP 连接中的数据传输速度较慢,但同时也减少了数据包的丢包率和延迟时间,反之亦然。
了解了上面说的滑动窗口之后,我们再来看一下TCP是如何进行流量控制的。
一般来说,流量控制就是为了让发送方发送数据的速度不能太快。要让接收方来得及接收。TCP采用大小可变的滑动窗口进行流量控制,窗口大小的单位是字节。
大致的流程如下:
1、当一个连接建立时,连接的每一端分配一个缓冲区来保存输入的数据,并将缓冲区的大小发送给另一端。
2、当数据到达时,接收方发送确认,其中包含了自己剩余的缓冲区大小。(剩余缓冲区空间的大小称之为窗口,指出窗口大小通知的称为窗口通告。接收方在发送的每一确认中都含有一个窗口通告)
3、如果接收方应用程序读数据的速度能够与数据到达的速度一样快,接收方将在每一确认中发送一个正的窗口通告。
4、如果发送方操作的速度快于接收方,接收到的数据最终将充满接收方的缓冲区,导致接收方通告一个零窗口。发送方收到一个零窗口通告时,必须停止发送,直到接收方重新通告一个正的窗口。
理解了上面TCP的传输机制之后,下面我们来总结一下:为什么TCP是一个安全可靠的传输。
为什么 TCP 是一个可靠的传输机制
TCP 的可靠传输机制是基于连续ARQ协议
(自动重传请求)和滑动窗口协议
的。
TCP协议在发送方维持了一个发送窗口
,发送窗口以前的报文段是已发送且确认的报文段。发送窗口中包含:已发送但未确认的报文段、允许发送但未发送的报文段。发送窗口以后的报文段是还不允许发送的报文段。当发送方向接收方发送报文时,会依次发送窗口中的所有报文,并且设置一个定时器,
如果在定时器内
收到某个报文段的确认回答,则滑动窗口,将窗口首部向后滑动到确认报文的后一个位置,如果此时还有发送但未确认的报文段,则重新设置定时器,如果没有,则关闭定时器。
如果超时
,则重新发送所有已发送但未确认的报文段,并将超时时间设置为之前两倍。当发送方收到接收方的三个冗余的确认应答后,这是一种指示,说明该报文段很有可能发生了丢失,那么发送方会启用快速重传的机制
,就是当前定时器结束前,发送所有已发送且确认的报文。
发送窗口的大小是变化的,它是由接收窗口剩余大小
和网络中拥塞程度
来决定的,TCP 就是通过控制发送窗口的长度来控制报文段发送的速率。
这里提及一下粘包的概念:
粘包: 指的是当前TCP数据包在传输过程中遇到错误时,TCP协议会将其重新发送。然而,在某些情况下,TCP数据包可能重新发送多次,这会导致数据包粘在一起,形成一个更大的数据包。
默认情况下,TCP 连接会启用延迟传送算法(Nagle算法),在数据发送之前缓存他们,如果短时间内多个数据发送,会缓冲到一起作为一次发送,这样可以减少IO消耗提高性能。
UDP
UDP是一种不可靠的、无连接的传输层协议
。它不像TCP协议那样具有可靠的数据传输机制,它不具备流量控制和拥塞控制
等功能,在传输的过程中容易出现丢失、重复、延迟等问题。适合效率要求高,对准确性要求相对低的场景。
为什么是无连接
首先UDP是不需要和TCP一样,在发送数据之前进行三次握手建立连接。想发数据就可以发数据,并且也只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接操作。
具体一点来说:
发送端:应用层将数据传递给传输层的UDP协议,UDP只会给数据增加一个UDP头标标识下这个是UDP协议,然后就传递给网络层了。
接收端:网络层将数据传递给传输层,UDP只去除IP报文头就传递给应用层,不会有任何的拼接操作。
UDP为什么是不可靠、不安全
主要是因为他在传输的过程中缺乏头部信息来保证数据的可靠性和完整性。
首先不可靠体现在无连接上
,通信不需要建立连接,想发就发,这样的情况肯定是不可靠的。并且收到什么数据就传递什么数据,不会进行数据的备份,发送数据时是不关心对方是否能够接收到。
其次,UDP数据包没有大小的限制,
因此可能会传输不完整或错误的数据包。如果数据包在传输过程中发生错误,UDP协议是不会返回错误信息,也不会重新传输数据包,而是直接丢弃数据包。
最后,UDP没有拥塞控制、流量控制,
会一直以恒定的速度发送数据。即使网络条件不好,也不会对发送速率进行调整,这样就会导致在网络条件不好的情况下可能会出现丢包的现象。若数据包出现丢失,也无法感知到数据是否到达接收方,因此只能等待下一个数据包。
有了上面UDP以及TCP的概念之后,我们再来看一下一个数据包是如何从主机A传送到主机B上的。
一个数据包的“旅程”
下面主要从:“数据包如何送到主机”,“主机如何将数据包转交给应用”和“数据是如何被完整的送达到应用程序”,这三个角度来为你讲述数据的传输过程。
在互联网中的数据是通过数据包来传输的。如果发送的数据很大,那么该数据就会被拆分为多个小数的数据包来进行传输(比如文件上传、音频等数据都是拆分为一个个小的数据包来进行传输的,并不是一个大的文件一次性传输过来的)。
1、IP:将数据包传送到目的主机
如果你想数据在互联网上进行传输,就要符合网际协议(IP)标准。就好比如快速的传输,你需要提供对应的地址,然后就可以将快递就输送了。同样的,在互联网上,你也需要明确的将你的数据传输到哪里去,就是这里提及到的IP地址。
计算机的地址就称为IP地址,访问任务网站实际上只是你的计算机向另外一台计算机请求信息而已。
例如:想要把一个数据包从主机A发送到主机B,那么在传输之前,数据包上面需要附加上主机B的IP地址信息,这样在传输的过程中才能正确的数据传输到B中。当然这一个过程中还需要将主机A本身的IP地址附带上,有了主机A的IP地址,主机B才可以回复信息给主机A。这些附加的信息会被装进一个叫IP头的数据结构里。IP头是IP数据包开头信息,包含IP版本、源IP地址、目标地址、生存时间等信息。
为了方便理解,将网络简单的分为三层结构:
我们大概来梳理一下整个流程:
1、上层将含有“稀土掘金”的数据包交给网络层
2、网络层再将IP头附加到数据包上,组成新的IP数据包,并交给底层
3、底层通过物理网络将数据包传输给主机B
4、数据包被传输到主机B的网络层,在这里主机B拆开数据包的IP头信息,并将数据交给上层
5、最终,含有“系统掘金”信息的数据包就被送达到主机B的上层了
2、UDP:将数据包传送到应用程序
IP是非常底层的协议,只负责将数据包传送到对方的电脑上,在传送到对方电脑上面时,其实对方是不知道将数据包传送给哪个应用程序的。因此,还需要基于IP之上开发能和应用打交道的协议,最常见的是“用户数据包协议”,简称UDP协议。
UPD中一个最重要的信息是端口号,端口号其实就是一个数字,每个想访问网络的程序都需要绑定一个端口号。通过端口号UDP就能把指定的数据包发送给指定的程序,所以IP通过IP地址信息把数据包发送给指定的电脑,而UDP通过端口号把数据分发给正确的程序。和IP头一样,端口号会被装进UDP头里面,UDP头再和原始数据包合并组成新的UDP数据包。UDP头中除了目的端口号,还有源端口号等信息。
结合上面IP三层的图,我们可以新增一个传输层。如下:
过程如下:
1、上层将含有“稀土掘金”的数据包交给传输层;
2、传输层会在数据包前面附加上UDP头,组成最新的UDP数据包,再将新的UDP数据包交给网络层。
3、网络层再将IP头附加到数据报上,组成新的IP数据包,并交给底层。
4、数据包被传输到主机B的网络层,在这里主机B拆开IP头信息,并将拆开来的数据部分交给传输层。
5、在传输层中,数据包中的UDP头会被拆开,并根据UDP中所提供的端口号
,把数据部分交给上层的应用程序。
6、最终,含有“系统掘金”信息的数据包就旅行到了主机B上层应用程序这里。
上面有提及到,在使用UDP发送数据时,有各种因素会导致数据包出错,虽然UDP可以校验数据是否正确,但是对于错误的数据包,UDP并不提供重传机制
,只是会丢弃当前的包,而且UDP在发生之后也无法知道是否能够到达目的地。
虽说UDP不能保证数据可靠性,但是传输速度却是非常快的,所以UDP会应用在一些关注速度、但不那么严格要求数据完整性的领域,如在线视频、互动游戏等。
3、TCP:将数据完整的送达到应用程序
针对上面所说的,对于浏览器的请求,或者邮件这类要求数据传输可靠性的应用,如果使用UDP来传输会存在两个问题。
1、数据包在传输的过程中容易丢失。
2、大文件会被拆分成很多小的数据包来传输,这些小的数据包会经过不同的路由。并在不同的时间到达接收端,而UDP协议并不知道如何组装这些数据包,从而把这些数据包还原成完整的文件。
基于上面的两个问题,我们引入了TCP协议,如果你阅读了上面TCP概念以及特点,你就会知道相对于UDP,TCP有两个重要的特点。
对数据包丢失的情况,TCP提供了重传机制
。TCP引入了数据包排序机制,用来保证把乱序的数据包组合成一个完整的文件
。
和UDP头一样,TCP头除了包含了目标端口和本机端口号外,还提供用于排序的序列号,以便接收端通过序号来进行重排数据包。
通过上图你应该可以了解一个数据包是如何通过TCP来传输的,TCP单个数据包的传输流程和UDP流程差不多,不同的地方在于,通过TCP头的信息保证了一块大的数据传输的完整性。
TCP 完整连接过程
有了上面的知识铺垫,我们来看一下完整的TCP连接过程,通过这个过程你可以明白TCP是如何保证重传机制
和数据包的排序功能
的。
下面简单的将其生命周期分为三个阶段,分别是建立连接
、传输数据
、断开连接
。
说一下这个图的基本流程:
首先,建立连接阶段
。这个阶段是通过“三次握手
”来建立客户端和服务器之间的连接。TCP提供面向连接的通信传输,面向连接
是指在数据通信开始之前先做好两端之间的准备工作。所谓的三次握手
,是指建立一个TCP连接时,客户端和服务器总共需要发送三个数据包以确认连接的建立。(后面我会详细讲述三次握手和四次挥手)。其次,传输数据阶段
。在该阶段,接收端需要对每一个数据包进行确认操作,也就是接收端在接收到数据包之后,需要发送确认消息数据包给发送端。所以当发送端发生了一个数据包之后,在规定时间内没有接收到接收端反馈的确认消息,则判断为数据包丢失,并触发发送端的重传机制。同样,一个大的文件在传输过程中会被拆分为很多小的数据包,这些数据包到达接收端后,接收端会按照TCP头中的序号为其排序,从而保证组成完整的数据。最后,断开连接阶段
。数据传输完毕之后,就要终止连接了,涉及到最后一个阶段“四次挥手”来保证双方都能断开连接。
到这里你应该就明白了,TCP为了保证数据传输的可靠性,牺牲了数据包的传输速度,因为“三次握手”和“数据包检验机制”等把传输过程中的数据包的数量提高了一倍。
总结
最后,我们来总结一下这篇文章中讲述的主要内容有以下几点:
- 互联网中传输数据是通过数据包的形式来传输的,数据包在传输过程中内容容易出现丢失或者错误的情况,因此有
TCP
的诞生。 - 在传输的过程中,需要知道接收端以及发送端的地址,因此引入了
IP,主要负责将数据包传送到目的主机
。 - 传送到目的主机之后,还需要
使用UDP或者TCP
将数据包送达到具体的应用中。 - 而TCP保证了数据完整地传输,它的连接可以分为三个阶段:
建立连接
、传输数据
和断开连接
。