IP, 以太网, ARP, DNS
IP协议
回顾IP地址
IP地址, 主要就是描述了一个设备在网络上的地址. 目前常用的 IPv4 版本的 IP 地址, 都是采用 32 位的二进制数来表示的, 并且采用了点分十进制来使其更好阅读.
而这个 IP 地址, 实际上就是被网络层中的一个重要协议, IP协议来进行维护的, 那么什么是 IP 协议, 它做了什么工作呢? 我们接下来来看
报文格式
我们先来看一下IP协议的数据报文格式, 它在传输层的数据报基础上又进行了一层打包, 如下图
接下来依次来看里面的各个内容
- 版本: 指的是IP协议的版本, 一般只有 IPv4 和 IPv6, 其他版本并不是没有, 但是并不常用
- 首部长度: 和TCP的4位首部长度一样, 是用于控制下面的选项的, 也就是说IP协议的报头也是可变长的. 这里的单位是 4 字节 , 同时需要注意, 这个首部长度包括报头, 并不是说只描述选项, 也就是说这个报头最长就是
4 * 15 = 60
字节
- 服务类型(TOS, Type Of Service): 可以切换 IP 协议的服务类型, 但是其中有三位已经废弃, 还有一位是保留的, 所以实际上只有四种服务类型. 分别是: 最小延时, 最大吞吐量, 最高可靠性, 最小成本. 这四个位是冲突的, 假如有一个位是 1, 其他则必须是 0. 但是实际上这个类型的影响并不是很大, 了解即可
- 总长度: 用来描述报文总长度, 虽然很小, 和 UDP 一样只有 64kb, 但是 IP 协议自身支持"拆包组包"的功能
下面的三个字段就是用来支持拆包和组包操作的
- 标识: 由同一个大包拆分出来的小包的标识都是相同的, 用于标识这个包是谁拆出来的
- 标志: 有一位用来表示这个包是否能拆包, 还有一位用来表示这个包是不是拆出的一堆包中的最后一个包
- 片偏移: 用于描述拆除的各个小包的相对位置
拆包的时候, 我们就可以设置这些字段, 然后组包的时候我们就可以按照如下的要求进行组合:
- 将标识一致的包进行组合
- 根据片偏移去排列顺序
- 通过标志来判断这个数据报是否组合完成
这个拆包组包的思想, 实际上是可以用来借鉴的, 例如UDP的拆包组包的实现
接下来继续看后续的字段
- 生存时间(TTL, Time To Live): 这个字段用来描述这个数据报在网络中的存活时间, 但是这个并不是以时间为单位, 而是以经过的节点为单位. 当数据报被创建的时候, TTL 会被设定为一个初始值(32, 64, 128…), 随后传输过程中每经过一个节点, TTL 就会减少 1. 当 TTL 用尽的时候, 如果这个数据包还没有到达目的地, 那么这个数据报就会自动销毁
这个机制, 主要就是为了防止 IP 地址是不存在的, 导致这个数据一直在传个没停, 占用网络带宽.
但是此时可能有人就有问题: 有没有可能我的 TTL 不够用呢?
确实有这个可能, 但是可能性很低. 此时就不得不说到一个社会科学中的理论, 六度空间理论. 简单地说就是: 世界上任意两个人之间的距离不会超过六个人. 网络中的各个节点之间的联系也是如此, 所以实际上即使 TTL 是32, 那么也是绰绰有余的, 但是即便真的不够, TTL 也提供了更大的数据作为选择.
- 协议: 协议用来说明数据报的载荷部分是一个 TCP 数据报还是 UDP 数据报, 也就是说明传输层的协议是哪一个协议
- 校验和: 和之前的一样都是为了检测数据是否丢失的, 但是只会检测报头部分, 因为载荷部分有 TCP 和 UDP 进行检测
- IP地址: IP 地址是整个 IP 协议中最重要的部分, 用于描述设备在网络上的地址
IP 协议最为主要的, 实际上就是两个功能, 分别是:
- 地址管理: 制定一系列的规则, 来通过地址描述设备在网络中的位置
- 路由选择: 为网络通信规划路线
下面我们来依次了解一下这两个功能
功能介绍
地址管理
IP地址数量问题
IP 地址, 本质上是一个 32 位的整数, 但是为了方便人来理解, 采用了点分十进制来表示这个地址
上面也说过IP地址是用来区分各个设备的, 因此理论上来说, 这个IP地址是不应该重复的, 但是从IP地址的大小来看, 我们也知道, IP 地址只能表示 2 的 32 次方个数字. 实际上在当初设计这个IP地址的时候, 这个数字是绰绰有余的, 但是随着互联网的高速发展, 如今要使用网络的设备, 早就超过了这个数字.
那么要如何解决这个 IP 地址不够用的问题呢? 实际上有两个方案
- 动态分配 IP
- NAT(Network Address Translation)机制
动态分配 IP 实际上比较容易理解, 就是让那些暂时用不着网络的设备先不要占着 IP, 后面要用了再给它进行分配. 这个方案很明显, 虽然提高了利用率, 但是并没有实际意义上的增加 IP 地址的数量, 因此治标不治本.
此时就又有了第二个方案, NAT 机制.
初识 NAT 机制
NAT 机制本质上就是让一个 IP 地址表示一批设备, (就好比送快递, 很多人在同一个小区里面, 地址都会填小区地址, 而不是具体的门牌号)
首先它将将网络 IP 分为了两个大类, 分为内网 IP 和外网 IP.
内网 IP, 换句话说就是局域网 IP, 这种 IP 通常是以10.*
, 172.16.* ~ 172.31.*
或者 192.168.*
为格式的. 而外网IP同理, 也就是广域网IP, 只要不是局域网IP的IP地址就是广域网IP.
对于这两种 IP, 要求是局域网内部的内网IP不能重复, 而不同局域网之间可以出现同样的IP(和文件夹类似), 而外网IP则不允许重复, 必须唯一
这种情况下, 很多个设备组成的局域网通过路由器共用同一个外网IP, 此时就大大的提高了IP地址的利用率
通信机制
那么在这种机制下, 我们的设备是如何和外面的服务器进行通信的呢?
在通信过程中, 假设我们的设备先发送请求, 此时我们设备发出的数据报, 会先经过路由器, 然后路由器再将里面的内网IP替换为外网IP, 同时记录下这个映射关系, 为了等会接受请求做准备
服务器返回响应的时候, 路由器就会通过刚刚映射关系, 来确认这个响应要返回给谁了
此时可能有人要问了: 假如我有多个设备同时给同一个服务器发送请求呢? 此时目标服务器 IP 不久重复了? 你怎么辨别呢?
此时不要忘记了, 我们还有一个重要的信息就是端口号. 此时我们就可以用到端口号的信息, 来进一步进行区分(就好像送快递, 快递小哥通过地址送到了快递站, 然后就通过电话号码来区分哪个包裹是谁的)
那可能还有人问了: 那如果我的源端口号都一样怎么办?
实际上路由器在发送数据前, 不仅会更换 IP 地址, 同时也是会将这个端口, 换为路由器的公网端口的, 那么自然它这里就不会让它直接重掉, 而是会建立一个端口的映射关系
实际上, 这个添加了端口后的操作, 也可以叫其为 NAPT 协议, 全称 Network Address Port Translation. 从字面上也可以看出, 就是多分配了一个端口, 只不过这个协议和 NAT 实际上本质相同, 只不过有略微特殊之处, 因此放一起讲了.
IP数量的解决方案
NAT 机制还有一个很大的优势, 就是 NAT 机制是一个可以通过纯软件的方案去实现的, 替换成本十分的低. 另外这个机制也使得只有内网可以主动访问外网的设备, 而外网无法主动访问内网的设备, 一定程度上保证了设备的安全, 可以说既有劣势也有优势.
如今 IP 地址不够用的问题, 大部分都是基于 NAT + 动态分配的方式解决 IP 地址不够用的问题的.但是这个方案依旧没有本质上解决IP地址不够用的问题, 都是通过提升 IP 地址的利用率来变向解决问题.
实际上, IPv6 才是 IP 地址不够用的问题的最终解, IPv6 的解决方式也很简单, 就是延长表示长度, 将表示 IP 地址的大小从4个字节变成了16个字节, 那么 IPv6 能表示的 IP 地址会不会也不够用呢? IPv6 能表示的IP地址能有多少呢?
如果我们尝试去计算, 也就是2的128次方, 也就是IPv4能表示的地址相乘四下, IPv4是2的32次方, 大约是42亿左右, 那么2的128次方就是42亿 * 42亿 * 42亿 * 42亿
左右, 这个数据是非常惊人的, 甚至可以说是能够给世界上的每一粒沙子都分配一个 IP 地址, 因此在目前来看, 只是给电子设备来进行分配那属于是绰绰有余
但是 IPv6 实际上普及程度并不高, 这是为什么呢?
实际上 IPv6 和 NAT 诞生的时间是差不多的, 但是 IPv6 最大的劣势就在于如果要支持它则需要更换硬件设备, 成本较高. 其次是已经有 NAT 这种低成本的替代方案, 因此也就没有人乐意去升级 IPv6 了. 不过由于我国的大力推广, 实际上我们 IPv6 的普及率还是比较高的, 虽然可能很多人没用到, 但是确实是有的
网段划分
IP 对于地址的管理, 还有一个部分就是网段的划分. 网段划分就是将一段IP地址, 划分为了两部分, 分为网络号 + 主机号
.
网络号用来表示一个局域网本身, 主机号用来表示局域网中的设备
例如下面这是两个局域网
其中左边这一块局域网, 它的网络号就是192.168.0
, 里面的.1
, .10
, .11
, .12
就是对应设备的主机号, 右侧的也是同理.
网络号和主机号的分配也是有一定规定的: 1. 同一个局域网中的设备的网络号必须相同, 而主机号必须不同. 2. 相邻局域网的网络号必须不同
此时可能有人问: 什么是相邻局域网?
像图上这样的, 两个局域网之间是通过路由器连通的局域网就被称作是相邻局域网
此时可能有人问了: 那这个网络号一定是前三个数字, 主机号就一定是最后一个数字吗? 如果不是的话, 应该怎么看哪部分是哪个呢?
答案是: 不一定, 另外需要纠正一个误区就是, 我们只是以点分十进制来看这个IP地址, 实际上如何划分并不是按照点看的, 而是应该根据32位的每一个位来看. 假如我们要看哪一部分是网络号, 那么就要通过子网掩码来看.
子网掩码也是一个32位的整数, 其中左侧是1, 右侧是0 , 不会出现交叉01的情况, 其中是1的部分就是网络号
例如我的子网掩码是255.255.255.0
, 翻译过二进制来就是11111111 11111111 1111111 0000000
, 也就是前 24 位就是网络号, 那么剩下的就是主机号了.
特殊IP地址
假如有一个IP地址, 它的主机号全是0, 那么当前的这个IP就是用来表示"网络号"的, 这个"网络号"代表了整个局域网, 是不能分配给任何设备的.
假如有一个IP地址, 它的主机号全是1, 那么这个IP地址就是"广播地址", 主要是用来实现广播传输的, 这个IP地址也是不能分配给任何设备的.
假如有一个IP地址是以127
开头的, 此时的这个IP就是"环回地址", 操作系统提供了一个虚拟网卡关联到了这个IP上, 如果往这个地址发送的数据, 最终也会传回到自身. 这个IP主要就是用来进行一些测试工作, 因为这个IP的数据是能排除网络不良因素, 保证数据发送成功的, 因此可以用来检测代码中是否出现问题.
路由选择
众所周知, 网络环境是很复杂的, 那么 IP 协议是如何在如此复杂的情况下, 给数据的传输规划路线的呢?
实际上, 进行 IP 数据报转发的时候, 每一个路由器都是不知道网络的"全貌"的, 只会知道一些局部信息, 因此想要总览全局, 规划多条路线选择出最优解很明显是不现实的. 实际上, IP 数据在转发过程中, 是一个"探索式"的过程
什么意思呢? 如果举一个生活中的例子, 假设我们想要从 A 地点到达 B, 此时我们没有导航, 也没有地图, 并且我们也不认识路, 那么我们就只能通过问身边的人, 通过这种问路的方式去寻找 B. 可能刚开始问的时候, 由于离得有点远, 问的人也不知道这个 B 具体应该怎么走 ,但是它知道一个大概的方向, 它会把方向告诉我们. 然后我们再逐渐接近 B 的过程中, 就会遇到知道 B 具体位置的人, 那么此时我们就直接过去 B 那就行了.
一个网络层的数据报, 每次到达一个路由器, 也会经历上述这样的"问路"的过程, 此时路由器假如被"问路"了, 那么它就会去它内部的一个"路由表"来根据数据报中的目的 IP 进行查找. 如果找到了, 那么就依照这个路由表把数据往指定方向发送. 如果没找到, 此时路由表中有一个默认的表项, 此时就会按照这个默认项发送.
以太网协议
报文格式
在数据链路层中, 比较常见的就是以太网协议, 同时这个以太网协议也横跨到了物理层中也有使用, 其中通过网线/光纤来通信使用的协议就是以太网协议.
我们依旧是先看这个协议的报文格式, 可以看到, 这个报文相较于 IP 和 TCP 的协议还是简单很多的, 因此我们就简单了解一下就行
我们这里依旧是先看一些容易理解的部分
- 类型: 主要就是指后面存储的数据的类型, 一般有三种, 一个是 IP, 一个是 ARP, 还有一个就是 RARP. 其中 ARP 和 RARP 主要是一个辅助协议, 我们在后面进行简单介绍
- 数据: 顾名思义就是传输的数据, 其中数据链路层的这个数据载荷长度也被称作是 MTU. 此时可能有人发现这个 MTU 似乎特别的小, 才 1500 字节 ,2kb不到. 其实这也是 IP 数据报为什么要支持分包组包的原因之一, IP 数据报的分包组包, 大概率是由于这里 MTU 的上限引起的, 而不是 IP 协议本身的上限引起的.
- CRC: 这个实际上就是一个校验和, 这里采取的校验和算法叫做循环冗余算法, 它的原理使用文字描述还是比较迷惑的, 因此推荐去观看一些视频手算的过程, 这里就不介绍了, 感兴趣的可以自行去了解一下
源MAC/目的MAC
MAC地址是什么
MAC 地址, 实际上也是用来描述一个设备的地址
此时可能就会有人问了: 我这 IP 地址不是已经有用来描述设备的地址了吗? 为啥还要整一个MAC地址?
实际上这并不是故意搞两个的, 这个主要是一个历史问题. 因为刚开始设计网络层和数据链路层的时候, 就是分开独立发明的, 此时两边都想着要搞一个地址, 但是后来合并的时候呢, 也不好说就直接弃置哪一个, 因此就直接继续用着了.
并且由于MAC地址是6个字节, 能够表示的数字还是挺多的, 因此在当下 MAC 地址去给每一个设备分配一个也是足够的, 因此目前来讲每个设备都有它唯一的MAC地址, 这个地址被绑定在了网卡上面, 并且在网卡出厂的时候就是写死的, 无法改变. 因此MAC地址有时候也被称为物理地址
MAC地址格式
MAC 地址, 通常是采用十六进制去表示的, 将每一个字节进行分割, 分为六块. 其中每一块有一个字节, 8 个二进制位, 使用两个十六进制位来进行表示.
例如00-E1-4C-8D-44-E5
, 或者是00:E1:4C:8D:44:E5
. 这里它既可以采用-
分割, 也可以采用:
来进行分割.
MAC的作用
MAC 地址, 可以说是以太网数据报中最重要的信息, 其主要关注的是传输过程中的相邻节点.
举一个例子, 假如我要从北京到上海, 假设路线是: 北京 -> 河北 -> 山东 -> 江苏 -> 上海
IP的标识始终就是: 源IP: 北京 目的IP: 上海
而假设此时我们还没出发, 人还在北京, 那么 MAC 的标识就是: 源 MAC: 北京 目的 MAC: 河北, 那假如我中间到了河北了, 此时我的 MAC 就会更新, 源 MAC 就会变为河北, 目的 MAC 就会变为山东. 后续也是同理的.
ARP
ARP (Address Resolution Protocol) 协议, 是一个介于网络层和数据链路层的协议, 它主要用于实现的是建立主机 IP 地址和 MAC 地址之间的关系. 我们这里简单了解一下
在数据链路层打包前, 由于一般情况下 MAC 地址并不会说和 IP 地址一样直接暴露出去, 此时打包 MAC 地址的时候, 可能不知道下一个节点的设备的 MAC 地址是什么.
那么此时在打包前, 数据的发送端就会去查看自己的 ARP 缓存表, 看看有没有下一级 IP 地址对应的 MAC 地址. 但此时如果找不到, 那么就会给广播地址FF:FF:FF:FF:FF:FF
发送一个信息, 询问这个 IP 设备它的 MAC 是什么. 随后对应的设备收到这个广播后, 就会把对应的 MAC 发过来. 此时发送这个广播的设备拿到了, 就会将这个 IP 和 MAC 的对应关系存储在 ARP 缓存里面.
这个寻址的过程, 也被称作是 ARP 寻址, 广播的过程, 就被称作为 ARP 广播.
同时, 这个机制在交换机中也存在, 假如有两个设备, 连在了一个交换机上面, 此时它们在进行网络交互的过程中, 就会去通过一个叫做网口的东西. 例如下面这个交换机里面的这些插口, 就是网口
那么此时你的传过去的数据, 首先就会从其中的某一个口进去, 然后需要根据接下来目的地的MAC地址找到对应的口, 因为一个口就相当于插着一个设备, 对应着一个 MAC 地址.
那它怎么知道哪一个口对应着哪一个 MAC 地址? 实际上就是需要靠 ARP 协议, 去在内部维护一张映射表. 同时, 如果没有找到对应的 MAC 地址, 此时就需要通过广播去找.
实际上这里有一个类似的协议叫做 RARP (Reverse Address Resolution Protocol), 它多的一个 R 就是 reverse, 相反的. 那么它实际上就是把映射关系反过来了, 将 MAC 地址对应 IP 地址去了, 不过这个协议不常用, 我们这里就不介绍了
DNS
初识DNS
DNS, 全称 Domain Name System, 又称域名解析系统, 它是用来将IP转换成域名的一套系统. 那我们为什么需要这个系统呢? 其实就是为了方便传播.
IP地址即使在点分十进制的优化下, 可读性有所提高, 但是如果是非专业的人士, 是压根看不懂这一串数字是在表达什么意思的, 因此在传播上, IP地址天然就具有劣势, 因为具有一定的门槛
于是, 为了方便普通大众更好理解这些IP是指向哪的, 是干嘛的, 于是便有了域名, 这个域名就相当于是给IP取了一个外号, 域名可以使用一些带有实际含义的文本来代表IP地址, 方便人们理解
为了能够使用域名访问网站, 就需要一个将域名和IP通过映射关系进行自动转换的系统, 也就是域名解析系统.
但是早期的域名解析系统, 并没有非常的复杂, 而是通过一个本地的文件来实现的, 也就是hosts
文件, 如今这个机制已经不再使用了, 因此我们现在找到这个文件则大概率是空的
那么为什么不再使用了呢? 实际上是因为使用这个本地文件来维护域名和 IP 的映射关系实在是太麻烦了. 因为互联网的发展速度很快, 可能没一会就多了一两个网站, 就有了新的域名, 就需要建立新的映射关系.
因此就有人搭建了一套 DNS 系统, 把这个 IP 和域名的映射关系保存在服务器中. 随后假如有人想要访问某个域名, 就要先给这个 DNS 服务器发起请求, 通过映射关系查询 IP, 然后访问对应的网站.
后续如果有域名需要更新, 则直接在对应的服务器更新即可, 不需要修改每一个用户电脑中的hosts
文件
此时可能就会有人问了: 全世界这么多设备, 如果访问网站全都要访问这个DNS服务器, 这服务器撑得住吗? 撑不住那怎么办呢?
要说撑不撑的住, 那刚开始还没有这么多设备的时候, 肯定还是可以接受的. 但是随着时代的发展, 人们用到的设备越来越多, 也会逐渐的顶不住. 因此为了解决这种"高并发"问题, 有两种思路: 1. 开源 2. 节流
- 开源: 设计 DNS 的人们就开始召集各个网络运营商, 让他们可以根据这个根 DNS 服务器, 建立镜像服务器. 然后人们正常生活中的请求都会发送到离自己最近的镜像服务器那里, 同时镜像服务器也会隔一段时间和根 DNS 服务器进行同步, 保证域名对的上
- 节流: 让请求量变少, 当一个用户访问 DNS 服务器后, 会暂时存一个本地的缓存, 如果连续的访问同一个域名, 那么就直接从缓存里面拿, 而不是每一次访问都要向 DNS 服务器申请一次
DNS 系统也是由美国那边掌控的, 包括所谓的根DNS服务器, 也就是说, 如果美国不想让我们访问 DNS 根服务器, 其实也是完全做的到的. 而面对这个问题唯一的解决方案就是, 发展 IPv6, 因为 IPv6 的 DNS 系统和 IPv4 的截然不同, 这也是为什么中国大力发展 IPv6 的原因之一.
DNS主要功能
DNS 的一个主要功能实际上我们在上面也提到了, 就是根据域名去解析 IP 地址. 但实际上他还有一些其他功能, 例如负载均衡
什么是负载均衡? 其实和我们上面说的节流有一点点像, 它主要就是将请求去分离到不同的服务器上, 从而降低服务器运行的压力.
那么此时假如有一个网站, 他有很多的服务器来提供服务, 此时 DNS 就可以根据情况去将这个域名解析到不同的服务器上, 达到一种虽然访问的好像是同一个域名, 但是实际上并不是同一个服务器这样的效果, 实现负载均衡.
DNS的查询过程
其实要细致的来说, DNS的查询过程还是比较繁琐的, 但是其实本质上就是一直不断地向一级一级的域名服务器请求的过程, 因此我们这里就以比较简单的方式直接描述一下
当计算机访问一个域名的时候, 首先计算机会先查看一下本地的缓存, 看看有没有这个域名, 有的话就直接返回, 如果没有那么就会开始向上级的域名服务器开始请求解析.
域名会进行一段一段的解析, 例如有一个example.com
, 那么此时它首先会去.com
负责的域名服务器. 先找example.com
的对应 IP, 如果没找到, 那么就找example.com
负责的域名服务器, 然后再到这个域名服务器找example.com
对应的 IP 地址.
那如果在这个逐个解析的过程中, 最后还是没有找到, 那么此时就会去向根域名服务器去寻找, 如果还是没有找到, 那么就返回域名不存在.