1、OSI与TCP参考模型
OSI七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
TCP/IP 四层:数据链路层、网络层、传输层、应用层。
1)应用层
应用层(application-layer)的任务是通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应用进程(进程:主机中正在运行的程序)间的通信和交互的规则。对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如域名系统DNS,支持万维网应用的 HTTP协议,支持电子邮件的 SMTP协议等等。我们把应用层交互的数据单元称为报文。
2)传输层
传输层(transport layer)的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输服务。由于一台主机可同时运行多个线程,因此运输层有复用和分用的功能。所谓复用就是指多个应用层进程可同时使用下面运输层的服务,分用和复用相反,是运输层把收到的信息分别交付上面应用层中的相应进程。
运输层主要的两种协议:
传输控制协议 TCP(Transmission Control Protocol):提供面向连接的,可靠的数据传输服务。
用户数据协议 UDP(User Datagram Protocol):提供无连接的,尽最大努力的数据传输服务(不保证数据传输的可靠性)。
TCP和UDP的区别:
TCP是面向连接的、可靠的、基于字节流的传输层协议;UDP是一个面向无连接的传输层协议,即发送数据前不需要先建立链接。
可靠性:TCP是基于连接的,无差错,不丢失,不重复,且按序到达,可靠性高(TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源);UDP是基于无连接的,尽最大努力交付,即不保证可靠交付,可靠性较低;
安全性:由于TCP是连接的通信,需要有三次握手、重新确认等连接过程,会有延时,实时性差,由于协议所致,安全性较高;而UDP无连接,无建立连接的过程,因而实时性较强,安全略差;如果对实时性要求高和高速传输的场景下需要使用UDP;如果需要传输大量数据且对数据可靠性要求高的场景使用TCP;
TCP是面向字节流,将应用层报文看成一串无结构的字节流,分解为多个TCP报文段传输后,在目的站重新装配;UDP面向报文,不拆分应用层报文,只保留报文边界,一次发送一个报文,接收方去除报文首部后,原封不动将报文交给上层应用,并且网络出现拥塞不会使得发送速率降低(因此会出现丢包,对实时的应用比如IP电话和视频会议等)。
开销方面:在传输相同大小的数据时,TCP首部开销20字节;UDP首部开销只有8个字节,TCP报头比UDP复杂,故实际包含的用户数据较少。TCP无丢包,而UDP有丢包,故TCP开销大,UDP开销较小;
连接数:每条tcp连接只能是点到点的;udp支持一对一、一对多、多对一、多对多的交互通信。
TCP三大核心:
面向连接:所谓面向连接,指的是客户端与服务端的连接,在双方互相通信之前,TCP需要三次握手简历连接,而UDP没有相应的简历连接的过程
可靠性:TCP可靠性主要体现在有状态和可控制。有状态是指TCP会精准记录哪些数据发送了,被对方接受了,哪些没有,而保证数据按序到达,不允许差错;可控制:意识到丢包或者网络环境差,TCP根据具体情况调整自己的行为,控制自己发送速度或重发。
面向字节流:UDP数据传输基于数据报,仅仅是继承了IP层的特性,而TCP为维护状态,将IP包变成了字节流
3)网络层
在计算机网络中进行通信的两个计算机之间可能会经过很多个数据链路,也可能还要经过很多通信子网。网络层的任务就是选择合适的网间路由和交换结点, 确保数据及时传送。 在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组和包进行传送。在 TCP/IP 体系结构中,由于网络层使用 IP 协议,因此分组也叫 IP 数据报 ,简称数据报。
交换机工作在数据链路层,路由器工作在网络层
4)数据链路层
数据链路层通常简称为链路层。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如:同步信息,地址信息,差错控制等)。
5)物理层
物理层的作用是实现相邻计算机节点之间比特流的透明传送。
2、DNS协议
DNS(Domain Names System),域名系统,是互联网一项服务,是进行域名和与之相对应的 IP 地址进行转换的服务器。DNS相当于一个翻译官,负责将域名翻译成ip地址;
IP 地址:一长串能够唯一地标记网络上的计算机的数字。
域名:是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时对计算机的定位标识。
域名分为:根域名、顶级域名、二级域名、三级域名等等。
DNS 查询的方式
DNS使用什么协议?客户端查询DNS服务器时用 UDP;DNS服务器间进行域传输的时候用TCP。
1)递归查询
2)迭代查询
解析域名的过程
a、首先搜索浏览器的 DNS 缓存,缓存中维护一张域名与 IP 地址的对应表
b、若没有命中,则继续搜索操作系统的 DNS 缓存
c、若仍然没有命中,则操作系统将域名发送至本地域名服务器,本地域名服务器采用递归查询自己的 DNS 缓存,查找成功则返回结果
c、若本地域名服务器的 DNS 缓存没有命中,则本地域名服务器向上级域名服务器进行迭代查询:
首先本地域名服务器向根域名服务器发起请求,根域名服务器返回顶级域名服务器的地址给本地服务器
本地域名服务器拿到这个顶级域名服务器的地址后,就向其发起请求,获取权限域名服务器的地址
本地域名服务器根据权限域名服务器的地址向其发起请求,最终得到该域名对应的 IP 地址
d、本地域名服务器将得到的 IP 地址返回给操作系统,同时自己将 IP 地址缓存起来。
e、操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起,至此,浏览器就得到了域名对应的 IP 地址,并将 IP 地址缓存起。
3、CDN介绍
CDN (全称 Content Delivery Network),即内容分发网络。构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。
CDN就是根据用户位置分配最近的资源。用户在上网的时候不用直接访问源站,而是访问离他“最近的”一个 CDN 节点,术语叫边缘节点,其实就是缓存了源站内容的代理服务器。
原理分析
我们使用域名访问某一个站点时的路径:用户提交域名→浏览器对域名进行解释→DNS解析得到目的主机的IP地址→根据IP地址访问发出请求→得到请求数据并回复。
应用CDN后,DNS返回的不再是IP地址,而是一个CNAME(Canonical Name ) 别名记录,指向CDN的全局负载均衡。
负载均衡系统
由于没有返回IP地址,于是本地DNS会向负载均衡系统再发送请求 ,则进入到CDN的全局负载均衡系统进行智能调度:
a、看用户的 IP 地址,查表得知地理位置,找相对最近的边缘节点
b、看用户所在的运营商网络,找相同网络的边缘节点
c、检查边缘节点的负载情况,找负载较轻的节点,还有其他的检测,比如节点的“健康状况”、服务能力、带宽、响应时间等
结合上面的因素,得到最合适的边缘节点,然后把这个节点返回给用户,用户就能够就近访问CDN的缓存代理。
缓存代理
缓存系统是CDN的另一个关键组成部分,缓存系统会有选择地缓存那些最常用的那些资源。
两个衡量CDN服务质量的指标:
命中率:用户访问的资源恰好在缓存系统里,可以直接返回给用户,命中次数与所有访问次数之比。
回源率:缓存里没有,必须用代理的方式回源站取,回源次数与所有访问次数之比。
缓存系统也可以划分出层次,分成一级缓存节点和二级缓存节点:
a、一级缓存配置高一些,直连源站;
b、二级缓存配置低一些,直连用户。
回源的时候二级缓存只找一级缓存,一级缓存没有才回源站,可以有效地减少真正的回源。
4、IPV4和IPV6的区别
1)IPv6把IP地址由32位(4 个字节)增加到128位(16 个字节),从而能够支持更大的地址空间。IPv4地址是以小数表示的二进制数。 IPv6地址是以十六进制表示的二进制数。
2)灵活的首部格式。IPv6将IPv4中的部分作用不大的报头转化为可选的扩展报头,减轻了核心网路由器对数据报分析的负担。
3)支持即插即用(即自动配置)。IPv4协议的地址可以通过手动或DHCP配置的。
4)IPv6的路由表更小。可使路由器能在路由表中,用一条记录表示一片子网。大大减小了路由器中路由表的长度,提高了路由器转发数据包的速度。
5)IPv6具有更高的安全性。在使用IPv6网络中,用户可以对网络层的数据进行加密并对IP报文进行校验。为用户提供客户端到服务端的数据安全,保证数据不被劫持。
5、TCP粘包问题
首先我们介绍一下保护消息边界和流。保护消息边界,就是指传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息(UDP不会出现粘包,因为它有消息边界)。也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。而面向流则是指无保护消息保护边界的(TCP是基于流的传输,所以无消息保护边界),如果发送端连续发送数据,接收端有可能在一次接收动作中,会接收两个或者更多的数据包。
例如,我们连续发送三个数据包,大小分别是2k,4k ,8k这三个数据包,都已经到达了接收端的网络堆栈中,如果使用UDP协议,不管我们使用多大的接收缓冲区去接收数据,我们必须有三次接收动作,才能够把所有的数据包接收完;而使用TCP协议,我们只要把接收的缓冲区大小设置在14k以上,我们就能够一次把所有的数据包接收下来,只需要有一次接收动作。
TCP无保护消息边界的解决
针对这个问题,一般有3种解决方案:
a、发送固定长度的消息。
b、把消息的尺寸与消息一块发送。
c、使用特殊标记来区分消息间隔。
TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
1)沾包出现的原因
如果双方建立连接,需要在连接后一段时间内发送不同结构数据。如果发送方连续发送这个两个包出去,接收方一次接收可能就会产生沾包问题。出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。
a、发送端需要等缓冲区满才发送出去,造成粘包。由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法.简单的说,当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间,看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去。
b、接收方不及时接收缓冲区的包,造成多个包接收。
发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。
接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。
粘包情况有两种:一种是粘在一起的包都是完整的数据包;另一种情况是粘在一起的包有不完整的包。
不是所有的粘包现象都需要处理,若传输的数据为不带结构的连续流数据(如文件传输),则不必把粘连的包分开(简称分包)。但在实际工程应用中,传输的数据一般为带结构的数据,这时就需要做分包处理。
在处理定长结构数据的粘包问题时,分包算法比较简单;在处理不定长结构数据的粘包问题时,分包算法就比较复杂。特别是粘在一起的包有不完整的包的粘包情况,由于一包数据内容被分在了两个连续的接收包中,处理起来难度较大。
2)避免粘包现象的措施
a、对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;
b、对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;
c、由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。
不足:
a、第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。
b、第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。
c、第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。
6、QUIC协议
QUIC (Quick UDP Internet Connection),又名HTTP3,它利用UDP解决了当前基于TCP协议的HTTP的许多问题,提升了在弱网环境下的网络通信体验。QUIC是谷歌推出的一套基于UDP的传输协议,它实现了TCP + HTTPS + HTTP/2的功能,目的是保证可靠性的同时降低网络延迟。因为UDP是一个简单传输协议,基于UDP可以摆脱TCP传输确认、重传慢启动等因素,建立安全连接只需要一的个往返时间,它还实现了HTTP/2多路复用、头部压缩等功能。
1)QUIC的关键特性
a、连接迁移
一条 TCP 连接是由四元组标识的(源 IP,源端口,目的 IP,目的端口)。
连接迁移就是当其中任何一个元素发生变化时,这条连接依然维持着,能够保持业务逻辑不中断。
主要关注的是客户端的变化,因为客户端不可控并且网络环境经常发生变化,而服务端的 IP 和端口一般都是固定的。比如使用手机在 WIFI 和 4G 移动网络切换时,客户端的 IP 肯定会发生变化,需要重新建立和服务端的 TCP 连接。有些连接竞争时需要重新绑定端口,导致客户端的端口发生变化,同样需要重新建立 TCP 连接。这就是TCP连接重连之痛。
解决方案:基于UDP的QUIC连接迁移
当用户的地址发生变化时,如 WIFI 切换到 4G 场景,基于 TCP 的 HTTP 协议无法保持连接的存活。QUIC 基于连接 ID 唯一识别连接。当源地址发生改变时,QUIC 仍然可以保证连接存活和数据正常收发。
QUIC是基于UDP协议的,任何一条 QUIC 连接不再以 IP 及端口四元组标识,而是以一个 64 位的随机数作为 ID 来标识,这样就算 IP 或者端口发生变化时,只要 ID 不变,这条连接依然维持着,上层业务逻辑感知不到变化,不会中断,也就不需要重连。
b、低连接时延
以一次简单的浏览器访问为例,在地址栏中输入https://www.abc.com,实际会产生以下动作:
DNS递归查询www.abc.com,获取地址解析的对应IP;
TCP握手,我们熟悉的TCP三次握手需要需要1个RTT;
TLS握手,以目前应用最广泛的TLS 1.2而言,需要2个RTT。对于非首次建连,可以选择启用会话重用,则可缩小握手时间到1个RTT;
HTTP业务数据交互,假设abc.com的数据在一次交互就能取回来。那么业务数据的交互需要1个RTT;
经过上面的过程分析可知,要完成一次简短的HTTPS业务数据交互,需要经历:新连接 4RTT + DNS;会话重用3RTT + DNS。
所以,对于数据量小的请求而言,单一次的请求握手就占用了大量的时间,对于用户体验的影响非常大。同时,在用户网络不佳的情况下,RTT延时会变得较高,极其影响用户体验。
解决方案:真0-RTT的QUIC握手
QUIC 由于基于 UDP,无需 TCP 连接,在最好情况下,短连接下 QUIC 可以做到 0RTT 开启数据传输。
基于 TCP 的 HTTPS连接时延,一方面是TCP和TLS分层设计导致的,分层的设计需要每个逻辑层次分别建立自己的连接状态。另一方面是TLS的握手阶段复杂的密钥协商机制导致的。要降低建连耗时
QUIC具体握手过程如下:
1.客户端判断本地是否已有服务器的全部配置参数(证书配置信息),如果有则直接跳转到(5),否则继续 ;
2.客户端向服务器发送inchoate client hello(CHLO)消息,请求服务器传输配置参数;
3.服务器收到CHLO,回复rejection(REJ)消息,其中包含服务器的部分配置参数;
4.客户端收到REJ,提取并存储服务器配置参数,跳回到(1) ;
5.客户端向服务器发送full client hello消息,开始正式握手,消息中包括客户端选择的公开数。此时客户端根据获取的服务器配置参数和自己选择的公开数,可以计算出初始密钥K1;
6.服务器收到full client hello,如果不同意连接就回复REJ,同(3);如果同意连接,根据客户端的公开数计算出初始密钥K1,回复server hello(SHLO)消息,SHLO用初始密钥K1加密,并且其中包含服务器选择的一个临时公开数;
7.客户端收到服务器的回复,如果是REJ则情况同(4);如果是SHLO,则尝试用初始密钥K1解密,提取出临时公开数;
8.客户端和服务器根据临时公开数和初始密钥K1,各自基于SHA-256算法推导出会话密钥K2;
9.双方更换为使用会话密钥K2通信,初始密钥K1此时已无用,QUIC握手过程完毕。之后会话密钥K2更新的流程与以上过程类似,只是数据包中的某些字段略有不同。
C、可自定义的拥塞控制
Quic使用可插拔的拥塞控制,相较于TCP,它能提供更丰富的拥塞控制信息。比如对于每一个包,不管是原始包还是重传包,都带有一个新的序列号(seq),这使得Quic能够区分ACK是重传包还是原始包,从而避免了TCP重传模糊的问题。Quic同时还带有收到数据包与发出ACK之间的时延信息。这些信息能够帮助更精确的计算RTT。此外,Quic的ACK Frame 支持256个NACK 区间,相比于TCP的SACK(Selective Acknowledgment)更弹性化,更丰富的信息会让client和server 哪些包已经被对方收到。
d、无队头阻塞
虽然 HTTP2 实现了多路复用,但是因为其基于面向字节流的 TCP,因此一旦丢包,将会影响多路复用下的所有请求流。QUIC 基于 UDP,在设计上就解决了队头阻塞问题。
TCP 队头阻塞的主要原因是数据包超时确认或丢失阻塞了当前窗口向右滑动,解决队头阻塞的方案是不让超时确认或丢失的数据包将当前窗口阻塞在原地。QUIC也正是采用上述方案来解决TCP 队头阻塞问题的。TCP 为了保证可靠性,使用了基于字节序号的 Sequence Number 及 Ack 来确认消息的有序到达。
解决方案:QUIC的无队头阻塞
QUIC 同样是一个可靠的协议,它使用 Packet Number 代替了 TCP 的 Sequence Number,并且每个 Packet Number 都严格递增,也就是说就算 Packet N 丢失了,重传的 Packet N 的 Packet Number 已经不是 N,而是一个比 N 大的值,比如Packet N+M。
QUIC 使用的Packet Number 单调递增的设计,可以让数据包不再像TCP 那样必须有序确认,QUIC 支持乱序确认,当数据包Packet N 丢失后,只要有新的已接收数据包确认,当前窗口就会继续向右滑动。待发送端获知数据包Packet N 丢失后,会将需要重传的数据包放到待发送队列,重新编号比如数据包Packet N+M 后重新发送给接收端,对重传数据包的处理跟发送新的数据包类似,这样就不会因为丢包重传将当前窗口阻塞在原地,从而解决了队头阻塞问题。
那么,既然重传数据包的Packet N+M 与丢失数据包的Packet N 编号并不一致,我们怎么确定这两个数据包的内容一样呢?
QUIC使用Stream ID 来标识当前数据流属于哪个资源请求,这同时也是数据包多路复用传输到接收端后能正常组装的依据。重传的数据包Packet N+M 和丢失的数据包Packet N 单靠Stream ID 的比对一致仍然不能判断两个数据包内容一致,还需要再新增一个字段Stream Offset,标识当前数据包在当前Stream ID 中的字节偏移量。
有了Stream Offset 字段信息,属于同一个Stream ID 的数据包也可以乱序传输了(HTTP/2 中仅靠Stream ID 标识,要求同属于一个Stream ID 的数据帧必须有序传输),通过两个数据包的Stream ID 与 Stream Offset 都一致,就说明这两个数据包的内容一致。
2)QUIC协议组成
a红色部分是 Stream Frame 的报文头部,有认证。绿色部分是报文内容,全部经过加密。
Flags:用于表示Connection ID长度、Packet Number长度等信息;
Connection ID:客户端随机选择的最大长度为64位的无符号整数。但是,长度可以协商;
QUIC Version:QUIC协议的版本号,32位的可选字段。如果Public Flag & FLAG_VERSION != 0,这个字段必填。客户端设置Public Flag中的Bit0为1,并且填写期望的版本号。如果客户端期望的版本号服务端不支持,服务端设置Public Flag中的Bit0为1,并且在该字段中列出服务端支持的协议版本(0或者多个),并且该字段后不能有任何报文;
Packet Number:长度取决于Public Flag中Bit4及Bit5两位的值,最大长度6字节。发送端在每个普通报文中设置Packet Number。发送端发送的第一个包的序列号是1,随后的数据包中的序列号的都大于前一个包中的序列号;
Stream ID:用于标识当前数据流属于哪个资源请求;
Offset:标识当前数据包在当前Stream ID 中的字节偏移量;