小编个人主页详情<—请点击 小编个人gitee代码仓库<—请点击 linux系统编程专栏<—请点击 linux网络编程专栏<—请点击 倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己! 
目录
前言
【linux】网络基础(十五)IP协议,私有IP地址和公网IP地址,运营商和全球网络的理解——书接上文 详情请点击<——,本文会在上文的基础上进行讲解,所以对上文不了解的读者友友请点击前方的蓝字链接进行学习 本文由小编为大家介绍——【linux】网络基础(十六)IP协议,
一、路由
路由的本质就是在复杂的网络结构中,找出一条通往中点的路线,所以我们该如何理解路由呢?如下
那么左侧的第一个圆圈是家用路由器构建的局域网,第二个圆圈是运营商路由器构建的局域网,当然家用路由器处于运营商构建的局域网中,所以家用路由器的WIN口IP是运营商路由器构建的局域网的私有IP,运营商路由器的WIN口IP就是公网IP了
所以此时最左侧的家用路由器的局域网内有一个client主机的IP地址是私有IP地址192.186.3.2,那么它想要访问最右侧的第一个圆圈内的一个server主机,server主机的IP地址是公网IP地址122.11.11.11,所以此时client主机就开始构建请求报文了,源IP是自己主机的私有IP地址192.186.3.2,目的IP是它想要访问的server主机的公网IP地址122.11.11.11,所以此时client主机就将请求报文构建出来了,接下来client主机要进行报文的转发了,那么不着急,下面我们先了解一点关于路由器的前置知识
我们知道路由器具有将报文进行路由转发的功能,那么为什么路由器具有将报文进行路由转发的功能呢?核心原因是由于路由器工作在网络层,所以路由器具有路由表,同样的道理,client主机是工作在应用层的,client主机要进行构建报文以及进行报文的转发就要从上向下贯穿网络协议栈,别忘了网络协议栈不光有应用层的,网络协议栈也有网络层,所以小编想说什么呢?client主机也可以工作在网络层,即client主机也具有路由表
所以我们该如何查看路由表呢?那么在linux中,我们使用route指令查看即可,所以如上图就是小编linux上的路由表,虽然很简洁,但是足够说明问题,那么接下来我们看第二行 IP routing table就是路由表的意思,那么我们下面开始正式认识路由表第三行的字段,首先Destination字段表示网络号(目标网络),GetWay表示下一跳地址,通常是一个路由器的IP地址,Genmask表示子网掩码,Flags中的U表示正在使用,G表示此条目下一跳地址是某个路由器的地址,Iface是使用对应的方法就可以将报文交付下一跳,即当前行对应的主机
目的IP = 目标网络 + 目标主机,在之前的文章中 详情请点击<——,小编已经多次提起,要去到目标主机,就要先到目标主机所在的目标网络,所以呢?我们如何去到目标网络呢?不断的查路由表,路由表中的Destination字段就是目标网络呀,但是我们拿到一个目的IP的时候,只有一个目的IP,那么如何才能得到目标网络呢?
别忘了路由表中可是有Genmask字段的,这个Genmask就是子网掩码,所以 目的IP 按位与& Genmask子网掩码 = Genmask目标网络,所以我们该如何查路由表呢?那么本质就是遍历路由表每一行,对于一行,就拿着 目的IP 按位与& Genmask子网掩码 得到目标网络,拿着这个目标网络和当前行的Destination进行对比,如果相同那么说明此时我们找到了目标主机对应的目标网络,所以此时就通过Iface对应的eth0方法将报文交给目标网络对应的路由器即可,那么如果不相同,继续向下遍历,所以我们才说目的IP可以指导我们进行路径选择,可是小编如果遍历到路由表的结尾都没有找到目标网络那么该怎么办?
交给default默认路由即可,默认路由通常是当前路由器所在的构建局域网的路由器,即类似于client主机处于家用路由器构建的局域网,所以此时clinet主机就要将报文通过default默认路由器那一行Iface对应的方法交给家用路由器,类似于家用路由器处于运营商路由器构建的局域网,家用路由器就要将报文通过default默认路由器那一行Iface对应的方法交给运营商路由器,那么当交给默认路由之后,例如交给运营商路由器,运营商路由器就会查找运营商路由器的路由表,看看是否目标网络是否处于运营商路由器的路由器的Destination中,如果处于,那么运营商路由器就将报文交给对应的目标网络,否则运营商路由器就会将这个报文继续交付到默认路由器上
那么此时我们总的来看一下查路由表的结果 (一)不知道(?),不行,这种情况不存在,因为有默认路由器兜底,所以查询路由表最次的结果就是找不到,但是会继续交付给默认路由器,不能说查完路由表之后什么都不转跳 (二)给你具体下一跳,这就好比问路,我虽然不知道具体的位置在哪,但是我知道要去往具体位置你下一步要去哪个位置,即路由器和路由器之间是通过网络相连的,所以路由器可以相互之间获悉各自的路由表内的信息字段,所以当前路由器或许知道当前报文要去的目标网络处于附近的一个路由器所构成的网络那个相连接的一个路由器网络内,所以当前路由器就会给你具体的下一跳,然后调用Iface的方法去往附近的一个路由器 (三)路由器不清楚,但是转入默认路由器,默认路由器是用来兜底的,当查询了路由表的所有字段之后发现没有找到目标网络,此时就会将报文交给默认路由器 ,默认路由器就是同网段的另一台路由器,没错,家用路由器由于处于运营商路由器构建的局域网,所以家用路由器和运营商路由器也是处于同一个网段 (四)找到目标网络,到达目标网络对应的入口路由器处,即目标网络就在当前路由器的路由表的Destination中,所以就可以直接调用Iface的方法去往目标网络所对应IP地址为Getway的入口路由器,此时报文就可以达到目标网络,所以目标IP = 目标网络 + 目标主机,此时已经成功的到达了目标网络,接下来交给目标主机即可,关于如何交?通过Mac地址,这是数据链路层要解决的问题,小编后面的文章会讲解数据链路层
所以有了上面这么多前置知识的学习之后,我们此时再来看报文的路由就可以很好的理解了,那么继续续接小编上面讲解到的client此时已经构建出了请求报文,其中的源IP是私有IP地址192.168.3.2,目的IP是公网IP地址122.11.11.11.11,那么首先client主机在自己的路由表中通过目的IP按位与子网掩码查询目标网络发现没有找到,那么就将报文交给默认路由器,即交给家用路由器,那么此时家用路由器就在自己的路由表中通过目的IP按位与子网掩码查询目标网络发现没有找到,那么就交给默认路由器
家用路由器的默认路由器是运营商路由器,可是在将报文交给运营上路由器之前,别忘了这个报文的源IP地址还是192.168.3.2,这个IP地址192.168.3.2可是一个私有IP,未来这个报文是要交到公网上的,公网上不允许私有IP出现,所以在报文不断交付的过程中要不断的使用NAT技术,将源IP地址不断的替换为路由器的WAN口IP才可以,所以此时家用路由器就将报文的源IP替换为路由器的WAN口IP地址,然后将替换后报文交给默认路由器,即交给运营商路由器
所以此时运营商路由器就拿到了报文,运营商路由器可以组建局域网,运营商路由器组建的局域网内的主机都是家用路由器,那么这些家用路由器的网络号以及其它字段的一些信息可都在运营商路由器的路由表中,所以此时运营商路由器经过查询路由表,有可能发现目的IP按位与上子网掩码之后的目标网络就是家用路由器的网络(网路号)
但是此时很不幸,运营商路由器并没有在自己的路由表中找到匹配的目标网络,所以运营商此时就使用NAT技术将报文中的源IP替换为运营商路由器的WAN口IP地址122.22.22.22(公网),此时报文中的源IP就是公网了,紧接着,路由器就将报文转发到了默认路由器,此时已经进入了公网,即广域网,然后在经过类似重复的过程,最终,在一个路由器查找路由表的时候,将目标IP122.11.11.11与子网掩码255.255.255.0进行按位与得出目标网络为122.11.11.0,此时恰好就是路由表中的目标网络
所以路由器就通过路由表中Iface对应的方法将报文转发到目标网络的入口路由器处,所以此时就找到了目标网络了,即对应上图最右侧的圈,此时就是局域网之间报文的转发了,那么就是通过mac地址进行转发,至于如何转发,是数据链路层要做的事情,小编会在后面的文章中进行讲解,这里我们就认为报文到了目标网络之后就可以直接到目标主机,所以此时目标主机server就收到了报文
所以如上就是路由的过程,我们可以看出,路由的过程本质就是不断的通过目标IP和路由表中的每一行条目的Genmask子网掩码进行按位与得到目标网络,将得到的目标网络与Destination进行比对寻找目标网络的过程
我们在windows中也可以查看windows的路由表,那么在cmd中使用route PRINT即可查看
二、分片和组装
前置知识

如上便是网络协议栈,实际上,在一台主机中,报文并没有通过网络层直接发送出去,而是继续交给了自己的下一层协议数据链路层,别忘了数据链路层这一层协议对应的可直接就是网卡的驱动程序了,网卡可就是硬件了
在网卡这个硬件设计上,网卡发送的数据包的大小不能太大,那么谁来控制网卡?数据链路层对应网卡的驱动程序,即数据链路层控制网卡,所以由于网卡的硬件设计要求发送的数据包不能太大,由于数据链路层控制网卡,换句话来说,数据链路层,不能一次发送过大的报文
那么数据链路层如何做到一次不能发送过大报文呢?数据链路层的上层协议可是IP协议呀,所以这也就意味着网络层IP协议不能交给我过大的报文,那么如何理解这个过大呢?我们可以使用config执行查看mtu最大传输单元为1500,其实也就意味着数据链路层的上层协议网络层的IP协议交给数据链路层的报文大小最大为1500字节
那么IP协议此时就说了,我也没办法,我也是个办事的,我发送的数据是上层协议tcp传输层给我的,如果tcp向下层ip协议交付的数据就是很大呢?已经超过了1500字节,所以ip协议就要解决这个问题,如何解决呢?要求IP层将数据进行分片转发
那么当左侧客户端的tcp传输层如果交付给网络层ip协议的数据如果很大,那么在左侧客户端的网络层ip协议就要将数据进行分片,然后逐个的对每一个分片交给数据链路层,数据链路层再将分片的报文通过网卡发送出去
那么右侧的服务器的网卡接收到了分片的报文之后,数据链路层就会将分片的报文都交给右侧服务器的网络层IP协议,IP协议对已经分片了的多个报文进行组装成一个报文,去掉ip报头之后,将有效载荷数据交给传输层tcp,所以如何进行分片和组装呢?如下
分片和组装原理讲解
那么小编在之前的文章中,讲解IP协议报头的时候说,IP报头中的红色框内的字段小编会到后面合适的时机进行讲解,那么现在就是合适的时机,红色框内的字段是用于IP协议分片和组装的字段,所以IP协议要进行分片和组装也就必须要使用到红色框内的三个字段,下面我们来学习一下这三个字段,进而引出ip协议如何进行分片和组装
那么首先就是16位标识(id),唯一的标识主机发送的报文,如果来自传输层的数据被IP协议分片了,那么每一个片里面的这个16位标识(id)值都是相同的
接下来是3位标志,顾名思义,就是有三个标志位,分别是保留位,不分片位,更多分片位 (一)第一个标志位是保留位,目前没有使用到,以后可能使用到所以先保留不使用 (二)第二个标志位是不分片位,如果设置为0,表示允许分片,如果设置为1,表示不允许分片,这时候如果报文长度超过mtu触发了ip协议的分片条件,但是由于不分片位被设为为1,所以此时ip协议就会将报文直接丢弃,如果报文长度没有超过mtu那么ip协议就会将这个报文向下交付到数据链路层 (三)第三个标志位是更多分片位,换句话来说也就是结束标志,如果是最后一个分片,那么此时后面没有更多分片了,所以此时设置为0,即由于是最后一个分片,那么所有的分片已经结束了,也就是此时已经结束分片了,如果不是最后一个分片,那么就代表有更多的分片,那么设置为1,即没有结束分片
接下来是13位片偏移,13位片偏移是分片相对于原始报文开始处的偏移,实际上就是表示当前报文在原始报文中处于哪个位置,单位是8字节,即实际偏移的字节数是13位片偏移 * 8得到的,因此除了最后一个报文之后,其它报文的长度必须是8的整数倍,否则报文就不连续了
所以此时有了这些字段的介绍之后,我们就可以谈谈分片和组装了,那么假设传输层的给向下交付到IP协议的报文大小是4440,所以IP协议判定这个报文的大小4440超过了mtu对应的1500字节,所以IP协议要对这个报文进行分片,所以如何分片呢?如下
首先将4440分成3份,每一份的大小是1480,别忘了IP协议还要封装IP报头,IP协议的报头是20字节,所以必须给每一份都要添加上IP报头成为分片,所以此时就有了三个分片的报文了,那么对于这三个报文的分片中的三个关于分片与组装的字段我们应该填充什么呢?即16位标识,3位标志,13位片偏移都应该填充什么呢?
那么此时我们对于所有的分片宏观上分为三个部分,开头的分片,中间的分片,结尾的分片,那么对于第一个字段16位标识如何填充呢?16位标识可以表示的范围最小为0,最大为65535,操作系统会维护一个全局的计数器,范围就是从0到65535,每发送一个IP数据报,全局的计数器就加1(当全局的计数器的值超过了65535之后就重新从0继续开始计数),然后将这个全局计数器加1后的值赋值给报文的16位标识
如果这个报文被分片了,那么所有的分片的报文共享这个值,所以假设此时全局计数器加1之后的值为1234,那么此时开头的分片,中间的分片,结尾的分片的16位标识都为1234,所以此时对于第一个字段16位标识我们搞定
那么紧接着就是第二个字段,三个标志位了,那么第一个标志位,保留位,那么全部的分片默认为0即可,第二个标志位,不分片位,全部的分片默认设置为0,表示允许分片,那么第三个标志位,更多分片位,对于开头的分片,中间的分片来讲,后面都是有更多的分片的,所以我们设置为1,那么对于结尾的分片来讲,后面已经没有更多分片了,那么就设置为0即可,所以此时对于第二个字段三个标志位我们搞定
紧接着就是第三个字段,13位片偏移,这个13位片偏移表示的是当前分片在原始报文的偏移量,这个偏移量要去掉IP报头所占的20个字节,所以对于第一个分片的13位偏移应该是0,对于中间的分片的13位偏移应该是中间的分片的偏移量,即中间的分片实际上是放在第一个分片去掉了IP报头的后面,所以中间的分片13位偏移应该是第一个分片去掉了IP报头的长度1480,同样的道理,结尾的分片的13位偏移应该是前面的第一个分片和中间的分片去掉了IP报头的总长度即为2960
其实小编像上面那样写13位片偏移的方式是不规范的,因为小编写的是字节数,实际上13位片偏移的单位是8字节,所以前面的13位片偏移实际存储的时候都要在小编计算出来的值除以8,例如第一个分片是0,那么除以8仍然是8,这个可以理解,中间的分片是1480那么除以8等于185,即第二个分片的13位片偏移在实际存储的时候应该是185,结尾的分片是2960那么除以8等于370,即结尾的分片的13位片偏移在实际存储的时候应该是370
所以此时对于报文如何分片,我们已经较为了解,并且如果没有分片,那么3个标志位中的更多分片应该设置为0,13位片偏移应该设置为0,这些我们都很好理解,那么理解了分片,接下来我们来理解一下组装,在理解组装之前,我们要搞懂一个前置知识,我们如何得知一个IP报文是被分片了呢?
那么我们观察上图,首先是中间的分片和结尾的分片,被分片的依据是13位片偏移不为0,那么对于开头的分片和中间的分片,被分片的依据是更多分片位被设置为1,所以我们可以得出一个IP报文被分片需要满足 13位片偏移不为0或者更多分片位被设置为1
那么在了解了前置知识之后,下面我们就可以学习组装了,首先第一步要确保所有的分片聚集在一起,那么我们怎么知道这些分片可以组成一个报文呢?别忘了被分片的16位标识是相同的,所以呢?分片丢失无非分为这三类,即开头的分片丢失,中间的分片丢失,结尾的分片丢失,所以我们该如何甄别出来分片的丢失呢? (一)对于开头的分片丢失,首先我们是可以判断出来一个IP报文是被分片了的,所以此时我们就等待多个分片,将所有分片根据16位标识聚集起来,但是发现13位片偏移没有为0的,此时就说明开头的分片丢失了,因为在所有的分片中,只有开头的分片的13位片偏移位0,其余的分片都不为0 (二)对于结尾的分片丢失,那么将所有分片根据16位标识聚集起来,发现聚集起来的分片中的更多分片的标志位没有为0的,说明此时结尾的分片丢失,因为在所有的分片中,只有结尾的分片的更多分片的标志位为0,其余的分片都为0 (三)对于中间的分片丢失,例如此时收到了开头的分片和结尾的分片,那么对于结尾的分片来讲,它的13位片偏移此时对应的字节数为2960,但是它前面的字节数只有开头的分片所对应的字节数1480,此时就说明中间的分片丢失了,如果中间的分片没有丢失,那么此时结尾的分片前面的分片的字节数的大小应该是2960,即开头的分片去掉IP报头的大小加上中间分片去掉IP报头的大小等于结尾的分片13位片偏移对应的字节数2960
所以对于开头的分片丢失,中间的分片丢失,结尾的分片丢失我们都可以甄别出来了,如果分片有任何一个丢失,那么都应该进行重发,所以我们可以保证IP协议接受到的分片的报文的个数是完整的不缺失的,即我们此时已经将所有的分片聚集起来了,那么接下来按照13位片偏移进行排序即可将分片的报文组装成原来的报文,所以我们通过16位标识,3位标志,13位片偏移的巧妙配合可以完整分片的组装工作
所以呢?我们是否建议分片呢?不建议,因为进行了分片也就意味着发送方发送的报文的数量变多了,假设一个报文的丢包概率是百分之1,那么经过分片之后分成了10个报文,那么10个报文丢包的概率就是百分之9.56,如何计算是数学问题,这里小编就不进行解释了,所以将报文分片之后,丢包的概率会增加
并且将IP报文分片之后,分片之中任何一个分片丢失,别忘了分片之后丢包的概率增大,对于所有的分片都要重新发送的概率也会增大,既然要重新发送的概率也会增大,那么此时消耗系统资源,网络资源,网络带宽压力增大的概率就更大,所以这里我们知道分片不好,我们不建议分片
那么对于数据链路层不能一次发送过大报文,所以就要求网络层IP协议不能交给数据链路层过大的报文,可是事实是网络层IP协议也是一个办事的,它添加的IP报头仅仅占报文中的20个字节,真正的报文发送多少的控制者是处于传输层的tcp决定的,所以我们尽可能的减少分片还要从tcp这个根源入手
在网络层有MTU等于1500,要求网络层IP协议交付给数据链路层的报文最大不能超过MTU对应的1500字节,那么IP协议是始终要封装IP报头的,IP报头的大小是20个字节,所以对于一个IP报文来讲,IP报文 = IP报头 + 有效载荷,IP报文最大长度不能超过MTU对应的1500字节,IP报头为固定长度20字节,所以有效载荷的最大长度 = IP报文的最大长度 – IP报头 = MTU – 20 = 1500 – 20 = 1480
所以IP报文的有效载荷的最大长度为1480,那么此时我们就要思考IP报文的有效载荷来源于哪里?来源于传输层的tcp,所以也就意味着tcp报文的最大长度为1480,那么tcp报文 = tcp报头 + 有效载荷,而tcp报文的最大长度为1480,tcp报头为固定长度20字节,所以在tcp协议上,有效载荷的最大长度为1460,我们把tcp的有效载荷发送最大长度对应的1460叫做MSS
只要tcp的有效载荷的长度不超过MSS,那么此时报文在IP协议就不分片了,那么tcp的有效载荷从哪里来的?从tcp的发送缓冲区来的中滑动窗口中来的,所以呢?为什么我们之前看到的滑动窗口是分段的呢?因为为了避免报文在IP层分片,在tcp的有效载荷的长度有MSS的限制,因为有MSS的限制,tcp的有效载荷的长度才不会让报文在IP层分片,所以我们之前看到的tcp的滑动窗口才是分段的,为了效率考虑,滑动窗口分成的段以MSS对应的1460为一个段,所以在传输层使用tcp协议可以有效避免报文在IP协议分片,那么如果传输层是udp呢?
无法避免,因为udp是面向数据报的,udp也没有滑动窗口,也无法分段,也无法让发送的udp的有效载荷的长度不超过MSS,所以一旦udp发送的有效载荷的长度超过MSS,那么IP协议就会对udp报文进行分片,但是udp其实也不怕分片丢失,因为udp的特性就是不可靠的,即使分片丢失,我udp也不重发,因为udp的特性就是不可靠的
总结
以上就是今天的博客内容啦,希望对读者朋友们有帮助 水滴石穿,坚持就是胜利,读者朋友们可以点个关注 点赞收藏加关注,找到小编不迷路!