计算机网络基础 | 青训营笔记

53 阅读21分钟

计算机网络基础

前言|课程介绍

课程介绍

  • 通过示例对计算机网络建立整体认识
  • 建立对网络分层认知
  • 分析HTTP 1、2、3的关系
  • 介绍CDN运行的基本原理
  • 了解网络安全的最基本原则

分析方法

自底向上

  • 从简单开始,逐渐变复杂
  • 将模块逐步拼凑成一个系统

自顶向下

  • 从复杂开始,逐渐变简单
  • 从复杂的系统问题入手,拆分为模块问题

计算机基础

网络基础

网络组成部分

  • 主机:客户端和服务端
  • 路由器
  • 网络协议

网络结构

  • 比奇堡和小区网络:本地网络
  • 北京和上海比奇堡:三个本地节点的网络
  • 全国通信网络:本地网络的网络
  • 区域网络、城域网和广域网

电路交换和分组交换

电路交换:可以看成电话,必须先建立连接,即使没有数据传输也会占用资源
分组交换:可以看成传真,不会一直占用带宽,会使用队列接受。

网络分层

  • 快递员不会关心包裹内容
  • 卡车司机不关心车厢拉的是什么
  • 高速公路开的什么车

五层结构:物理层,数据链路层,网络层,传输层,应用层

协议

协议的存在依赖于连接。

协议定义了两个或多个通信实体之间交换报文格式和顺序,以及报文发送和接受一条报文或其他事件所采取的动作。

标头和载荷

image.png 标头:快递的面单
载荷:包裹中的内容

HTTP协议示例

image.png 左边是wireshack解析后的结果,右边是原始数据,右边左半部分是16进制,右半部分是以ASCII码显示的结果。

image.png NULL表示这个帧是本机的帧,源地址和目标地址都是本机,不需要经过别的交换机 链路层的数据对应的是蓝色的四个字节:18 00 00 00换成10进制是24代表这个帧使用IPV6

image.png

IP协议都不包含了第五到第四十四个字节。这四十个字节中包含了版本号,源IP地址,目的IP地址和常用信息。

image.png

TCP头部包含20个字节,包含了源端口号,目的端口号,序列号,头部长度,载荷信息等。

image.png HTTP头部中定义了一些信息,比如GET(请求类型)等

报文 = 链路层头+IP协议的头+TCP协议的头+HTTP协议的头+HTTP协议正文

image.png

HTTP协议中值得注意的一个点是:HTTP1.1中头部和载荷是通过两个换行符和两个回车划分,而TCP和IP协议不是。如图在TCP协议中头部和载荷的划分是固定的,而HTTP协议中需要对HTTP报文(二进制)做解析,转换成ASCII码之后,发现有连续的换行和回车之后才确定边界。

小结:

  • 网络组成部分:由主机、路由器、交换机等组成
  • 网络结构:网络的网络
  • 信息交换方式:电路交换和分组交换
  • 网络分层:分清职责
  • 网络协议:标头和载荷(每一层都会将上一层的标头和载荷视为本层载荷,并添加本层的标头)

Web中的网络

在这一章中,我们将从Web的基础协议HTTP协议开始,阐述HTTP协议从1到3的过程中使用了哪些技术使HTTP协议变得更快,但对于Web应用来说,除了HTTP协议以外仍然会有很多因素影响Web性能,比如节点之间的物理距离,路由选择等等。我们先从HTTP协议开始,再描述如何缓解HTTP协议以外的影响因素,最后会用与HTTP密切相关的Websocket协议来结束本章。

HTTP协议

image.png HTTP基本结构
在上图示意中,有两种颜色的消息,其中红色的是请求,蓝色的是响应。

先来看请求,第一行也称为起始行,包含三个要素,GET / HTTP1.1,这三个要素分别代表了请求方法,资源路径和HTTP版本。跟在后面的是头部,每一行代表一个头部,头部用冒号分隔,左边为头部的名称,右边为头部的值,头部名称不区分大小写。头部后面的空行将头部和正文分割。由于图中是GET请求,没有正文,所以看不见正文。

再来看响应,响应的第一行称为状态行,包含三个要素,HTTP1.1 200 OK,这三个要素分别代表了HTTP版本,状态码,状态信息。其中状态信息是可以自定义的。跟在后面的是头部,和请求中的头部类似,头部后面的空行将头部和正文分隔开来。这个示意中响应是包含正文的,内容是一个HTML文档。

image.png 观察HTTP报文和TCP报文可以发现,HTTP报文比较好理解,而TCP报文就比较晦涩难懂。 他们在网络中都是以二进制传输,不同之处在于HTTP报文是可以直接用ASCII码(明文)展示出来,这种形式对于人来说比较友好,不过相对的对于计算机来说不那么友好。HTTP的这种特性引发了一些问题,HTTP模型是一种典型的请求-响应模型,也就是说当客户端发起第一个请求后,需要等待服务端先返回第一个请求的响应,客户端才能发起第二个请求,这种方式网络利用率显然不高,这种东西导致的另一个问题是无法在一个连接上进行多路复用,在一个完整请求中间插入另一个请求的内容会导致HTTP无法分辨这个请求是来自哪个内容,请求头部和响应头部都有很多重复,比如cookie就经常会重复发很多次,这些头部会重复发送,无法压缩,无形间增加了报文的体积。这张图展示了HTTP1和HTTP1.1的模型。在HTTP1.0时代,每个请求都需要创建一个新的TCP连接,用完就会销毁,每个HTTP请求都会创建一个新TCP的连接,这种方式过于浪费,会让模型效率低下,虽然可以指定Connection keep-alive保持连接,但是HTTP1.0时代默认行为是响应后销毁TCP连接。在第二种模型中,Connection keep-alive成为了默认式,只要不显示声明使用短链接模型,默认会保持TCP连接一段时间以实现连接重用。也就是图中第二个持久连接模型。这种模型解决了重复建立TCP连接的问题,但是另一个被称为队头堵塞的问题依旧影响着HTTP的性能 。当一个请求非常大,服务器需要很长时间进行处理时,第二个很小的请求依旧需要等到前一个请求结束后才能发出,导致很大的资源浪费,进而导致完成所有请求时间远大于其应该耗费的时间。第三个模型是HTTP管线,默认情况下HTTP协议是严格按顺序的,即第一个请求发出等到第一个响应到达后才会发出第二个请求,这模型效率不高,因此就有了HTTP管线,允许客户端发送多个请求,服务端按顺序进行响应,这个模型看起来似乎很好,但现实情况是几乎没有浏览器使用这个方案,原因在于这种模型对解决队头堵塞问题几乎没有帮助,另外还有可能导致一些潜在的安全问题,性能提升也不高。 对于队头堵塞问题,HTTP1.1最常用的方案是同时建立多个TCP连接,将请求分散在多个连接里,这种方案确实可以缓解队头堵塞的问题,但建立多条TCP连接的成本是巨大的,尤其在使用HTTPS时,TLS在每条连接上都需要重新协商,当请求数量超过TCP连接数量时,仍然需要排队,TCP连接也不是无条件增长的,用户和服务器带宽是固定的,TCP连接数量增加时,每条TCP连接分得的带宽也会相应减少,完成一个完整的HTTP请求的时间也会拉长,因此,在大部分情况下,浏览器会限制一个域名下的TCP连接数量,通常是六个。 前面我们提到过在一条TCP连接上,HTTP报文是严格按顺序的。如果服务器将两个响应交替发送,会有什么样的结果呢?这里有一个例子:

image.png

假设现在有两个请求,main.js和style.css,内容如图所示。如果按行交错发送(也可以按字节,策略不重要),客户端收到的可能是如下图所示,这种结果对于客户端是无法判断流的每一行都是属于哪一个请求的, 因此是无法实现多路复用的。

image.png

而在HTTP2中,帧的方式即将多个HTTP请求拆分到帧里面,每个帧可以携带来自不同HTTP请求的设计,(这里的帧和链路层的帧是不同的,工作原理类似),这种方式来组织一条TCP连接上流数据的传输方式,可以让HTTP2在连接上每个数据包都有自己的身份,也就是可以标识每个数据包是属于哪个请求,进而实现TCP连接上的多路复用,HTTP2:帧的结构如下图所示:

image.png

最开始的三个字节表示了这个帧的长度,第四字节表示帧的类型,第五个字节对于不同帧类型有不同的涵义,用来传递当前帧的状态,第六个字节的第一位是保留位,后面31位表示这个帧所属流的ID。再往后就是当前帧帧的载荷,通过每个帧的头部就可以分析出当前帧属于哪个流(在建立连接后,一次的请求与被响应视为流),进而分析出每个帧的载荷属于哪个请求,最终在浏览器或者服务器中重组为完整的请求或者是响应,通过上述方式实现了HTTP2中传输流。

image.png 这是一张HTTP2的wireshark截图,从图中可以看到HTTP2下,HTTP不再是简单的请求和响应两个步骤了,而是多了很多类型,Magic,Headers,Data等等。

image.png

以Data帧为例,前面三个字节是载荷长度,表示当前帧的载荷有8192个字节,第四个字节是类型,这里值是0,表示这是一个Data类型的帧,第五个字节是类型对应的标志位,比如最后一位零,表示当前帧还没有结束。第六到九个字节:第一位是保留位,现在是0,随后的31位是流的ID,这里是1。再往后的8192个字节就是当前帧的载荷。

拆分成帧的形式解决了多路复用和队头堵塞的问题,同时还可以实现其他的特性。

HTTP2:帧带来的其他好处。

  • 调整响应传输的优先级
  • 头部压缩
  • Server Push

既然HTTP2已经解决了多路复用的问题,那么为什么还有HTTP3呢? 简单来说,HTTP3的出现就是因为HTTP2还不够快。虽然HTTP2解决了HTTP层面的队头堵塞,但是再TCP层面依然有可能存在队头堵塞。另外HTTPS是绝对的主流,而HTTPS在HTTP之上增加了SSL/TLS协议,HTTP和TLS的分裂关系导致了HTTPS有着双倍的握手延迟,也就是TCP握手和TLS握手有着各自的延迟。

image.png

在HTTP2中引入了帧的概念,帧包含了这个帧属于哪个流的信息以及属于这个流的载荷。 来看这个HTTP2中的例子。我们假设有四个连续的帧,如上图所示依次标记为a,b,c,d。这四个帧分别由四个TCP数据包承载。假设因为当前网络波动,第二个数据包也就是b的数据包丢失了,由于TCP的机制,它会重传, TCP并不会把第三个和第四个数据包交给HTTP2,TCP必须告诉服务器数据包b没有收到,服务器重新发送一次数据包b,且浏览器收到之后,才会把c,d交给HTTP2。即使HTTP2知道数据包b只包含main.js的内容,并不影响style.css文件。在这个例子里由于TCP对上层数据缺乏必要信息也就是数据包b不影响c和d,导致HTTP2上的队头堵塞。这个问题很难在已有的TCP协议上解决,目前网络上设备已经非常好的支持TCP协议,对目前现有的TCP连接进行修改几乎是不可能的。

在TCP和TLS层面要启动一个HTTPS连接,要进行三个RTT(Round-Trip Time),这三个RTT分别是TCP建立连接需要一个RTT,TLS连接需要两个RTT。TLS1.2及以下需要两个RTT,1.3以上可以实现首次1RTT,二次0RTT。

因此在真正进行HTTP报文交互之前,已经过去三个RTT,如果客户端和服务端的RTT是50ms,那至少需要150ms才能开始交换HTTP报文,这个效率显然很低,这个延迟是TCP和TLS固有的延迟,TCP并不知道自己在运送TLS的报文,和之前HTTP2的问题一样,无法改造现有的TCP进行优化。

image.png

既然已有的协议无法解决上述问题,必须设计一个新的协议,这个协议就是QUIC(Quick UDP Internet Connection)。QUIC将TLS作为自身的一部分,解决TCP+TLS需要各自握手的问题。又吸取了HTTP2流的概念,在QUIC中提供互相独立的流,进而解决了原先TCP队头堵塞的问题。同时引入了新的机制,可以实现首次连接1RTT,后续连接0RTT的特性。顾名思义,QUIC是基于UDP,为什么不直接开发一个新的运输层协议呢?原因在于现有的网络设备都为TCP和UDP协议提供了良好的支持,如果设计一个新的协议需要非常久的时间才能得以应用。因此权衡之后,基于UDP的基础之上设计了一个QUIC协议。HTTP3基于QUIC,QUIC是和TCP,UDP一样的运输层协议,它可以为除了HTTP协议以外的应用层协议提供支持。UDP是一个不靠谱的协议,如果包丢了,UDP是不管的,而且UDP没有拥塞控制,没有顺序保证,应用层给多少数据它就往网络里发多少数据,到没到,谁先到一概不闻不问,这些特性不适合在网络应用中使用。QUIC在UDP基础上实现了拥塞控制,丢包重传等特性,保证了传入数据的稳定性和完整性。我们来看看HTTP3是如何借助QUIC实现首次连接1RTT,后续连接0RTT。

image.png

先看首次连接,在HTTP客户端第一次与服务端发起连接的时候,QUIC客户端先发起一个连接建立,请求服务器给一把钥匙,也就是加密密钥,QUIC服务器收到请求后会把证书和加密密钥一起发送给客户端,QUIC客户端随后告知HTTP客户端可以发送报文了,后续的通信都使用加密密钥进行加密,除此服务端还会告诉QUIC客户端,这有一个新的密钥,下次连接可以直接使用。QUIC相比于之前的TLS+TCP,把之前分别属于TCP的握手和TLS的握手组合起来了。因此可以实现1RTT的连接。

image.png 接下来再看看二次连接,当同一个客户端再次发起请求时,QUIC客户端会使用上次QUIC服务端偷偷给的密钥,直接把HTTP请求加密发送给服务端,这样服务端就能直接响应请求,这种方式就可以实现0RTT连接了。

至此HTTP部分内容就结束了,我们做一下回顾,HTTP中请求的第一行为起始行,响应的第一行为状态行,请求响应第二行开始到第一个空行为止为头部,空行之后的部分为正文。在HTTP1.0中,请求结束后默认行为时关闭TCP连接,这个行为可以设置Connection头部修改。这种模型效率太低,在1.1中改成了不关闭连接,HTTP2中通过帧提供了多路复用和头部压缩,Server Push等功能,也缓解了队头阻塞的问题。但HTTP2并不能解决队头堵塞问题,HTTP3使用基于UDP重新设计的QUIC协议,避免了TCP中的队头堵塞,并将TLS内置进QUIC,降低握手延迟。可以实现首次1RTT,后续0RTT的连接。到HTTP3时,能在HTTP协议上进行的优化方案,基本都已经实现了。接下来我们来讨论一下HTTP协议之外的影响web性能的方案。

image.png

除了HTTP之外,依然有很多因素影响web性能,这些是HTTP协议本身是无法解决的,纵使HTTP3可以实现1RTT甚至0RTT,但其并不能突破物理极限。如果一个在北京的客户端,和一个在美国的服务器相连,其物理距离就决定了其延迟远远高于北京和上海的通信延迟。如果一个网站它只有一个中心服务器,那么世界各地的流量会经过非常多的路由器,而 对于很多内容,尤其是视频内容,页面通过同一个网络发送数据非常浪费资源,还要给网络服务提供商交很多钱,比如广东的用户刷抖音,如果只有北京的服务器,那么需要经过非常多的网络节点才可以到达客户端,而如果在广州就有一台服务器,可以接入视频的话,那么就可以省非常多的网络流量,而且可以降低延迟,另一方面单个服务器能承载的网络流量是有限的,对于一个大型网站来说,有可能有上百万个请求,全部交给一台服务器,可能很快就会崩溃,无法提供正常的服务。而且如果只有一台服务器,那么单点故障就会导致全部服务无法运行,给站点带来很大的损失。因此为了在世界各地都能获得高速的网络体验,CDN(ContentDeliveryNetwork,即内容分发网络),就必不可少。

image.png

通常CDN提供商都会在其服务范围内根据地理位置进行服务器的分布,如图连线表示两个路由器直接有链路,假设一个CDN提供商选择在上海,北京,广州等城市做自己的cdn服务器,全国的用户都可以在很低的路由器跳数内响应内容。

image.png 只要在这几个城市部署cdn服务器,全国用户都可以在两条内访问到cdn服务器。

image.png

全国用户都使用的是www.douyin.com 这个域名,并没有什么前缀或者后缀,而一个域名对应一个IP,一般情况下一台服务器对应一个IP,如何让北京的用户访问北京的服务器,上海的用户访问上海的服务器呢?答案在DNS上。一台服务器对应一个IP这句话并不严谨,但在大部分情况下是没错的。计算机网络中还有一个叫任播(anycast)的机制,这种机制允许多个服务器共享一个IP,有兴趣可以自己查一查任播相关资料。离我们最近的任播应用应该就是谷歌的DNS了,8.8.8.8,可以试一试发现在世界各地ping这个IP,你会发现无论从哪里ping这个IP,速度都差不太多。

在没有使用CDN时候,一个站点比如抖音,它的DNS记录是自己维护的,一般都会指向自己的服务器IP,但在使用CDN的时候,一般情况下CDN提供商会让站点将要加速的域名的DNS解析交给CDN提供商,而不是由站点自行解析。假设身在昆明的小明正在访问抖音上的一个视频,其URL中的域名是video.douyin.com,大致的流程是这样的:第一步浏览器需要向本地DNS查询这个域名对应的IP地址,于是向上一层DNS服务器发送了查询请求,DNS查询记录发现这个域名应该交由douyin解析,于是交给了抖音的域名解析服务器,抖音的域名解析服务器收到这个请求以后,发现这个域名是CDN的加速域名,于是没有返回IP,而是返回一个新的DNS地址,这个新的DNS地址是由CDN管理的,本地的DNS又向CDN的DNS发起了请求,CDN的DNS根据IP确定离得最近的CDN是广州的CDN,于是返回了广州CDN的IP,浏览器最终跟广州CDN服务器建立连接,而不是和北京的服务器进行连接。

image.png

那么如何选择CDN服务器呢?一种比较简单的策略是:根据DNS查询来源IP的地理位置,确定最近的CDN服务器,比如身在福州的小红,在访问抖音时根据地理位置应该选择上海的CDN服务器,因为619.9<686.6,但是离得越近不一定越好,因为每增加一个路由器节点都需要增加一次转发延迟,路由器接受需要时间,处理分组也需要时间,转发分组也需要时间,而这个耗时相比于几十公里的传播时延,在光纤里显然要更长。另一种情况是用户可能不使用本地网络提供商提供的DNS,而指定另外的DNS,比如谷歌的8.8.8.8。这时基于地理位置的重定向很可能适得其反了。除此之外,CDN服务器当时的负载,用户到CDN网络链路负载都可能会影响最终用户访问的速度和质量。因此如何选择一个合适的CDN服务器是复杂的,这里就不过多介绍了。

image.png

对于大型网站来说,尤其是大型视频网站,视频的总体积非常的庞大,在全国的CDN服务器都维护一份拷贝是成本非常大的方案,因此大部分站点都会使用一些策略确保大部分用户的使用体验,而又降低成本。即只在CDN服务器上维护部分内容,但这部分内容可以满足大部分用户的需求。

拉策略是一种非常常用的策略,当身在福州的小红访问视频a时,DNS将其指向了广州的CDN服务器,广州的CDN服务器发现本地没有该视频a拷贝,会向北京的总服务器发起请求,再流式返回给小红,同时将这个视频存于广州CDN视频服务器上,以便下个用户访问使用,一般CDN服务器都会有流量限制,每过一段时间都会按照一定策略,比如LRU策略,清除掉使用量最少的视频或者是最近访问最少的视频,以腾出空间。

推策略和拉策略相反,拉策略是用户指定CDN服务器上的拷贝,而推策略是网站指定CDN服务器上的拷贝。对一个电影网站,刚出的电影往往都是非常热门的。于是网站在电影上映前,在CDN负载较低的时候,向CDN服务器推送最新电影的拷贝,这样当电影上映时,全国各地的用户都可以以最快的速度获取刚上映的电影。对于一些冷门电影则可以使用拉策略,按需进行分发。

CDN可以在物理层面解决HTTP协议无法解决的问题,进而提升web服务的性能不过某些场景下使用HTTP协议可能并不合适。在本章最后一节我们讨论与HTTP协议有关系,但又没什么关系的另一个协议:Websocket。