转载自:脚本之家
仅用于自我学习,侵权删!
详解输入网址点击回车,后台到底发生了什么。透析 HTTP 协议与 TCP 连接之间的千丝万缕的关系。掌握为何是三次握手四次挥手?time_wait 存在的意义是什么?全面图解重点问题,再也不用担心面试被问到。
大致流程:
- URL解析
- DNS查询
- TCP连接
- 服务器处理请求
- 客户端接收HTTP报文响应
- 渲染页面
重点来了:
- 如何理解 TCP 的三次握手和四次挥手?每次握手客户端与服务端是怎样的状态?
- 为何挥手会出现 2MSL ,遇到大量的 Socket 处在 TIME_WAIT 或者 CLOSE_WAIT 状态是什么问题?
- 三次握手与四次挥手的过程是怎样的?
- HTTP的报文格式又是怎样的?
地址解析:首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。
HSTS 由于安全隐患,会使用 HSTS 强制客户端使用 HTTPS 访问页面。
其他操作浏览器还会进行一些额外的操作,比如安全检查、访问限制(之前国产浏览器限制 996.icu)。
- 浏览器缓存:先查询是否在缓存中,没有则调用系统库函数进行查询。
- 操作系统缓存:操作系统也有自己的 DNS 缓存,但在这之前,会先检查域名是否存在本地的 Hosts 文件里,没有则向 DNS 服务器发送查询请求。
- 路由器缓存。
- ISP DNS 缓存: ISP DNS 就是在客户端电脑上设置的首选 DNS 服务器,它们在大多数情况下都会有缓存。
根域名服务器查询
在前面所有步骤没有缓存的情况下,本地 DNS 服务器会将请求转发到互联网上的根域,下面这个图很好的诠释了整个流程:
需要注意的是:
- 递归方式:一路查下去中间不返回,得到最终结果才返回信息(浏览器到本地 DNS 服务器的过程)
- 迭代方式,就是本地 DNS 服务器到根域名服务器查询的方式
- 什么是 DNS 劫持
- 前端 dns-prefetch 优化
TCP/IP 分为四层,在发送数据时,每层都要对数据进行封装:
应用层:发送 HTTP 请求
浏览器从地址栏得到服务器 IP ,接着构造一个 HTTP 报文,其中包括:
- 请求报头(Request Header):请求方法、目标地址、遵循的协议等
- 请求主体,请求参数、比如 body 里面的参数
传输层:TCP 传输报文
传输层会发起一条到服务器的 TCP 连接,为了方便传输,会对数据进行分割(以报文段位单位),并标记编号,方便服务器接受时能够准确地还原报文信息。在建立连接前,会先进行 TCP 三次握手。
网络层:IP 协议查询 MAC 地址
将数据段打包,并加入源及项目的 IP 地址,并且负责寻找传输路线。判断目标地址是否与当前地址处于同一网络中,是的话直接根据 MAC 地址发送,否则使用路由表查找下一个地址,以及使用 ARP 协议查询它的 MAC 地址。
链路层:以太网协议
根据以太网协议将数据分为以“帧”为单位的数据包,每一帧分为两个部分:
- 标头:数据包的发送者、接受者、数据类型
- 数据:数据包具体内容
MAC地址
以太网规定了连入网络的所有设备都必须具备“网卡”接口,数据包都是从一块网卡传递到另一块网卡,网卡的地址就是 Mac 地址。每一个 Mac 地址都是独一无二的,具备了一对一的能力。
主要的请求过程:
- 浏览器从地址栏中获取服务器的 IP 和端口号;
- 浏览器与服务器之间通过 TCP 三次握手建立连接;
- 浏览器向服务器发送报文;
- 服务器接收报文处理,同时将响应报文发给浏览器;
- 浏览器解析报文,渲染输出到页面;
在传输层传输数据之前需要建立连接,也就是三次握手创建可靠连接。
首先建立连接前需要 Server 端先监听端口,因此 Server 端建立连接前的初始状态就是 LISTEN 状态,这时 Client 端准备建立连接,先发送一个 SYN 同步包,发送完同步包后, Client 端的连接状态变成了 SYN_SENT 状态。Server 端收到了 SYN 后,同意建立连接,会向 Client 端恢复一个 ACK。
由于 TCP 是双工传输,Server 端也会同时向 Client 端发送了一个 SYN ,申请 Server 向 Client 方向建立链接。发送完 ACK 和SYN 后,Server 端的链接状态就变成了 SYN_RCVD。
Client 收到 Server 的 ACK 后,Client 端的链接状态就变成了 ESTABLISHED 状态,同时, Client 向 Server 端发送 ACK,回复 Server 端的 SYN 请求。
Server 端收到 Client 端的 ACK 后,Server 端的链接状态也就变成了 ESTABLISHED 状态,此时建连完成,双方随时可以进行数据传输。
在面试时需要明白三次握手是为了建立双向的链接,需要记住 Client 端和 Server 端的链接状态变化。另外回答建连的问题时,可以提到 SYN 洪水攻击发生的原因,就是 Server 端收到 Client 端的 SYN 请求后,发送了 ACK 和 SYN,但是 Client 端不进行回复,导致 Server 端大量的链接处在 SYN_RCVD 状态,进而影响其他正常请求的建连。可以设置 tcp_synack_retries = 0 加速半链接的回收速度,或者调大 tcp_max_syn_backlog 来应对少量的 SYN 洪水攻击。
我们只要关注 80 端口与 13743 端口建立的连接断开过程,浏览器通过 13747 端口发送 [ FIN,ACK ] 这里是不是跟很多网上看到的不一样?
- 其实是客户端在发送 [ FIN ] 报文的时候顺带发了一个 [ ACK ] 确认上次传输确认。
- 接着服务端通过 80 端口响应了 [ ACK ] ,然后立马响应 [ FIN , ACK ] 表示数据传输完了,可以关闭连接。
- 最后浏览器通过 13743 端口发送 [ ACK ] 包给服务端,客户端与服务端连接就关闭了。
具体流程如下图抓包所示:
客户端:
- SYN_SENT - 客户端发起第 1 次握手后,链接状态为 SYN_SENT ,等待服务端内核进行应答,如果服务端来不及处理(例如服务端的 backlog 队列已满)就可以看到这种状态的链接。
- ESTABLISHED - 表示链接处于正常状态,可以进行数据传送。客户端收到服务器回复的 SYN + ACK 后,对服务端的 SYN 单独回复(第 3 次握手),连接建立完成,进入 ESTABLISHED 状态。服务端程序收到第 3 次握手包后,也进入 ESTABLISHED 状态。
- FIN_WAIT_1 - 客户端发送了关闭连接的 FIN 报文后,等待服务端回复 ACK 确认。
- FIN_WAIT_2 - 表示我方已关闭连接,正在等待服务端关闭。客户端发了关闭连接的 FIN 报文后,服务器发回 ACK 应答,但是没进行关闭,就会处于这种状态。
- TIME_WAIT - 双方都正常关闭连接后,客户端会维持 TIME_WAIT 一段时间,以确保最后一个 ACK 能成功发送到服务端。停留时长为 2 倍的 MAL (报文最大生存时间),Linux 下大约是 60 秒。所以在一个频繁建立短连接的服务器上通常可以看到成千上万的 TIME_WAIT 连接。