网络是怎样连接的-IP与以太网的包收发操作(二)

2.5.3 生成包含接收方 IP 地址的 IP 头部

IP头部包含的内容

IP 模块接受 TCP 模块的委托负责包的收发工作,它会生成 IP 头部并附加在 TCP 头部前面。

IP 头部包含的内容如表 2.2 所示,其中最重要的内容就是 IP 地址,它表示这个包应该发到哪里去。

接受方IP地址:应用程序告知TCP,TCP告知IP模块

这个地址是由 TCP 模块告知的,而 TCP 又是在执行连接操作时从应用程序那里获得这个地址的,因此这个地址的最初来源就是应用程序。

IP 不会自行判断包的目的地,而是将包发往应用程序指定的接收方,即便应用程序指定了错误的 IP 地址,IP 模块也只能照做。

当然,这样做肯定会出错,但这个责任应该由应用程序来承担。

在连接操作中发送第一个 SYN 包时就可能发生这样的情况,一旦 TCP 连接完毕,就已经确认能够正常和对方进行包的收发,这时就不会发生这样的情况了。

发送方的IP地址来自使用的网卡对应的IP地址

IP 头部的“接收方 IP 地址”填写通信对象的 IP 地址。

发送方 IP 地址需要判断发送所使用的网卡,并填写该网卡的 IP地址。

如何判断把包交给哪块网卡

和路由器使用 IP 表判断下一个路由器位置的操作是一样的。

因为协议栈的 IP 模块与路由器中负责包收发的部分都是根据 IP 协议规则来进行包收发操作的,所以它们也都用相同的方法来判断把包发送给谁。

查看路由表

这个“IP 表”叫作路由表,我们将在第 3 章探索路由器时详细介绍它的用法,这里先简单讲个大概。

如图 2.18 所示,我们可以通过 route print命令来显示路由表。

Network Destination 网络目标列

例如TCP 模块告知的目标 IP 地址为 192.168.1.21,那么就和第 6 行的 192.168.1 的部分相匹配。

Interface 网络接口列

表示网卡等网络接口,这些网络接口可以将包发送给通信对象。

Gateway 网关列

表示下一个路由器的 IP 地址,将包发给这个 IP 地址,该地址对应的路由器就会将包转发到目标地址。

默认网关

路由表的第 1 行中,目标地址和子网掩码都是 0.0.0.0,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行。 

这样一来,我们就可以判断出应该使用哪块网卡来发送包了,然后就可以在 IP 头部的发送方 IP 地址中填上这块网卡对应的 IP 地址。

TCP/UDP协议号

接下来还需要填写协议号,它表示包的内容是来自哪个模块的。

例如,如果是 TCP 模块委托的内容,则设置为 06(十六进制),如果是 UDP 模块委托的内容,则设置为 17(十六进制),这些值都是按照规则来设置的。

在现在我们使用的浏览器中,HTTP 请求消息都是通过 TCP 来传输的,因此这里就会填写表示 TCP 的 06(十六进制)。

其他字段内也需要填写相应的值,但对大局没什么影响,我们会在第3 章进行介绍,这里就先省略了。

注释

①Gateway(网关)在 TCP/IP 的世界里就是路由器的意思。

②如果 Gateway 和 Interface 列的 IP 地址相同,就表示不需要路由器进行转发,可以直接将包发给接收方的 IP 地址。我们将在第 3 章详细介绍。

 

2.5.4 生成以太网用的 MAC 头部

MAC头的作用

生成了 IP 头部之后,接下来 IP 模块还需要在 IP 头部的前面加上MAC 头部(表 2.3)。

IP 头部中的接收方 IP 地址表示网络包的目的地,通这个地址我们就可以判断要将包发到哪里,但在以太网的世界中,TCP/IP 的这个思路是行不通的。

以太网在判断网络包目的地时和 TCP/IP 的方式不同,因此必须采用相匹配的方式才能在以太网中将包发往目的地,而MAC 头部就是干这个用的。

IP 模块在生成 IP 头部之后,会在它前面再加上 MAC 头部。

MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息。

MAC头的结构

MAC 头部的开头是接收方和发送方的 MAC 地址,和 IP 头部中的接收方和发送方 IP 地址的功能差不多,只不过 IP 地址的长度为 32 比特,而 MAC 地址为 48 比特。

IP 地址是类似多少弄多少号这种现实中地址的层次化的结构,而 MAC 地址中的 48 比特可以看作是一个整体。

从表示接收方和发送方的意义上来说,MAC 地址和 IP 地址是没有区别的。

以太类型字段和 IP 头部中的协议号类似。在 IP 中,协议号表示 IP 头部后面的包内容的类型,以太类型就表示后面内容的类型。

以太网包的内容可以是 IP、ARP等协议的包,它们都有对应的值,这也是根据规则来确定的。

以太类型

这里填写表示 IP 协议的值 0800(十六进制)。

发送方MAC地址

这里填写网卡本身的 MAC 地址。MAC 地址是在网卡生产时写入 ROM 里的,只要将这个值读取出来写入 MAC 头部就可以了。

对于多块网卡的情况,类似设置发送方 IP 地址的方法。

设置发送方 IP 地址时,我们已经判断出了从哪块网卡发送这个包,那么现在只要将这块网卡对应的 MAC 地址填进去就好了。

注意

实际上,只有在操作系统启动过程中对网卡进行初始化的时候才会读取MAC 地址,读取出来之后会存放在内存中,每次执行收发操作时实际上使用的是内存中的值。

此外,读取 MAC 地址的操作是由网卡驱动程序来完成的,因此网卡驱动程序也可以不从网卡 ROM 中读取地址,而是将配置文件中设定的 MAC 地址拿出来放到内存中并用于设定 MAC 头部,或者也可以通过命令输入 MAC 地址。

接收方 MAC 地址

只要告诉以太网对方的 MAC 的地址,以太网就会帮我们把包发送过去,那么很显然这里应该填写对方的 MAC 地址。

此时我们还没有把包发送出去,所以先得搞清楚应该把包发给谁,这个只要查一下路由表就知道了。

在路由表中找到相匹配的条目,然后把包发给 Gateway 列中的IP 地址就可以了。

既然已经知道了包应该发给谁,那么只要将对方的 MAC 地址填上去就好了,但到这里为止根本没有出现对方的 MAC 地址,也就是说我们现在根本不知道对方的 MAC 地址是什么。

因此,我们还需要执行根据 IP 地址查询 MAC 地址的操作。

2.5.5 通过 ARP 查询目标路由器的 MAC 地址

这里我们需要使用 ARP ,它其实非常简单。在以太网中,有一种叫作广播的方法,可以把包发给连接在同一以太网中的所有设备。

ARP:Address Resolution Protocol,地址解析协议。

ARP 就是利用广播对所有设备提问:“×× 这个 IP 地址是谁的?请把你的 MAC 地址告诉我。”

然后就会有人回答:“这个 IP 地址是我的,我的 MAC 地址是××××。”

不是这个 IP 地址的设备会忽略广播,什么都不回答。

如果对方和自己处于同一个子网中,那么通过上面的操作就可以得到对方的 MAC 地址。

然后,我们将这个 MAC 地址写入 MAC 头部,MAC头部就完成了。

如果路由表的设置正确,那么对方应该在同一子网,否则对方无法作出ARP 响应,这时只能认为对方不存在,包的发送操作就会失败。

ARP缓存

不过,如果每次发送包都要这样查询一次,网络中就会增加很多 ARP包,因此我们会将查询结果放到一块叫作 ARP 缓存的内存空间中留着以后用。

在发送包时,先查询一下 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址。而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 查询。

显示 ARP 缓存的方法和 MAC 地址的写法如图 2.20 和图 2.21 所示,供大家参考。

 

 

ARP缓存会定时删除

如果总是使用 ARP缓存中保存的地址会产生问题。

例如当 IP 地址发生变化时,ARP 缓存的内容就会和现实发生差异。

为了防止这种问题的发生,ARP 缓存中的值在经过一段时间后会被删除,一般这个时间在几分钟左右。

这个删除的操作非常简单粗暴,不管 ARP 缓存中的内容是否有效,只要经过几分钟就全部删掉,这样就不会出问题了。

当地址从 ARP 缓存中删除后,只要重新执行一次 ARP 查询就可以再次获得地址了。

上面这个策略能够在几分钟后消除缓存和现实的差异,但 IP 地址刚刚发生改变的时候,ARP 缓存中依然会保留老的地址,这时就会发生通信的异常。

将打包工作交给IP模块的利处

将 MAC 头部加在 IP 头部的前面,整个包就完成了。到这里为止,整个打包的工作是由 IP 模块负责的。

有人认为,MAC 头部是以太网需要的内容,并不属于 IP 的职责范围,但从现实来看,让 IP 负责整个打包工作是有利的。

如果在交给网卡之前,IP 模块能够完成整个打包工作,那么网卡只要将打好的包发送出去就可以了。

对于除 IP 以外的其他类型的包也是一样,如果在交给网卡之前完成打包,那么对于网卡来说,发送的操作和发送 IP 包是完全相同的。

这样一来,同一块网卡就可以支持各种类型的包。

至于接收操作,我们到后面会讲,但如果接收的包可以原封不动直接交给 IP 模块来处理,网卡就只要负责接收就可以了。

这样一来,一块网卡也就能支持各种类型的包了。

与其机械地设计模块和设备之间的分工,导致网卡只能支持 IP 包,不如将分工设计得现实一些,让网卡能够灵活支持各种类型的包。

原文地址:https://www.cnblogs.com/errornull/p/9966523.html