
⭐️在这个怀疑的年代,我们依然需要信仰。
个人主页:YYYing
⭐️计算机网络系列专栏:
系列上期内容:【计算机网络 | 第五篇】计网之链路层
系列下期内容:暂无
目录
[● 网络层的主要任务](#● 网络层的主要任务)
[● 面向连接的虚电路服务](#● 面向连接的虚电路服务)
[● 无连接的数据报服务](#● 无连接的数据报服务)
前言:
在我们学习完计算机网络中的数据链路层之后 ,我们了解了网络抓包 ,数据在链路层传输的一些格式规范,那么今天我将带着大家学习网络层的内容。
(我们的此篇章依然会用到网络抓包软件------Wireshark ,如有需要可以看我们第五篇博客的下载流程)
那么在开始前,我想提一嘴我们为什么要有网络层:
其实我们每个国家每个省 都有其自己的网络组网格式,**那么我们如何实现我们网络的互联互通?**这个时候我们网络层就出现了。
一、网络层的设计选择
● 网络层的主要任务
○ 就是将分组从源主机经过多个网络和多段链路传输到目的主机
○ 可以将该任务划分为分组转发和路由选择两种重要的功能。
我们网络层向上提供了两种服务:
● 面向连接的虚电路服务
这种方法虽然现在仍存在,但我们的因特网是很少用的。
○ 核心思想是"可靠通信应由网络自身来保证"。
○ 通信双方沿着已建立的虚电路发送分组。
此方法的好处也很明显,可以看到我们的图中,在我们传输过程中,我们的通路是会一直保持连接状态的,那么我们就不用太担心数据丢失问题了 ,他会帮我们实现稳定传输 。而缺点就是只有这一条通路。
很多广域分组交换网都使用面向连接的虚电路服务。例如,曾经的X.25和逐渐过时的帧中继(Frame Relay,FR )、异步传输模式(Asynchronnous Transfer Mode,ATM)。
● 无连接的数据报服务
我们现在用的比较多的也就是这种服务
○ 核心思想是"可靠通信应由用户主机来保证"。
○ 不需要建立网络层连接。
○ 每个分组可走不同的路径,因此,每个分组的首部都必须携带目的主机的完整地址。
○ 通信结束后,没有需要释放的连接。
这种通信方式所传送的分组可能误码、丢失、重复和失序。但由于网络自身不提供端到端的可靠传输服务,这就使得网络中的路由器可以做得比较简单,我们的路由器就不用大量的缓存接收五湖四海来的数据包,不用检查我们数据包的问题,然后跟发送端沟通,如果这样设计,上层确实会轻松很多,但我们互联网的核心骨干------路由器的软件要求就太复杂了 。
所以,这种服务会放弃可靠传输,当然上层需不需要可靠那是上层的事,我们网络层不操心。那如果我们途中丢包了,那么我们就只需要让对方重传就好了。
不同异构网络的拓扑、性能以及所使用的网络协议都不尽相同,这是由用户需求的多样性造成的, 没有一种单一的网络能够适应所有用户的需求。
二、网络层协议
看到这,那就到了我们本章节最重要的部分了,我们先来看看这个协议在网络中是如何表示的。
提一嘴,我们底层协议 往往采用的都是二进制格式 ,而应用层都是文本格式(其实也慢慢转二进制格式了)。因为我们底层的效率非常重要,所以底层协议采取二进制格式主要是考虑到两方面:
1、字符串有长有短,解析不方便,效率低。
2、更容易出现粘包问题。
可以看出来,在我们没学数据报格式之前,我们根本就看不懂这块的二进制数据,这45是什么含义?这00又是怎么出来的?这些数该怎么划分?
2.1、IPv4数据报格式
● 在TCP/IP标准中,各种数据格式常常以32比特(即4字节)为单位来描述。
● 网络层数据包(IP数据包,Packet)由首部、数据2部分组成
○ 数据:很多时候是由传输层传递下来的数据段(Segment)
我们依次来看:首先我们每行是占了32个比特也就是4个字节,固定部分总共有5行,所以我们固定部分总共有20个字节。
2.2、IPv4数据报首部协议
2.2.1、版本
● (Version)版本
● 长度为4个比特,用来表示IP协议的版本。
例如我们此处0100,就是IPv4。
2.2.2、首部长度
● (Header Length)首部长度
● 占4个比特,乘以4才是最终长度,用来表示IPv4数据报的首部长度,也就是协议头整体的长度。
○ 最小取值为二进制的0101,20字节
○ 最大取值为二进制的1111,60字节
也就是说,我们接收端并不会把此处的数值单纯的看成一个数字来解读,而是把他看作一种"权值",当接受端拿到这个数之后,我们接收端会自动把这个值*4来解读。可以看我圈起来的两个地方。
**我们设计首部长度的目的就是为了防止粘包,因为接收端也不知道哪部分是协议头哪部分是上面的数据。****因为我们IP头是变长的,所以我们要有首部长度,我们后面学的传输层协议也是职业的,但我们上节课讲的因特网中的协议并不是变长,所以他没有首部长度。**而且我们IP头的首部长度能表示最大是长度60字节,最小是20字节,所以我们的可选字节才会是40个字节。
2.2.3、可选字段
● 长度从1字节到40字节不等,用来支持排错、测量以及安全措施等功能。
● 虽然可选字段增加了IPv4数据报的功能,但这同时也使得IPv4数据报的首部长度成为可变的,这就增加了因特网中每一个路由器处理IPv4数据报的开销。
2.2.4、区分服务
● (Differentiated Services Field)区分服务
○ 占8个比特
○ 可以用于提高网络的服务质量
为了让我们在某种网络场合传输更快,行为更复杂,就会在我们可选字段那加一些特殊的数据,然后这些数据会根据我们不同的区分服务情况快速的让我们路由器处理,可能会跟传统的路由器处理情况不一样,但这一定是定制化的,而我们平时在网上看到的IP协议几乎都是20个字节的头。
2.2.5、填充
● 当首部长度(20字节固定部分+可变部分)的长度不是4字节整数倍时,填充相应数量的全0字节, 以确保IPv4数据报的首部长度是4字节的整数倍。
也就是说,此处是在确保我们的首部长度是能被4整除的数,才好表示协议头里的权重。
● 使用全0进行填充。
2.2.6、总长度
● (Total Length)总长度
○ 占16个比特
○ 首部 + 数据的长度之和,最大65535
我们首部长度计算的是我们IP头的长度,那么我们总长度就是IP头加上上面的数据包的长度。接收端收到该信息后,自己做减法就能算出上面的数据包的长度,这样就能避免粘包。
下图就是我们IPv4的包和UDP的包的对比,可以发现我们87字节 - 20个固定部分的字节 = 67字节。
2.2.7、标识、标志、片偏移
我们看到,此处的数据长度实在是太长了 ,我们必须要遵从MTU的长度来剪切 ,且我们要保证每个头都必须有一个新的IP头,但我们要知道这三个包到达的顺序可是随机的 ,也就是说数据包过去以后,我们的接收端不能很好的知道这几个包该怎么拼 ,那么这个时候就需要我们IP头第二行的三件法宝------标识、标志、片偏移。
标识(Identification):
● 占16比特
● 数据包的ID,当数据包过大进行分片时,同一个数据包的所有片的标识都是⼀样的
● 有一个计数器专门管理数据包的ID,每发出一个数据包,ID就加1
标志(Flags):
● 占3比特
● 最低位(More Fragment,MF)
○ MF=1表示本分片后面还有分片
○ MF=0表示本分片后面没有分片
● 中间位(Don't Fragment,DF)
○ DF=1表示不允许分片
○ DF=0表示允许分片
● 最高位为保留位,必须设置为0
片偏移(Fragment Offset):
● 占13比特
● 片偏移乘以8:字节偏移
● 每一片的长度一定是8的整数倍
理论基础讲完了,那么我们不妨抓个包看一下到底是怎么回事
我们这边先ping一下百度,让其发几个ICMP包看看,可以看到现在有4个包。

我们点开其中的几个包,查看下我们的标识和标志,可以看到我们数据包的ID也就是标识,确实是自增长不断+1的,且我们的MF和DF都是0,这代表虽然我们允许分片但我们的包太小并没有剩余的分片包。

而太大的数据包我将会用一道考研例题来模拟并讲解。

看题目,我们的数据报总长为3820,去掉20字节的固定首部,那就还剩3800个字节,然后又说,每个分片不能超过1420字节,但我们每个分片都需要加IP头 ,故每个分片的数据就只剩1400字节了,我们设一个分片的字节为x ,也就是说x <= 1400是恒成立的 ,也就是说,我们现在可以分成x1 = 0 ~ 1399、 x2 = 1400 ~ 2799、 x3 = 2800 ~ 3799 这三个包,但就如同上述所说,如果这三个包到达接受端的顺序是1,3,2,那么接收端怎么知道这三个包现在乱了呢? 那么我们片偏移就是干这个活的,假如我们现在正在分x2这个包,那么我们就会拿x2的第一个字节数来做片偏移的数字 ,那么当接受端解析完这个片偏移后,他就知道我们现在的这个x2包是从1400开始收的,那么我就把x2放在缓存区中1400的位置,同时一看我们x2的MF=1,就知道了我们的包还有多余的片,那么我就继续等包,同理其他包也一样,直到所有的包都在缓存区,那么这样我们就可以避免接收端不知道怎么组合的问题。
最后当然别忘了我们的头里面还有别的信息呢,所以还得再装一个之前的IP头。
虽然我们的流程讲完了,但还有细节需要注意,就是我们的片偏移为什么要/8再放进去 ,我们知道总长度的16位最多可以表示最大值65535的数据包,那么如果我们不除8我们片偏移的总宽度至少也得跟65535差不多吧,因为我们在接收端的缓存区处可是需要解析这个长度从而放进去的,但我们片偏移只有13位,还有3位是我们的标志位,那么我们是不是可以这样理解:我们缺的3位可以看作片偏移在二进制上右移了3位那也就是/8,那这样我们是不是就可以表示足够大的数了。
那么我们就可以正式开始做上面那道例题了
我们先看我们的常规做法:我们将1580字节分为1560字节数据和20头协议 ,那么我们每个分片最多最多是不是就只能装800 - 20 = 780了 ?那么我们第一个分片x1是0~779 ,x2那就是780~1559了 ,且后续没有多余分片了,说明MF为0 ,我们第二个包大小也是800, 那么这道题就选C?对吗?
那我都这么问了,那肯定多少出了点问题(doge)那问题在哪呢?我们看我们如果第一个分片的最终位置是779,那么我们第二个分片的起始位置那就是780,这个和我们刚才上面一模一样,但是你好像忘了什么(doge),我们的片偏移量是必须得被 8 整除的**,但780 mod 8并不=0,那么就说明我们第二个包的片偏移量是错的,所以我们的第二个包的片偏移量必须得少(因为第二个包的容量可以少但不能再多了),所以我们想让他整除必须得少加一点然后再让发第三个包,因为题目要求每个分片尽量大,那么我们就看最少中最多的情况,其实看到这我想应该都知道选啥了,那就是A。**
到这,我想大家应该都把这其中的细节搞清楚了,那么我们这道题也就讲到这里了。
2.2.8、生存周期
● 生存时间(Time To Live,TTL),占8比特
● 每个路由器在转发之前会将TTL减1,一旦发现TTL减为0,路由器会返回错误报告

2.2.9、协议
● 长度为8个比特,用来指明IPv4数据报的数据载荷是何种协议数据单元PDU。

那我们计算机拿到我们的包解析过后究竟要交给上层的哪个服务呢?其实就靠的我们这块的字段。以下就是我们的案例:


剩下的三块内容其实也不用讲了,在上篇我们已经说过这些了。
结语
那么关于计网之网络层的讲解就到这里了。
我是YYYing, 后面还有更精彩的内容,希望各位能多多关注支持一下主包。
无限进步, 我们下次再见。