HTTP 最最最本质的拆解

HTTP 最最最本质的拆解

HTTP 最最最本质的拆解

开发中每天都用的 HTTP 协议, 本质装了什么, 怎么一步步拆分的? 你每天都使用, 有没有好奇过底层的原理, 经历是怎么样的? 跟着我们的实验走一遍流程, 狠狠的最本质的底层原理刻进脑子里吧~


实验环境与准备

实验环境是 mac, 自带tcpdump, 我们拿这个抓包, 然后在 wireshark 上看抓包到的结果, 为了理解 http , 我们要选择一个请求仅 http 的网站, 方便抓取对应的数据, 这里我们选取:
http://example.com/

开始实验前我们先检查环境是否齐全, 具体是我们要有 tcpdump 和 wireshark, 并且可以在 wireshark 中确认我们使用的网卡是哪一个, 像我这里使用的无线 WiFi, 可以看到我使用的是 en0 网卡

我们的实验很简单, 开两个终端:

终端 A — 抓包

sudotcpdump-ien0-s0-w~/http_capture.pcap'port 80'

这里是抓包 80 端口上的流量

参数说明:

  • -i en0— 监听 en0 网卡(可能是 en5,看你 Mac 型号)
  • -s 0— 抓完整包,不截断
  • -w ~/http_capture.pcap— 写到文件
  • 'port 80'— BPF 过滤,只看 HTTP 流量

终端 B — 发起请求

curlhttp://example.com/

可以看到, 这里获取到了对应网页的数据, 此时可以使用 ctrl+c 关闭终端 A

在 WireShark 中分析

此时, 抓包的完整文件已经保存在~/http_capture.pcap, 我们可以使用 wireshark 对这次的 curl http 流程做一个全流程解析了!

首先我们按照以下顺序打开Wireshark → File → Open → 选~/http_capture.pcap

然后在输入框中输入tcp.stream eq 0(此处需要修补, 不同环境不一定一致)这样后, 我们就可以看到只和 example.com 这条 curl 命令相关的所有流程


三次握手

这里的 No. 7, 8, 9 是我们熟悉的三次握手环节, 简单来说, 每个包干了这么个事:

  1. No7: [SYN]我->example.com 你好, 我要跟你连接一下
  2. No8: [SYN, ACK]example.com-> 我 好的, 你来吧, 我准备好了
  3. No9: [ACK]我->example.com 好, 我来了

三次握手完成


HTTP 数据传输

从 No10 到No15 中间是 Http 真正传输的包


四次挥手

No16到 No18, 是四次挥手的过程, 一个标注的四次挥手流程如下:

  • : FIN → “我不发了”
  • example.com: ACK ← “收到你的 FIN”
  • example.com: FIN ← “我也不发了” ← 这里应该还有一个包
  • : ACK → “收到,关了”

这里是服务器端处理成了 3 次, 看下面的讲解,

  • No16我 → example.com[FIN, ACK]① 我:“我不发了(FIN)”
  • No17example.com → 我[FIN, ACK]②+③ example.com:“收到你的 FIN,我也不发了” ← 合并了
  • No18我 → example.com[ACK]④ 我:“好,关了(ACK)”

TCP/IP 四层模型

好, 现在三次握手和四次挥手看完了, 我们来深入 http 协议中, 计算机网络中到底在做什么操作?
(推荐阅读 https://xiaolincoding.com/network/1_base/tcp_ip_model.html, 本文参考了相关知识点的内容)

这里我们先看看TCP/IP 网络分为这四层, 我们记牢这个架构, 后续的环环都会相扣在这个上面

来, 我们继续 WireShark 中的操作吧! 可以看到 No10 的包的协议是 HTTP 的, 我们以这个数据包来进行展开

点进来后, 我们可以看到划分清晰的上下两块部分, 分别以红色和蓝色表示, 红色部分, 是我们打过去的请求, 下半部分的蓝色是服务器响应我们的内容, Show as 部分可以选择不同的显示格式, 同学们可以自行探索

接下来, 让我们深入四层来进行剖析(不记得的同学上去再看一眼是哪四层!)

我们先对这四层, 每一层的数据包, 有一个印象:

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

然后我们双击 WireShark 中的 No10 请求, 进入这个界面, 我们在上半部分选中的内容, 在下面的十六进制报文中可以同步高亮, 这里有五个折叠块, 从上到下依次是:

  • 整体
  • 网络接口层
  • 网络层
  • 传输层
  • 应用层

WireShark 逐层深入分析

① 应用层(Application Layer)

图中点亮的是应用层, 可以看到应用数据, 放在我们所有请求的最后, 我们要开始发送了, 接下来, 我们将从发送的角度, 从应用层, 逐步深入到传输层, 到网络层, 再到网络接口层, 逐层看看里面经历了什么

② 传输层(Transport Layer)

现在我们进入了传输层, 这里也是字段最多的一层, 让我们来一个个看看:

  • Source Port: 表示我们从 65025端口 发出的这个请求
  • Destination Port: 表示我们请求对方 80 端口上的服务
  • Sequence Number & Acknowledgement Number:1 表示前面的三次握手已经顺利完成, 这里是建立连接成功后的第一条有有效信息的包
  • Flags: ACK 代表确认收到服务器之前的响应, PSH(push), 是命令服务器, 不要让这个数据包在操作系统的缓冲区排队了, 已经是完整的 HTTP 请求了, 赶紧交给 Nginx 等业务程序去处理吧!
  • Window: 基础接收窗口大小
  • Calculated window size: WireShark 帮我们计算的真正的接收窗口大小, 是基础接收窗口大小*放大因子, 即 Window size scaling factor: 64
  • Checksum: 校验和。用来检查整个 TCP 头部和数据在网线传输过程中有没有因为电磁干扰、丢包而导致二进制位发生反转(从 0 变成 1), 此处不做详细赘述

③ 网络层(Network Layer)

现在我们进入网络层, 这一层比传输层, 多了 IP 头, 让我们看看这里的关键字段:

  • Src: 源 IP, 图中是 192.168.11.136, 是我们在局域网内的私网 IP
  • Dst: 目标 IP, 图中是 172.66.147.243, 这是 example.com 在经过 DNS 解析后, 找到的对应服务器的公网 IP
  • Protocol: 传输层使用的协议是 TCP 协议
  • Time to Live: TTL, 每经过一个路由器会自减 1 防止数据包在网络中陷入无限死循环
  • Differentiated Services Field: 这个字段是表示这个包被处理的优先级, 可以让路由器决定拥堵时, 谁先走, 谁后走

④ 网络接口层(Network Interface Layer)

现在我们到达了网络接口层, 相比上一层, 我们会在这里包上帧头和帧尾, 可以看到三个关键字段, 分别是, 而看我们真实的 16 进制数据包时, 会发现, 这部分没有帧尾, 那是为什么? 因为事实上, 现代为了减轻 CPU 的计算负担, 现代操作系统, 都把"计算 CRC 校验码并追加到帧尾" 的工作完全交给了网卡芯片来处理, 也就是说, 得等这个包从我们的内核中出来, 进到物理网卡芯片时, 才会有帧尾, 我们这里抓不到是正常的

  • Dst(Destination): 表示目的 MAC 地址, 这里的目的 MAC 地址不是example.com 的服务器的, 而是当前我本地连接的 WIFI 的, 网关的 MAC 地址, 因为数据包要出局域网, 必须先交由路由器来进行转发
  • Src(Source): 我们使用的本地的网卡的 MAC 地址
  • Type: IPv4, 告诉当对方接收我们的数据时, 我们里面包裹的内容是一个 IPv4 的网络层数据包