网络层协议——IP协议

目录

IP协议的简介

IP协议所处的位置

IP协议的格式

如何理解报头?(包含4位首部长度字段和选项字段的知识点)

8位生存时间字段(TTL)

16位标识字段、3位标志字段、13位片偏移字段

发送端在网络层中进行分片的危害

对全部的IP地址进行网段划分(或者说按照某种规则将不同的IP地址归类到不同子网)

特殊的IP地址

IP地址的数量限制

IP地址数量不足问题

那么如何解决IP地址不足的问题呢?(包括私有IP、公网IP、NAT技术的知识点)


IP协议的简介

IP协议全称为“网际互连协议(Internet Protocol)”,IP协议是TCP/IP体系中的网络层协议。TCP作为传输层控制协议,其保证的是数据传输的可靠性和传输效率,TCP提供的仅仅是数据传输的策略,而真正负责数据在网络中传输的则传输层之下的网络层和链路层,举个例子,小明他老爹是教导主任,为了保证小明每次都能考100分,只要小明在没有考到100分时,老爹就宣布全员重考,在这个例子中,老爹就像TCP协议,只负责可靠性和效率,不能代小明去考试(即完全不负责传输数据),小明就像网络层的IP协议和数据链路层,是真正参加考试的人(即真正负责传输数据)

说一下,更准确地说实际上IP协议也不真正负责传输数据,IP协议和TCP协议一样也只负责提供传输数据的策略,只不过策略的着重点不一样,IP协议提供的策略主要是负责给数据指路,真正负责传输数据的只有数据链路层,举个例子,当我们想去某个地方旅游时是需要一本指南攻略和一俩车的,IP协议就像这本指南攻略,数据链路层就像车,如果没有IP协议,每到达一个中间站就不知道下一步该往哪走,而如果没有车,则一步都无法前进,一切都是空谈了。另外,下图是表示数据在网络中转发的过程图中,如果网络层直接就具备传输数据的能力,那数据就不需要从主机A的网络层向下交付给数据链路层、而是直接从主机A的网络层发给路由器的网络层了。

IP协议所处的位置

如上图就很好地说明了IP协议所处的位置。额外说下两个知识点:

1、在socket套接字编程时用到的各种socket套接字接口都是位于应用层和传输层之间的一层系统调用接口,这些接口是系统提供的,我们可以通过这些接口搭建上层应用。我们经常说HTTP是基于TCP的,实际就是因为HTTP报文是通过调用【和TCP协议强相关的socket套接字接口】实现在网络中传输的。

2、socket套接字层往下的传输层实际就是由操作系统管理的,因此UDP和TCP是属于内核当中的,是操作系统本身协议栈自带的,其代码不是由上层用户编写的,UDP和TCP的所有功能都是由操作系统完成,因此网络也是操作系统的一部分。

IP协议的格式

IP报文的报头当中各个字段的含义如下: 

  • 4位版本字段:表示IP协议的版本,可以认为该字段的值永远是二进制数0100。0100换算成10进制数是4,表示IPV4,小伙伴们可能还听说过另一个IP协议的版本叫IPV6,IPV6并不是主流,IPV4才是主流、是标准,所以才说可以认为4位版本字段的值永远是二进制数0100。
  • (不重要、了解即可)8位服务类型:3位优先权字段(已经弃用),4位TOS字段,和1位保留字段(必须置为0)。4位TOS分别表示:最小延时,最大吞吐量,最高可靠性,最小成本。这四者相互冲突,只能选择一个,比如对于ssh/telnet、直播这样的应用程序,最小延时比较重要,那就只把最小延时对应的比特位填充成1;而对于ftp、下载器这样的程序,最大吞吐量比较重要,那就只把最大吞吐量对应的比特位填充成1。
  • 16位总长度:表示整个IP报文的大小,单位是字节。
  • 32位源IP地址:表示发送端bind绑定的IP地址。以前在编写TCP或者UDP服务端时,我们会显示bind绑定ip和port,port端口号就是在传输层起作用、ip就是在网络层起作用。
  • 32位目的IP地址:表示接收端bind绑定的IP地址。以前在编写TCP或者UDP服务端时,我们会显示bind绑定ip和port,port端口号就是在传输层起作用、ip就是在网络层起作用,即网络中的中间设备比如路由器会在收到IP报文后,会根据报文中的32位目的IP地址进行数据路由、决定继续往哪发送。
  • 8位协议:表示该IP报文需要向上交付给传输层的哪个协议(即TCP和UDP中的一个)。
  • (不重要、了解即可)16位首部检验和:用于证明一个IP报文是否有问题。一个IP报文被某台主机收到后,该主机首先会提取16位首部校验和字段,如果发现报文有问题,就直接丢弃该IP报文。注意这并不会影响双方通信,因为接收端丢弃该IP报文后,并不会发送ACK,发送端就不会收到ACK应答,发送端根据TCP协议的超时重传策略就会进行重传。
  • 剩余的内容会在下文中慢慢揭晓。   

如何理解报头?(包含4位首部长度字段和选项字段的知识点)

 (结合下图思考)操作系统内核是C语言写的,而IP协议和TCP协议一样又是属于内核协议栈的,因此IP协议和TCP协议也一定是用C语言编写的,在讲解TCP协议时我们说过TCP报头实际就是一个如下图的位段类型,现在我们要知道IP报头也一样是一个位段类型,为了方便,这里笔者就不画IP报头的位段类型struct ip_header了,您只需要知道其结构和下图基本类似即可。

然后注意,虽然struct ip_header的大小是固定的20字节,但这并不表示IP协议的报头就是固定的20字节,IP协议的报头长度是变化的,这是因为在IP协议中有一个选项字段(可以在上上图中看到),选项字段的长度是变化的,该字段没有被包含进struct ip_header中(就是因为变长的选项字段没有被包含进struct ip_header中,所以struct ip_header的大小才能是固定的),但该选项字段的确是属于IP协议的报头部分的。既然选项字段的长度是变化的,那谁知道选项字段到底多长呢?答案:IP协议的报头由两个部分组成,第一是struct ip_header部分,第二是选项字段,我们通过IP协议的报头中的4位首部长度字段就能知道IP协议的报头整体有多长(如何知道会在下一段中说明),知道报头整体有多长后,用整体长度减去struct ip_header所占的20字节,剩余的就是选项字段的长度大小了。

有人可能会说【4位首部长度字段是4个二进制比特位,要么为0,要么为1,就算是全1,那4个二进制比特位能表示出的十进制数字也只不过是15,现在光struct ip_header都有固定的20字节,更何况还有选项字段,你凭什么说通过IP协议的报头中的4位首部长度字段就能知道IP协议的报头整体有多长呢?】,这里笔者想说,的确4个二进制比特位最大表示的十进制数字是15,但这里15这个数字的单位并不是1字节,而是4字节,所以虽然4位首部长度字段的值的区间是左闭右闭的【0,15】,但所表示的IP协议的报头的整体长度的区间是左闭右闭的【0,15*4】,即【0,60】,又因为即使选项字段的长度是0字节,IP协议的报头中还有固定20字节的struct ip_header,所以表示IP协议报头的整体长度的4位首部长度字段的值不可能小于5,所以准确来说4位首部长度字段的值的区间是左闭右闭的【5,15】,其所表示的IP协议的报头的整体长度的区间是左闭右闭的【5*4,15*4】,即【20,60】。

IP协议如何进行数据封装?

(结合下图思考)当应用层将数据交给传输层(即应用层将数据交给发送缓冲区、发送缓冲区再把数据交给TCP协议)后,此时数据就被添加上了TCP的报头进而变成了TCP报文,然后传输层再把TCP报文交给网络层的IP协议后,在网络层就会创建一个IP报头类型的变量,然后填充IP报头当中的各个字段,此时就得到了一个IP报头。此时操作系统再在内核当中开辟一块空间,将IP报头和TCP报文拼凑到一起,此时就形成了IP报文。 

从代码层面上如何体现上一段的内容呢?答案:在<<数据链路层——以太网协议、ARP协议>>一文中讲解过代码层面上的以太网帧的封装过程,那个过程和这里的过程基本是完全一致的,请移步去看。

IP协议如何进行数据分用? 

(结合下图思考)当网络层从下层获取到一个IP报文后,就会先读取该IP报文的报头,先从报头中提取出4位首部长度字段、16位总长度字段和8位协议字段,然后通过16位总长度字段表示的IP报文的总长度减去4位首部长度字段表示的IP报头的长度即可把IP报文的报头和有效载荷(有效载荷是一个完整的TCP报文或者UDP报文)分离,然后就可以把有效载荷全向上交付给8位协议字段所代表的传输层协议(即TCP协议或者UDP协议)了,假如IP报文的有效载荷是TCP报文,则8位协议字段的值所表示的含义一定是TCP协议,那么IP协议就会把IP报文的有效载荷(即TCP报文)向上交给TCP协议,后序当传输层的TCP协议收到网络层向上交付的有效载荷、即TCP报文后该如何继续解包并向上交付则在讲解TCP协议的文章中已经进行过说明了,这里不再赘述;假如IP报文的有效载荷是UDP报文,则8位协议字段的值所表示的含义一定是UDP协议,那么那么IP协议就会把IP报文的有效载荷(即UDP报文)向上交给UDP协议,后序当传输层的UDP协议收到网络层向上交付的有效载荷、即UDP报文后该如何继续解包并向上交付则在讲解UDP协议的文章中已经进行过说明了,这里不再赘述。

8位生存时间字段(TTL)

主机A给主机B发送信息时,这条信息会先经过网络(说具体点就是会先经过网络中的许多中间设备,如路由器),然后才能到达主机B。无论主机A和主机B相隔多远,当一个报文的信号减弱时,因为有集线器等能增强报文信号的中间设备存在,所以从理论上说,报文是能一直在网络中被转发的,这就会有一个问题,如下图红色环状的线路所示,如果主机A给主机B发信息时,因为路由器的路由算法出了点小bug或者因为物理线路的布置出了点问题,而导致某条信息不断地在相同的几个路由器里相互环路转发,那么这条信息就永远到达不了主机B。到达不了都只是小问题,因为主机A在一定时间内收不到应答,则就会根据TCP协议的超时重传策略进行重传信息,重传时,这条信息就不一定会走和上次一样的路线了,于是信息就能成功到达主机B;

真正的大问题在于,因为这条信息永远到达不了主机B,所以中间设备就失去了转发这条信息的意义,但即使继续转发已经没有意义,因为没有相关策略控制中间设备停止转发,所以中间设备也只能继续转发,注意中间设备转发一条信息是需要网络资源、内存资源(比如说需要存储这条信息)、CPU资源(比如说需要计算路线;执行任何操作对应的代码都需要CPU资源)等资源的,所以中间设备永远无脑地继续转发带来的结果只有浪费各类资源,并且在网络中可不止只有主机A发送的信息会出现这样的情况,而是整个互联网中的主机在发送信息时都有可能出现这种情况,所以随着时间的推移,届时游离在网络中的无效数据就会越来越多,直到把网络里的全部中间设备占满导致网络瘫痪。

为了解决这个问题,于是IP协议都想到了设置一个8位生存时间字段,该字段就是一个计数器,让发送端在发送IP报文前,根据IP协议计算出该信息此行大约最多经过多少个路由器,这条IP报文在网络中被转发时,每经过一个路由器就让路由器把这条报文的8位生存时间字段上的值减一(路由器也是遵守IP协议的,具有解包IP报文和重新封装IP报文的能力),如果路由的路线是正常的,没有出现bug,则该报文在到达目标主机前,该报文的8位生存时间字段一定不会变成0,一定能被目标主机接收到;而如果路由的路线出现了bug,则该报文在到达目标主机前,该报文的8位生存时间字段大概率就会变成0,只要当某个路由器收到该IP报文后,发现该IP报文的8位生存时间字段上的值是0,那么路由器就知道这条报文的生命需要被自己亲手结束了,路由器就会丢弃该IP报文(即不再转发这条IP报文,并且允许让后来的IP报文覆盖这条IP报文),这样一来,在网络中被转发的所有IP报文就都有了一个生存时间,不会一直在网络中存活,上面的问题也就完美地被解决了。

16位标识字段、3位标志字段、13位片偏移字段

网络层的下一层、即数据链路层因为其物理特性,一般是无法在网络中转发太大的数据的,数据链路层一次性可以转发到网络中的数据的上限MTU(Maximum Transmission Unit,翻译为最大传输单元)的缺省值是1500。

我们知道,一个TCP报文的有效载荷有多大完全是看滑动窗口和拥塞窗口的,所以一个TCP报文极有可能大于1500,这就会导致该TCP报文到了网络层加上IP报头变成IP报文后,再被送到数据链路层时,数据链路层无法发送这个IP报文。

(结合下图思考)为此,在网络层中是要对体量大于MTU的IP报文进行分片的(小于MTU的IP报文当然就不用被分片了),比如网络层收到一个完整的TCP报文后,先直接给该TCP报文添加上IP报头将其变成IP报文A,看IP报文A的大小是否超过MTU,如果超过了,则将IP报文A切分成若干个部分(如何切分会在下一段中说明)并为每个部分都添加上IP报头以将每个部分都变成IP报文,通过这样的方式让每个IP报文的大小都不超过MTU,然后网络层再把多个IP报文发给数据链路层,这样一来,数据链路层就可以串行化地把这多个体量小于MTU的IP报文挨个转发到网络中了。额外说一下,当对端主机接收到这多个IP报文后,它也需要在自己的网络层中先将这多个IP报文的IP报头拆解丢弃掉,然后再把这多个只表示完整TCP报文一部分的IP报文的有效载荷重新组装拼凑成1个完整的TCP报文,然后再将这个完整的TCP报文交给上层的传输层。

怎么分呢?规则为:【假设MTU是1500,则每个部分加上IP报头后都要尽可能的等于1500字节,也就是说假如IP报头没有选项字段、IP报头就是固定的20字节时,被分出来的每个部分都尽可能的要等于1480字节,当IP报文A被分得不够1480字节后,最后剩多少字节,就将多少字节分为一部分】。所以根据这个划分规则,会直接把IP报文A的第一个部分分成1500字节(因为第一个部分天然就包含了IP报头、第一个部分天然就已经是一个完整的IP报文,所以不用再额外为第一部分添加IP报头,只需要将报头中的16位总长度字段的值改成1500即可,表示目前的IP报文的总长度是1500字节),并且后面被分出来的每个部分都尽可能的要等于1480字节,当IP报文A被分得不够1480字节后,最后剩多少字节,就将多少字节分为一部分,然后为每个部分都添加上IP报头将每个部分都变成IP报文,这样每个IP报文的大小就都不会超过MTU、即1500字节了。(顺便说一下,因为后面的每个部分里都不存在TCP报头,所以为后面的每个部分都添加上IP报头将每个部分都变成IP报文后,这些IP报文的有效载荷都不是一个完整的TCP报文)


问题:为什么在上文中数据分片和重组的工作都是由网络层做的,而不是由数据链路层做的呢?

答案:可以简单理解为,因为局域网技术有多种,比如以太网,无线网、令牌环网等,而不同局域网技术的数据链路层是不一样的、具有差异,所以如果把分片和重新组装的任务交给数据链路层,那就需要为每种局域网技术都设计一套分片和重组的方案,这样成本就太高了,除此之外,把分片和重组的任务交给数据链路层还有很多不可控因素,所以倒不如直接把这些任务交给网络层来做,比如因为即使是使用不同的局域网技术,所有的主机都还是需要遵守TCP/IP协议体系的规则的,如果把分片和重组的任务交给数据网络层来做,那就只需要设计一套方案。


问题:根据上文的内容,需要网络层进行数据分片的原因我理解了,但我不理解的是为什么对端主机的网络层收到被分片的数据后,还需要先将数据重组然后再把数据交付给位于上层的传输层?

答案:根据上图的第四行可以发现,如果不重组,位于右边的两个部分只是TCP报文的有效载荷的一部分,而并不是一个完整的TCP报文,传输层是处理不了这样的数据的,所以必须重组。


问题:上文所说的网络层会进行分片和重组是如何做到的呢?

答案:需要靠IP报头中的16位标识字段、3位标志字段和13位片偏移字段做到,先介绍一下这3个字段,然后再详细说明它们分别是如何起到作用的。

16位标识字段:

表示IP报文的序号,其就是一个数字,用于在不是分片(将大于MTU字节的大IP报文分成若干个部分,然后为每个部分添加上IP报头将其变成小IP报文,每个小IP报文就是一个分片)的IP报文中区分不同的IP报文,比如说不同IP报文的该字段值是不同的;然后用于在是分片的IP报文中区分哪些分片是一类(即哪些分片是同一个大IP报文的分片),注意即使多个IP报文的该字段是相同的,也不表示这多个IP报文是同一个IP报文(因为每个IP报文的报头不完全相同,每个IP报文的有效载荷也不完全相同,只是每个IP报文的报头中的16位标识字段相同,只有一个字段相同而已),而只表示这多个IP报文都是同一个IP报文的分片。

有人可能会有疑问,说【该字段只有16位,那么可以表示的数字是有限的,假如发送端的网络层一下子发送了大量的IP报文,这些IP报文中的一部分IP报文的16位标识字段把所有数字都用掉了,那另一部分IP报文的16位标识字段就只能使用重复的数字,比如只能从头也就是从0开始从小到大重新使用这些数字,那么当接收端的网络层接收了这些的IP报文,根据IP报文的16位标识字段不就区分不开哪些IP报文是否是分片了、也区分不开哪些分片是一类了吗?】

这里笔者想说的是,不必担心,网络层是不存在接收缓冲区这样的概念的,网络层收到一个IP报文后会快速地将IP报头去掉、如果该IP报文是分片则还会快速地进行重组处理、然后将IP报文的有效载荷快速地交付给上层传输层,这些动作都是在一瞬间就完成了,换言之,网络层中是不会存在数据积压的,同一时间在网络层中存在的IP报文是非常少的,所以当发送端后序再发送一个16位标识字段和之前发的IP报文的该字段相同的IP报文、但IP报文本身并不和之前的IP报文一样是同一个大IP报文的分片时, 之前发送的IP报文早就被接收端的网络层解包并向上交付了,所以就不会出现【接收端的网络层中存在多个不是同一个大IP报文分片的IP报文、但这多个IP报文的16位标识字段却相同】的情况,所以只要多个IP报文的16位标识字段相同,这多个IP报文一定是同一个大IP报文的分片,所以当接收端的网络层接收到多个IP报文,根据IP报文的16位标识字段就能区分哪些IP报文是否是分片了,也能区分哪些分片是一类了(比如16位标识字段相同的IP报文就是同一类,并且这些IP报文都是分片)

 13位片偏移字段:

该字段用于表示不同分片(将一个完整的IP报文切分成多个部分,然后给每个部分添加上IP报头将其变成IP报文后,每个IP报文都被称为一个分片)的相对位置,该字段的值越大,说明该IP报文(即该分片)的位置越靠右,当对端主机收到多个分片后,它就能通过该字段判断某个分片应该位于哪个分片的右边,这样对端主机的网络层才能顺利完成重组的任务。

举个例子,如上图所示,假如一个IP报文有3000字节,因为大于了MTU,所以数据链路层是发不出去的,于是就需要在发送端的网络层中对该IP报文进行分片,根据上文所讲的分片规则,可知此时该IP报文会在网络层中被从左到右切分成3份,假设第一份叫做分片1、第二份叫分片2、...,那么这3个分片(即3个IP报文)的相对位置就应该如上图所示。计算13位片偏移字段的规则是【分片1的IP报头中的13位片偏移字段永远是0,分片2的IP包头中的13位片偏移字段永远是MTU的大小,后面的分片的IP报文中的13位片偏移字段=该分片前一个分片的IP报头中的13位片偏移字段+(该分片前一个分片的总长度-IP报头的长度)】,其中每个分片(即IP报文)的总长度通过IP报头中的16位总长度字段即可得知,所以分片1的13位片偏移字段就是0,分片2的13位片偏移字段就是1500,分片3的13位片偏移字段就是1500+1500-20=2980,所以当接收端主机收到多个分片(每个分片都是一个IP报文)后,根据IP报头中的13位片偏移字段的大小就能知道分片1是位于最左边的(因为分片1的该字段最小),紧接着是分片2位于分片1的右边(因为分片2的该字段比分片1的大,并且数字之间最相近),最后是分片3位于分片2的右边(因为分片3的该字段又比分片2的该字段大)

3位标志字段:

(不是重点,了解即可)第一位比特位是保留位,表示目前这个比特位不会被用到,随着时代的发展以后可能会有新的需求,才会使用到该位;

(不是重点,了解即可)第二个比特位表示是否禁用分片功能(如果为1则表示禁用,反之不禁用),如果设置为1,则表示不能在网络层中进行分片。该比特位通常用于配合管理通信,网络中的中间设备比如路由器的内核中也是存在网络层、数据链路层的,有时候需要测试出这些中间设备的数据链路层一次性可以转发到网络中的数据的上限MTU,此时就需要在网络层将IP报文的报头中的该位标志字段设置成1,这样在网络层中就不会继续将大于MTU的IP报文进行分片,而是直接将该IP报文向下交付给数据链路层,此时因为IP报文大于了MTU、数据链路层发不了,那数据链路层就会直接丢弃,于是我们就能通过不断地发送大小不一的IP报文测试出MTU的值了,比如发现某条IP报文没有被发出去、被丢弃了,则说明该报文的大小大于MTU,如果发现某条IP报文被发出去了,则说明该报文的大小小于MTU,被发送的IP报文的大小是我们自己指定的,所以就能一点一点定位出MTU的大小。

(重点)第三个比特位表示后序是否还存在更多分片,什么意思呢?举个例子,结合下图思考,如果在主机A的网络层中的IP报文的大小大于MTU,则会在网络层中对完整的IP报文进行切分、将一个IP报文切分成多个部分,然后再分别给每个部分添加IP报头将它们变成IP报文,最后再把多个大小小于MTU的IP报文发给数据链路层并让数据链路层发给主机B的数据链路层,主机B的数据链路层把收到的多个IP报文上交给主机B的网络层后,主机B的网络层是需要将这多个IP报文的IP报头丢弃并将它们的有效载荷重组拼凑在一起形成一个完整的TCP报文的,那如何得知某个IP报文的有效载荷是不是应被拼凑在最右边呢?可以通过IP报文的报头中的该位标志位,如果该位值为1,则表示当前IP报文并不是位于最右边的分片,该IP报文的右边还存在更多分片;如果该位值为0,则表示当前IP报文就是位于最右边的分片,该IP报文的右边不存在更多分片。

走到这里,关于这3个字段的前置知识点就讲解完毕了,接下来咱们来进一步理解这3个字段的工作模式。

问题:接受端的网络层收到多个IP报文时,接收端如何区分一个IP报文是否是某个大小大于MTU的大IP报文的分片呢?

答案:答案如下。

  • 接受端在网络层中读取该IP报文的报头时,只要发现3位标志字段的第三个比特位(即“更多分片”标志位)为1,那么根据上文的相关内容可知此时就表示该IP报文是一个分片;
  • 如果发现“更多分片”标志位的值为0,则继续读取13位片偏移字段上的值,如果发现值不为0,则根据上文的相关内容可知此时就表示该IP报文是所有分片中位于最右边的分片,也就是说该IP报文也是一个分片,而如果继续读取13位片偏移字段上的值时发现该值为0,则根据上文的内容可知该IP报文就不是一个分片了。

可以发现通过这样的手段识别到一个IP报文是不是分片后,就可以通过上文所讲的16位标识符字段将所有的分片归类,比如说发现分片1、2、4的16位标识符字段相同,发现分片3、5的16位标识符字段相同,则将分片1、2、4放到一边,将分片3、5放到另一边,这样就能方便网络层进行数据的重组。

问题:当接收端的网络层收到一批分片、并根据上一段的内容将属于不同大IP报文的分片进行归类放到不同位置后,接收端如何得知自己是否将属于某个大IP报文的所有分片接收完全了呢?

答案:接收端会在网络层中找到存放属于某个大IP报文的所有分片的位置,然后在其中挨个寻找自己理论上应收到的分片,如果发现有部分分片丢失,则接收端的网络层就知道了自己没有将属于该大IP报文的所有分片接收完全,于是接收端就不会给予发送端应答以让发送端根据TCP协议进行超时重传,即【重新将TCP报文交付给网络层将其变成IP报文、重新把该IP报文切分成若干个小IP报文、最后发给数据链路层让它转发这些小于MTU的IP报文】;而如果接收端在网络层中挨个寻找自己理论上应收到的分片时发现每个分片都能找到,则就说明属于该大IP报文的所有分片都已经接收完全了。具体如何做到挨个寻找呢?如下。

  • 接收端的网络层首先会寻找“更多分片”标志位为1并且13位片偏移字段为0的IP报文1(即先寻找在所有分片中位于最左边的分片1),然后会寻找13位片偏移字段为MTU的IP报文2,如果IP报文1和IP报文2中的任意一个没有被找到,则说明接收端没有将所有分片给接受完全(IP报文1和IP报文2在发送端的网络层中是一定存在的,因为如果连两个IP报文或者说分片都没有,那在发送端的网络层中就压根没有对某个大小大于MTU的IP报文进行切分,所以如果在接收端的网络层中没有找到这两个分片,则直接就能说明接收端没有将所有分片接收完全,需要发送端根据TCP协议进行重传,即【重新将TCP报文交付给网络层将其变成IP报文、重新把该IP报文切分成若干个小IP报文、最后发给数据链路层让它转发这些小于MTU的IP报文】)
  • 反之,如果IP报文1和IP报文2都被找到了,则然后读取IP报文2的“更多分片”标志位,如果发现为0,则说明IP报文2(或者说分片2)就是在所有分片中位于最右边的分片,后面已经不存在分片了,不需要继续向后找了,接收端将所有分片接受完全了;如果发现不为0,则说明IP报文2后还有表示分片3的IP报文3,如何找到IP报文3或者说分片3呢?在上文中讲解13位片偏移字段的计算规则时我们说过规则为:【分片1的IP报头中的13位片偏移字段永远是0,分片2的IP包头中的13位片偏移字段永远是MTU的大小,后面的分片的IP报文中的13位片偏移字段=该分片前一个分片的IP报头中的13位片偏移字段+(该分片前一个分片的总长度-IP报头的长度)】,所以这里接收端在网络层中找IP报文3或者说分片3时可以先将分片3的13位片偏移字段根据计算规则计算出来,然后在所有分片中挨个查找,看是否有分片的13位片偏移字段和我们计算出来的理论值匹配,如果没有匹配的,则表示接收端没有接收到分片3,如果有匹配的,则表示接收端接收到了分片3,然后读取IP报文3的“更多分片”标志位,如果发现为0,则说明IP报文3(或者说分片3)就是在所有分片中位于最右边的分片,后面已经不存在分片了,不需要继续向后找了,接收端将所有分片接受完全了;如果发现不为0,则说明IP报文3后还有表示分片4的IP报文4,至于如何寻找IP报文4(或者说分片4)以及找到分片4后如何判断是否需要继续向后寻找分片的方式和之前完全相同,不再赘述。

可以看到,通过上面这样的方式,接收端就能得知自己是否将属于某个大IP报文的所有分片接收完全了。

(结合下图第二行和第三行进行思考)走到这里我们还能发现为什么发送端在自己的网络层中对一个大小大于MTU的IP报文进行分片时,将IP报文切分成多个部分后,要给除了最左边的第一个部分外,给其他部分都添加上一个IP报头将每个部分变成IP报文(不给最左边的部分添加IP报头是因为其天然就具有IP报头),这就是因为如果不添加IP报头,接收端在收到若干分片后就没法完成重组(毕竟根据上文的内容,接收端的网络层在进行重组时完全依靠的是IP报头中的16位标识字段以将属于不同大IP报文的分片进行归类、完全依靠3位标志字段和13位片偏移字段将属于同一个大IP报文或者说属于同一类的不同分片重组到正确的位置)

以上就是最初的问题【上文所说的网络层会进行分片和重组是如何做到的呢?】的全部答案了。


发送端在网络层中进行分片的危害

走到这里,根据上文内容我们就明白了发送端进行分片和接收端进行重组的具体过程以及底层原理,现在我们需要知道的是:虽然发送端的网络层具有将一个大于MTU字节的IP报文进行分片、具有将其分成多个小于MTU字节的IP报文的能力,接收端的网络层具有将多个小于MTU字节的属于同一个大IP报文的分片进行重组的能力,但发送端的网络层是不推荐进行分片的。为什么呢?

举个例子,发送端将IP报文向下交付给数据链路层、将IP报文封装成mac帧后,mac帧在网络中被中间设备转发时是有可能丢包的(丢包的原因也有很多种可能,比如因为光纤、电缆的损坏;又比如路由器故障;再比如受到考试时防作弊的信号屏蔽仪等设备的干扰),如果发送端在网络层中需要对一个IP报文进行分片、比如需要将其变成多个IP报文再向下交付变成多个mac帧并转发到网络中,那么这多个mac帧同时不丢包的概率肯定是要比1个mac帧不丢包的概率要低的。

而如果多个mac帧中真出现了部分丢失的情况,接收端的数据链路层收到这些mac帧将它们解包变成IP报文并向上交付后,接收端的网络层是无法将多个IP报文里的有效载荷拼凑成一个完整的TCP报文或者UDP报文的,也就不会将这个不完整的TCP报文或者UDP报文继续向上交付给传输层,而是会直接将这个不完整的TCP报文或者UDP报文丢弃并且不给予发送端应答,此时对于TCP报文还好说,因为如果双方的传输层协议是TCP,则发送端会进行超时重传,即【重新将TCP报文交付给网络层将其变成IP报文、重新把该IP报文切分成若干个小IP报文、最后发给数据链路层让它转发这些小于MTU的IP报文】;但如果双方的传输层协议是UDP,则因为发送端根据UDP协议是不会进行重传的,毕竟UDP协议不保证可靠性,所以接收端的网络层将这个不完整的UDP报文丢弃后,就真的丢包了。

所以根据上文的内容可以发现,如果发送端在网络层中将从上层传输层里获取的传输层数据进行分片,则后序这个传输层数据会有更大的概率在网络中丢包,如果传输层数据是TCP报文、双方的传输层使用的协议是TCP,则就会造成发送端根据TCP协议进行重传的概率变得更高,而每次重传都是需要网络资源、内存资源、CPU资源的,所以发送端的网络层进行分片是会导致浪费更多资源的概率变高的;如果传输层数据是UDP报文、双方的传输层使用的协议是UDP,因为UDP协议不保证数据的可靠性,那双方基于UDP协议进行通信时就可能压根无法正常稳定地通信了。这就是为什么上文中说发送端的网络层是不推荐进行分片的全部原因了。


上文中说发送端的网络层是不推荐进行分片的,现在问题来了,你网络层不推荐进行分片就能决定不进行分片吗?答案是决定不了,网络层中的TCP报文(或者UDP报文)是传输层根据TCP协议(或者UDP协议)交给它的,报文的大小是多少,报文什么时候发给网络层,都是由传输层协议说了算,所以传输层如果交给网络层的数据很大,则网络层即使不情愿,它也必须进行分片。于是网络层就和传输层商量,【传输层啊,你可不可以不要给我发很大的TCP报文或者UDP报文了,虽然你发得很大我也能处理,但我不是很愿意处理,毕竟进行处理、即进行分片是有消耗的,更何况我进行分片还可能影响到你,比如当传输层协议是TCP时,网络层进行分片导致丢包的概率变大就意味着需要传输层根据TCP协议进行超时重传的概率变大】,然后传输层一听,觉得是有道理的,于是传输层协议就会控制发出去的单个TCP报文和UDP报文的大小,控制让单个报文的大小不能太大。

根据上一段的内容,我们也就可以发现为什么当时在<<传输层协议——TCP协议>>一文中标题为滑动窗口的部分中说【主机A把自己滑动窗口中的所有数据全发给主机B时,主机A的OS会根据TCP协议把滑动窗口中的所有数据分成多个TCP报文再一次性全发给主机B的接收缓冲区、而不是直接将数据以一个TCP报文发送】了,就是因为主机A的传输层为了尽可能地不让网络层收到太大的TCP报文从而进行分片操作进而影响到双方的通信效率。

对全部的IP地址进行网段划分(或者说按照某种规则将不同的IP地址归类到不同子网)

在TCP/IP体系下进行网络通信时,为了保证能正常通信,每个设备都需要配置 IP 地址,否则无法实现正常的通信。(结合下图思考)标准的IP地址(即IPv4 地址,注意IPv6不是主流、不是标准),由32位二进制正整数来表示,IP地址在计算机是以二进制的方式处理的,但人类为了方便记忆采用了点分十进制的标记方式,也就是将32位IP地址以每8位为一组,共分为4组,每组以  .  隔开,并将每组二进制数转换成十进制数。

IP地址的构成

IP地址由网络号和主机号两部分构成:

  • 网络号:用于区分不同子网的标识。(可以认为一个子网就是由一个路由器和多个主机组成的)
  • 主机号:同一子网内,主机之间具有相同的网络号,但是必须有不同的主机号。

组建子网其实就是把网络号相同的主机放到一起。如果在子网中新增一台主机,则这台主机的网络号和这个子网的网络号一致,但是主机号必须不能和子网中的其他主机重复。

通过合理设置主机号和网络号,就可以保证在相互连接的网络中,每台主机的IP地址都不相同,那么问题来了,手动管理子网内的IP是一个相当麻烦的事情,该怎么办呢?有一种技术叫做DHCP,能够自动的给子网内新增的主机分配IP地址,避免了手动管理IP的不便,一般的路由器都带有DHCP功能,因此路由器也可以看做一个DHCP服务器。

为什么要进行网段划分?


回答这个问题前,首先需要讲解一下主机A将数据跨网络发送到另一台主机B时,数据在网络中是如何进行数据路由的,如下:

首先要知道一个关于子网掩码的前置知识点:子网掩码是一个32位的正整数,通常用一串“0”来结尾,为了区分IP地址中的网络号和主机号,于是引入了子网掩码(subnet mask)的概念,每一个子网都有自己的子网掩码,将子网中一个主机的IP地址与当前子网的子网掩码进行“按位与”操作,就能够得到当前所在网络的网络号,说一下,每个子网都是被某个路由器进行管理的,而子网掩码就位于路由器的路由表(路由表是什么会在下面几段中进行说明)中。

如上图,当一台主机A将IP报文跨网络发送到另一台主机B时,其实不是直接将IP报文发送到了目标主机B,而是先将IP报文发送到目标主机B所在的子网,然后再将IP报文发送到目标主机B(或者说先将数据发送到管理着包含目标主机B在内的多台主机的路由器里,再由路由器发给目标主机B),因此IP报文在路由时的第一目的并不是找到目标主机B,而是找到目标主机B所在的子网,然后再在目标子网当中找到目标主机B。在找目标主机B所在的子网的过程中,IP报文在传输时会遇到很多路由器,这些路由器会帮助IP报文进行路由转发,每当IP数据包遇到一个路由器后,对应路由器都会查看该IP报文中的目的IP地址,并根据该IP地址查询路由器中的路由表(路由表是什么会在下面几段中进行说明),查询得出结果后就告知该IP报文下一步应该前往哪个子网。

路由器的查询结果有以下三种可能:

  • 路由器经过路由表查询(如何查询会在下面几段中进行说明)后,得知该数据下一跳应该跳到哪一个子网。
  • 路由器经过路由表查询(如何查询会在下面几段中进行说明)后,没有发现匹配的子网,此时路由器会将该数据转发给默认路由。
  • 路由器经过路由表查询(如何查询会在下面几段中进行说明)后,得知该数据的目标网络就是当前所在的网络,此时路由器就会将该数据转给当前网络中对应的主机。

路由表查询的具体过程如下:

每个路由器内部会维护一个路由表,我们可以通过route命令查看云服务器上对应的路由表。

  • Destination代表的是目的网络地址。
  • Gateway代表的是下一跳地址。
  • Genmask代表的是子网掩码,可以看到一个路由表中是可以有多个子网掩码的,因为云服务器只是一台主机,并不是路由器,所以上图中的子网掩码比较少,实际上在路由器中的子网掩码是有很多的。
  • Flags中,U标志表示此条目有效,G标志表示此条目的下一跳地址是某个路由器的地址,没有G标志的条目表示目的网络地址是与本机接口直接相连的网络地址,不必经路由器转发,即该条目不是路由器,只是一台其他设备。
  • Iface代表的是发送接口。

当IP数据包到达路由器时,路由器就会用该IP数据包的目的IP地址与路由表中的位于第一行的子网掩码Genmask进行“按位与”操作得到一个网络号,然后将该网络号与位于第一行的目的网络地址Destination进行比对,如果相等则说明该数据包下一跳就应该跳去这个网络号对应的子网,此时就会将该IP数据包通过对应的发送接口Iface发送到这个网络号对应的子网;如果不相等,则路由器就会用该IP数据包的目的IP地址与路由表中的位于第二行的子网掩码Genmask进行“按位与”操作得到一个网络号,然后将该网络号与位于第二行的目的网络地址Destination进行比对,结果是相等或者不相等分别会执行的操作和之前一样;如果将路由表中的每一行进行比对后都没有找到和网络号相等的目的网络地址,则此时路由器就会将这个IP数据包(或者说IP报文)发送到默认路由,也就是路由表中目标网络地址中的default。可以看到默认路由对应的Flags是UG,实际就是将该数据转给了另一台路由器(因为在上面说过G标志表示此条目的下一跳地址是某个路由器的地址),让该数据在另一台路由器继续进行路由。

IP数据包不断经过路由器路由后,最终就能到达目标主机所在的目标网络,IP报文到达目标网络后,最后就不再根据该IP数据包的目的IP地址当中的网络号进行路由了,而是根据目的IP地址当中的主机号进行路由,最终根据该主机号就能将数据发送给目标主机了。

详细说明一下上一段中【IP数据包不断经过路由器路由,最终到达目标主机】的过程,如下:

  • 说一下,在实际中范围较大的子网的网络号从左向右数使用的比特位数量是比较少的(如果不是很理解“从左到右数使用的比特位数量”所想表示的含义,建议先在下文讲解什么是网段划分的部分中看看IP地址的构成图后再回来继续阅读),范围较小的子网的网络号从左向右数使用的比特位数量是比较多的,举个例子,如果中国的国际网络号是从左向右数使用了5个比特位,那么湖北省的网络号从左向右数使用的比特位数量一定是会多于5个的,所以在实际中会给不同的路由器配置不同位数的子网掩码,让不同的路由器得到同一个目的IP地址后能看到不同的网络号,让管理较大子网的路由器看到位数较少的网络号,让管理较小子网的路由器看到位数较多的网络号。
  • 比如说只让国际路由器的子网掩码的前5个比特位是1,后32-5=27个比特位为0,这样当有美国的一条IP数据包里的目的IP地址是湖北省某市某区的某台主机时,该IP数据包就会先经过国际路由器,国际路由器将目的IP地址和子网掩码进行按位与后能得到的网络号从左到右使用的比特位就只有5位,将该网络号和路由表中存储的各国的国际网络号进行对比后就会发现得到的这个网络号和中国的国际网络号相等,然后该IP数据包就会被发到跨省路由器,然后因为跨省子网比跨国子网的范围要小,所以根据上文可知各省的网络号从左向右数使用的比特位数量一定是会多于5个的,假如是8,则可以让跨省路由器的子网掩码的前8个比特位是1,后32-8=24个比特位为0,该IP数据包经过跨省路由器时,跨省路由器将目的IP地址和子网掩码进行按位与后能得到的网络号从左到右使用的比特位就只有8位,将该网络号和路由表中存储的各省的省网络号进行对比后就会发现得到的这个网络号和湖北省的省网络号相等,然后该IP数据包就会被发到湖北省的省路由器,后序省到市、市到区、最后到目标主机的过程和前面完全相同。
  • 可以发现这样一来,IP数据包不断经过路由器进行路由、不断地靠近目标主机所在的子网时,路由器将IP数据包里的目的IP地址和路由表中的子网掩码进行按位与得到的网络号所使用的比特位位数会越来越多,目的IP地址中的主机号所使用的比特位就会越来越少,也就是能逐步定位到范围越来越小的子网(毕竟之前说过,范围越小的子网的网络号从左向右数使用的比特位数量是越多的),最后定位到目的IP地址所在的主机。

以上就是IP报文在网络中路由的大概过程了。


讲解完IP报文在网络中路由的大概过程后,也就进行完了对前置知识点的铺垫,接下来回到正题:为什么要进行网段划分呢?

在上文讲解数据路由的过程时说过,主机A给主机B发送一条IP报文时,该IP报文在进行数据路由时并不会直接去找目标主机,而是会先找到目标主机所在的子网,然后再找目标主机,而之所以不一开始就以找目标主机为目的是因为这样效率太低了。比如说找主机的过程本质是排除的过程,如果一开始就以找目标主机为目的,那么在查找的过程中一次只能排除一个主机;而如果一开始先以找目标网络为目的,那么在查找过程中就能一次排除大量和目标主机不在同一子网的主机,这样就可以大大提高检索的效率。

而之所以上面这些用于提高数据路由效率的操作能够实现,即之所以一台主机A给目标主机B发IP报文时能先把IP报文发到目标主机B所在的子网里、然后再把报文发给目标主机B(或者说之所以路由器能通过IP数据包里的目的IP地址定位到该数据下一跳该前往哪个子网,进而让IP数据包通过路上的多个路由器逐步前往主机B所在的子网,最后到达主机B),就是因为运营商对网络(说一下,一个网络号就表示一个网络)或者说对所有的IP地址进行了网段划分、即按照某种规则将不同的IP地址归类到了不同的子网里(说得更详细点就是定制网络标准的组织把某个范围的IP地址全交给一个国家,假设这个范围是20.0.0.0,即前8位是网络号,后24位是主机号,一共可表示16777214台主机,一个国家拿到这个范围后又将其分成多个更小块的范围并分给每个省,比如每个小块范围分别是20.1.0.0、20.2.0.0、20.3.0.0,即前16位是网络号,后16位是主机号,每个小块范围可表示65534台主机,每个省拿到这个小块范围后又将其分成更小块的范围分给每个市...逐步下分,这里所说的进行切分对应的实际操作就是给管理不同地区的子网的运营商路由器设置不同位数的子网掩码,给不同的运营商路由器设置不同的路由表),比如既然最初你运营商能把目标IP地址根据划分规则划分到目标子网里,那现在我也能拿着目标IP地址根据同样的规则找到目标子网。所以当前板块的问题【为什么要进行网段划分?】的答案就是:第一、只有进行了网段划分才能让数据在网络中进行数据路由;第二、能方便在找目标IP地址时定位到该IP地址所在的子网(或者说网段)以提高数据路由的效率。

网段划分

过去曾经提出一种划分网络号和主机号的方案,就是把所有IP地址分为五类,如下图所示:

因此,各类IP地址的取值范围如下:

通过这样对IP地址进行网段划分的优势为:不管是路由器还是主机,在解析一个IP报文里的IP地址字段时能很快地区分出网络号和主机号,比如当要判断一个IP地址是属于哪一类时,只需要遍历IP地址的前5个比特位,第几个比特位最先出现0值,那么这个IP地址就属于A、B、C、D、E类地址,于是就能很快地区分出这条IP报文属于哪个子网,就能很快地将它推送给下一个路由器或者主机。

但随着网络的飞速发展,这种划分方案的局限性很快就显现出来了。比如一些学校、公司、实验室等组织想要申请自己的局域网,因为A类地址的网络号只占7个比特位,A类地址所能表示的子网只有2^7=128个,所以该地址是比较稀缺的,更何况A类的主机号占24个比特位,理论上一个A类网络当中允许有16777214台主机(如何计算已经在上图中说明过了),具有这个量级主机的组织一般都是一个国家了,所以大多数组织都不会选择申请A类地址,否则一下子就浪费了海量的IP地址;而C类地址大多数组织又都看不上,因为C类地址的主机号只占8个比特位,一个C类网络只能允许有(2^8)- 2 = 254 个主机(如何计算已经在上图中说明过了),估计一个网吧都不够用,所以大多数组织也不会选择C类地址;D、E类地址不是让主机用的,那最后大多数组织就只有选择B类地址了,但这也有问题,因为B类地址的主机号占16个比特位,所以理论上一个B类网络当中允许有65534台主机(如何计算已经在上图中说明过了),而实际网络架设中一般不会存在一个局域网当中有这么多主机的情况,也就意味着大量的IP地址实际都被浪费掉了。

为了避免这种情况发生,于是又提出了新的划分方案,称为CIDR(Classless Interdomain Routing)。如下图的下半部分,在这种方式中,32个比特位的IP地址就不再被分成3个部分了,而是被划分为两部分,前面是网络号,后面是主机号,不再有分类号、分类地址的概念了(分类地址就是说该IP地址的网络号和主机号所占的位数是固定的),而是能够随意灵活地控制一个IP地址中的网络号和主机号各自所占的比特位的数量的(当然不管如何变化,网络号和主机号各自所占的比特位数相加还是必须等于32的)。这样一来,对于在上一段中说的大多数组织选择B类地址会造成大量IP地址被浪费的问题就有办法解决了,(结合上一段思考)比如我们就可以让B类地址中用于表示网络号的比特位个数变得更多、让用于表示主机号的比特位个数变得更少(当然这样一来该IP地址就不是B类地址了),最后达成的效果就是相比于进行网段划分前,现在该IP地址所能表示的子网的数量变多了,并让每个子网中允许存在的最大主机数变少了。可以发现,通过这样的办法就可以解决在上一段中说的造成大量浪费的问题了。

问题:上面的方案的确很巧妙,但它还有一点没有交代清楚,那就是如何随意灵活地控制一个IP地址中的网络号和主机号各自所占的比特位的数量呢?

先回顾一下上文所讲的关于子网掩码的知识点:子网掩码是一个32位的正整数,通常用一串“0”来结尾,为了区分IP地址中的网络号和主机号,于是引入了子网掩码(subnet mask)的概念,每一个子网都有自己的子网掩码(或者说管理一个子网的路由器中的路由表里都有自己的子网掩码),将子网中一个主机的IP地址与当前子网的子网掩码进行“按位与”操作,就能够得到当前所在网络的网络号,说一下,每个子网都是被某个路由器进行管理的,而子网掩码就位于路由器的路由表中。

答案:通过修改子网掩码即可修改一个固定的IP地址中的网络号和主机号各自所占的比特位的数量。举个例子。

  • 假设某一子网当中有一台主机A对应的IP地址是192.168.128.192(最低位的192的二进制是1100 0000),那么如果想在该子网中将IP地址的前24位作为网络号,那么就可以通过将该网络对应的子网掩码的32个比特位中的前24位设置为1,剩下的8个比特位设置为0(具体对应的操作就是修改管理该子网的路由器中的路由表里的子网掩码),将其用点分十机制表示就是255.255.255.0,那么将主机A的IP地址与该网络对应的子网掩码进行“按位与”操作后得到的就是192.168.128.0,此后这就是主机A所在的子网对应的网络号,主机A的主机号就是192;
  • 如果此时又想在该子网中将IP地址的前25位作为网络号,那么就可以通过将该网络对应的子网掩码的32个比特位中的前25位设置为1,剩下的7个比特位设置为0(具体对应的操作就是修改管理该子网的路由器中的路由表里的子网掩码),将其用点分十机制表示就是255.255.255.128(最低位的128的二进制是1000 0000),那么将主机A的IP地址与该网络对应的子网掩码进行“按位与”操作后得到的就是192.168.128.128,此后这就是主机A所在的子网对应的网络号,主机A的主机号就是64(主机A的IP地址的最低8位是1100 0000,现在前25位是网络号, 那么最低的7位就是主机号,也就是说主机号是二进制的100 0000,十进制的64)
  • 实际在用子网掩码与子网当中主机的IP地址进行“按位与”操作时,本质就是保留了主机IP地址中前24个比特位的原貌,将剩下的8个比特位的值清0了而已,也就是将主机号清0了,所以“按位与”后的结果就是该网络对应的网络号。

说一下,可以在IP地址的后面加一个 /,并在 / 后面加上一个数字,比如a.b.c.d/x,其中/x 表示前 x 位属于网络号, x 的范围是0 ~ 32,比如 10.100.122.2/24,这种地址表示前 24 位是网络号,剩余的 8 位是主机号,实际上这里的 x 本质就是子网掩码,比如x=24时,说明子网掩码等于二进制数11111111 11111111 11111111 00000000,将其用点分十机制表示就是255.255.255.0。

特殊的IP地址

并不是所有的IP地址都能够作为主机的IP地址,有些IP地址本身就是具有特殊用途的。

  • 将IP地址中的主机地址全部设为0,就成为了网络号,代表这个局域网,这个IP就不能被主机使用。
  • 将IP地址中的主机地址全部设为1,就成为了广播地址,用于给同一个链路中相互连接的所有主机发送数据包,关于广播的知识点等到讲解数据链路层的文章中再进行说明,这里只需要知道广播地址这个IP也是不能被主机使用的。
  • 127.*的IP地址用于本机环回(loop back)测试,该IP地址被称为本地环回地址,通常是127.0.0.1,这个特殊IP是能被主机使用的。

也就是说,IP地址中主机号为全0的代表的是当前局域网的网络号,IP地址中主机号为全1的代表的是广播地址,这两个IP地址都是不能作为主机的IP地址的。因此在某个局域网中最多能存在的主机个数是2^(主机号位数)-2。

IP地址的数量限制

IP地址数量不足问题

我们知道,IP地址(IPv4)是一个4字节、即32个比特位的正整数,因此一共有2^32个IP地址,也就是将近43亿个IP地址。TCP/IP协议规定,每个主机都需要有一个IP地址,而现在全世界人口已经有70多亿了,就算有一半的人没有智能手机,算下来也有30多亿台智能手机需要IP地址,随着科技的发展,我们使用的电脑、智能手表、智能冰箱、智能洗衣机等设备如果要入网也是需要IP地址的,另外,IP地址并不是按照主机台数来配置的,因此一个主机可能需要多个IP地址,更别谈还有很多组网的路由设备也需要IP地址,以及一些特殊的IP地址不能使用的问题,所以43亿个IP地址其实早就不够用了,因此才提出了CIDR的方案以减少IP地址的浪费,注意CIDR虽然在一定程度上缓解了IP地址不够用的问题(因为CIDR提高了IP地址的利用率,减少了浪费),但IP地址的上限并没有增加,IP地址依然是不够用的。

那么如何解决IP地址不足的问题呢?(包括私有IP、公网IP、NAT技术的知识点)

解决IP地址不足有以下几种方式:

  • 动态分配IP地址:只给接入网络的设备分配IP地址,当设备不需要上网时就回收该IP地址以让其他设备使用该IP地址。注意这样本质也只是提高了IP地址的利用率,能够在一定程度上缓解IP地址不足的情况,但并没有提高同时可用的IP地址的上限,比如当有50亿个设备同时上网时,动态分配IP地址这种办法就无法解决IP地址不足的问题了,所以光靠动态分配IP地址无法彻底解决问题。
  • NAT技术:是能极大地解决IP地址不足问题的方案,三言两语说不清楚,在下文中再进行说明。
  • IPv6:IPv6用16字节、即128个比特位来表示一个IP地址,IPv6所能表示的IP地址有2^128个,这个量级理论上都能给地球上的每颗沙子分配一个IP地址了,但IPv6并不是IPv4的简单升级,它们彼此并不兼容,想要让全球都使用IPv6版本的IP地址,就需要对全球所有主机的操作系统内核进行修改,这样成本就太高了,不可能做到,所以目前IPv6还没有普及,所以IPv6也就无法解决IP地址不足的问题了。

问题:既然说NAT技术是能极大地解决IP地址不足问题的方案,那它是如何解决的呢?

答案:回答这个问题前,需要先讲解几个前置知识点。

私有IP和公网IP

公网IP:每个公网IP地址是全球范围内唯一的IP地址,用于在互联网上进行通信,具有公网IP地址的设备可以被所有设备(包括只具有私有IP的主机)访问。

私有IP(又称内网IP):私有IP并不是全球唯一的,不同局域网(局域网中只能存在私有IP,如果某主机具有公网IP,那它就不属于局域网,而是独立于局域网外、属于公网)中可以存在相同的私有IP,但同一个局域网中不可以存在相同的私有IP。这些私有IP地址专门用于内部网络通信,通常在局域网中被使用,即它们可以被用于内部设备之间的通信,如家庭网络、企业内部网络或组织内部网络。位于同一个局域网中的两个主机是可以直接进行通信的(原因会在下文中进行说明),即位于同一个局域网中的设备在进行互相通信时,数据只需要经过交换机,而不需要经过任何一个路由器(即既不会经过家用路由器,也不会经过运营商路由器),位于不同局域网中的两个主机是不能直接进行通信的(原因会在下文中进行说明),这两台主机想要进行通信就必须借助公网或者内网穿透技术(这一点可能需要看完下文的内容才能成功理解)

注意,只有私有IP的设备是无法和互联网或者说公网中的具有公网IP的设备直接通信的、而需要借助具有公网IP的运营商路由器间接进行通信,原因如下:

  • 不同的局域网中主机的私有IP地址可能是相同的,所以私网IP无法唯一标识一台主机,这样一来当公网中的设备想要给这台只具有私有IP的主机发信息时,就无法根据该私有IP定位到这台主机了。
  • 此外,只有私有IP的设备无法和互联网或者说公网中的设备直接通信的原因还包括:我们主机上的数据包是必须要经过运营商的中间设备路由器进行转发的,如果我们只具有私有IP的主机能直接和公网中的设备进行通信,那也就意味着我们再也不用给运营商交网费了,这是不现实的。

上文中说过IPv4版本的IP地址共有2^32个,现在我们要知道的是这2^32个IP地址并不都表示公网IP,而有一部分是私有IP,给各个国家,各个国家的各个区域进行网段划分时也不会将任何一个私有IP划分给它们,也就是说私有IP不会出现在公网上。

私有IP地址的范围如下,包含在这些范围内的IP地址都是私有IP,可以看到总和有近2000万个IP地址。

  • 10.*,前8位是网络号,共16,777,214个地址。
  • 172.16.*到172.31.*,前12位是网络号,共1,048,574个地址。
  • 192.168.*,前16位是网络号,共65,534个地址。

我们可以在自己电脑的cmd中输入ipconfig指令查看自己电脑的IP配置情况,如下图所示,下图中的以太网适配器表示有线连接,因为笔者使用的不是有线连接,而是使用的wifi,所以关于以太网适配器的那一行显示的是媒体已断开连接,而关于无线局域网适配器的那一行就会有配置信息,可以看到IPv4地址的开头是192.168,这就说明笔者使用的IP地址是私有IP,默认网关表示笔者主机所在的子网中的第一台设备的IP地址,即通常是管理该子网的路由器的IP地址。

如下图所示,我们连接云服务器时,连接的这个IP地址(即第一个红框处的IP地址)就是云服务器的公网IP地址。我们还可以通过ifconfig命令来查看云服务器这台机器的私网IP,可以看到私网IP地址是172.25.34.82。需要注意的是,这里连接云服务器时看到的IP地址47.115.228.32是云服务器的公网IP,并且因为我使用的是阿里云,所以这里的172.25.34.82是我这个云服务器在阿里内部的私网IP,可以看到这个IP正好在上文所说的第二种私网IP的范围内。

我们享受抖音、美团、头条、百度..等等软件的服务,可是到了月底却不需要给这些提供服务的互联网公司交钱,而是交钱(比如充话费、网费)给了运营商、即中国移动、中国联通等等公司,这是为什么呢?

实际上网络通信的基础设施都是运营商搭建的,我们下载抖音客户端软件、通过客户端给抖音服务器发送数据以访问服务器时,客户端的数据并不是直接被发送到抖音服务器,而是需要经过运营商建设的各种基站以及各种路由器,最终数据才能到达抖音服务器。

从上一段我们就能发现,如果没有运营商,我们甚至连信息都发不出去,互联网公司想要给我们提供服务就更是天方夜谭了,所以如果没有运营商提供的这些基础设施,就不会诞生所谓的互联网公司,因为互联网公司是诞生在网络通信基础之上的。也正是因为运营商为我们提供了通信的基础设施,所以我们每个月才需要给运营商交网费、交话费,这实际就相当于购买入网许可一样,就像高速路,如果你想上,就必须交钱,交钱的原因就是路是他铺设的。从上一段我们也能发现只要你欠话费或者欠网费就无法打电话和上网的原因,就是因为用于传输数据的中间设备都是运营商铺设的,只要运营商发现你是欠费用户,就直接会让中间设备把你发出去的通信数据丢弃,于是你发出去的通信数据就永远不可能到达目的主机(如果你使用的客户端是基于TCP协议进行通信的,那中间设备最初就会把TCP连接请求丢弃,目的主机也就不会进行应答,等你使用的主机的传输层进行个几次超时重传后,根据超时重传策略,客户端就会停止继续发送TCP连接请求,并告知用户无法上网或者打电话;如果你使用的客户端是基于UDP协议进行通信的,因为UDP通信是无连接且不可靠的,客户端不需要发起连接请求,服务端收到UDP报文后也不需要进行应答,所以中间设备把你发出去的通信数据丢弃后就没有下文了,表现出来的现象也是你无法上网或者打电话),于是你就无法上网或者打电话了。而即使你手机欠话费也能打通10086,就是因为中间设备发现你是在给10086打电话,于是它就不主动丢弃你的通信数据,于是你就能正常通信了。

至于为什么我们不需要给提供服务的互联网公司交钱,是因为比如你抖音或者其它软件都不是不可替代的,如果你抖音要收费,那我就玩b站、快手去了,所以一般来说互联网提供的服务都是免费模式。

你可以尝试一下访问谷歌或者Facebook等网站,可以发现是访问不了的,那么为什么会访问不了呢?

在我们的常识中,我们可能会听说过其原因就是因为有墙的存在,所以才导致我们访问不了。大多数人可能只记得答案是这样,但并不能深入理解其中本意,现在我们要知道,所谓的墙就是互联网中的某个路由器,既然在上文中说中间设备路由器能因为识别到用户欠费而丢弃用户的数据以让用户无法上网,那在这里中间设备路由器当然也能因为识别到用户的数据访问的是某个外网的网站而丢弃用户的数据以让用户无法上网。

再谈数据路由的过程

在上文讲解网段划分时,我们详细介绍过主机A给主机B发数据时数据在网络中进行数据路由的过程,现在我们有了私有IP的概念后,再对数据路由的过程做一点补充说明。

首先需要知道路由器是连接两个或多个网络的硬件设备,在路由器上有两种网络接口,分别是LAN口和WAN口:

  • LAN口(Local Area Network):表示连接当前子网的端口,主要与家庭网络中的交换机、集线器或PC相连。
  • WAN口(Wide Area Network):表示连接本地网络外的网络的端口,一般指比当前子网更大的子网。

我们将LAN口的IP地址叫做LAN口IP,也叫做子网IP,将WAN口的IP地址叫做WAN口IP,也叫做外网IP,也就是说,一个路由器至少具有两个IP地址,每个IP地址可能是私有IP,也可能是公网IP(比如家用路由器的LAN口IP和WAN口IP都是私有IP,而离家用路由器最近的运营商路由器的LAN口IP则是私有IP,WAN口IP则是公网IP,其他运营商路由器的两个IP地址全都是公网IP),当讨论的情况是路由器需要访问比该路由器管理的子网更大的子网时,这时我们认为路由器的IP地址是WAN口IP;当讨论的情况是路由器需要访问自己管理的子网时,或者子网内的设备想要访问路由器时,这时我们认为路由器的IP地址是LAN口IP。

然后要知道我们使用的电脑、家用路由器、运营商路由器、广域网以及我们要访问的服务器之间的关系大致如下:

  • 不同的家用路由器的子网IP其实都是一样的(通常都是192.168.1.1),子网内的主机IP地址不能重复,但是子网之间的IP地址就可以重复了。
  • 如果希望我们自己实现的服务器程序,能够在公网上被访问到,就需要把程序部署在一台具有外网IP的服务器(即主机)上,这样的服务器可以在阿里云/腾讯云上进行购买。

对数据路由的过程进行的补充说明为:

  • 局域网中的一台主机给公网中的目标服务器发数据时,数据在网络中进行数据路由时,因为私有IP不能出现在公网当中(其原因是私有IP在多个局域网中可以是相同的,这样一来当服务器主机收到局域网主机的请求信息准备给这个私有IP对应的主机发送响应信息时,就发现存在多个IP地址相同的主机,服务器就不知道发给谁了),因此局域网内的主机在和外网进行通信时,路由器会不断将数据包里的IP报头中的源IP地址替换成路由器的WAN口IP(因为数据包必须经过运营商的中间设备,既然在上文中运营商的中间设备能做到选择是否丢弃数据包,那在这里运营商的中间设备能做到修改数据包的源IP地址也没有什么不合理的了,有人可能会疑惑说【https协议不是做了加密吗?怎么能修改成功呢?】这里笔者要说的是,不要搞混淆了,https是在应用层起作用,只能给应用层数据加密,无法给下层的TCP/UDP报头和IP报头中的数据加密),这样逐级替换,替换到离家用路由器最近的运营商路由器时,数据包中的源IP地址就会成为一个公网IP(注意,数据包中的源IP地址被替换成公网IP后,后面的路由器再转发该数据包时就不会把其中的源IP地址替换成自己的WAN口地址了,已经没有必要了),然后由该运营商路由器代替局域网主机发数据给公网中的目标服务器,这样一来当服务器主机收到局域网主机的请求信息准备给这个主机发送响应信息时,就能通过这个公网IP把响应信息逐步发到这个公网IP对应的运营商路由器上,那运营商路由器如何再识别到该把数据发给局域网中的哪台主机并发送呢?如下。
  • 路由器在逐级替换数据包中的源IP地址时,不光只会替换源IP地址,还会构建一个如下图1所示的转换表,构建规则是:观察下图1中家用路由器的转换表,一个箭头的左边有两个【IP地址和端口号】,位于上面的【IP地址和端口号】表示局域网中的某台主机上的某个进程,位于下面的【IP地址和端口号】表示公网中的服务器上的服务端进程;一个箭头的右边也有两个【IP地址和端口号】,位于上面的【IP地址和端口号】表示被当前路由器进行过替换的IP地址和端口号(从这里我们就要知道实际上被逐级替换的可能不止是局域网主机的源IP地址,该主机上进程绑定的源端口号也是可能被替换的,那什么时候需要被替换、什么时候不需要被替换呢?如下图2的两个红框处所示,当一个箭头右边的两个【IP地址和端口号】和其他任意一个箭头右边的两个【IP地址和端口号】相等时就需要替换,即当同一个局域网中的两台主机都要访问同一个服务器时,如果这两台主机给各自进程绑定的端口号相同,那么在同一个路由器转换表中,就会如下图2的两个红框处所示存在两个完全相同的条目,这时后创建的条目中的源端口号就需要被替换成一个独一无二的端口号,比如需要如下图2所示,把第二个红框中的8080改成8081。反之,当一个箭头右边的两个【IP地址和端口号】和其他任意一个箭头右边的两个【IP地址和端口号】都不相等时就不需要替换了。为什么要替换端口号以保证每个条目独一无二呢?这就和路由器通过转换表来转发数据的工作模式有关了,因为位于箭头左边的IP地址和端口号标识子网中唯一的一台主机,所以在转换表中位于箭头左边的部分是独一无二的;同时虽然位于箭头右边的IP地址都是相同的,都表示拥有该转换表的当前路由器的IP地址,但通过之前所说的替换端口号的操作是能保证源端口号不一样的,所以在转换表中位于箭头右边的部分也是独一无二的。这样一来,(结合下图2思考)当服务器给主机A和B发送响应数据时,数据被发到家用路由器后,家用路由器一看数据的IP报头和TCP/UDP报头,发现发送端源IP地址和发送端源端口号分别是服务器的公网IP和服务端进程的绑定的端口号,发现目的IP地址和目的端口号分别是自己的IP和8080,也就是第一个红框中第一行的值,然后家用路由器就会在转发这条数据时,根据转换表把数据中的目的IP地址字段和目的端口号字段替换成第一个红框映射的、位于箭头左边的蓝框中的第一行的值198.168.1.201:8080,该值中的IP地址和端口号分别是用于表示主机A和对应的通信进程的,所以家用路由器在查询路由表进行比对后也就能把数据发给发给主机A了;同理当家用路由器一看数据的IP报头和TCP/UDP报头,发现发送端源IP地址和发送端源端口号分别是服务器的公网IP和服务端进程的绑定的端口号,发现目的IP地址和目的端口号分别是自己的IP和8081,也就是第二个红框中第一行的值,然后家用路由器就会在转发这条数据时,根据转换表把数据中的目的IP地址字段和目的端口号字段替换成第二个红框映射的、位于箭头左边的蓝框中的第一行的值198.168.1.200:8080,该值中的IP地址和端口号分别是用于表示主机B和对应的通信进程的,所以家用路由器在查询路由表进行比对后也就能把数据发给主机B了。所以读到这里,我们就能理解为什么要替换端口号以保证每个条目独一无二,其就是为了建立独一无二的映射关系,保证路由器能正确地将位于更大子网中的主机回复的数据逐级转发回给位于更小子网中的主机。走到这里就回答完了在上一段末尾留下的问题),位于下面的【IP地址和端口号】表示公网中的服务器上的服务端进程。
  • 像这种逐级替换WAN口IP的技术就叫做NAT技术(Network Address Translation,网络地址转换)。说一下,有人可能会对上一段的内容感到疑惑,说【当一个子网中有很多主机时,管理该子网的路由器岂不是要维护大量的这样的转换关系?路由器能承受得住吗?】,这里笔者要说的是,不必担心,如果一个路由器能管理很大的子网,那么这个路由器就不是我们买到的几百块的家用路由器了,而是企业级的路由器,这样的路由器的算力或者其他配置是很高的,能承受得住,再者这样的转换关系也不是需要一直维护的,因为当你的主机发起四次挥手断开和服务器的连接时,沿路上的所有具有转换表的路由器都能识别出这是用于断开连接的报文,于是这些路由器就都会清除对应的转换关系以减轻路由器的负担(当然不具有转换表的路由器就不必清理了)

图1如下。

图2如下。

走到这里,对于最初的问题【既然说NAT技术是能极大地解决IP地址不足问题的方案,那它是如何解决的呢?】的前置知识点也就讲解完毕了,接下来公布答案,如下:

由于IP地址不足的原因,我们不能让主机直接使用公网IP而让主机使用私网IP,因为私网IP可以重复也就意味着我们可以在不同的局域网使用相同的IP地址,这就能极大地解决IP不足的问题,以中国举例,我们可以把一个省内的各市构建成不同的大型局域网,即【让每个市里不具有公网IP的主机使用相同的私有IP,并让一个WAN口IP是公网IP的运营商路由器管理一整个市的私有IP】,这样一来,每当有局域网中的主机想访问公网上的某个公网IP对应的服务器时,就通过上文所讲的NAT技术把主机发送的数据包里的私有IP逐级替换成运营商路由器的公网WAN口IP以让运营商路由器代理局域网主机访问服务器、以让服务器能够在可能相同的私有IP中区分出该局域网主机在哪,进而双方能够成功通信。假如某个市中的主机数量非常多,只构建一个局域网可能容纳不下,那就再给这个市分配一个具有公网WAN口IP的路由器以再组建并管理一个局域网,然后就可以让剩下的主机再使用和另一个局域网中相同的私有IP地址了。可以看到通过这样的方式,假如一个省有20个市,那么一个省中就只有管理每个市的运营商路由器才需要被分配公网IP地址,换句话说就是通过NAT技术和私有IP地址,只需要给每个省分配20个公网IP地址即可让整个省的主机都能上网,这就极大了缓解了IP地址不足的问题,这就是问题的最终答案了。对于本段中的内容,常见的疑惑有如下几点:

  • 有人可能会疑惑【一个市里有些服务器、即主机是具有公网IP的,如果把一整个市构建成一个大型局域网,那公网IP岂不是也被算进局域网内了?】,这里笔者想说的是,如果主机具有公网IP,那它就不会被构建进任何一个局域网里,而是直接位于公网中,可以被任何设备访问到;
  • 有人还可能会疑惑【把一个市中的所有只具有私有IP的主机构建成两个局域网后,如果主机A属于局域网A,主机B属于局域网B,那么主机A和主机B可以进行通信吗?如果不能,在一个市里构建两个局域网难道是合理的吗?毕竟我俩主机相隔这么近,却无法通信?】,这里笔者想说的是,因为局域网中的主机只具有私有IP而不具有公网IP,那么不同局域网中的主机实际上是不能直接进行通信的,因为局域网A中的主机A完全不具备识别其他局域网中的任意一台主机的能力,主机A压根找不到主机B在哪,这又是因为主机A和主机B的IP地址都是私有IP地址,主机B的IP地址可能不止存在于局域网B,还可能存在于局域网C、D、E,在这种情况下,主机A没法识别该到哪个局域网中找主机B,于是主机A就只会在自己局域网中进行查找,所以位于不同局域网中的主机是不能进行通信的,只有位于相同局域网中的主机才能进行互相通信。既然位于不同局域网中的主机不能直接进行通信,那在一个市里构建两个局域网难道是合理的吗?毕竟我俩主机相隔这么近,却无法通信?这里笔者想说,是很合理的,位于两个不同局域网中的主机本来就不该能进行通信,在上文中说过,只有具有公网IP的主机才能被所有设备访问到,所以如果你想让不和主机A属于同一个局域网的主机B能被主机A访问到,那一开始就不应该让主机B位于一个局域网中,而是给主机B分配一个公网IP、把主机B放到公网中,另外,主机A想要和主机B进行通信也并非只能靠直接通信这种办法,是可以靠间接通信的,拿QQ举个例子,一般主机A给主机B发数据时,并不是直接把数据发给了位于另一个局域网中的主机B,当然从技术角度上也发送不了,而是先把数据发给具有公网IP的位于公网中的QQ服务器,然后QQ服务器再把数据经过公网发给管理局域网B的运营商路由器(QQ服务器通过服务端的代码编写逻辑,是能知道管理局域网B的具有公网WAN口IP的运营商路由器的公网IP的,所以服务器能找到该运营商路由器),最后再由该运营商路由器将数据发给主机B。从这里也能看出,位于不同局域网中的两个主机想要进行通信,通常来说通信数据是不得不先经过公网的,说“通常”而不是说“绝对”是因为存在一种叫做内网穿透、也叫做NAT穿透的技术能够让主机A和主机B不通过公网进行间接通信,而是直接进行通信,除了这种特殊情况,可以认为位于不同局域网中的两台主机是不能直接进行通信的,如果对内网穿透感兴趣,请自行查阅资源。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值