计算机网络

基础篇

TCP/IP 网络模型

计算机与计算机之间要有统一的连接标准才能够完成相互通信,这个标准被称为互联网协议,而网络就是物理链接介质+互联网协议。按照功能不同,人们将互联网协议从不同维度分为 OSI 七层、TCP/IP 五层或 TCP/IP 四层,如下图所示

image-20221018211041494

这个网络协议是分层的,每一层都有各自的作用和职责。

TCP/IP 五层模型中每层运行的常见设备如下图所示。

image-20221018211134767

TCP/IP 四层模型中数据传输过程中每一层的封装格式:

img

网络接口层的传输单位是帧(frame),IP 层的传输单位是包(packet),TCP 层的传输单位是段(segment),HTTP 的传输单位则是消息或报文(message)。但这些名词并没有什么本质的区别,可以统称位数据包

我们主要学习前三层,所以这里以 TCP/IP 四层模型来介绍。

应用层

应用层位于 TCP/IP 模型的最上层,也是用户能直接接触到的,如电脑或手机上使用的应用软件都是在应用层上实现。

当两个不同设备的应用需要进行通信的时候,应用就把应用数据传输给下一层,也就是传输层

所以,应用层只需要专注于为用户提供应用功能,而不用去关心数据是怎么传输的,数据传输是由下层次的协议负责。就类似于我们寄快递的时候,只需要把包裹交给快递员,由他负责运输快递,我们不需要关心快递是如何被运输的。

应用层是工作在操作系统中的用户态,传输层以及下则工作在内核态。

常见的应用层协议:HTTPFTPTelnetDNSSMTP等。

传输层

应用层的数据包会传给传输层,传输层(Transport Layer是为应用层提供网络支持的。

img

在传输层中有两个传输协议,分别是 TCPUDP

TCP 全称叫传输控制协议(Transmission Control Protocol),大部分应用使用的正是 TCP 传输协议,比如 HTTP 应用层协议。TCP 相较于 UDP 多了很多特性,比如流量控制、超时重传、拥塞控制等,这些特性的目的都是为了保证数据传输时的可靠性

UDP 相对来说就很简单,UDP 只负责发送数据包,不保证数据包是否能抵达对方,但它实时性相对更好,传输效率也更高(不用花费额外的时间去建立连接)。UDP 也可以实现可靠传输,把 TCP 的特性在应用层上实现就可以,但要是实现一个可以商用的可靠 UDP 传输协议,并不是一件简单的事。

应用需要传输的数据可能会非常大,如果直接传输就不好控制,因此当传输层的数据包大小超过 MSS(TCP 最大报文段长度),就需要将数据包分块,这样即使中途有一个分块丢失或损坏了,只需要重新发送这个分块,而不用发送整个数据包。在 TCP 协议中,我们把每个数据分块称为一个 TCP 报文段TCP Segment

img

当设备作为接收方时,传输层则要负责把数据包传给应用,但是一台设备上可能会有很多应用进程在接收或者传输数据,因此需要用一个标识来区分不同的应用进程,这个标识就是端口号(0~65535)

80 端口通常是 Web 服务器用的,22端口通常是远程登录服务器用的。而对于浏览器(客户端)中的每个标签页都是一个独立的进程,操作系统会为这些进程分配临时的端口号。

由于传输层的报文中会携带端口号,因此接收方可以识别出该报文是发送给哪个应用进程的。

网络层

传输层事实上也不负责将数据从一个设备传输到另一个设备。

实际场景中的网络环节是错综复杂的,中间有着各种各样的线路和分岔路口,如果一个设备的数据传输给另一个设备,就需要在各种各样的路径和节点之间进行选择,而传输层的设计理念是简单、高效、专注,如果传输层还负责这一块功能就有点违背设计原则了。

也就是说,传输层专注于为应用层服务,让其作为应用间数据传输的媒介,帮助实现应用到应用之间的通信,而实际的传输功能功能就交给了下层,也就是网络层Internet Layer)。

img

网络层中最常(但不只有)使用的是 IP 协议(Internet Protocol),IP 协议会将传输层的报文做为数据部分,再加上 IP 包头组装成 IP 报文,如果 IP 报文大小超过了 MTU(以太网中一般为 1500 字节)就会再次进行分片,得到一个即将发送到网络的 IP 报文。

img

网络层负责将数据从一个设备传输到另一个设备,世界上有这么多的设备,网络层是如何找到目标设备的呢?因此,网络层需要有区分设备的标识

我们通常使用 IP 地址作为设备的标识,对于 IPv4 协议,IP 地址供 32 位,分成了四段(如:192.168.100.1),每段是 8 位。只有一个单纯的 IP 地址虽然做到了区分设备,但是寻址起来就特别麻烦了,那么多的设备,一个一个去匹配显然不科学。

实际上 IP 地址由两部分组成:网络部分(用来标识子网)和主机部分(用来标识主机)

  • 一个是网络号,负责标识该 IP 是属于那个 「子网」的;
  • 一个是主机号,负责标识同一 「子网」下的不同主机;

注意:单纯的 IP 地址段只是标识了 IP 地址的种类,从网络部分或主机部分都无法辨识一个 IP 地址所处的子网。例如,并不能确定172.16.10.1与172.16.10.2处于同一子网。因此,就需要子网掩码

什么是子网?

为了确定网络区域,分开主机和路由器的每个接口,从而产生了若干个分离的网络岛,接口端连接了这些独立网络的端点。这些独立的网络岛叫做子网(subnet)。子网之间用路由器互联。

什么是子网掩码?

所谓“子网掩码”,就是表示子网络特征的一个参数。它在形式上等同于IP地址,也是一个32位二进制数字,它的网络部分全部为1,主机部分全部为0。

配合子网掩码可以计算出 IP 地址的网络号和主机号。

举例:

比如 10.100.122.0/24,后面的 /24标识就是 255.255.255.0 子网掩码,255.255.255.0 二进制是「11111111-11111111-11111111-00000000」,可以看出有 24 个 1,为了简化子网掩码的表示,用 /24 代替 255.255.255.0

使用子网掩码和 IP 地址计算出网络地址和主机地址。

网络号 = IP 地址和子网掩码进行按位与运算

主机号 = 子网掩码取反后与 IP 地址进行按位与运算


寻址过程:先匹配到相同的网络号(表示要找到同一个子网),才会去找对应的主机。

上述寻址过程可以理解为我们要找到一个人,我们首先要找到这个人所在的小区(网络号),找到这个小区后,我们才去找对应的人。

除了寻址能力,IP 协议还有另一个重要的能力就是路由。实际场景中,两台设备并不是用同一条网线连接起来的,而是通过很多网关、路由器、交换机等众多网络设备连接起来的,那么就会形成很多条网络的路径,因此当数据包到达一个网络节点,就需要通过路由算法决定下一步走那条路径。

路由器寻址工作中,就是要找到目标地址的子网,找到后进而把数据包转发给对应的网络内。

IP地址的网络号

所以,**IP 协议的寻址作用是告诉我们去往下一个目的地该朝那个方向走,路由则是根据「下一个目的地」选择路径。寻址更像在导航,路由更像在操作方向盘。**

网络接口层

生成了 IP 报文后,接下来要交给网络接口层Link Layer)在 IP 头部前面加上 MAC 头部,并封装成数据帧(Data frame)发送到网络上。

img

IP 头部中的接收方 IP 地址表示网络包的目的地,通过这个地址我们就可以判断要将包发到哪里,但在以太网的世界中,这个思路是行不通的。

什么是以太网呢?

电脑上的以太网接口,Wi-Fi接口,以太网交换机、路由器上的千兆,万兆以太网口,还有网线,它们都是以太网的组成部分。以太网就是一种在「局域网」内,把附近的设备连接起来,使它们之间可以进行通讯的技术。

使用以太网进行数据传输,基于 MAC 地址的广播方式实现数据传输,只能在局域网内广播。

以太网在判断网络包目的地时和 IP 的方式不同,因此必须采用相匹配的方式才能在以太网中将包发往目的地,而 MAC 头部就是干这个用的,所以,在以太网进行通讯要用到 MAC 地址。

MAC 头部中包含的源地址和目标地址的由来: Ethernet(以太网协议) 规定接入Internet的设备必须配有网卡,发送端和接收端的地址便是指网卡的地址,即MAC地址。

MAC 地址:每块网卡出厂时都被印上一个世界唯一的 MAC 地址,它是一个长度为48位的二进制数,通常用12位十六进制数表示(前6位是厂商编号,后6位是流水线号)。

MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息,我们可以通过 ARP 协议获取对方的 MAC 地址。ARP | Devil的个人博客 (devildyw.github.io)

所以说,网络接口层主要为网络层提供「链路级别」传输的服务,负责在以太网、WiFi 这样的底层网络上发送原始数据包,工作在网卡这个层次,使用 MAC 地址来标识网络上的设备

输入网址到网页显示,期间的过程

简单的网络模型

1. HTTP

浏览器做的第一步工作是解析 URL

浏览器首先对 URL 进行解析,从而生成发送给 Web 服务器的请求

一条 URL 中各个元素代表什么

URL 解析

由上可知,URL 实际上是请求服务器里面的文件资源(前后端分离过后,这里的服务器指的是前端服务器)。

URL 进行解析之后,浏览器确定了 Web 服务器和文件名,接下来就是根据这些信息来生成 HTTP 请求报文了。

HTTP 报文分为请求报文和响应报文。(下图很详细的展示了 HTTP 报文的结构)

HTTP 的消息格式

2. 真实地址查询 – DNS

通过浏览器对于 URL 的解析并生成 HTTP 请求报文后,需要委托下层协议将报文发送给 Web 服务器。

但是往往解析出来的服务器地址是一个域名地址,我们必须借助 DNS 服务器查询出域名对应的真实 IP 地址这样操作系统才能在发送报文时,知道目的地。

DNS(域名解析协议)服务器:记录了域名与 IP 之间的映射关系。

DNS 中的域名都是用句点来分隔的,比如 www.server.com ,这里的句点代表了不同层次之间的界限

在域名中,越靠右的位置表示其层次越高。

实际上域名最后还有一个点,比如 www.server.com.,这个最后的一个点代表根域名。

也就是,. 根域是在最顶层,它的下一层就是 .com 顶级域,再下面是 server.com

域名的层次关系类似一个树状结构:

  • 根 DNS 服务器(.)
  • 顶级域 DNS 服务器(.com)
  • 权威 DNS 服务器 (server.com)

DNS 树状结构

根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器中。

所以,任何 DNS 服务器都可以找到并访问根域 DNS 服务器了。

因此,客户端只要能够找到任意一台 DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器。

域名解析的工作流程

  1. 客户端首先会发出一个 DNS 请求,问 www.server.com 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)。
  2. 本地域名服务器收到客户端的请求后,如果缓存里的表格能找到 www.server.com,则它直接返回 IP 地址。如果没有,本地 DNS 会去问它的根域名服务器:“老大, 能告诉我 www.server.com 的 IP 地址吗?” 根域名服务器是最高层次的,它不直接用于域名解析,但能指明一条道路。
  3. 根 DNS 收到来自本地 DNS 的请求后,发现后置是 .com,说:“www.server.com 这个域名归 .com 区域管理”,我给你 .com 顶级域名服务器地址给你,你去问问它吧。”
  4. 本地 DNS 收到顶级域名服务器的地址后,发起请求问“老二, 你能告诉我 www.server.com 的 IP 地址吗?”
  5. 顶级域名服务器说:“我给你负责 www.server.com 区域的权威 DNS 服务器的地址,你去问它应该能问到”。
  6. 本地 DNS 于是转向问权威 DNS 服务器:“老三,www.server.com对应的IP是啥呀?” server.com 的权威 DNS 服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。
  7. 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。
  8. 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。

至此,我们完成了 DNS 的解析过程。

域名解析的工作流程

实际上并不是每次解析域名都需要这么多的步骤,由于缓存的存在,能够帮助我们省去一大部分的解析花费的时间。

实际情况是:浏览器会先看自身有没有对这个域名的缓存,如果有,就直接返回,如果没有,就去问操作系统,操作系统也会去看自己的缓存,如果有,就直接返回,如果没有,再去 hosts 文件看,也没有,才会去问「本地 DNS 服务器」。

3. 协议栈

通过 DNS 获取到 IP 后,就可以把 HTTP 的传输工作交给操作系统中的协议栈

协议栈的内部分为几个部分,分别承担不同的工作。上下关系是有一定的规则的,上面的部分会向下面的部分委托工作,下面的部分收到委托的工作并执行。

img

应用程序通过调用 Socket 库,来委托协议栈工作。协议栈的上半部分有两块,分别是负责收发数据的 TCPUDP 协议,这两个传输协议会接受来自应用层的委托执行收发数据的操作。

协议栈的下半部分是用 IP 协议控制网络包收发操作,在互联网上传数据时,数据会被切分成一块块的网络包,而将网络包发送给对方的操作就是 IP 负责的。

此外 IP 中还包括 ICMP 协议和 ARP 协议。

  • ICMP用于告知网络包传送过程中产生的错误以及各种控制信息。
  • ARP用于根据 IP 地址查询相应的以太网 MAC 地址。

IP 下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收操作。

4. 可靠传输 – TCP

HTTP 是居于 TCP 协议传输的。

TCP 报文头部格式:

TCP 包头格式

源端口号和目标端口号:用于指定发送的目的端口和用于接收方回传时指定端口。

序号:为了解决包乱序的问题。

确认号:确认发出去的包对方是否收到。为了解决丢包问题。

状态位:例如 SYN 是发起一个连接,ACK 是回复,RST 是重新连接,FIN 是结束连接等。TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。

窗口大小:TCP 要做流量控制,通信双方各声明一个窗口(缓存大小),标识自己当前的处理能力。

拥塞控制:控制发送包的速度(TCP 独有)。


三次握手

在使用 TCP 传输数据之前,首先需要建立 TCP 连接,TCP 连接的建立,通常称为三次握手

这个所谓的「连接」,只是双方计算机里维护一个状态机,在连接建立的过程中,双方的状态变化时序图就像这样。

TCP 三次握手

  • 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。
  • 然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。
  • 服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。
  • 客户端收到服务端发送的 SYNACK 之后,发送对 SYN 确认的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。
  • 服务端收到 ACKACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

所以三次握手目的是保证双方都有发送和接收的能力


TCP 分割数据

如果 HTTP 请求消息比较长,超过了 MSS 的长度,这时 TCP 就需要把 HTTP 的数据拆解成一块块的数据发送,而不是一次性发送所有数据。

MTU 与 MSS

  • MTU一个网络包的最大长度,以太网中一般为 1500 字节。
  • MSS除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。

数据会被以 MSS 的长度为单位进行拆分,拆分出来的每一块数据都会被放进单独的网络包中。也就是在每个被拆分的数据加上 TCP 头信息,然后交给 IP 模块来发送数据。

数据包分割


TCP 报文生成

TCP 协议里面会有两个端口,一个是客户端(浏览器)监听的端口(通常是随机生成的),一个是 Web 服务器监听的端口(HTTP 默认端口号是 80, HTTPS 默认端口号是 443)。

在双方建立了连接后,TCP 报文中的数据部分就是存放 HTTP 头部 + 数据,组装好 TCP 报文之后,就需交给下面的网络层处理。

报文段的报文如下图所示:

TCP 层报文

5. 远程定位 – IP

6. 两点传输 – MAC

生成了 IP 头部之后,接下来网络包还需要在 IP 头部的前面加上 MAC 头部

MAC 包头格式

MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息。

MAC 包头格式

在 MAC 包头里需要发送方 MAC 地址接收方目标 MAC 地址,用于两点之间的传输

一般在 TCP/IP 通信里,MAC 包头的协议类型只使用:

  • 0800 : IP 协议
  • 0806 : ARP 协议

MAC 发送方和接收方如何确认?

发送方的 MAC 地址获取就比较简单了,MAC 地址是在网卡生产时写入到 ROM 里的,只要将这个值读取出来写入到 MAC 头部就可以了。

接收方的 MAC 地址就有点复杂了,只要告诉以太网对方的 MAC 的地址,以太网就会帮我们把包发送过去,那么很显然这里应该填写对方的 MAC 地址。

所以先得搞清楚应该把包发给谁,这个只要查一下路由表就知道了。在路由表中找到相匹配的条目,然后把包发给 Gateway 列中的 IP 地址就可以了。

既然知道要发给谁,按如何获取对方的 MAC 地址呢?

不知道对方 MAC 地址?不知道就喊呗。

此时就需要 ARP 协议帮我们找到路由器的 MAC 地址。

ARP 广播

ARP 协议会在以太网中以广播的形式,对以太网所有的设备喊出:“这个 IP 地址是谁的?请把你的 MAC 地址告诉我”。

然后就会有人回答:“这个 IP 地址是我的,我的 MAC 地址是 XXXX”。

如果对方和自己处于同一个子网中,那么通过上面的操作就可以得到对方的 MAC 地址。然后,我们将这个 MAC 地址写入 MAC 头部,MAC 头部就完成了。

好像每次都要广播获取,这不是很麻烦吗?

放心,在后续操作系统会把本次查询结果放到一块叫做 ARP 缓存的内存空间留着以后用,不过缓存的时间就几分钟。

也就是说,在发包时:

  • 先查询 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址。
  • 而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 广播查询。

查看 ARP 缓存内容

在 Linux 系统中,我们可以使用 arp -a 命令来查看 ARP 缓存的内容。

ARP 缓存内容

MAC 报文生成

至此,网络包的报文如下图。

MAC 层报文

7. 出口 – 网卡

网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。因此,我们需要将数字信息转换为电信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。

负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序

网卡驱动获取网络包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列

数据包

  • 起始帧分界符是一个用来表示包起始位置的标记
  • 末尾的 FCS(帧校验序列)用来检查包传输过程是否有损坏

最后网卡会将包转为电信号,通过网线发送出去。

8. 交换机

下面来看一下包是如何通过交换机的。交换机的设计是将网络包原样转发到目的地。交换机工作在 MAC 层,也称为二层网络设备

交换机的包接收操作

首先,电信号到达网线接口,交换机里的模块进行接收,接下来交换机里的模块将电信号转换为数字信号。

然后通过包末尾的 FCS 校验错误,如果没问题则放到缓冲区。这部分操作基本和计算机的网卡相同,但交换机的工作方式和网卡不同。

计算机的网卡本身具有 MAC 地址,并通过核对收到的包的接收方 MAC 地址判断是不是发给自己的,如果不是发给自己的则丢弃;相对地,交换机的端口不核对接收方 MAC 地址,而是直接接收所有的包并存放到缓冲区中。因此,和网卡不同,交换机的端口不具有 MAC 地址

将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了。

交换机的 MAC 地址表主要包含两个信息:

  • 一个是设备的 MAC 地址,
  • 另一个是该设备连接在交换机的哪个端口上。

交换机的 MAC 地址表

举个例子,如果收到的包的接收方 MAC 地址为 00-02-B3-1C-9C-F9,则与图中表中的第 3 行匹配,根据端口列的信息,可知这个地址位于 3 号端口上,然后就可以通过交换电路将包发送到相应的端口了。

所以,交换机根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口

当 MAC 地址表找不到指定的 MAC 地址会怎么样?

地址表中找不到指定的 MAC 地址。这可能是因为具有该地址的设备还没有向交换机发送过包,或者这个设备一段时间没有工作导致地址被从地址表中删除了。

这种情况下,交换机无法判断应该把包转发到哪个端口,只能将包转发到除了源端口之外的所有端口上,无论该设备连接在哪个端口上都能收到这个包。

这样做不会产生什么问题,因为以太网的设计本来就是将包发送到整个网络的,然后只有相应的接收者才接收包,而其他设备则会忽略这个包

有人会说:“这样做会发送多余的包,会不会造成网络拥塞呢?”

其实完全不用过于担心,因为发送了包之后目标设备会作出响应,只要返回了响应包,交换机就可以将它的地址写入 MAC 地址表,下次也就不需要把包发到所有端口了。

局域网中每秒可以传输上千个包,多出一两个包并无大碍。

此外,如果接收方 MAC 地址是一个广播地址,那么交换机会将包发送到除源端口之外的所有端口。

以下两个属于广播地址:

  • MAC 地址中的 FF:FF:FF:FF:FF:FF
  • IP 地址中的 255.255.255.255

9. 路由器

路由器与交换机的区别

网络包经过交换机之后,现在到达了路由器,并在此被转发到下一个路由器或目标设备。

这一步转发的工作原理和交换机类似,也是通过查表判断包转发的目标。

不过在具体的操作过程上,路由器和交换机是有区别的。

  • 因为路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址;
  • 交换机是基于以太网设计的,俗称二层网络设备,交换机的端口不具有 MAC 地址。

路由器基本原理

路由器的端口具有 MAC 地址,因此它就能够成为以太网的发送方和接收方;同时还具有 IP 地址,从这个意义上来说,它和计算机的网卡是一样的。

当转发包时,首先路由器端口会接收发给自己的以太网包,然后路由表查询转发目标,再由相应的端口作为发送方将以太网包发送出去。

路由器的包接收操作

首先,电信号到达网线接口部分,路由器中的模块会将电信号转成数字信号,然后通过包末尾的 FCS 进行错误校验。

如果没问题则检查 MAC 头部中的接收方 MAC 地址,看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。

总的来说,路由器的端口都具有 MAC 地址,只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃。

查询路由表确定输出端口

完成包接收操作之后,路由器就会去掉包开头的 MAC 头部。

MAC 头部的作用就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃

接下来,路由器会根据 MAC 头部后方的 IP 头部中的内容进行包的转发操作。

转发操作分为几个阶段,首先是查询路由表判断转发目标。

路由器转发

具体的工作流程根据上图,举个例子。

假设地址为 10.10.1.101 的计算机要向地址为 192.168.1.100 的服务器发送一个包,这个包先到达图中的路由器。

判断转发目标的第一步,就是根据包的接收方 IP 地址查询路由表中的目标地址栏,以找到相匹配的记录。

路由匹配和前面讲的一样,每个条目的子网掩码和 192.168.1.100 IP 做 & 与运算后,得到的结果与对应条目的目标地址进行匹配,如果匹配就会作为候选转发目标,如果不匹配就继续与下个条目进行路由匹配。

如第二条目的子网掩码 255.255.255.0192.168.1.100 IP 做 & 与运算后,得到结果是 192.168.1.0 ,这与第二条目的目标地址 192.168.1.0 匹配,该第二条目记录就会被作为转发目标。

实在找不到匹配路由时,就会选择默认路由,路由表中子网掩码为 0.0.0.0 的记录表示「默认路由」。

路由器的发送操作

接下来就会进入包的发送操作

首先,我们需要根据路由表的网关列判断对方的地址。

  • 如果网关是一个 IP 地址,则这个IP 地址就是我们要转发到的目标地址,还未抵达终点,还需继续需要路由器转发。
  • 如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址,也是就终于找到 IP 包头里的目标地址了,说明已抵达终点

知道对方的 IP 地址之后,接下来需要通过 ARP 协议根据 IP 地址查询 MAC 地址,并将查询的结果作为接收方 MAC 地址。

路由器也有 ARP 缓存,因此首先会在 ARP 缓存中查询,如果找不到则发送 ARP 查询请求。

接下来是发送方 MAC 地址字段,这里填写输出端口的 MAC 地址。还有一个以太类型字段,填写 0800 (十六进制)表示 IP 协议。

网络包完成后,接下来会将其转换成电信号并通过端口发送出去。这一步的工作过程和计算机也是相同的。

发送出去的网络包会通过交换机到达下一个路由器。由于接收方 MAC 地址就是下一个路由器的地址,所以交换机会根据这一地址将包传输到下一个路由器。

接下来,下一个路由器会将包转发给再下一个路由器,经过层层转发之后,网络包就到达了最终的目的地。

不知你发现了没有,在网络包传输的过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输。


至此我们的请求就顺利传输到 Web 服务器了,服务器响应请求按相同的步骤将资源传输到客户端(HTTP 响应报文)。

HTTP 篇

HTTP 常识

提纲

HTTP 基本概念

HTTP 是一个应用层的超文本传输协议(HyperText Transfer Protocol)

HTTP的名字「超文本协议传输」,可以拆成三个部分。

  • 超文本
  • 传输
  • 协议

三个部分

1. 「协议」

在生活中随处可见协议。例如:

  • 保密协议
  • 三方协议

生活中的协议本质上与计算机网络中的协议是相同的,协议的特点:

  • 」字,代表的意思是必须有两个以上的参与者
  • 」字,代表的意思是对参与者的一种行为约定和规范

因此针对 HTTP 协议,我们可以这么理解。

HTTP 是一个用在计算机世界里的协议,它使用计算能够理解的语言确立了一种计算机之间交流通信的规范(两个以上的参与者),以及相关的各种控制和错误处理方式(行为规定和规范)。

协议:就是两个以上的参与者能够理解,能够达成共识,能够相互遵守的一套行为规范。

  1. 「传输」

HTTP 是一个双向协议

HTTP 是一个在计算机世界里面专门用来在两点之间传输数据的约定和规范。

  1. 「超文本」

HTTP 传输的内容是「超文本」。

我们先来理解「文本」,在互联网早期的时候只是简单的字符文字,但现在「文本」的涵义已经可以扩展为图片、视频、压缩包等,在 HTTP 眼里这些都算作「文本」。

再来理解「超文本」,它就是超越了普通文本的文本,它是文字、图片、视频等的混合体,最关键有超链接,能从一个超文本跳转到另外一个超文本。例如:HTML


综上得出答案:HTTP 是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」。


HTTP 常见状态码

 五大类 HTTP 状态码

  • 2XX:是我们最常看见的状态码,表示服务器成功处理了客户端的请求。

    • 200 OK」是最常见的成功状态码,表示一切正常。如果是非 HEAD 请求,服务器返回的响应头都会有 body 数据

    • 204 No Content」也是常见的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据

    • 206 Partial Content」是应用于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。

  • 3XX:表示客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,即重定向

    • 301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问。

    • 302 Found」表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。

      301 和 302 都会在响应头里使用字段 Location,指明后续要跳转的 URL,浏览器会自动重定向新的URL

    • 304 Not Modified」不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,也就是告诉客户端可以继续使用缓存资源,用于缓存控制

  • 4XX:表示客户端发送的报文有误,服务器无法理解,也就是错误码的含义。

    • 400 Bad Request」表示客户端请求的报文有错误,但只是个笼统的错误

    • 401 Unauthorized请求未经授权,这个状态码必须和 WWW-Authenticate 报文头域一起使用。

    • 403 Forbidden」表示服务器禁止访问资源,并不是客户端的请求出错。

    • 404 Not Found」表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。

  • 5XX:表示客户端的请求报文正确,但是服务器处理时内部发生了错误,属于服务端的错误码。

    • 500 Internal Server Error」与 400 类型,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道。
    • 501 Not Implemented」表示客户端请求的功能还不支持,类似“即将开业,敬请期待”的意思。
    • 502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常访问后端服务器发生了错误
    • 503 Service Unavailable」表示服务器当前很忙,暂时无法响应客户端,类似“网络服务正忙,请稍后重试”的意思。

HTTP 报文常见字段

  1. Host 字段

    客户端发送请求时,用来指定服务器的域名。

    Host: www.A.com
  2. Content-Length 字段

    响应报文中,表明此次响应的数据长度。

    Content-Length: 1000

    该字段告诉浏览器,本次数据长度是1000,超过这个长度后面的字节就属于下一个响应了。

    HTTP 协议是基于 TCP 传输协议通信的,而使用了 TCP 传输协议,就会存在一个”粘包”的问题

    解决方案:

    • 通过 Content-Length 字段作为 HTTP body 的边界。
    • 通过设置回车符、换行符作为 HTTP header 的边界。
  3. Connection 字段

    Connection 字段常用于客户端要求服务器使用「 HTTP 长连接」机制,以便其他请求复用。

    img

    HTTP 长连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。

    开启了 HTTP Keep-Alive 机制后, 连接就不会中断,而是保持连接。当客户端发送另一个请求时,它会使用同一个连接,一直持续到客户端或服务器端提出断开连接。

    HTTP/1.1 版本的默认链接都是长连接,但为了兼容老版本的 HTTP,需要指定 Connection 首部字段的值为 Keep-Alive

  4. Content-Type 字段

    Content-Type 字段用于服务器回应时,告诉客户端,本次数据是什么格式。

    Content-Type: text/html; charset=utf-8

    上述类型表明,发送的是网页,编码是 UTF-8。

    客户端请求的时候可以使用 Accept 字段声明自己可以接受那些数据格式。

    Accept: */*  表示自己可以接受任何格式的数据。
  5. Content-Encoding 字段

    Content-Encoding 字段说明数据的压缩方法。表示服务器返回的数据使用的是上面压缩格式。

    Content-Encoding: gzip  表示服务器返回的数据采用了 gzip 方式压缩,告知客户端采用该方式解压

    客户端在请求时,用 Accept-Encoding 字段说明自己可以接受那些压缩方法。

    Accept-Encoding: gzip, deflate

GET 与 POST

GET 与 POST 之间的区别

根据 RFC 文档中的规范,GET 的语义是从服务器获取指定的资源,这个资源可以是静态的文本、页面、图片视频等。GET 请求的请求参数一般是写在 URL 中的,URL 规定只能支持 ACSII 编码,并且浏览器会对 URL 的长度有限制(HTTP 协议本身对于 URL 长度不做任何规定)。

POST 请求的语义是根据请求负荷(报文 body)对指定的资源做出处理,具体方式视资源类型而不同。POST 请求携带数据的位置一般是写在报文 body 中,body 中的数据可以是任意格式的数据(json,表单,文件,xml等),只要客户端与服务端协商好即可,并且浏览器不会对 body 大小做限制。


GET 和 POST 方法都是安全和幂等的吗?
  • 在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
  • 所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。

从 RFC 规范定义的语义来看:

  • GET 方法是安全且幂等的,因为它是「只读」操作,无论多少次,服务器上的数据都是安全的,且每次结果都是相同的。所以可以对 GET 请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发送请求),也可以做到代理上(如:nginx),而且在浏览器中 GET 请求可以保存为书签。

  • POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以也不是幂等的。因此浏览器一般不会缓存 POST 请求,也不能将其保存作为书签。

但从提交数据的安全性上来说:

POST 的安全性要比 GET 高,比如通过 GET 提交数据,用户名和密码将明文出现在 URL 上,因为 (1)登录页面可能被浏览器缓存;(2)其他人查看浏览器的历史记录,那么别人就能拿到你的账号密码了,除此之外,使用 GET 提交数据还可能会造成 Cross-site request forgery(csrf)攻击,POST 提交的内容由于在消息体中传输,因此不存在上述安全问题。

但实际上,开发者不一定会遵守 RFC 文档上规定的语音来实现 GET 和 POST 方法。如:

  • 用 GET 方法实现新增或删除数据的请求,这样 GET 请求就不再幂等和安全了。
  • 用 POST 方法实现查询数据的请求,这样 POST 请求自然就是幂等和安全的了。

HTTP 传输的内容都是明文的,虽然在浏览器地址栏上看不到 POST 请求的 body 数据,但是只要抓包就都能看到了,所以要避免传输过程中的数据被窃取,就要使用 HTTPS 协议,这样所有 HTTP 的数据都会被加密。

GET 请求可以带 body 吗?

RFC 文档中并没有规定 GET 请求不能携带 body,理论上任何请求都可以携带 body。只是 RFC 规定 GET 语义是获取资源,根据这个语义不需要用到 body。

但实际开发,GET 还是不要带 body 了。按照 RFC 语义规范来,否则会被前端骂的。


HTTP 缓存技术

对于一些重复性 HTTP 请求,比如在每次或一段事件内相同的请求得到的数据都是一样的,我们可以把这对「请求-响应」的数据都缓存在本地。

HTTP 设计者考虑到这一点,因此 HTTP 协议头部有不少的是针对缓存的字段

HTTP 缓存有两种实现方式,分别是强制缓存和协商缓存

强制缓存

强制缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边

如下图中,返回的是 200 状态码,但在 size 项中标识的是 from disk cache,就是使用了强制缓存

img

强制缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:

  • Cache-Control, 是一个相对时间;
  • Expires,是一个绝对时间;

如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control的优先级高于 Expires

Cache-control 选项更多一些,设置更加精细,所以建议使用 Cache-Control 来实现强缓存。具体的实现流程如下:

  • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 Cache-Control,Cache-Control 中设置了过期时间的大小。
  • 浏览器再次访问服务器中的该资源时,会先通过请求资源的时间与 Cache-Control 中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用缓存,否则重新请求服务器;
  • 服务器再次收到请求后,会再次更新 Response 头部的 Cache-Control。

协商缓存

当我们在浏览器使用开发者工具的时候,你可能会看到过某些请求的响应码是 304,这个是告诉浏览器可以使用本地缓存的资源,通常这种通过服务端告知客户端是否可以使用缓存的方式被称为协商缓存

img

从上图中可以看到一个协商缓存的过程,所以协商缓存就是与服务端协商过后,通过协商结果来判断是否使用本地缓存。

协商缓存可以基于两种头部来实现。

第一种:请求头部中的 If-Modified-Since 字段与响应头部中的 Last-Modified 字段实现,这两个字段的意思是:

  • 响应头部中的 Last-Modified:标示这个响应资源的最后修改时间;
  • 请求头部中的 If-Modified-Since:当资源过期了,发现响应头中具有 Last-Modified 声明,则再次发起请求的时候带上 Last-Modified 的时间,服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行对比(Last-Modified),如果最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;如果最后修改时间较旧(小),说明资源无新修改,响应 HTTP 304 走缓存。

第二种:请求头部中的 If-None-Match 字段与响应头部中的 ETag 字段,这两个字段的意思是:

  • 响应头部中 Etag:唯一标识响应资源;
  • 请求头部中的 If-None-Match:当资源过期时,浏览器发现响应头里有 Etag,则再次向服务器发起请求时,会将请求头If-None-Match 值设置为 Etag 的值。服务器收到请求后进行比对,如果资源没有变化返回 304,如果资源变化了返回 200。

第一种实现方式是基于时间实现的,第二种实现方式是基于一个唯一标识实现的,相对来说后者可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠问题。

如果在第一次请求资源的时候,服务端返回的 HTTP 响应头部同时有 Etag 和 Last-Modified 字段,那么客户端再下一次请求的时候,如果带上了 ETag 和 Last-Modified 字段信息给服务端,这时 Etag 的优先级更高,也就是服务端先会判断 Etag 是否变化了,如果 Etag 有变化就不用在判断 Last-Modified 了,如果 Etag 没有变化,然后再看 Last-Modified。

为什么 ETag 的优先级更高?这是因为 ETag 主要能解决 Last-Modified 几个比较难以解决的问题:

  1. 在没有修改文件内容情况下文件的最后修改时间可能也会改变,这会导致客户端认为这文件被改动了,从而重新请求;
  2. 可能有些文件是在秒级以内修改的,If-Modified-Since 能检查到的粒度是秒级的,使用 Etag就能够保证这种需求下客户端在 1 秒内能刷新多次;
  3. 有些服务器不能精确获取文件的最后修改时间。

注意,协商缓存这两个字段都需要配合强制缓存中 Cache-control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求

下图是强制缓存和协商缓存的工作流程:

img

当使用 ETag 字段实现的协商缓存的过程:

  • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;
  • 当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期
    • 如果没有过期,则直接使用本地缓存;
    • 如果缓存过期了,会在 Request 头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识;
  • 服务器再次收到请求后,会根据请求中的 If-None-Match 值与当前请求的资源生成的唯一标识进行比较:
    • 如果值相等,则返回 304 Not Modified,不会返回资源
    • 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识
  • 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。

HTTP 特性

到目前为止,HTTP 常见的版本有 HTTP/1.1,HTTP/2.0,HTTP/3.0,不同版本的 HTTP 特性是不一样的。这里先介绍 HTTP 1.1 版本介绍。

HTTP/1.1 的优点

HTTP 最突出的优点就是「简单、灵活和易于扩展、应用广泛和跨平台」

  1. 简单

    客户端向服务端请求服务时,只需指定服务 URL,携带必要的请求参数或者消息体。

    HTTP 基本的报文格式就是 header + body ,头部信息也是 key-value 简单文本的形式,易于理解,降低了学习和使用的门槛。

  2. 灵活和易于扩展

    HTTP 协议里的各类请求方法、URI/URL、状态码、头字段等每个组成要求都没有被固定死,都允许开发人员自定义和扩充

    同时 HTTP 由于是工作在应用层(OSI 第七层),则它的下层可以随意变化,比如:

    • HTTPS 就是 HTTP 与 TCP 层之间增加了 SSL/TLS 安全传输层;
    • HTTP/1.1 和 HTTP/2.0 传输协议使用的是 TCP 协议,而到了 HTTP/3.0 传输协议改用成了 UDP 协议。
  3. 应用广泛和跨平台

    HTTP 的应用非常广泛,从电脑的浏览器到手机上的各种 APP,各种各样的应用程序,HTTP 应用遍地开花,同时天然具有跨平台的优越性。


HTTP/1.1 缺点

HTTP 优缺点:「不安全」这一个大缺点,除此之外还有「无状态、明文传输」

  1. 无状态

    无状态是一把双刃剑既有好处也有坏处。

    好处:因为服务器不会去记忆 HTTP 的状态,所以不需要额外的资源来记录状态信息,这能减轻服务器的负担,把更多的资源用来对完提供服务。(CPU 和 内存)

    坏处:服务器没有记忆能力,他在完成有关联性的操作时会非常麻烦。即一些用户的登录状态 HTTP 是没有办法保存的,如果不做任何处理,需要用户每次执行操作时都要进行登录,这大大的折扣了用户的体验。

    对于无状态的问题,解决方案很多,其中比较简单的方式就是使用 Cookie 技术。

    Cookie 相当于,在客户端第一次请求后,服务器会下发一个装有客户信息的「小贴纸」,后续客户端请求服务器的时候,带上「小贴纸」,这样服务器就能认得你了。

    Cookie 技术

  2. 明文传输

    明文传输同样也是一把双刃剑,明文传输意味着在传输过程中的信息,是可方便读的,比如被抓包工具,抓到请求包后都可以之间肉眼查看,为我们调试工作带来了极大的便利性。

    但正是因为这样,HTTP 所有信息都暴露在光天化日之下,相当于信息裸奔。在传输的漫长过程中,所有的信息都毫无隐私可言,很容易被窃取,如果有隐私信息,被不法之人获取到了,那将是灾难级的。

  3. 不安全

    HTTP 最为严重的缺点就是不安全:

    • 通信使用明文(不加密),内容可能会被窃听。比如账号信息容易泄露,那对于用户而言将是灾难。
    • 不验证通信方的身份,因此有可能遭遇伪装。比如,访问假的淘宝、pdd、那对于用户也是灾难。
    • 无法证明报文的完整性,所以有可能已遭篡改。比如,网页上植入垃圾广告,对用户的体验也是不利的。
HTTP/1.1 性能

HTTP/1.1 是基于 TCP/IP,并且使用了「请求 - 应答」的通信模式,所以性能的关键就在这两点上。

  1. TCP 长连接

    早期 HTTP/1.0 性能上有一个很大的问题,那就是每发起一个请求,都要新建一次 TCP 链接(三次握手),而且是串行请求,做了无谓的 TCP 请求建立和连接断开,增加了通信开销。

    为了解决上述 TCP 连接问题,HTTP/1.1 提出了长连接(持久连接)的通信方式,这种方式的好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。

    持久连接的特点是:只要任意一端没有明确提出断开连接,则保持 TCP 保持状态。

    短连接与长连接

    当然,如果某个 HTTP 连接超过一定时间没有任何数据交互,服务端就会主动断开这个连接。

  2. 管道网络传输

    HTTP/1.1 采用了长连接的方式,这使得管道(pipeline)网络传输成为了可能。

    即可在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求除去,可以减少整体的响应时间

    举例来说,客户端需要请求两个资源。以前的做法是,在同一个 TCP 连接里面,先发送 A 请求,然后等待服务器做出回应,收到后再发出 B 请求。那么,管道机制则是允许浏览器同时发出 A 请求和 B 请求。如下图:

    管道网络传输

    但是服务器必须按照接收请求的顺序发送对这些管道化请求的响应

    如果服务器在处理 A 请求时的耗时比较长,那么后续的请求都会被阻塞住,这被称为「队头堵塞」

    所以,HTTP/1.1 管道解决了请求的对头阻塞,但是没有解决响应的对头阻塞。

    TIP

    注意!!!

    实际上 HTTP/1.1 管道化技术不是默认开启,而且浏览器基本都没有支持,所以后面所有文章讨论HTTP/1.1 都是建立在没有使用管道化的前提。大家知道有这个功能,但是没有被使用就行了。

  3. 队头阻塞

    「请求 - 应答」的模式加剧了 HTTP 的性能问题。

    因为当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一同被阻塞了,会招致客户端一直请求不到数据,这也就是「队头阻塞」,好比上班的路上塞车。

    队头阻塞

    总结:HTTP/1.1 的性能还是较差,虽然较与 HTTP/1.0 有了提升,但还是一般般,后续的 HTTP/2 和 HTTP/3 就是在优化 HTTP 的性能。(主要是提升性能)

HTTPS

HTTP 与 HTTPS 有哪些区别?
  • HTTPS 解决了 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
  • HTTP 较与 HTTP 在 TCP 三次握手之后,还需要进行 SSL/TLS 的握手过程,才可以进入加密报文传输。
  • HTTPS 与 HTTP 默认端口号不同,HTTP 是 80,而 HTTPS 是 443。
  • HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。

HTTP 与 HTTPS 网络层

HTTPS 解决了 HTTP 的那些问题?

HTTP 由于是明文传输,所以安全上存在以下风险:

  • 窃听风险,比如通信链路上可以获取通信内容。
  • 篡改风险,比如强制植入垃圾广告,视觉污染。
  • 冒充风险,比如冒充别的网站。

HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS 协议,可以很好地解决上述的风险:

  • 信息加密:交互信息无法被窃取。
  • 校验机制:无法篡改通信内容,篡改了就不能正常显示了。
  • 身份证书:证明网站是我们要访问的网站。

可见,只要我们自己不作,SSL/TLS 协议是能够保证通信是安全的。

HTTPS 是如何解决上面三个风险的?

  • 混合加密的方式实现信息的机密性,解决了窃听的风险。
  • 摘要算法的方式来实现完整性,它能够生成独一无二的「指纹」,指纹用于校验数据的完整性 ,解决了篡改的风险。
  • 将服务器公钥放到数字证书中,解决了冒充的风险。
  1. 混合加密

通过混合加密的方式可以保证信息的机密性,解决了窃听的而风险。

混合加密

HTTPS 采用的是对称加密非对称加密结合的「混合加密」方式:

  • 在通信建立前采用非对称加密的方式交换「会话秘钥」,后续就不再使用非对称加密。
  • 在通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。

采用「混合加密」的方式的原因:

  • 对称加密只是用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换。
  • 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢。
  1. 摘要算法 + 数字签名

为了保证传输的内容不被篡改,我们需要对内容计算出一个「指纹」,然后同内容一起传输给对方。

对方收到后,显示对内容也计算出一个「指纹」,然后跟发送方发送的「指纹」做一个比较,如果「指纹」相同,说明内容没有被篡改,否则就可以判断出内容被篡改了。

那么,在计算机里会用摘要算法(哈希函数)来计算出内容里的哈希值,也就是内容的「指纹」,这个哈希值是唯一的,且无法通过哈希值推导出内容

img

通过哈希算法可以确保内容不会被篡改,但是并不能保证「内容 + 哈希值」不会被中间人替换,因为这里缺少对客户端收到的消息是否来源于服务端的证明

你想向老师请假,一般来说是要求由家长写一份请假理由并签名,老师才能允许你请假。

但是你有模仿你爸爸字迹的能力,你用你爸爸的字迹写了一份请假理由然后签上你爸爸的名字,老师一看到这个请假条,查看字迹和签名,就误以为是你爸爸写的,就会允许你请假。

那作为老师,要如何避免这种情况发生呢?现实生活中的,可以通过电话或视频来确认是否是由父母发出的请假,但是计算机里可没有这种操作。

为了避免这种情况,计算机里会用非对称加密算法来解决,共有两个密钥:

  • 一个是公钥,这个是可以公开给所有人的;
  • 一个是私钥,这个必须由本人管理,不可以泄露。

这两个密钥是可以双向加解密的,比如可以用公钥加密内容,然后用私钥解密,也可以用私钥加密内容,公钥解密内容。

流程的不同,意味着目的也不相同:

  • 公钥加密,私钥解密。这个目的是为了保证内容传输的安全,因为被公钥加密的内容, 其他人是无法解密的,只有持有私钥的人,才能解密出实际的内容;
  • 私钥加密,公钥解密。这个目的是为了保证消息不会被冒充,因为私钥是不可泄漏的,如果公钥能正常解密出私钥加密的内容,就能证明这个消息的来源于持有私钥身份的人发送的。

一般不会使用 非对称加密来加密实际的传输内容,因为非对称加密的计算比较耗费性能。

所以非对称加密的用途主要在于通过「私钥加密,公钥解密」的方式,来确认消息的身份,我们常说的数字签名算法,就是用这种方式,不过密钥加密内容不是内容本身,而是对内容的哈希值加密

img

私钥是由服务器保管,然后服务端会向客户端颁发对应的公钥。如果客户端接收到的信息,能被公钥解密,就说明该消息是由服务器发送的。

  1. 数字证书

前面我们知道:

  • 可以通过哈希算法来保证消息的完整性;(通过哈希运算内容得到的哈希值来比较是否值相同,防止发送过来的内容被篡改);
  • 可以通过数字签名来保证消息的来源可靠性(能确认消息是由持有私钥的一方发送的);

但是这还远远不够,还缺少了身份验证的环节,万一公钥是被伪造的呢?

为了防止公钥伪造,我们需要委托一个权威机构,他们会用他们自己的私钥对公钥做了个数字前面,然后把「个人信息 + 公钥 + 数字签名」打包成一个数字证书,也就是说这个数字证书包含你的公钥。

接收方拿到数字证书后,首先会去这个权威机构哪里验证这个数字证书是否合法,权威机构接收到数字证书后,使用自己的公钥对数字证书进行解密,如果解密成功,就说明这个数字证书是合法的,然后权威机构就会把数字证书内的公钥发送给刚刚的接收方。

由于通过权威机构的验证了数字证书是合法的,那么就能证明这个公钥不是被伪造的了。

在计算机里,这个权威机构就是 CA(数字证书认证机构),将服务器公钥放在数字证书(由数字证书认证机构颁发)中,只要证书是可信的,公钥就是可信的。

流程如下:

数子证书工作流程

通过数字证书的方式保证了服务器公钥的身份,解决冒充的风险。

TCP 篇

RTT(Round-Trip Time),往返时延。在计算机网络中它是一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。

IP 篇