赞
踩
目录
对全部的IP地址进行网段划分(或者说按照某种规则将不同的IP地址归类到不同子网)
那么如何解决IP地址不足的问题呢?(包括私有IP、公网IP、NAT技术的知识点)
IP协议全称为“网际互连协议(Internet Protocol)”,IP协议是TCP/IP体系中的网络层协议。TCP作为传输层控制协议,其保证的是数据传输的可靠性和传输效率,TCP提供的仅仅是数据传输的策略,而真正负责数据在网络中传输的则传输层之下的网络层和链路层,举个例子,小明他老爹是教导主任,为了保证小明每次都能考100分,只要小明在没有考到100分时,老爹就宣布全员重考,在这个例子中,老爹就像TCP协议,只负责可靠性和效率,不能代小明去考试(即完全不负责传输数据),小明就像网络层的IP协议和数据链路层,是真正参加考试的人(即真正负责传输数据)。
说一下,更准确地说实际上IP协议也不真正负责传输数据,IP协议和TCP协议一样也只负责提供传输数据的策略,只不过策略的着重点不一样,IP协议提供的策略主要是负责给数据指路,真正负责传输数据的只有数据链路层,举个例子,当我们想去某个地方旅游时是需要一本指南攻略和一俩车的,IP协议就像这本指南攻略,数据链路层就像车,如果没有IP协议,每到达一个中间站就不知道下一步该往哪走,而如果没有车,则一步都无法前进,一切都是空谈了。另外,下图是表示数据在网络中转发的过程图中,如果网络层直接就具备传输数据的能力,那数据就不需要从主机A的网络层向下交付给数据链路层、而是直接从主机A的网络层发给路由器的网络层了。
如上图就很好地说明了IP协议所处的位置。额外说下两个知识点:
1、在socket套接字编程时用到的各种socket套接字接口都是位于应用层和传输层之间的一层系统调用接口,这些接口是系统提供的,我们可以通过这些接口搭建上层应用。我们经常说HTTP是基于TCP的,实际就是因为HTTP报文是通过调用【和TCP协议强相关的socket套接字接口】实现在网络中传输的。
2、socket套接字层往下的传输层实际就是由操作系统管理的,因此UDP和TCP是属于内核当中的,是操作系统本身协议栈自带的,其代码不是由上层用户编写的,UDP和TCP的所有功能都是由操作系统完成,因此网络也是操作系统的一部分。
IP报文的报头当中各个字段的含义如下:
(结合下图思考)操作系统内核是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协议的文章中已经进行过说明了,这里不再赘述。
主机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报文就都有了一个生存时间,不会一直在网络中存活,上面的问题也就完美地被解决了。
网络层的下一层、即数据链路层因为其物理特性,一般是无法在网络中转发太大的数据的,数据链路层一次性可以转发到网络中的数据的上限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报文是不是分片后,就可以通过上文所讲的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报文的所有分片都已经接收完全了。具体如何做到挨个寻找呢?如下。
可以看到,通过上面这样的方式,接收端就能得知自己是否将属于某个大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报文从而进行分片操作进而影响到双方的通信效率。
在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命令查看云服务器上对应的路由表。
当IP数据包到达路由器时,路由器就会用该IP数据包的目的IP地址与路由表中的位于第一行的子网掩码Genmask进行“按位与”操作得到一个网络号,然后将该网络号与位于第一行的目的网络地址Destination进行比对,如果相等则说明该数据包下一跳就应该跳去这个网络号对应的子网,此时就会将该IP数据包通过对应的发送接口Iface发送到这个网络号对应的子网;如果不相等,则路由器就会用该IP数据包的目的IP地址与路由表中的位于第二行的子网掩码Genmask进行“按位与”操作得到一个网络号,然后将该网络号与位于第二行的目的网络地址Destination进行比对,结果是相等或者不相等分别会执行的操作和之前一样;如果将路由表中的每一行进行比对后都没有找到和网络号相等的目的网络地址,则此时路由器就会将这个IP数据包(或者说IP报文)发送到默认路由,也就是路由表中目标网络地址中的default。可以看到默认路由对应的Flags是UG,实际就是将该数据转给了另一台路由器(因为在上面说过G标志表示此条目的下一跳地址是某个路由器的地址),让该数据在另一台路由器继续进行路由。
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地址中的网络号和主机号各自所占的比特位的数量。举个例子。
说一下,可以在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地址中主机号为全0的代表的是当前局域网的网络号,IP地址中主机号为全1的代表的是广播地址,这两个IP地址都是不能作为主机的IP地址的。因此在某个局域网中最多能存在的主机个数是2^(主机号位数)-2。
我们知道,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地址不足有以下几种方式:
问题:既然说NAT技术是能极大地解决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地址。
我们可以在自己电脑的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口的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。
然后要知道我们使用的电脑、家用路由器、运营商路由器、广域网以及我们要访问的服务器之间的关系大致如下:
对数据路由的过程进行的补充说明为:
图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地址不足的问题,这就是问题的最终答案了。对于本段中的内容,常见的疑惑有如下几点:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。