摘要

当你在浏览器中访问某一个网址时候,背后的经历那些流程?本博文主要来详细的介绍的网络访问的全部流程中涉及到计算机网络的相关知识。帮助大家很好的理解和学习计算机。

计算机网络——HTTP网络访问全流程_服务器

一、HTTP 解析

⾸先浏览器做的第⼀步⼯作就是要对URL 进⾏解析,从而生成发送给Web服务器的请求信息。所以图中的⻓⻓的 URL 实际上是请求服务器⾥的⽂件资源。当没有路径名时,就代表访问根⽬录下事先设置的默认⽂件,也就是 /index.html 或者 /default.html 这些⽂ 件,这样就不会发⽣混乱了。

计算机网络——HTTP网络访问全流程_IP_02

计算机网络——HTTP网络访问全流程_IP_03

计算机网络——HTTP网络访问全流程_网络_04

1.1 HTTP请求报文

计算机网络——HTTP网络访问全流程_IP_05

 1.2 HTTP响应报文

计算机网络——HTTP网络访问全流程_客户端_06

二、DNS域名解析

通过浏览器解析 URL并生成HTTP 消息后,需要委托操作系统将消息发送给Web服务器。但在发送之前,还有⼀项⼯作需要完成,那就是查询服务器域名对应的 IP 地址,因为委托操作系统发送消息时, 必须提供通信对象的 IP 地址。比如我们打电话的时候,必须要知道对⽅的电话号码,但由于电话号码难以记忆,所以通常我们会将对⽅电话号 + 姓名保存在通讯录⾥。 所以,有⼀种服务器就专门保存了 Web 服务器域名与 IP 的对应关系,它就是 DNS 服务器。

2.1 域名的层级关系

DNS 中的域名都是⽤句点来分隔的,⽐如 www.server.com ,这⾥的句点代表了不同层次之间的界限。在域名中,越靠右的位置表示其层级越⾼。 毕竟域名是外国⼈发明,所以思维和中国⼈相反,⽐如说⼀个城市地点的时候,外国喜欢从⼩到⼤的⽅式顺序说起 (如 XX 街道 XX 区 XX 市 XX 省),⽽中国则喜欢从⼤到⼩的顺序(如 XX 省 XX 市 XX 区 XX 街道)。根域是在最顶层,它的下⼀层就是 com 顶级域,再下⾯是 server.com。所以域名的层级关系类似⼀个树状结构

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

计算机网络——HTTP网络访问全流程_网络_07

根域的 DNS 服务器信息保存在互联网中所有的DNS服务器中。 这样⼀来,任何 DNS 服务器就都可以找到并访问根域 DNS 服务器了。 因此,客户端只要能够找到任意⼀台 DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再⼀路顺藤摸⽠找到 位于下层的某台目标DNS服务器。

2.2 域名解析的⼯作流程

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

三、协议栈解析

通过 DNS 获取到 IP 后,就可以把 HTTP 的传输⼯作交给操作系统中的协议栈。 协议栈的内部分为⼏个部分,分别承担不同的⼯作。上下关系是有⼀定的规则的,上⾯的部分会向下⾯的部分委托 ⼯作,下⾯的部分收到委托的⼯作并执⾏。

计算机网络——HTTP网络访问全流程_IP_08

应⽤程序(浏览器)通过调⽤ Socket 库,来委托协议栈⼯作。协议栈的上半部分有两块,分别是负责收发数据的 TCP 和 UDP 协议,它们两会接受应⽤层的委托执⾏收发数据的操作

协议栈的下⾯⼀半是⽤ IP 协议控制⽹络包收发操作,在互联⽹上传数据时,数据会被切分成⼀块块的⽹络包,⽽ 将⽹络包发送给对⽅的操作就是由 IP 负责的。此外 IP 中还包括 ICMP 协议和 ARP 协议。

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

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

四、TCP 协议传输

HTTP 是基于 TCP 协议传输的,所以在这我们先了解下 TCP 协议。

计算机网络——HTTP网络访问全流程_网络_09

⾸先,源端口号和⽬标端口号是不可少的,如果没有这两个端口号,数据就不知道应该发给哪个应⽤。接下来有包的序号,这个是为了解决包乱序的问题。

还有应该有的是确认号,⽬的是确认发出去对⽅是否有收到。如果没有收到就应该重新发送,直到送达,这个是为 了解决不丢包的问题。

接下来还有⼀些状态位。例如 SYN 是发起⼀个连接, ACK 是回复, RST 是重新连接, FIN 是结束连接 等。TCP 是⾯向连接的,因⽽双⽅要维护连接的状态,这些带状态位的包的发送,会引起双⽅的状态变更。

还有⼀个重要的就是窗口大小。TCP 要做流量控制,通信双方各声明⼀个窗⼝(缓存大小),标识⾃⼰当前能够的 处理能⼒,别发送的太快,撑死我,也别发的太慢,饿死我。除了做流量控制以外,TCP还会做拥塞控制,对于真正的通路堵⻋不堵⻋,它⽆能为⼒,唯⼀能做的就是控制⾃ ⼰,也即控制发送的速度。

4.1 三次握手连接

计算机网络——HTTP网络访问全流程_IP_10

三次握手流程:

  1. 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
  2. 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
  3. 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

为什么是三次握手?不是两次、四次

三次握手才可以阻止重复历史连接的初始化(主要原因)

⽹络环境是错综复杂的,往往并不是如我们期望的⼀样,先发送的数据包,就先到达⽬标主机,可能会由于⽹络拥堵等乱七⼋糟的原因,会使得旧的数据包,先到达⽬标主机,那么这种情况下 TCP 三次握⼿是如何避免的呢?

计算机网络——HTTP网络访问全流程_客户端_11

客户端连续发送多次 SYN 建⽴连接的报⽂,在网络拥堵情况下:

  • ⼀个旧 SYN 报⽂⽐最新的 SYN 报⽂早到达了服务端;
  • 那么此时服务端就会回⼀个 SYN + ACK 报⽂给客户端;
  • 客户端收到后可以根据⾃身的上下⽂,判断这是⼀个历史连接(序列号过期或超时),那么客户端就会发送 RST 报⽂给服务端,表示中止这⼀次连接。
  • 如果是两次握⼿连接,就不能判断当前连接是否是历史连接,三次握⼿则可以在客户端(发送⽅)准备发送第三次报⽂时,客户端因有⾜够的上下⽂来判断当前连接是否是历史连接:
  • 如果是历史连接(序列号过期或超时),则第三次握⼿发送的报⽂是 RST 报⽂,以此中⽌历史连接;
  • 如果不是历史连接,则第三次发送的报⽂是 ACK 报⽂,通信双⽅就会成功建⽴连接; 所以,TCP 使⽤三次握⼿建⽴连接的最主要原因是防⽌历史连接初始化了连接。

三次握手才可以同步双方的初始序列号

TCP 协议的通信双⽅, 都必须维护⼀个序列号, 序列号是可靠传输的⼀个关键因素,它的作⽤:

  • 接收方可以去除重复的数据;
  • 接收方可以根据数据包的序列号按序接收;
  • 可以标识发送出去的数据包中, 哪些是已经被对⽅收到的

序列号在 TCP 连接中占据着常重要的作⽤,所以当客户端发送携带初始序列号的 SYN 报⽂的时 候,需要服务端回⼀个 ACK 应答报⽂,表示客户端的 SYN 报⽂已被服务端成功接收,那当服务端发送初始序 列号给客户端的时候,依然也要得到客户端的应答回应,这样⼀来⼀回,才能确保双⽅的初始序列号能被可靠的 同步。

计算机网络——HTTP网络访问全流程_客户端_12

三次握手才可以避免资源浪费

如果只有两次握⼿,当客户端的 SYN 请求连接在⽹络中阻塞,客户端没有接收到 ACK 报⽂,就会重新发送SYN ,由于没有第三次握⼿,服务器不清楚客户端是否收到了⾃⼰发送的建⽴连接的 ACK 确认信号,所以每收到⼀个 SYN就只能先主动建⽴⼀个连接,

这会造成什么情况呢? 如果客户端的 SYN 阻塞了,重复发送多次 SYN 报⽂,那么服务器在收到请求后就会建⽴多个冗余的无效的连接,造成不必要的资源浪费

计算机网络——HTTP网络访问全流程_服务器_13

如何查看 TCP 的连接状态

TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。

计算机网络——HTTP网络访问全流程_IP_14

4.2 TCP 分割数据

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

计算机网络——HTTP网络访问全流程_IP_15

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

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

计算机网络——HTTP网络访问全流程_客户端_16

4.3 TCP报文生成

TCP 协议⾥⾯会有两个端⼝,⼀个是浏览器监听的端⼝(通常是随机⽣成的),⼀个是 Web 服务器监听的端⼝ (HTTP 默认端⼝号是 80 , HTTPS 默认端⼝号是 443 )。在双⽅建⽴了连接后,TCP 报⽂中的数据部分就是存放 HTTP 头部 + 数据,组装好TCP报⽂之后,就需交给下⾯的网络层处理。

计算机网络——HTTP网络访问全流程_客户端_17

五、IP地址查询

TCP 模块在执⾏连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成⽹络包发送给通信对象。

计算机网络——HTTP网络访问全流程_网络_18

在 IP 协议⾥⾯需要有源地址 IP 和 ⽬标地址 IP:

  • 源地址IP,即是客户端输出的 IP 地址;
  • ⽬标地址,即通过 DNS 域名解析得到的 Web 服务器 IP。

5.1 IP的选择

当存在多个网卡时,在填写源地址 IP 时,就需要判断到底应该填写哪个地址。这个判断相当于在多块⽹卡中判断 应该使⽤哪个⼀块⽹卡来发送包,这个时候就需要根据路由表规则,来判断哪⼀个⽹卡作为源地址 IP。在 Linux 操作系统,我们可以使⽤ route -n 命令查看当前系统的路由表。

计算机网络——HTTP网络访问全流程_客户端_19

举个例⼦,根据上⾯的路由表,我们假设 Web 服务器的⽬标地址是 192.168.10.200 。

计算机网络——HTTP网络访问全流程_IP_20

5.2 IP报文生成

计算机网络——HTTP网络访问全流程_网络_21

计算机网络——HTTP网络访问全流程_IP_22

计算机网络——HTTP网络访问全流程_客户端_23

六、MAC地址传输

⽣成了 IP 头部之后,接下来⽹络包还需要在 IP 头部的前⾯加上 MAC 头部

6.1 MAC 包头格式

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

计算机网络——HTTP网络访问全流程_IP_24

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

发送⽅的 MAC 地址获取就⽐较简单了,MAC 地址是在⽹卡⽣产时写⼊到 ROM ⾥的,只要将这个值读取出来写⼊到 MAC 头部就可以了。

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

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

6.2 Mac的地址查询

既然知道要发给谁,按如何获取对⽅的 MAC 地址呢?不知道对⽅ MAC 地址?不知道就喊呗。 此时就需要 ARP 协议帮我们找到路由器的 MAC 地址。

计算机网络——HTTP网络访问全流程_客户端_25

ARP 协议会在以太⽹中以⼴播的形式,对以太网所有的设备喊出:“这个 IP 地址是谁的?请把你的 MAC 地址告诉我”,然后就会有⼈回答:“这个 IP 地址是我的,我的 MAC 地址是 XXXX”。如果对⽅和自己处于同⼀个子网中,那么通过上⾯的操作就可以得到对⽅的 MAC 地址。然后,我们将这个 MAC 地址写⼊ MAC 头部,MAC 头部就完成了。

查看 ARP 缓存内容

计算机网络——HTTP网络访问全流程_服务器_26

6.3 MAC 报文生成

计算机网络——HTTP网络访问全流程_客户端_27

计算机网络——HTTP网络访问全流程_网络_28

计算机网络——HTTP网络访问全流程_客户端_29

计算机网络——HTTP网络访问全流程_客户端_30

七、网卡传输

⽹络包只是存放在内存中的⼀串二进制数字信息,没有办法直接发送给对⽅。因此,我们需要将数字信息转换为电信号,才能在⽹线上传输,也就是说,这才是真正的数据发送过程。负责执⾏这⼀操作的是⽹卡,要控制⽹卡还需要靠网卡驱动程序。

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

计算机网络——HTTP网络访问全流程_网络_31

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

八、交换机传输

下⾯来看⼀下包是如何通过交换机的。交换机的设计是将⽹络包原样转发到⽬的地。交换机⼯作在 MAC 层,也称 为⼆层⽹络设备

8.1 交换机的包接收操作

⾸先,电信号到达网线接口,交换机⾥的模块进⾏接收,接下来交换机⾥的模块将电信号转换为数字信号,然后通过包末尾的 FCS 校验错误,如果没问题则放到缓冲区。这部分操作基本和计算机的⽹卡相同,但交换机的⼯作⽅式和⽹卡不同。

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

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

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

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

计算机网络——HTTP网络访问全流程_客户端_32

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

地址表中找不到指定的 MAC 地址。这可能是因为具有该地址的设备还没有向交换机发送过包,或者这个设备⼀段 时间没有⼯作导致地址被从地址表中删除了。这种情况下,交换机⽆法判断应该把包转发到哪个端⼝,只能将包转发到除了源端⼝之外的所有端⼝上,⽆论该设 备连接在哪个端⼝上都能收到这个包。这样做不会产⽣什么问题,因为以太⽹的设计本来就是将包发送到整个⽹络的,然后只有相应的接收者才接收包, ⽽其他设备则会忽略这个包。

其实完全不⽤过于担⼼,因为发送了包之后⽬标设备会作出响应,只要返回了响应包,交换机就可以将它的地址写 ⼊ MAC 地址表,下次也就不需要把包发到所有端⼝了,局域⽹中每秒可以传输上千个包,多出⼀两个包并⽆⼤碍。 此外,如果接收⽅ MAC 地址是⼀个⼴播地址,那么交换机会将包发送到除源端⼝之外的所有端⼝。

九、路由器传输

9.1 路由器与交换机的区别

⽹络包经过交换机之后,现在到达了路由器,并在此被转发到下⼀个路由器或⽬标设备,这⼀步转发的⼯作原理和交换机类似,也是通过查表判断包转发的⽬标。不过在具体的操作过程上,路由器和交换机是有区别的。

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

9.2 路由器基本原理

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

当转发包时,⾸先路由器端⼝会接收发给⾃⼰的以太网包,然后路由表查询转发⽬标,再由相应的端口作为发送⽅ 将以太⽹包发送出去。

9.3 路由器的包接收操作

⾸先,电信号到达⽹线接口部分,路由器中的模块会将电信号转成数字信号,然后通过包末尾的FCS 进⾏错误 校验。 如果没问题则检查 MAC 头部中的接收⽅MAC地址,看看是不是发给⾃⼰的包,如果是就放到接收缓冲区中,否则就丢弃这个包。 总的来说,路由器的端⼝都具有MAC地址,只接收与⾃身地址匹配的包,遇到不匹配的包则直接丢弃。

9.4 查询路由表确定输出端口

完成包接收操作之后,路由器就会去掉包开头的 MAC 头部。 MAC 头部的作⽤就是将包送达路由器,其中的接收⽅ MAC 地址就是路由器端⼝的 MAC 地址。因此,当包到达路 由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃。 接下来,路由器会根据 MAC 头部后⽅的 IP 头部中的内容进⾏包的转发操作。转发操作分为⼏个阶段,⾸先是查询路由表判断转发⽬标。

计算机网络——HTTP网络访问全流程_网络_33

9.5 路由器的发送操作

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

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

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

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

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

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

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

十、服务器与客户端解析

计算机网络——HTTP网络访问全流程_服务器_34

数据包抵达服务器后,服务器会先扒开数据包的 MAC 头部,查看是否和服务器⾃⼰的 MAC 地址符合,符合就将 包收起来。

接着继续扒开数据包的 IP 头,发现 IP 地址符合,根据 IP 头中协议项,知道⾃⼰上层是 TCP 协议。

于是,扒开 TCP 的头,⾥⾯有序列号,需要看⼀看这个序列包是不是我想要的,如果是就放⼊缓存中然后返回⼀ 个 ACK,如果不是就丢弃。TCP头部⾥⾯还有端⼝号, HTTP 的服务器正在监听这个端⼝号。

于是,服务器⾃然就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。服务器的 HTTP 进程看到,原来这个请求是要访问⼀个⻚⾯,于是就把这个⽹⻚封装在 HTTP 响应报⽂⾥,HTTP 响应报⽂也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,⽬的地址是客户端 IP 地 址。穿好头部⾐服后,从⽹卡出去,交由交换机转发到出城的路由器,路由器就把响应数据包发到了下⼀个路由器,就 这样跳啊跳。 最后跳到了客户端的城⻔把⼿的路由器,路由器扒开 IP 头部发现是要找城内的⼈,于是⼜把包发给了城内的交换 机,再由交换机转发到客户端。 客户端收到了服务器的响应数据包后,同样也⾮常的⾼兴,客户能拆快递了! 于是,客户端开始扒⽪,把收到的数据包的⽪扒剩 HTTP 响应报⽂后,交给浏览器去渲染⻚⾯,⼀份特别的数据包 快递,就这样显示出来了! 最后,客户端要离开了,向服务器发起了 TCP 四次挥⼿,⾄此双⽅的连接就断开了。

10.1 四次挥手断开连接

计算机网络——HTTP网络访问全流程_网络_35

计算机网络——HTTP网络访问全流程_网络_36

四次挥手的流程:

  1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
  2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
  3. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
  4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
  5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
  6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

为什么需要四次挥⼿?

  • 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务器收到客户端的 FIN 报⽂时,先回⼀个 ACK 应答报⽂,⽽服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报⽂给客户端来表示同意现在关闭连接。 从上⾯过程可知,
  • TCP 是全双工模式,并且支持半关闭特性,提供了连接的一端在结束发送后还能接收来自另一端数据的能力。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。
  • 通俗的来说,两次握手就可以释放一端到另一端的 TCP 连接,完全释放连接一共需要四次握手。

为什么 TIME_WAIT 等待的时间是 2MSL?

  1. MSL是Maximum Segment Lifetime,报⽂最⼤⽣存时间,它是任何报⽂在网络上存在的最⻓时间,超过这个时间报⽂将被丢弃。因为 TCP 报⽂基于是 IP 协议的。
  2. TTL字段:IP 头中有⼀个 TTL 字段,是IP 数据报可以经过的最大路由数,每经过⼀个处理他的路由器此值就减 1,当此值为0则数据报将被丢弃,同时发送 ICMP 报⽂通知源主机 。

MSL 与 TTL 的区别:MSL 的单位是时间,⽽TTL 是经过路由跳数。所以MSL 应该要⼤于等于 TTL 消耗为 0 的 时间,以确保报文已被⾃然消亡。

TIME_WAIT 等待 2 倍的 MSL,比较合理的解释: 网络中可能存在来⾃发送⽅的数据包,当这些发送⽅的数据包 被接收⽅处理后⼜会向对⽅发送响应,所以⼀来⼀回需要等待2倍的时间。

如果被动关闭⽅没有收到断开连接的最后的 ACK 报⽂,就会触发超时重发 Fin 报⽂,另⼀⽅接收到 FIN 后, 会重发 ACK 给被动关闭⽅,⼀来⼀去正好2个MSL

2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端⼜接收到了服务端重发的 FIN 报⽂,那么 2MSL 时间将重新计时。

在Linux 系统⾥ 2MSL 默认是 60 秒,那么⼀个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时 间为固定的 60 秒 。 其定义在 Linux 内核代码⾥的名称为 TCP_TIMEWAIT_LEN : define TCP_TIMEWAIT_LEN (60*HZ) ,如果要修改 TIME_WAIT 的时间⻓度,只能修改 Linux 内核代码⾥ TCP_TIMEWAIT_LEN 的值,并重新编译 Linux 内核 。

为什么需要 TIME_WAIT 状态?

主动发起关闭连接的⼀⽅,才会有 TIME-WAIT 状态。 需要 TIME-WAIT 状态,主要是两个原因:

  • 防⽌具有相同四元组的旧数据包被收到;
  • 保证被动关闭连接的⼀⽅能被正确的关闭,即保证最后的 ACK 能让被动关闭⽅接收,从⽽帮助其正常关闭;

假设 TIME-WAIT 没有等待时间或时间过短,被延迟的数据包抵达后会发生什么呢?

计算机网络——HTTP网络访问全流程_客户端_37

如上图黄色框框服务端在关闭连接之前发送的 SEQ = 301 报⽂,被网络延迟了。 这时有相同端⼝的 TCP 连接被复⽤后,被延迟的 SEQ = 301 抵达了客户端,那么客户端是有可能正常接收 这个过期的报⽂,这就会产⽣数据错乱等严重的问题。 所以TCP 就设计出了这么⼀个机制,经过 2MSL 这个时间,⾜以让两个⽅向上的数据包都被丢弃,使得原来 连接的数据包在⽹络中都⾃然消失,再出现的数据包⼀定都是新建⽴连接所产生的。

假设 TIME-WAIT 没有等待时间或时间过短,断开连接会造成什么问题呢?

计算机网络——HTTP网络访问全流程_客户端_38

  • 如上图红⾊框框客户端四次挥⼿的最后⼀个 ACK 报⽂如果在⽹络中被丢失了,此时如果客户端 TIME- WAIT 过短或没有,则就直接进⼊了 CLOSED 状态了,那么服务端则会⼀直处在 LASE_ACK 状态。
  • 当客户端发起建⽴连接的 SYN 请求报⽂后,服务端会发送RST报⽂给客户端,连接建⽴的过程就会被 终⽌。

如果 TIME-WAIT 等待⾜够⻓的情况就会遇到两种情况 ?

  • 服务端正常收到四次挥⼿的最后⼀个 ACK 报⽂,则服务端正常关闭连接。
  • 服务端没有收到四次挥⼿的最后⼀个 ACK 报⽂时,则会重发 FIN 关闭连接报⽂并等待新的 ACK 报⽂。

所以客户端在 TIME-WAIT 状态等待 2MSL 时间后,就可以保证双⽅的连接都可以正常的关闭。

TIME_WAIT 过多有什么危害?

如果服务器有处于 TIME-WAIT 状态的 TCP,则说明是由服务器⽅主动发起的断开请求。 过多的 TIME-WAIT 状态主要的危害有两种:

  • 第⼀是内存资源占⽤;
  • 第⼆是对端⼝资源的占⽤,⼀个 TCP 连接⾄少消耗⼀个本地端⼝

如何优化 TIME_WAIT?

  1. 打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项;

如下的 Linux 内核参数开启后,则可以复⽤处于 TIME_WAIT 的 socket 为新的连接所⽤。 有⼀点需要注意的是,tcp_tw_reuse 功能只能⽤客户端(连接发起⽅),因为开启了该功能,在调⽤ connect() 函数时,内核会随机找⼀个 time_wait 状态超过 1 秒的连接给新的连接复⽤。

  1. net.ipv4.tcp_max_tw_buckets。

这个值默认为 18000,当系统中处于 TIME_WAIT 的连接⼀旦超过这个值时,系统就会将后⾯的 TIME_WAIT 连接 状态重置 。 这个⽅法过于暴⼒,⽽且治标不治本,带来的问题远⽐解决的问题多,不推荐使⽤

  1. 程序中使⽤ SO_LINGER ,应⽤强制使⽤ RST 关闭

我们可以通过设置 socket 选项,来设置调⽤ close 关闭连接⾏为,我们可以通过设置 socket 选项,来设置调⽤ close 关闭连接⾏为 。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP 有⼀个机制是保活机制。这个机制的原理是这样的: 定义⼀个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作⽤,每隔⼀个时间间 隔,发送⼀个探测报⽂,该探测报文包含的数据⾮常少,如果连续几个探测报⽂都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应⽤程序 :

在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值:
tcp_keepalive_time=7200:
表示保活时间是7200秒(2⼩时),也就2⼩时内如果没有任何连接相关的活动,则会启动保活机制
tcp_keepalive_intvl=75:
表示每次检测间隔75秒;
tcp_keepalive_probes=9:
表示检测9次⽆响应,认为对⽅是不可达的,从⽽中断本次的连接。

如果开启了 TCP 保活,需要考虑以下⼏种情况:

  • 第⼀种,对端程序是正常⼯作的。当 TCP 保活的探测报⽂发送给对端, 对端会正常响应,这样 TCP 保活时间会被 重置,等待下⼀个 TCP 保活时间的到来。
  • 第⼆种,对端程序崩溃重启。当 TCP 保活的探测报⽂发送给对端后,对端是可以响应的,但由于没有该连接的 有效信息,会产⽣⼀个 RST 报⽂,这样很快就会发现 TCP 连接已经被重置。
  • 第三种,是对端程序崩溃,或对端由于其他原因导致报⽂不可达。当 TCP 保活的探测报⽂发送给对端后,⽯沉⼤ 海,没有响应,连续⼏次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡。

博文参考

《小林图解网络》