- posper V1.0
- date:2021/10/19
讲述了从 浏览器输入 URL 按下回车键,到页面在浏览器上显示这个过程中 网络内部发生了什么
生成 HTTP 请求消息
-
浏览器解析 URL
格式:协议类型 + 服务器域名/IP + 目录名 + 文件名
省略文件名case:当 URL 以
/
(或者/
)都没有时,访问 默认文件(一般是index.html
) -
生成 HTTP 请求消息
HTTP 请求消息主要包括 2 个部分:
- “对什么”:根据解析 URL 中获取,即 "目录名 + 文件名‘
- “执行什么操作”:即,方法。常用方法为
GET
(只读)、POST
(提交 data 给服务器)
HTTP 请求消息格式:
- 第一行:请求行。即 “方法 + URI + HTTP版本”
- 第二行开始:消息头。记录额外的详细信息,如,日期、语言、压缩格式 等等
3. 接受响应信息
HTTP响应报文格式:
- 第一行:响应行。即,“状态码 + 响应短语”
- 状态码:数字表示,5 大类
- 响应短语:文字表示,告知执行结果。状态码和响应短语表示的内容应该一致
- 第二行开始:
注意:每条请求消息只能写一个 URI
。如果需要获取多个文件,则需要对每个文件单独发送1条请求
所以,当一个网页有 图片等资源时,还需要单独执行 请求消息(比如,1个网页有3张图片,则需要发送 4 个请求消息)
域名解析 DNS
概念
- 域名是分层次的,越往右等级越高
- 顶级域名 > 二级域名 > …
- 顶级域名上面还有一个 “根级域名”,用来存放 顶级域名的信息
- 根域名服务器
- 一共 13 个 IP 地址,但是服务器远远不止 13 台(多台 根域名服务器对应一个 IP 地址)
- 根域名 DNS 服务器的信息保存在所有的 DNS 服务器中
- 任何 DNS 服务器都可以找到 根DNS服务器,从根DNS服务器就可以向下找到任意一台 DNS 服务器了
- 上级域名服务器存放的是下级域名服务器的 域名-IP 信息
DNS 过程
- 客户端访问最近的 DNS 服务器(即,客户端 TCP/IP 设置中写的 DNS 服务器地址)
- 查找 DNS 服务器中的 “缓存”,若当前 域名 命中 DNS服务器缓存,则不必再到根域DNS服务器中自顶向下查找;否则,还需要进行 3
- 本地 DNS 服务器将 DNS 查询信息转发给 根域DNS服务器
- 根域DNS服务器 返回给本地DNS服务器 “顶级域名服务器”的IP地址
- 本地 DNS 服务器将 DNS 查询信息转发给 顶级DNS服务器
- 顶级DNS服务器 返回给本地DNS服务器 “二级域名服务器”的IP地址
…
重复上述过程,直至找到一个 DNS 服务器中包含待查“域名”
Note🔥:DNS服务器中的”缓存信息“都是有 有效期的,超过有效期后会自动删除。(因为 域名 和 IP的对应关系不是永久不变的)
委托协议栈发送消息
知道了IP 地址之后,就可以委托操作系统内部的协议栈向这个目标IP地址(服务器)发送消息了
- 创建套接字;
- 连接阶段:将管道连接到服务器端的套接字上
- 收发数据(通信阶段)
- 断开阶段:断开管道并删除套接字
连接阶段
- 目的:通信双方交换控制信息(IP地址、端口号等…)
- 其实就是 “三报文握手”建立 TCP 连接
收发数据
-
将HTTP 请求消息交给协议栈
-
应用程序调用write 将要发送的数据交给协议栈
-
将数据存放在内部的发送缓冲区中,并等待下一段数据;
-
等到数据长度接近 MSS 时,再一起发出去(为了提高传输效率)
- 但是,也不能一直等。不仅仅要考虑接近 MSS 时才发送,还要考虑 时间(不能为了凑够 MSS 而等太久,也是不可以的)
-
-
对较大的数据进行拆分
当发送数据超过 MTU 时,需要对数据进行 拆分;
拆分后的每个数据块要添加 “序号”;
然后,为每个数据块前面加上 TCP 头部 -
使用 ACK 确认网络包已经收到
- 单单凭着 HTTP 是无法进行网络通信的,所以需要将 HTTP请求消息 放入 TCP 中,利用 运输层的 TCP 协议来进行 可靠传输
- 使用 TCP 协议,当接受方收到一个数据包时,需要给发送方返回一个 ACK 号,表明 序号 ACK 之前的数据包 都已经正常收到了
- 但是,实际通信中 序号并不是从 1 开始的,而是使用一个 随机数 作为序号的初始值(因为,如果每次都是用 1 作为“序号 初始值”,很容易被网络攻击,被别人获取 数据包内容,这样 不安全)
- TCP 还能根据 窗口大小,进行 流量控制、拥塞避免(见 湖科大笔记)
-
接受 HTTP 响应消息
- 协议找调用
read
程序来获取响应消息 - 协议栈检查收到的数据块和 TCP 头部内容,判断是否有数据丢失,如果没有问题则返回 ACK 号;
- 然后,接受数据暂存到 接受缓存区中;
- 将数据块从接受缓存中按顺序连接起来并还原成原始的数据;
- 最后将数据交给应用程序
- 协议找调用
断开阶段
4 次挥手
下图为 TCP 收发数据的流程
IP 模块上场
TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托IP 模块将数据封装成包发送给通信对象
即,仍然套娃,把 TCP 报文作为 IP 的数据部分,并给其加上 IP 头部,构成 IP 数据包
添加 IP 地址后,相当于 "远程定位”,但是 浩瀚的网络中,如何能够到达 这一目的地呢?(类似于知道了 快递的 目的地,但是如何讲过一位位 快递小哥将包裹送到目的地呢?也即,如何找到 包裹 [数据包] 的下一个 转发点(下一位快递小哥)呢?答:需要用 MAC地址))
如何生成 IP 头部
以太网中,无法直接根据 IP 地址来区分 数据包,所以需要 添加 MAC 头部 来实现 数据包在 以太网中的 传输。
- 目的 IP 地址:根据 DNS 得到的 IP 地址;
- 源 IP 地址
- 如果只有一块网卡,因此也就只有一个IP 地址,则该 IP 地址就是 源IP地址;
- 如果有多快网卡,就稍微复杂一些了,如下
IP 地址实际上不是分配给计算机的,而不是分配给网卡的。如果有多块网卡,则有多个 IP 地址。
当主机中有多块网卡时,如何确定 IP 头部中的 源IP地址 使用哪一个IP地址?
- windows 系统可以使用
route print
查看本机“路由表”
- 将 目的IP 地址和 网络掩码(Netmask)进行“逻辑与”运算;
- 如果结果和 “网络目标”匹配,则选取匹配行中 “接口 interface” 对应的 IP 地址作为 源IP地址;
- 如果结果和路由表中的“网络目标”都不匹配,则会自动匹配**“默认网关”**(即,目标地址 和 网络掩码都是
0.0.0.0
的行),选取该行中 “接口 interface” 对应的 IP地址作为 源 IP地址
UDP
还有就是 可以通过 UDP 协议来进行数据包收发。
UPD 特点:
- 面向无连接;
- 不可靠(丢包、发生错误的包 一概不管);
- 相比 TCP 更简单;
UDP 适用于:
- 发送 控制用的短数据(DNS 报文就是UDP):
- 即时通信更适合使用 UDP(比如,播放器中的 音频/视频传输、视频通话 )
MAC 模块上场
以太网 概念
总线型以太网
:- 使用总线、基于 CSMA/CD 协议;
- 以太网中所有主机都可以收到 数据包;
- 一次只能有一台主机通信
- 使用
集线器 的以太网
:- 集线器 本质仍然是使用总线、基于 CSMA/CD 协议;
- 以太网中所有主机都可以收到 数据包;
- 一次只能有一台主机通信
- 使用
交换机 的以太网
:- 交换机具有“缓存”功能,不再基于 CSMA/CD;
- 交换机会利用“帧交换表”根据 目的MAC地址,将数据包从 特定的“接口”转发出去,而不是向 以太网中所有主机 广播数据包;
- 可以通信有多台主机通信
现在的以太网中,大多都采用 交换机了。
以太网经过太多版本变迁,现在认为只要满足以下 3 个性质的就是以太网:
- 将包发送到MAC 头部的接收方MAC 地址代表的目的地;
- 用发送方MAC地址识别发送方;
- 用以太类型识别包的内容
添加 MAC 头部
IP 地址相当于“远程定位”,但是在 茫茫的网络世界中,如何将 “数据包” 传输到目的地呢?即,如何每次判断 数据包 的 下一招是什么呢?
所以,需要给 IP 数据,添加一个 MAC 头部,实现数据包在 以太网中的 传输
MAC 头部
- 源 MAC 地址:从网卡的 ROM 中直接读取;
- 目的 MAC 地址:
- 首先,查询
ARP缓存
中是否有 目的IP地址。如果命中缓存,则直接返回,否则需要执行 APR 协议; - 然后,在 以太网中 广播
ARP协议
,获取 目的IP地址 对应的 MAC 地址
- 首先,查询
Note :
ARP缓存
是有 有效期的,一段时间后会自动删除!(因为 IP地址和 MAC地址的对应关系不是永久的。)
网卡 发挥作用
- 将 MAC 头部添加在 IP 头部前面后,整个 数据包就构建完成了。
- 但是,IP 模块生成的网络包只是存放在内存中的一串数字信息(即,010101…),没有办法直接发送给对方。
- 因此,我们需要将数字信息转换为电或光信号,才能在网线/光纤 上传输
而负责将 将数字信息转换为电或光信号的便是:网卡
+ 网卡驱动程序
网卡 ROM 中固化的 MAC 地址会由网卡驱动程序读取并分配给 MAC 模块
网卡驱动从IP 模块获取包之后,会将其复制到网卡内的缓冲区中,并在开头加上 报头和起始帧分界符
(用于 帧定界),在末尾加上用于检测错误的帧校验序列 FCS
(用于 差错检测)
最终,网卡 将此时的数据包转换为 电/光信号,利用网线转发出去
到这为止,数据包添加无数个 头部后,并由网卡 转换为 电/光信号后,才能真正离开计算机,踏上 寻找目的地的征途
网卡-接受数据包
- 将电/光信号转换为 数字信息,并放入 缓存区中;
- 检查 FCS,检测包在传输过程中是否发成错误:
- 发生错误:丢弃
- 无错误: 检测 目的MAC地址 和 自己的 MAC地址是否一致:如果一直,则将数据包放入 缓存区中;否则(不是自己的包),则 丢弃之。
- 此时,MAC 模块的工作就完成了。接下来网卡会发出 “中断” 通知计算机,让 网卡驱动程序工作
交换机
交换机并不只是简单地让信号流过,而是先接收信号并将其还原为数字信息,然后再重新转换成信号并发送出去的过程。
- 首先,信号到达网线接口,并由PHY(MAU)模块进行接收;
- PHY(MAU)模块会将网线中的信号转换为通用格式,然后传递给 MAC 模块;
- MAC 模块将信号转换为数字信息,然后通过包末尾的 FCS 校验错误,如果没有问题则存放到缓冲区中(这个过程基本和 网卡接受数据包 相同)
交换机端口的 MAC 模块 “类似于” 网卡。但仅仅是“类似”,不同点在于 交换机的端⼝不具有 MAC 地址
如何转发?
- 答:根据“帧交换表” + 目的 MAC 地址,判断从 哪个“端口”转发 数据包
如何维护 “帧交换表”?(即,自学习算法)
- 收发包时,将发送方 MAC 地址 和 输入端口号写入 帧交换表;
- 一段时间后,自动删除“帧交换表”中 过期的条目(因为 MAC 地址 和 端口的对应关系不是永久的,比如 换网卡了)
特殊操作:
- 当 转发的目的端口 和输入端口相同时,则直接丢弃该包(相当于兜圈了,丢之)
- 当 帧交换表中查询不到 目的MAC地址时,采用 “洪泛”(将该包从除了输入端口的所有端口转发出去)
有人会说:“这样做会发送多余的包,会不会造成网络拥塞呢?”
- 其实完全不用过于担心,因为发送了包之后目标设备会作出响应,只要返回了响应包,交换机就可以将它的地址写入地址表,下次也就不需要把包发到所有端口了。
- 局域网中每秒可以传输上千个包,多出一两个包并无大碍。
广播地址:
- MAC 地址中的
FF:FF:FF:FF:FF:FF
; - IP 地址中的
255.255.255.255
都是广播地址
交换器 vs. 集线器
- 交换机只将包转发到具有特定MAC 地址的设备连接的端口,可以同时转发多个包;
- 集线器会将输入的信号广播到所有的端口,如果同时输入多个信号就会发生碰撞,无法同时传输多路信号
路由器
路由器 vs. 交换机
:
- 路由器是基于 IP 设计,是 三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址;
- 交换机是基于**以太网(MAC层)**设计,是 二层网络设备,交换机的端口不具有 MAC 地址;
路由器的结构
路由器分为 2 个主要模块:
- 转发模块:负责判断包的转发目的地;
- 端口模块:负责包的收发操作
可以将路由器的转发模块想象成IP 模块,将端口模块想象成网卡
通过更换网卡,计算机不仅可以支持以太网,也可以支持无线局域网,路由器也是一样。
如果路由器的端口模块安装了支持无线局域网的硬件,就可以支持无线局域网了。
此外,计算机的网卡除了以太网和无线局域网之外很少见到支持其他通信技术的品种,而路由器的端口模块则支持除局域网之外的多种通信技术,如ADSL、FTTH,以及各种宽带专线等,只要端口模块安装了支持这些技术的硬件即可
路由器工作原理
-
首先,会通过端口将发过来的包接收进来
这一步的工作过程取决于端口对应的通信技术。
对于以太网端口来说,就是按照以太网规范进行工作,而无线局域网端口则按照无线局域网的规范工作 -
转发模块会根据接收到的数据包中的 目的IP 地址,在路由表中进行查询,以此判断转发目标;
-
转发模块将包转移到转发目标对应的端口,端口再按照硬件的规则将包发送出去,也就是转发模块委托端口模块将包发送出去的意思
路由聚合:
- 即,将多个子网合并成一个子网,只将 合并后的子网存入 路由表中;
- 好处:减少路由表的条目数,提高查询效率 和 可维护性
路由器会忽略主机号,只匹配网络号。
有时,也可以将某台具体计算机的地址写入路由表中:
- 这时的子网掩码为
255.255.255.255
路由器- 接收包
路由器的端口有各种不同的类型,这里我们只介绍以太网端口是如何接收包的
- 首先,信号到达网线接口部分,其中的PHY(MAU)模块和MAC 模块将信号转换为数字信息;
- 然后通过包末尾的FCS 进行错误校验,如果没问题则检查MAC 头部中的接收方MAC 地址,看看是不是发给自己的包:
- 如果是,就放到接收缓冲区中;
- 否则(接收方MAC地址和 目的MAC地址不一致),就丢弃这个包;
路由器 - 发送包
完成包接收操作之后,路由器就会丢弃包开头的MAC 头部。
MAC 头部的作用就是将包送达路由器,其中的接收方MAC 地址就是路由器端口
的MAC 地址
接下来,路由器会根据MAC 头部后方的IP 头部中的内容进行包的转发操作。
路由器转发操作分为以下几个阶段,首先 需要查询路由表判断转发目标。
1、判断转发目标
- 利用
子网掩码 + 目的 IP 地址进行“逻辑与”
运算,找到 结果和 路由表中 第一列匹配的行; - 如果成功匹配,路由器就会将网络包交给
接口列
中指定的网络接口(即,端口。也即,源 IP 地址),并转发到网关列
中指定的 IP 地址(即,目的 IP 地址。【这里可能为空,见第 2 步】); - 如果 “每一条都无法匹配”,则会转发给 “默认路由/网关”(即,子网掩码为
0.0.0.0
行对应的 “网关列”)
2、包发送操作
假设这里路由器让然是基于 以太网规则进行发送 数据包的,则需要在包前面 添加 MAC 头部
如何 判断转发目标?
- 如果路由表的网关列内容为 IP 地址,则该地址就是下一个转发目标;
- 如果路由表的网关列内容为空,则 IP 头部中的接收方 IP 地址就是下一个转发目标
找到转发目标的 IP 地址后,则需要添加 MAC 头部:
- 源 MAC 地址:路由器输出端口的 MAC 地址(见 1 中第 2 步);
- 目的 MAC 地址:根据 转发目标的 IP 地址,执行 ARP 协议,获取目的 MAC 地址
- 执行 ARP 之前,先查询路由器的 ARP 缓存
以下参考《小林coding-图解网络》
在网络包传输的过程中,源 IP 地址和 目的IP地址始终是不会改变的,**一直变化的是 MAC 地址,**因为需要 MAC 地址在 以太网内进行 两个设备之间的包传输。
为什么有了IP地址,还需要 MAC 地址?
IP 协议本身没有传输包的功能,因此包的实际传输要委托以太网来进行。
路由器是基于IP 设计的,而交换机是基于以太网设计的。
换句话说,路由器将包的传输工作委托给交换机来进行。
当然,这里讲的内容只适用于原原本本实现IP 和以太网机制的纯粹的路由器和交换机,实际的路由器有内置交换机功能的,比如用于连接互联网的家用路由器就属于这一种,对于这种路由器,上面内容可能就不适用了。但是,如果把这种“不纯粹”的路由器拆分成“纯粹”的路由器和“纯粹”的交换机,则它们各自都适用上面的内容。
IP 并不是委托以太网将包传输到最终目的地,而是传输到下一个路由器。
- 在创建MAC 头部时,也是从IP 的路由表中查找出下一个路由器的IP 地址;
- 然后,通过ARP 查询出MAC 地址,然后将MAC 地址写入MAC 头部中的
这表示IP 对以太网的委托只是将包传输到下一个路由器就行了。
当包到达下一个路由器后,下一个路由器又会重新委托以太网将包传输到再下一个路由器。
随着这一过程反复执行,包就会最终到达IP 的目的地,也就是通信的对象。
IP 本身不负责包的传输,而是委托各种通信技术将包传输到下一个路由器,这样的设计是有重要意义的:
- 即,可以根据需要灵活运用各种通信技术
- 这也是IP 的最大特点。正是有了这一特点,我们才能够构建出互联网这一规模巨大的网络。
服务器的保安
《网络连接》ch5
数据包真正被服务器接受前,还需要通过服务器前面的防火墙、缓存服务器、负载均衡器等。这些类似于 保安/海关,帮助过滤一些 服务器不想接受的 数据包。
防火墙
如果不管什么网络包都能达到 服务器的话,那服务器也忒不安全了
所以需要在服务器前面加一层 防火墙(类似 海关),屏蔽一些不允许通过的包,降低服务器的风险
可以在防火墙中设置各种规则,当包到达防火墙时,会根据这些规则判断是允许通过还是阻止通过:
- 如果判断结果为阻止,那么这个包会被丢弃并被记录下来(用于分析,更好地防止非法入侵)
- 如果包被判断为允许通过,则该包会被转发出去,这个转发的过程和路由器是相同的
包过滤方式的防火墙可根据接收方IP 地址、发送方IP 地址、接收方端口号、发送方端口号、控制位等信息来判断是否允许某个包通过。
负载均衡
当服务器的访问量上升时,仅仅一台服务器的性能是跟不上的。
此时,可以考虑使用多台服务器来分担负载
要采用这样的方法,必须有一个机制将客户端发送的请求分配到每台服务器上(即,实现负载均衡)
常见的 实现负载均衡的方法:
- 轮询
- DNS 服务器中填写域名对应多个 IP 地址,通过 DNS 服务器按次序返回不同的 IP 地址(挨次用,转一圈又回到第一个 IP 地址)
- 缺点:
- case1:如果其中一台服务器 宕机了,但是 轮询 的方式不会跳过该台服务器,DNS 服务器仍然会返回这台 宕机的 服务器 IP 地址
- case2:当页面操作需要 跨网页时,轮询不可用(例如在购物网站中,可能会在第一个页面中输入地址和姓名,在第二个页面中输入信用卡号,轮询 可能会将这 2 个操作分配给不同的服务器去执行,这样的执行结果肯定是不对的)
- 负载均衡器(为了解决 轮询的问题)
- 首先,要用负载均衡器的 IP 地址代替 Web 服务器的实际地址注册到 DNS 服务器上
- 然后,由负载均衡器来判断将请求转发给哪台 Web 服务器
如何利用 负载均衡器 解决 轮询中 “跨页面” 的不足?🔥
- 如果操作
没有跨多个页面
,则可以根据 Web 服务器的负载状况来进行判断; - 当操作
跨多个页面时
,则不考虑 Web 服务器的负载,而是必须将请求发送到同一台 Web 服务器上
缓存服务器
除了使用多台功能相同的Web 服务器分担负载之外,还有另外一种方法,就是将整个系统按功能分成不同的服务器,如 Web 服务器、数据库服务器。缓存服务器就是一种按功能来分担负载的方法。
- 缓存服务器是一台通过代理机制对数据进行缓存的服务器。
- 代理介于 Web 服务器和客户端之间,具有对 Web 服务器访问进行中转的功能
- 当进行中转时,它可以将 Web 服务器返回的数据保存在磁盘中,并可以代替 Web 服务器将磁盘中的数据返回给客户端。这种保存的数据称为缓存
- Note:缓存 数据是有 有效期的,不是永久可用(如果 Web 服务器更新了数据,则之前的 缓存 失效)
如何找到最近的缓存服务器
互联网中有很多缓存服务器,如何才能从这些服务器中找到离客户端最近的一个,并让客户端去访问那台服务器呢?
参考 《网络是怎样连接的》p282
服务器程序的结构
《网络连接》ch6
上一章,我们探索了Web 服务器前面的防火墙、缓存服务器、负载均衡器等设备,现在网络包已经通过这些设备,到达了Web 服务器中
-
服务器需要同时和多个客户端通信
-
具体做法为:每有一个客户端连接进来,就启动一个新的服务器程序,确保服务器程序和客户端是一对一的状态
服务器程序分成两个模块:
- 等待连接模块:创建套接字,等待客户端连接;一旦收到客户端连接,便将刚刚创建完成的套接字移交给 客户端通信模块(b)
- 负责与客户端通信的模块:每次有新的客户端发起连接,都会启动一个新的客户端通信模块(b)【保证 b 与客户端是一对一的关系
客户端 vs. 服务器:发起连接的一方是客户端,等待连接的一方是服务器
客户端的数据收发:
- 创建套接字(创建套接字阶段)
- 用管道连接服务器端的套接字(连接阶段)
- 收发数据(收发阶段)
- 断开管道并删除套接字(断开阶段)
服务器收发数据:(将阶段(2)改成了等待连接)
-
创建套接字(创建套接字阶段)
-
等待连接阶段
-
(2-1)将套接字设置为等待连接状态(等待连接阶段)
-
(2-2)接受连接(接受连接阶段)
-
-
收发数据(收发阶段)
-
断开管道并删除套接字(断开阶段)
服务器的接收操作
网卡将接收到的 电/光信号 转换成 数字信息
-
接收操作的第一步是网卡接收到信号,然后将其还原成数字信息;
-
接下来,需要根据包末尾的帧校验序列(FCS)来校验错误
-
如果计算出来的 FCS 和 帧末尾的 FCS 不一致(帧传输中发生错误),则丢弃之;
-
如果二者一致,需要检查 MAC 头部中的接收方 MAC 地址,看看这个包是不是发给自己的
- 目的 MAC 地址和主机 MAC 地址不一致,则丢之(不是给自己的包);
- 目的 MAC 地址和主机 MAC 地址不一致,则接受(到这里,接收信号并还原成数字信息的操作就完成了,接下来 第3步)
-
-
还原后的数字信息被保存在网卡内部的缓冲区中
上面这些操作都是由网卡的 MAC 模块来完成的。
小结:网卡的 MAC 模块将网络包从信号还原为数字信息,校验 FCS并存入缓冲区。
-
网卡发送中断,告知 CPU 网络包已经到达
因为 CPU 上是 多进程(图像)在跑的,即 服务器的CPU 并不是一直在监控网络包的到达,而是在执行其他的任务
所以,当经过以上 1-3 步,网络包到达后,需要网卡发送 中断 给 CPU 告知它网络包来了,要放下手中的活,来处理网络包
-
网卡驱动会根据 MAC 头部判断协议类型,并将包交给相应的协议栈
经过 网卡中断后,CPU 就会暂停当前的工作,并切换到网卡的任务。
然后,网卡驱动会开始运行,从网卡缓冲区中将接收到的包读取出来,根据 MAC 头部的以太类型字段判断协议的种类,并调用负责处理该协议的软件。
这里,以太类型的值应该是表示 IP 协议,因此会调用 TCP/IP 协议栈,并将包转交给它
IP 模块 开始工作
- IP 模块开始工作
协议栈的 IP 模块会检查 IP 头部:
-
(1) 判断是不是发给自己的;【
目的 IP 地址
】 -
(2) 判断网络包
是否经过分片
;- 如果是分片的包,则将包暂时存放在内存中,等所有分片全部到达之后将分片组装起来还原成原始包;
- 如果没有分片,则直接保留接收时的样子,不需要进行重组
-
(3) 将包转交给 TCP 模块或 UDP 模块。
检查 IP 头部的协议号字段,并将包转交给相应的模块。
例如,如果协议号为 06(十六进制),则将包转交给 TCP 模块;
如果是 11(十六进制),则转交给 UDP 模块。
这里我们假设这个包接下来被交给 **TCP 模块 **处理
TCP 模块
TCP 模块
前面的步骤对于任何包都是一样的,但是 TCP模块 的网络包有所不同(主要分为
连接包
、数据传输包
、断开连接的包
)
TCP 模块如何处理连接包
当 TCP 头部中的控制位
SYN 为 1
时,表示这是一个发起连接的包
如果收到的是发起连接的包,则 TCP 模块会:⭐️
-
(1) 确认 TCP 头部的控制位
SYN
;SYN 为 1
时,表示这是一个发起连接的包 -
(2) 检查接收方端口号;
-
如果(服务器上)端口号(接受方端口)没有等待连接的套接字,则向客户端返回错误通知的包(即,
ICMP 报文
); -
如果存在等待连接的套接字,则为这个套接字复制一个新的副本,并将
发送方 IP 地址、端口号、序号初始值、窗口大小
等必要的参数写入这个套接字中,同时分配用于发送缓冲区和接收缓冲区的内存空间。- 然后,服务器生成
ACK 号
,用于从服务器向客户端发送数据的序号初始值,表示接收缓冲区剩余容量的窗口大小,并用这些信息生成 TCP 头部,委托 IP 模块发送给客户端 - 这个包到达客户端之后,客户端会返回表示
接收确认的 ACK 号
,当这个ACK 号返回服务器后,连接操作就完成了。
以上其实就是
TCP 三次握手
- 然后,服务器生成
-
-
(3) 为相应的等待连接套接字复制一个新的副本;
-
(4) 记录发送方 IP 地址和端口号等信息
TCP 模块如何处理数据包
经过
TCP 三报文握手
建立连接后,开始进入 数据收发阶段。
-
首先,TCP 模块会根据收到的包的
发送方 IP 地址
、发送方端口号
、接收方 IP 地址
、接收方端口号
找到相对应的套接字;- 在服务器端,可能有多个已连接的套接字对应同一个端口号,因此仅根据接收方端口号无法找到特定的套接字
- 所以,需要找到上述 4 种信息全部匹配的套接字
-
然后,将数据块拼合起来并保存在接收缓冲区中;
- 将数据块放入 接受缓冲区之前,TCP 模块会对比该套接字中保存的数据收发状态和收到的包的 TCP 头部中的信息是否匹配,以确定数据收发操作是否正常。
- 具体来说,就是根据套接字中保存的上一个序号和数据长度计算下一个序号,并检查与收到的包的 TCP 头部中的序号是否一
致。如果两者一致,就说明包正常到达了服务器,没有丢失。这时,TCP 模块会从包中提出数据,并存放到接收缓冲区中
-
最后,向客户端 返回 ACK 号
- 当收到的数据进入接收缓冲区后,TCP 模块就会生成确认应答的 TCP 头部,并根据接收包的序号和数据长度计算出 ACK 号,然后委托 IP 模块发送给客户端
TCP 模块的断开操作
当数据收发完成后,便进入
TCP 四报文挥手
释放连接
- 在 TCP 协议的规则中,断开操作可以由客户端或服务器任何一方发起,具体的顺序是由应用层协议决定的
- 在
HTTP1.0
中,是服务器先发起断开操作; HTTP1.1
中,是客户端先发起断开操作
- 在
Web 服务器程序解释请求消息并作出响应
将请求的 URI 转换为实际的文件名
这里以 Web 服务器为例,其它类型的服务器 大同小异
-
调用
reda
读取数据包;Web 服务器中,图 6.7 的
read
获取的数据内容就是 HTTP 请求消息; -
然后,Web 服务器 进行相应的处理,并生成响应消息,再通过
write
返回给客户端请求消息包括
“方法”
和URI
这里以
GET 方法
为例,GET 是读取 服务器上的文件并作为响应消息返回。所以,需要找到
URI
在服务器上对应的文件。由于为了提高服务器的安全性,Web 服务器公开的目录其实并不是磁盘上的实际目录,而是虚拟目录,而 URI 中写的就是在这个虚拟目录结构下的路径名(服务器会配置说明 根目录
/
表示服务器上的什么 路径)所以,需要根据 虚拟目录
/
+URI
找到其在服务器上对应的 文件,并作为响应消息返回给客户端当 URI 末尾为
/
时,表示使用了 默认文件(一般是index.html
)浏览器接收响应消息并显示内容
Web 服务器发送的响应消息会被分成多个包发送给客户端,然后客户端需要接收数据
- 首先,网卡将信号还原成数字信息,协议栈将拆分的网络包组装起来并取出响应消息;
- 然后,将消息转交给浏览器(这个过程和服务器的接收操作相同)
接下来,我们来看一看浏览器是如何显示内容的
浏览器是如何显示内容的:
-
首先,需要判断响应消息中的数据属于哪种类型
- 根据响应消息开头的
Content-Type
便可以判断 响应消息的类型; - 比如,
Content-Type: text/html
( / 左边是 “大类型”,/ 右边是 ”小类型“)
此外,当数据类型为文本时,还需要根据 响应消息头部中
charset
判断编码方式 - 根据响应消息开头的
-
接下来,只要根据数据类型调用用于显示内容的程序,将数据显示出来就可以了
- 对于 HTML文档、纯文本、图片这些基本数据类型,浏览器自身具有显示这些内容的功能,因此由浏览器自身负责显示;
- HTML文档:浏览器 解释 HTML标签,并显示即可
- 当 网页中有 图片等数据时,会根据 其中对应的 标签信息,再次发起 HTTP 请求,接受响应消息后,再在 浏览器显示
- 对于 HTML文档、纯文本、图片这些基本数据类型,浏览器自身具有显示这些内容的功能,因此由浏览器自身负责显示;