【杂谈】网络协议(二)

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

前言

文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…

每篇一句

星辰陨落只为铸就挥剑一瞬,日月无光只因执剑者的刹那回眸。

絮叨

今天我们继续加油呀。

网络为什么要分层?

这里我们先探讨第一个问题,网络为什么要分层?因为,是个复杂的程序都要分层。

理解计算机网络中的概念,一个很好的角度是,想象网络包就是一段 Buffer,或者一块内存,是有格式
的。同时,想象自己是一个处理网络包的程序,而且这个程序可以跑在电脑上,可以跑在服务器上,可
以跑在交换机上,也可以跑在路由器上。你想象自己有很多的网口,从某个口拿进一个网络包来,用自
己的程序处理一下,再从另一个网口发送出去。
当然网络包的格式很复杂,这个程序也很复杂。复杂的程序都要分层,这是程序设计的要求。比如,复
杂的电商还会分数据库层、缓存层、Compose 层、Controller 层和接入层,每一层专注做本层的事
情。

程序是如何工作的?

我们可以简单地想象“你”这个程序的工作过程

通过上图,我们再来看一下原来困惑的问题。

首先是分层的比喻。所有不能表示出层层封装含义的比喻,都是不恰当的。总经理握手,不需要员工在

吧,总经理之间谈什么,不需要员工参与吧,但是网络世界不是这样的。正确的应该是,总经理之间沟
通的时候,经理将总经理放在自己兜里,然后组长把经理放自己兜里,员工把组长放自己兜里,像套娃
娃一样。那员工直接沟通,不带上总经理,就不恰当了。

现实生活中,往往是员工说一句,组长补充两句,然后经理补充两句,最后总经理再补充两句。但是在

网络世界,应该是总经理说话,经理补充两句,组长补充两句,员工再补充两句。

那 TCP 在三次握手的时候,IP 层和 MAC 层在做什么呢?当然是 TCP 发送每一个消息,都会带着 IP 层

和 MAC 层了。因为,TCP 每发送一个消息,IP 层和 MAC 层的所有机制都要运行一遍。而你只看到
TCP 三次握手了,其实,IP 层和 MAC 层为此也忙活好久了。

这里要记住一点:只要是在网络上跑的包,都是完整的。可以有下层没上层,绝对不可能有上层没下

层。

所以,对 TCP 协议来说,三次握手也好,重试也好,只要想发出去包,就要有 IP 层和 MAC 层,不然

是发不出去的。

经常有人会问这样一个问题,我都知道那台机器的 IP 地址了,直接发给他消息呗,要 MAC 地址干啥?

这里的关键就是,没有 MAC 地址消息是发不出去的

所以如果一个 HTTP 协议的包跑在网络上,它一定是完整的。无论这个包经过哪些设备,它都是完整

的。

所谓的二层设备、三层设备,都是这些设备上跑的程序不同而已。一个 HTTP 协议的包经过一个二层设

备,二层设备收进去的是整个网络包。这里面 HTTP、TCP、 IP、 MAC 都有。什么叫二层设备呀,就
是只把 MAC 头摘下来,看看到底是丢弃、转发,还是自己留着。那什么叫三层设备呢?就是把 MAC
头摘下来之后,再把 IP 头摘下来,看看到底是丢弃、转发,还是自己留着。

理解网络协议的工作模式,有两个小窍门:

  • 始终想象自己是一个处理网络包的程序:如何拿到网络包,如何根据规则进行处理,如何发出去;
  • 始终牢记一个原则:只要是在网络上跑的包,都是完整的。可以有下层没上层,绝对不可能有上层没

下层。

IP

即便没有专业学过计算机的人,只要倒腾过电脑,重装过系统,大多也会知道这个问题的答案:
在 Windows 上是 ipconfig,在 Linux 上是 ifconfig。
那你知道在 Linux 上还有什么其他命令可以查看 IP 地址吗?答案是 ip addr。如果回答不上来这个问
题,那你可能没怎么用过 Linux

我们来运行一下 ip addr

root@test:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
 inet 127.0.0.1/8 scope host lo
 valid_lft forever preferred_lft forever
 inet6 ::1/128 scope host 
 valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
 link/ether fa:16:3e:c7:79:75 brd ff:ff:ff:ff:ff:ff
 inet 10.100.122.2/24 brd 10.100.122.255 scope global eth0
 valid_lft forever preferred_lft forever
 inet6 fe80::f816:3eff:fec7:7975/64 scope link 
 valid_lft forever preferred_lft forever
复制代码

这个命令显示了这台机器上所有的网卡。大部分的网卡都会有一个 IP 地址,当然,这不是必须的。在后
面的分享中,我们会遇到没有 IP 地址的情况。

IP 地址是一个网卡在网络世界的通讯地址,相当于我们现实世界的门牌号码。既然是门牌号码,不能大
家都一样,不然就会起冲突。比方说,假如大家都叫六单元 1001 号,那快递就找不到地方了。所以,
有时候咱们的电脑弹出网络地址冲突,出现上不去网的情况,多半是 IP 地址冲突了。

MAC 地址

在 IP 地址的上一行是 link/ether fa:16:3e:c7:79:75 brd ff:ff:ff:ff:ff:ff,这个被称为MAC 地址,是一个
网卡的物理地址,用十六进制,6 个 byte 表示。

MAC 地址是一个很容易让人“误解”的地址。因为 MAC 地址号称全局唯一,不会有两个网卡有相同的
MAC 地址,而且网卡自生产出来,就带着这个地址。很多人看到这里就会想,既然这样,整个互联网的
通信,全部用 MAC 地址好了,只要知道了对方的 MAC 地址,就可以把信息传过去。

这样当然是不行的。一个网络包要从一个地方传到另一个地方,除了要有确定的地址,还需要有定位功
能。而有门牌号码属性的 IP 地址,才是有远程定位功能的。

例如,你去杭州市网商路 599 号 B 楼 6 层找刘超,你在路上问路,可能被问的人不知道 B 楼是哪个,
但是可以给你指网商路怎么去。但是如果你问一个人,你知道这个身份证号的人在哪里吗?可想而知,
没有人知道。

MAC 地址更像是身份证,是一个唯一的标识。它的唯一性设计是为了组网的时候,不同的网卡放在一个
网络里面的时候,可以不用担心冲突。从硬件角度,保证不同的网卡有不同的标识

MAC 地址是有一定定位功能的,只不过范围非常有限。你可以根据 IP 地址,找到杭州市网商路 599 号
B 楼 6 层,但是依然找不到我,你就可以靠吼了,大声喊身份证 XXXX 的是哪位?我听到了,我就会站
起来说,是我啊。但是如果你在上海,到处喊身份证 XXXX 的是哪位,我不在现场,当然不会回答,因
为我在杭州不在上海

所以,MAC 地址的通信范围比较小,局限在一个子网里面。例如,从 192.168.0.2/24 访问
192.168.0.3/24 是可以用 MAC 地址的。一旦跨子网,即从 192.168.0.2/24 到 192.168.1.2/24,MAC
地址就不行了,需要 IP 地址起作用了。

IP 是地址,有定位功能;MAC 是身份证,无定位功能;

动态主机配置协议(DHCP)

大家想下,我们怎么的Ip是怎么来的

使用 net-tools:

 sudo ifconfig eth1 10.0.0.1/24 
 sudo ifconfig eth1 up
复制代码

你可能会问了,自己配置这个自由度太大了吧,我是不是配置什么都可以?如果配置一个和谁都不搭边
的地址呢?例如,旁边的机器都是 192.168.1.x,我非得配置一个 16.158.23.6,会出现什么现象呢?
不会出现任何现象,就是包发不出去呗。为什么发不出去呢?我来举例说明。

192.168.1.6 就在你这台机器的旁边,甚至是在同一个交换机上,而你把机器的地址设为了
16.158.23.6。在这台机器上,你企图去 ping192.168.1.6,你觉得只要将包发出去,同一个交换机的另
一台机器马上就能收到,对不对?
可是 Linux 系统不是这样的,它没你想得那么智能。你用肉眼看到那台机器就在旁边,它则需要根据自
己的逻辑进行处理。

还记得我们在第二节说过的原则吗?只要是在网络上跑的包,都是完整的,可以有下层没上层,绝对不
可能有上层没下层。

所以,你看着它有自己的源 IP 地址 16.158.23.6,也有目标 IP 地址 192.168.1.6,但是包发不出去,这
是因为 MAC 层还没填。

自己的 MAC 地址自己知道,这个容易。但是目标 MAC 填什么呢?是不是填 192.168.1.6 这台机器的
MAC 地址呢?

当然不是。Linux 首先会判断,要去的这个地址和我是一个网段的吗,或者和我的一个网卡是同一网段
的吗?只有是一个网段的,它才会发送 ARP 请求,获取 MAC 地址。如果发现不是呢?

Linux 默认的逻辑是,如果这是一个跨网段的调用,它便不会直接将包发送到网络上,而是企图将包发
送到网关。
如果你配置了网关的话,Linux 会获取网关的 MAC 地址,然后将包发出去。对于 192.168.1.6 这台机器
来讲,虽然路过它家门的这个包,目标 IP 是它,但是无奈 MAC 地址不是它的,所以它的网卡是不会把
包收进去的。

如果将网关配置为 192.168.1.6 呢?不可能,Linux 不会让你配置成功的,因为网关要和当前的网络至
少一个网卡是同一个网段的,怎么可能 16.158.23.6 的网关是 192.168.1.6 呢?

所以,当你需要手动配置一台机器的网络 IP 时,一定要好好问问你的网络管理员。如果在机房里面,要
去网络管理员那里申请,让他给你分配一段正确的 IP 地址。当然,真正配置的时候,一定不是直接用命
令配置的,而是放在一个配置文件里面。不同系统的配置文件格式不同,但是无非就是 CIDR、子网掩
码、广播地址和网关地址。

原来配置 IP 有这么多门道儿啊。你可能会问了,配置了 IP 之后一般不能变的,配置一个服务端的机器
还可以,但是如果是客户端的机器呢?我抱着一台笔记本电脑在公司里走来走去,或者白天来晚上走,
每次使用都要配置 IP 地址,那可怎么办?还有人事、行政等非技术人员,如果公司所有的电脑都需要 IT
人员配置,肯定忙不过来啊。

IP 地址的收回和续租

既然是租房子,就是有租期的。租期到了,管理员就要将 IP 收回。
如果不用的话,收回就收回了。就像你租房子一样,如果还要续租的话,不能到了时间再续租,而是要
提前一段时间给房东说。DHCP 也是这样。
客户机会在租期过去 50% 的时候,直接向为其提供 IP 地址的 DHCP Server 发送 DHCP request 消息
包。客户机接收到该服务器回应的 DHCP ACK 消息包,会根据包中所提供的新的租期以及其他已经更新
的 TCP/IP 参数,更新自己的配置。这样,IP 租用更新就完成了

ICMP与ping

ping 是基于 ICMP 协议工作的。ICMP全称Internet Control Message Protocol,就是互联网控制报文
协议。这里面的关键词是“控制”,那具体是怎么控制的呢?

接下来,我们重点来看 ping 的发送和接收过程。

假定主机 A 的 IP 地址是 192.168.1.1,主机 B 的 IP 地址是 192.168.1.2,它们都在同一个子网。那当
你在主机 A 上运行“ping 192.168.1.2”后,会发生什么呢?

ping 命令执行的时候,源主机首先会构建一个 ICMP 请求数据包,ICMP 数据包内包含多个字段。最重
要的是两个,第一个是类型字段,对于请求数据包而言该字段为 8;另外一个是顺序号,主要用于区分
连续 ping 的时候发出的多个数据包。每发出一个请求数据包,顺序号会自动加 1。为了能够计算往返时
间 RTT,它会在报文的数据部分插入发送时间

然后,由 ICMP 协议将这个数据包连同地址 192.168.1.2 一起交给 IP 层。IP 层将以 192.168.1.2 作为
目的地址,本机 IP 地址作为源地址,加上一些其他控制信息,构建一个 IP 数据包。

接下来,需要加入 MAC 头。如果在本节 ARP 映射表中查找出 IP 地址 192.168.1.2 所对应的 MAC 地
址,则可以直接使用;如果没有,则需要发送 ARP 协议查询 MAC 地址,获得 MAC 地址后,由数据链
路层构建一个数据帧,目的地址是 IP 层传过来的 MAC 地址,源地址则是本机的 MAC 地址;还要附加
上一些控制信息,依据以太网的介质访问规则,将它们传送出去。

主机 B 收到这个数据帧后,先检查它的目的 MAC 地址,并和本机的 MAC 地址对比,如符合,则接
收,否则就丢弃。接收后检查该数据帧,将 IP 数据包从帧中提取出来,交给本机的 IP 层。同样,IP 层
检查后,将有用的信息提取后交给 ICMP 协议。

主机 B 会构建一个 ICMP 应答包,应答数据包的类型字段为 0,顺序号为接收到的请求数据包中的顺序
号,然后再发送出去给主机 A

在规定的时候间内,源主机如果没有接到 ICMP 的应答包,则说明目标主机不可达;如果接收到了
ICMP 应答包,则说明目标主机可达。此时,源主机会检查,用当前时刻减去该数据包最初从源主机上
发出的时刻,就是 ICMP 数据包的时间延迟

动态路由算法

使用动态路由路由器,可以根据路由协议算法生成动态路由表,随网络运行状况的变化而变化。那路由
算法是什么样的呢?

我们可以想象唐僧西天取经,需要解决两大问题,一个是在每个国家如何找到正确的路,去换通关文
牒、吃饭、休息;一个是在国家之间,野外行走的时候,如何找到正确的路、水源的问题。

无论是一个国家内部,还是国家之间,我们都可以将复杂的路径,抽象为一种叫作图的数据结构。至于
唐僧西行取经,肯定想走得路越少越好,道路越短越好,因而这就转化成为如何在途中找到最短路径的
问题。
咱们在大学里面学习计算机网络与数据结构的时候,知道求最短路径常用的有两种方法,一种是
Bellman-Ford 算法,一种是 Dijkstra 算法。在计算机网络中基本也是用这两种方法计算的。

距离矢量路由算法

第一大类的算法称为距离矢量路由(distance vector routing)。它是基于 Bellman-Ford 算法的。
这种算法的基本思路是,每个路由器都保存一个路由表,包含多行,每行对应网络中的一个路由器,每
一行包含两部分信息,一个是要到目标路由器,从那条线出去,另一个是到目标路由器的距离。

由此可以看出,每个路由器都是知道全局信息的。那这个信息如何更新呢?每个路由器都知道自己和邻
居之间的距离,每过几秒,每个路由器都将自己所知的到达所有的路由器的距离告知邻居,每个路由器
也能从邻居那里得到相似的信息。

每个路由器根据新收集的信息,计算和其他路由器的距离,比如自己的一个邻居距离目标路由器的距离
是 M,而自己距离邻居是 x,则自己距离目标路由器是 x+M。

链路状态路由算法

第二大类算法是链路状态路由(link state routing),基于 Dijkstra 算法。
这种算法的基本思路是:当一个路由器启动的时候,首先是发现邻居,向邻居 say hello,邻居都回复。
然后计算和邻居的距离,发送一个 echo,要求马上返回,除以二就是距离。然后将自己和邻居之间的链
路状态包广播出去,发送到整个网络的每个路由器。这样每个路由器都能够收到它和邻居之间的关系的
信息。因而,每个路由器都能在自己本地构建一个完整的图,然后针对这个图使用 Dijkstra 算法,找到
两点之间的最短路径。

不像距离距离矢量路由协议那样,更新时发送整个路由表。链路状态路由协议只广播更新的或改变的网
络拓扑,这使得更新信息更小,节省了带宽和 CPU 利用率。而且一旦一个路由器挂了,它的邻居都会广
播这个消息,可以使得坏消息迅速收敛。

动态路由协议

OSPF(Open Shortest Path First,开放式最短路径优先)就是这样一个基于链路状态路由协议,广泛
应用在数据中心中的协议。由于主要用在数据中心内部,用于路由决策,因而称为内部网关协议
(Interior Gateway Protocol,简称IGP)。

内部网关协议的重点就是找到最短的路径。在一个组织内部,路径最短往往最优。当然有时候 OSPF 可
以发现多个最短的路径,可以在这多个路径中进行负载均衡,这常常被称为等价路由。

这一点非常重要。有了等价路由,到一个地方去可以有相同的两个路线,可以分摊流量,还可以当一条
路不通的时候,走另外一条路。这个在后面我们讲数据中心的网络的时候,一般应用的接入层会有负载
均衡 LVS。它可以和 OSPF 一起,实现高吞吐量的接入层设计。

有了内网的路由协议,在一个国家内,唐僧可以想怎么走怎么走了,两条路选一条也行。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享