哪里做网站做的好,亚马逊官网首页中国,手机论坛网站怎么做,wordpress 不发邮件目录
IP层 IP报文格式
IP的理解
运营商
分片与组装 IP层
传输层的TCP或者UDP协议能直接将数据发送到网络中吗#xff1f;显然不能#xff0c;封装完的TCP报文还是需要向下交付#xff0c;经过协议栈#xff0c;从链路层发送到物理层也就是网路中。
那么tcp做了什么工…目录
IP层 IP报文格式
IP的理解
运营商
分片与组装 IP层
传输层的TCP或者UDP协议能直接将数据发送到网络中吗显然不能封装完的TCP报文还是需要向下交付经过协议栈从链路层发送到物理层也就是网路中。
那么tcp做了什么工作呢 保证可靠性与效率。 传输层往下的协议就不会考虑可靠性或者像TCP一样保证可靠性的策略像网络层他的重点在于路由的转发以及定位对应的主机而链路层的核心在于真正的传输他是真正将数据发送到网络中的协议。
在我们讲协议栈的时候就说了网络协议栈之所以分层其中一个原因就是每一层都重点解决一类问题高内聚。 IP层(IP地址)的核心工作是什么呢
IP地址的作用就是用来定位主机的。同时IP层还要具有将数据报从a主机跨网络送到b主机的能力这个能力并不是说具体的传送工作而是说它可以指挥下层如何转发数据报以及如何选择路径。
但是IP层虽然能够将数据报跨网络转发但是有能力就一定能做到呢网络转发过程中一定可能会出现异常状况而如果在途中丢包了IP层也不需要关心因为他只负责选择路径和封装具体的关于可靠性等传输的策略都是由传输层来完成的而网络层主要负责定位和路由的功能。
那么IP和网络层就能保证数据报的跨网络的可靠传输了。而跨网络通信的本质就是网络通信的本质。
那么IP是怎么做到跨网络转发的呢
数据在跨网络过程中是要经过路由器的路由器就是一个网络传输中的节点或者我们也可以将其直接看成一个简易的主机只不过不考虑应用层和传输层工作在网络层路由器通过将报文中的目标IP地址提取出来然后选择一条最优路径将其转发给网络中的下一个节点最终交付到目标主机。 在路径选择中目的IP非常重要决定了报文该如何转发。 在报文的转发过程中目的IP是不变的而源IP可能发生变化(比如在运营商子网中)同时我们还有一个地址是时刻变化的就是我们前面讲过的 mac 地址报文中的源mac和目的mac地址在转发途中是不断在更新的永远保存的是上一个节点和下一个节点的mac。 所有的路由器在处理我们的报文的时候都是根据目的IP进行决策的。 IP 目标网络 目标主机 这一点我们可能不好理解但是后续我们会使用一些案例来说明这一点。我们的一个IP地址肯定不是当成一个整体来用的就跟我们的身份证一样前几位是用来表明我们的所属地的跟我们同一属地的人的身份证号码对应前几位都是相同的后面的数字就是用来标识我们的唯一性的这样设计的好处就是当你拿到一个身份证号的时候你只需要从前几位就能判断出这个身份证号是属于哪个地区的那么有什么优势呢我们后续会讲到。 IP 也是一样的可能前面几位是表示目标主机所在网络的后面的几位才表示的是该主机在该网络中的编号也就是主机号这样当然也能标识唯一性定位唯一主机。当然网络层也只负责送到目标主机至于到达主机之后交付给对应的进程的工作是传输层和端口号的工作。 目前我们可以认识一下三个概念
主机配有IP地址可以进行路由控制的设备
路由器配有IP地址主要负责路由控制
节点路由器和主机的统称 IP报文格式
报文格式如图 首先在初步学习IP协议的时候我们不关心16位标识3位标志和13位片偏移这是后续结合MAC协议来学习的。
首先我们根据报头字段来了解IP报文如何解包和分用
首先跟TCP协议一样他的报头是变长的所以他也需要有一个首部长度字段来表示报头的长度也就是四位首部长度这个首部长度字段和TCP中的首部长度是一模一样的单位是4字节那么IP报头的取值范围也是 [20,60] 字节。
那么我们如何保证一个IP报文是被接收方网络层完整收到的呢
也很简单首先报头中还有两个字段 16 位总长度和16位首部检验和首先根据首部检验和以及4位首部长度能够将IP报头提取出来并检验 IP 报头的完整性和正确性。检验正确之后使用16总长度来和我们收到的IP报文的长度做对比如果相等那么就意味着有效载荷的长度和发出的IP报文的有效载荷是一样的至于其中的内容是否出现差错这是TCP报头中的检验和来检查的。IP层只能保证收到的报文的有效载荷长度和发出时是一样的只能讲这种出现明显错误的报文检测出来具体的可靠性检验还是需要TCP来完成。
这也说明了IP层不负责数据的可靠传输这不是IP层该操心的。
然后就是如何交付给上层的问题传输层是由不同的协议的那么网络层收到IP报文之后解包出来该把有效载荷交给传输层的哪个协议呢
很明显IP报头中有一个字段是 8 位协议他表示的就是上层协议的协议号。
在发送方传输层将数据交给IP层的时候与此同时会将协议号交给IP层那么IP层会讲上层的协议号填充到报头的8位协议字段中以便接收方的网络层进行分用。、 8位生存时间
首先我们要了解一个概念数据在网络传输时注定要经过网络中的节点可能是路由器也可能是送达目标主机那么数据包从源主机发出后每经过一个路由器就称为一跳。
同时网络的拓朴结构是十分复杂的我们无法保证数据在网络的转发过程中不会出现问题因为数据一旦从源主机发出去之后就已经脱离了源主机的控制生死有命富贵在天了。 数据再转发的过程中有可能遇到故障的路由器或者遇到一些原因导致目标主机不可达最终可能会导致我们的报文在一条环路中进行转发我们可以理解为在死循环。 如果这种情况不进行处理就会导致网络中存在大量的环路转发的报文占用网络资源对路由器造成不必要的负担。 或者因为目标主机不可达了那么这时候路由器该怎么做 所以我们必须赋予路由器一个权力有权力丢弃转发到他这里的报文。
所以在IP报头中有一个 8 位生存时间这个生存时间并不是指的我们日常所有的时间几分几秒之类的而是指的报文的跳数(TTL)。TTL就代表了从源主机发送出去在网络中所经历的最大跳数。 我们知道路由器收到报文之后首先在链路层会进行解包交给网络层在网络层也要解包提取出目标 IP 地址然后决定转发的路径再重新封装IP报头并交给链路层。在这个时候路由器也会将该报文的TTL字段进行减一操作。 每经过一个网络节点报文的TTL都会进行减一操作当TTL减到0时就会被路由器直接丢弃。所以有了生存时间就不用担心一个报文在环路转发一直消耗资源的情况了因为经过一定的跳数就会被丢弃。 8位的跳数其实在现在的网络环境中已经完全够用了我们不要小看了路由器和底层的传输距离以及网络的拓扑结构的复杂程度。 目前来说我们的报文经过几个或者十几个路由器就已经完全能够送到目标主机了。
数据包脱离源主机之后路上的节点怎么知道这个报文被谁发出要交给谁 这就要看报头中的32位源IP地址和32位目标IP地址。 尤其是目的IP地址路由器会根据这个目的IP进行路由的选择。
所以我们在调用 bind函数进行绑定IP和端口号 或者说 connect 进行连接请求的时候其实把IP交给了网络层把端口交给了传输层最终网络层和传输层会使用目的IP和端口封装进自己的报头中。
4位版本版本其实指的是IP的版本。 一般填的是 4 表示使用ipv4的IP地址。
那么能不能在这个字段填上 6 表示ipv6呢显然是不可以的我们也能看到报头中的源IP和目的IP都是32位的也放不下ipv6的地址16个字节128位就算加上选项字段也放不进。
ipv4和ipv6是不兼容的。所以这个字段一般都是固定的我们也不需要关心这个字段。
我们也知道ipv4的32位IP地址在当前的网络环境是远远不够的入网的设备远远超过IP的总数毕竟设计的时候谁也无法想到未来网络设备会如此之多这是因为时代的局限性没有办法的事情。
既然ipv4地址远远不够那么就需要有对应的解决方案目前来说国际上主流的解决方案还是在ipv4的基础上进行填坑与挖坑虽然能暂时解决燃眉之急但是从长远来看不是根本的解决方案。
那么为什么不全部替换成ipv6的网络呢其中一个重要的原因还是政治因素由于我国的ipv6是世界领先的如果直接将全球网络替换为ipv6那么主导权就在我国了那么有些眼红的霸权国家肯定容不下。 其次基础设施也是一大因素因为ipv4和ipv6不兼容如果要全面更换到ipv6那么意味着目前的所有的只提供ipv4服务的基础设备都无法使用了需要更换到ipv6的设备这个工作量是十分巨大的虽然我国在内网中很多场景下都已经开始使用ipv6了但是想要全球都进行设备的更新还是太难做到了。还有一个原因就是网络层是植入在操作系统内核的如果要使用ipv6那么意味着我们的操作系统也需要做更新不只是一个人的操作系统需要更新而是全网都需要更新到支持ipv6的系统这也是很麻烦的影响太大。 所以目前来说我们在内网中虽然也会用到ipv6但是在公网环境还是使用ipv4。那么在内网发送到公网时就需要我们的路由器进行ip地址的替换。 这在后续我们提到NAT技术时会了解。 8位服务类型
通常指的是我们的数据包传输时选择最优路径的标准。 比如我们需要的是最快到达最高可靠性最小丢包还是其他的要求或者服务其实就是在为路由选择的最优路径下定义。
那么至此我们就已经将IP报头解析完了剩下的4个字节的内容在IP报文的分片与组装会用到。 IP的理解
我们说IP要拆分成两个部分来看网络号和主机号。
网络号和主机号分别是什么呢 为什么要这样划分IP呢怎么划分
先说为什么要划分本质是为了提高查找效率。
我们举一个例子在我们的大学中有许多学院同时在学校中我们使用学号来标识每一个学生学号在学校里是唯一的。假设学校为了管理和学生之间的交流将一个学院的学生都拉进了一个群聊中同时为每个学院的设计了一个管理者这个管理者也是学生他也有自己的学号。 同时为了方便学院之间的交流还存在一个学校的群但是这个群聊只有各个学院的管理者加入。那么各个学院的管理员我们就可以称之为与其他学院交流的出口。 在学校中我们的学号是被设计过的比如前几位表示所属学院。 那么在某一天一个普通的学生如果有消息需要转发给一个特定的学号的学生。首先在他看来他只知道所属学院的学院号如果目标学号的学院号和他的学院号相同那么他就能知道对方是跟他在一个群聊中的那么他就可以在群聊中的成员中找到对应的学号的学生将消息发送在群聊中并目标学生这时候所有学生都会收到这条消息但是绝大部分学生会直接忽略这个因为不是目标对象只有指定学号的学生会对这条消息进行处理。如果在群聊中有些学生并没有讲昵称修改为学号那么这时候他就需要在群聊中发送一条消息比如”谁是学号xxxxx请回复我“这时候群聊中所有的学生都会受到并处理这条消息但是绝大多数都不会进行恢复只有对应学号的学生会进行恢复并我们的询问方 。 这时候就能找到目标学生了我们可以对其设置标签将学号与这个微信号绑定并且将消息发送给他目标对象。
但是如果我们发现该学号的前几位学院号并不是我所处的学院那么由于我们不清楚或者说不敢自己猜测其他的学院的学号的定义方式这时候我们就无法知道这个消息该发送给谁。但是我们普通学生可能不知道管理员肯定是要知道的这时候我们会将消息以及消息要发送给哪个学号的学生发送在群聊中并且管理员。 而管理员收到我们发出的消息之后由于他的属性设定他是知道学号是怎么划分的这时候它能够将目标学号的学院号提取出来根据学院号将消息发送给学校群聊中的对应学院的管理员(入口)这时候对应的管理员拿到了消息就变成了学院群内的转发了那么就和前面的情况一样了。
那么在这里学号前面部分的学院号起了什么作用呢?
在跨学院转发时管理员能够根据学号前面的学院号得知消息属于哪个学院将该消息转发给对应的学院的入口就相当于将其他的学院都排除在外了。而消息的转发过程我们其实就是一个查找的过程查找的本质就是排除那么在我们学院的出口一次就将其他的学院的学生排除了排除的效率高了也就代表查找的效率高了。
在上面的案例中我们可以理解为学院的群聊就是局域网而普通学生就是一个个的主机管理者就是一个出口或者入口路由器所有的出口路由器处在一个我们也可以理解为大的局域网中。而学号就是我们的IP地址学号前面部分的学院号就是网络号能够以定位在那个局域网中而在局域网内则是主要依赖学号中的除学院号的字段来表示局域网内的主机这就是主机号。
所以在互联网中每一台主机都要隶属于一个子网为了方便定位这个主机提高查找的效率。
同时这也意味着全球在进行网络规划的时候也离不开一个话题如何进行子网划分。
也就是怎么进行IP的划分
互联网是一个被设计过的世界那么是谁设计的呢对于我国来说设计者就是运营商因为他们是基础设施的建设者有绝对的话语权。但是更高级别的运营商的IP又是怎么来的呢 这就需要全球的各个地区共同协商出一个标准将IP按照某些约定分配给各个区域。
我们假设IP是按照国家进行划分的实际上是按照区域划分IP地址一共是32位假设前 8 个位用来表示国家那么各国按照标准在自己的内网中只能使用前八位为自己编号的IP地址剩下的24位由各个国家自行划分。 同时每个国家都由一个国际路由器所有的国家的国际路由器都处在一个子网当中如果需要数据的跨网络传输就可以经过国际路由器进行转发给对应的国家的国际路由器然后由对方的国际路由器进行转发。
那么在国家内部假设在我国分配到的所有的ip地址的前八位都是相同的那么这些IP地址怎么划分给各个省份呢由运营商进行标准的指定比如再使用 6 个比特位用来表示各个省份每个省份只能够使用自己对应省份号的IP这时候前14位就相当于网络号。 同时IP分配给各个省份之后再在省内进行IP的分配比如再使用6个比特位来标识每个市的编号那么又将IP分配到了市而每个市还可以再使用几个比特位来表示区县将IP地址再划分。当到区县这一级之后我们就发现IP地址完全不够分配给每一个人了这时候运营商就会划分区域组建内网一个内网分配一个对外转发的公网IP。 当IP进行划分之后数据的转发就很明显了在各级网络中网络号的位数是不同的在各级转发时根据网络号决定将其转发给上级网络还是在本局域网内转发。
这就是一个潦草版的子网划分。 实际中的划分情况远比这种方式复杂 需要考虑各种因素不过我们不需要了解这么深入。
不管怎么说网络中的每个主机都必须处于同一网段内
不同网段的主机的网络号不同但是主机号可以相同。
而同一网段内的网络号相同主机号必须不同。 两个不同的网段如果是直接相连的他们之间必须要有路由器这也意味着路由器至少要同时处于两个子网中也就意味着路由器必须至少要有两个IP地址就必须至少要有两张网卡。一般情况下路由器的IP(在自己构建的子网那一端)主机号就是 1 。
同时在一个子网中不断会有主机接入到网络以及主机下线退出网络这时候就需要对子网的IP地址做管理这个管理工作也是路由器做的。以前是通过网络管理员手动为每一台入网的主机分配IP地址但是十分不方便后续出现了 DHCP 技术取代了手动的网管。 那么IP真实是怎么划分的呢
首先最开始的时候IP说是被分为 ABCDE 五类地址的。 首先这几类地址就是根据从左往右的第一个为 0 的比特位来进行标识的。
第一个比特位为 0 的IP是 A 类地址他的主机号有24位除去全0之外最多能给2^24-1台主机分配IP地址也就是一千六百多万
第二个比特位为 0 的IP是 B类地址他的主机号有16位除去全 0 之外 还有2^16-1,也就是65535 个
第三个比特位为 0 的是 C 类地址他的主机号有 8 位那么主机数最多就是 2^8-1 也就是 255 个
第四个比特位为 0 的是D类地址。D类地址一般用于多点广播也就是一个标识一个群组可以同时向该群组的多个主机发送消息其实就是广播地址。
第五个比特位为 0 的地址是 E类地址留待后用。
注意上面的地址的划分是对网络的整体进行划分他在划分的时候并没有考虑国家地区也没有规定哪些地址属于哪些地区。
划分好之后国家组织企业学校等就可以去申请这几类网络了。
但是这样一来其实使得本就不富裕的IP数量更加捉襟见肘了。
试想一下A类地址一般用来干什么 一千六百多万个主机的网络是不是太浪费了。 又比如说一个学校申请一个B类地址(先不考虑B类地址不够的问题)那么假设学校里只有 几千或者两三万人可是一个B类网络可以标识六万多IP 剩下的也是浪费了。
IP地址本身就不够用了通过这样固定的划分方式还会导致很多的浪费现象。
所以这种划分方案很快就被弃用了新的解决方案称为 CIDR 也就是地址掩码的方案。
CIDR就是在IP地址的基础上增加了一个子网掩码的概念用子网掩码从一个IP中提取出网络号和主机号。
子网掩码也是一个32位的整数由一串 1 和一串 0 构成。
IP地址按位与上子网掩码就是我们的网络号而剩下的就是主机号。 有了子网掩码的方案之后我们就可以自己定义网络号为多少位更加灵活的进行 IP 的划分。从此以后就只有子网掩码而跟ABCDE类网络的概念没有关系了。
虽然有了子网掩码的概念但是IP整体在公网中还是不能冲突的要保证唯一性。
那么所谓的子网 或者说 子网掩码在哪呢
子网是由路由器管理的子网的网络号子网掩码主机号都会有路由器进行配置或者分配。
所以所谓的子网掩码和网络号就是在路由器中事先配置好的数据。
当然主机号全 0 的还是不使用用来表示网络号全 1 也不用用来当作广播地址用来向局域网或者子网中所有主机广播数据时使用。 而 1 号主机还是路由器。
还有一类地址不在公网中使用就是 127.* 也就是前八位为 127 的IP都不能用在公网中而是用于本地环回代表本主机自己通常是 127.0.0.1
但是CIDR方案也只是优化了子网划分减少了浪费率并没有增加IP的数量上限IP仍然是不够用的状况。
有三种解决方案
1 动态分配IP地址只给接入网络的设备分配IP地址。 因此同一个设备接入用一个网络时每次接入可能分配的IP地址不一样。 这个我们的路由器中基本都有DHCP。
2 NAT 技术就是一种地址转换的服务内网IP和公网IP之间在路由器中相互转换。后续我们会将私网和公网的区别。
3 IPV6这是最根本的解决方案。
当然由于NAT技术只需要让我们的路由器支持NAT服务就可以了并不需要更改我们的操作系统的基础设施所以在目前来看还是一个能缓解IP不足的方案但是从长远来看根本的解决方案还是使用IPV6。NAT技术也间接的阻碍了ipv6地推广不过他也会IPV6提供有了一种思路既然NAT能将内网IP转换为公网IP自然也可以设计一个服务将IPV6转换为IPV4在内网中使用IPV6一旦要将数据转发到公网将地址更换为公网地IPV4地址。 所以我们在内网中可能是用的IP地址是IPV6地但是照样可以访问公网。
内网IP
如果一个组织内部组建局域网IP地址只能用于该局域网内的通信而不能直接连接到因特网(公网)理论上任意地址都可以用来构建内网但是 RFC 1918 规定了用于组建局域网的私有IP地址
10.* 前 8 位是网络号共一共16777216个地址
172.16./* ~ 173.31./* ,前12位为网络号共1048576个地址
192.168./* : 前16位是网络号共65536 个地址
规定只能用这三类地址来组建局域网在这三类地址范围内的IP我们成为私有IP。 除这些意外的都叫全局或者公网IP。
所以我们前面一直说的IP具有唯一性其实指的是公网IP具有唯一性。
如果是内网IP就只在他所处的局域网唯一。除了局域网没有唯一性。
我们经常使用的IP都是私有IP比如学校中使用的 192.168. 或者我们云服务器使用的IP就是 10.*或者172... 。
同时从上图中我们也可以看到并不是说网络号就只能是 8 位或者 16 位等组织内部还可以对其进行划分将更多的比特位作为网络号在内部进行使用。 运营商
运营上是网络基础设施的搭建者。
对于我们普通人来说我们要访问服务器是必须经过运营商的。因为在物理设施上就已经决定了。
平常我们所谓的入网其实就是附近的运营商路由器中拉一根网线然后街道我们自己的家用的路由器上在构建一个局域网。
因为我们的消息需要经由局域网在运营商网络中不断转发最终才能到达服务器那么就意味着我们的数据报文是在运营商的控制之下的。 如果你欠费了或者王菲非法IP那么运营商就可以在路由器转发的时候设置一定的策略直接将你的报文丢弃导致你连不上对应的服务器。
同时运营商也会对你的上网内容进行审核有时候会拦截你的范围这就叫做“长城”也叫“墙”其实就是在运营商的转发逻辑中对你的报文拦截。 目前我们所知道的路由器的功能
数据包的转发DHCP组建局域网(其实就是DHCP)NAT
想必我们也见过如何接网其实就是在附近的运营商路由器中接一根网线到我们的家用路由器然后配置家用路由器设置网络名称和密码等就组建好局域网了。我们的家用路由器一般功率都比较低覆盖范围小不过家庭用也够了而对应的运营商路由器肯定是要比我们的家用路由器要强大得多的同时他们也有组建内网的功能。
那么目前来说,我们的家用路由器要组建局域网也要连接运营商路由器其实就是在运营商路由器组建的子网中。
但是运营商的路由器一般可不只是一级而是有很多很多级的运营商路由器组建的子网在运营商内网中不断进行转发才能发送到公网。 一般路由器都会配有两个或以上的IP对内也就是处于自己所组建的子网的端口的IP叫做LAN口IP而对外的IP也就是他所在的别人组建的子网的端口的IP叫做WAN口IP。
LAN口IP对内WAN口IP对外。
路由器LAN口连接的主机都属于当前这个路由器中所组建的子网。
每一个家用路由器其实又作为运营商路由器中的一个节点这样的运营商路由器可能会有很多级最外层的路由器接入公网配有公网IP这个路由器我们称之为出口或者入口路由器。
其实从上面的知识点我们也能知道家用路由器作为运营商网络的节点本就可以将数据包转发给运营商路由器或者说我们就可以看作是一个运营商路由器所以其实家用路由器配置稍微好一点也是能够再接别的家用路由器的。
那么数据包是怎么从我们的家庭局域网发送到公网的服务器中的呢
很简单在经过每一个路由器节点时都在路由器中将该报文的源IP替换为 路由器的WAN口IP然后转发给上一级路由器以此往复知道交给出口路由器出口路由器再将该报文的源IP替换为自己的公网IP这样我们的报文就能在公网中进行转发了。 这样一来就能经过层层运营商路由器组建的内网将报文从我们的家庭局域网最终转发到公网最终转发给服务器。
私网IP是不能出现在公网中的但是不将一些IP划分为不具有唯一性的用于内网入网的私有IP的话我们的IP地址又远远不够可是数据包如何从私网经过公网转发到目标服务器又是一个问题于是就有了NAT技术我们上面的过程就是NAT技术的向外转发的过程NAT就是net address transform 也就是地址的替换。 那么再将这个内网的逻辑接入到我们上面所画的公网的划分中我们的整个网络的大体框架就有了我们可以将这个运营商的子网的拓扑的根节点连接到划分之后的公网IP中。 由于IP的划分大部分的时间我们的路由器其实拿到我们的目的IP之后并不是全部解析而是提取出目标IP的网络号。而路由器之间的转发其实就是一个一个的子网转发发送的端口和接收的端口都处于同一个子网中。 最终送到目标主机所在的子网之后就变成了局域网内的转发了。 当然其实路由器之间也是局域网内的转发。 而数据包走到路由器之后其实会存在以下几种情况
1 路由器不清楚目标主机的子网这时候不进行转发。 这种情况一般不会存在因为路由器的逻辑就是用来转发不可能拒绝转发报文
2 路由器不清楚目标主机的子网 路由器在查不到的转发路径的情况下将报文转发到一个默认路由器中。
3 路由器知道目标子网但是不是直接相连不知道具体的所有的路线但是他能查到下一个应该转发给哪个路由器这时候他会转发给下一个路由器。 其实数据包的转发大部分都是在重复这一步不断将数据包交给下一跳最终送到对方子网。
4 路由器就在目标子网中这时候就是同局域网也就是内网转发的通信逻辑了。
数据包的转发我们就可以理解为一跳一跳的问路的过程所谓的一跳就是数据链路层的一个区间具体一点就是在以太网中从源mac地址到目的mac地址的帧传输区间。 数据包最终在网络中传输都是以mac帧的形式传输的网络层交给链路层链路层封装完才交给物理层进行传输。
那么在路由器进行目的网络的路径查找的时候就需要用到路由表所谓的路由表就是在路由器中维护的一张表结构的数据包含目的网络目的网络对应的下一跳路由器端口标志位子网掩码等。
在Linux中我们可以使用 route 命令查看当前主机的路由表没错我们的普通的主机也是由路由表的不说别的他至少要保存他所在的子网的路由器的地址我们的主机可以看作一个简单路由器只不过他不是专门用来进行数据转发的。 link-local就是本地连接而上面的 10.2.8.0显而易见就是我们的云服务器所在的子网了。
当然上面显示路由信息其实没有按照一定的顺序把default 放在最后一行更适合我们理解。
其实路由器收到报文之后就是拿着目的IP然后将其与Genmask进行按位与判断得到的网络号是否和第一列的 Destination 相同如果相同就说明匹配上了转发到 Iface 指定的端口。 如果不相符那么就直接继续往下遍历每一个条目。 直到最后匹配到default的时候由于Genmask为0.0.0.0 不管什么IP与他按位与出来都是0.0.0.0都会走着一条转发路径。
当然路由器也有一些更新策略只不过我们不关心这个了在IP这一个章节我们主要就是理解网络的大体框架。
路由表中的默认网关其实可能就是表中已经出现过未匹配上的路由器无所谓。 数据包在网络中转发的过程中目标网络号是否会发生变化
会的网络是时刻变化的什么情况都有可能发生。就像上面说的有可能数据包还在转发途中目标网络直接或者说组网的路由器直接出故障了也是有可能的。 分片与组装
涉及到前面讲解报头时没有讲解的三个字段16为标识3个标志位13位片偏移。
IP协议并不是实际进行传输的协议真正将其发送到网络中还是数据链路层的MAC帧协议在物理层网线上传输的是MAC帧而IP协议最主要的作用是定位目标主机以及规划转发路径同时路由器还有屏蔽底层网络差异的功能但是最终封装成IP报文之后都是要向下交付给MAC帧协议作封装的也就是IP报文还会作为MAC帧的有效载荷部分进行封装。
而MAC帧协议有一个限制有效载荷的长度不能超过1500字节这叫做MTU最大传送单元一般MAC帧协议都是1500可以修改但是没有偶必要。
也就是说IP层交给数据链路层的IP报文不能超过 1500 字节。 如果链路层发现IP层给他的报文长度超过了 1500 这时候会直接丢弃不进行传送。 链路层不负责可靠传输他只负责在自己的能力范围内进行数据的传送。 但是IP层也无法决定整个IP报文的大小因为IP层的数据也是由传输层给的。 传输层有两个协议UDP和TCP如果是UDP的话那么交付给IP的报文的大小完全就是由用户层决定因为UDP是面向报文的用户给他一个报文他进行封装之后就直接交给网络层了。 而对于TCP协议而言如果数据很多窗口很大能够发送更多的数据那么为什么不直接一次性发出去呢减少网络IO的次数不是效率更高吗 这时候我们可以回想一下我在TCP协议时画的图为什么要一次 1000 字节的发而不是直接将所有数据一次性发完其实就是因为链路层有限制不允许发送太大的数据。
所以正常情况下TCP是不会将超过1460(TCP和IP的标准包报头都是20字节1500-401460)字节的数据一次性交给IP层的。
但是架不住特殊情况如果TCP就是一次性将超过1460字节的数据给IP层了呢或者说上层使用的是UDP用户一次性发的数据超过 1460 字节呢 这时候难道IP层也直接把数据丢掉吗 不能IP只能当个两头受气的角色。
IP层也要有对应的解决方案。这个方案就叫做 分片与组装。
在讲分片与组装之前我们要先建立一个共识 分片是谁做的组装又是谁做的
由发送方的IP层进行分片由接收方的IP层进行组装。
为什么需要分片呢 不就是因为TCP或者UDP给他的报文太大吗 所以分片的工作肯定跟TCP和UDP无关也不会跟数据链路层有关数据链路层只负责数据传输。而接收方的传输层也不会关心组装他只关心收到的数据是否完整是否跟发送的数据一样那么也就是说组装的工作也必须在接收方的IP层做完我将组装好的完整报文再交付给传输层。
分片与组装都是IP层自己的行为他要与TCP和MAC协议的工作进行解耦。 分片的时候我们不能随意分片必须要考虑对方的IP层的组装问题对端IP层可能会面临几个问题
1 如何确定一个报文是否被分片了
2 怎么识别同一个报文的分片(因为可能会有多个报文同时被分片然后接收到)
3 哪一个分片是报文的第一个分片哪一个是最后一个 如何确定是否收全或者丢失了
4 如何将所有的分片按顺序组装成原始报文
5 怎么保证组装之后的报文是完整的正确的
带着这5个问题我们就能知道每一个字段有什么功能
首先数据链路层并不会作任何有关分片的工作他只负责将IP层给的数据送到下一跳节点以及最后局域网的通信那么对方IP层怎么知道链路层交给他的报文是一个完整IP报文的多个分片呢也就是如何识别同一个报文的所有的分片 这就需要IP层给每一个报文(分片之前的) 设置一个标识/id。而16位标识就是用来标识IP报文在通信双方之间的唯一性的。
如果一个报文太大需要切片那么他的每一个切片的16位标识都是相同的。
这里我们要注意即使是切片也是要有IP报头的因为交给链路层的一定是一个有IP报头有数据(可选)的数据,我们不要认为切片就不需要报头了。
有了16位标识就能知道哪些分片报文是同一个报文的分片了。也就解决了第2个问题。
3位标识其中第一个比特位被保留留待后用。第二个比特位表示的是 禁止分片。 如果该比特位为1那么就是禁止被分片就算超过了1500也不会分片交付给链路层就被丢弃了这个比特位就是为了防止出现大报文不过一般不进行设置。 第三个比特位表示的是更多分片。如果为1表示的含义是有更多分片如果是0就表示没有更多分片。
也就是说如果对方IP层收到的一个IP报文中这个更多分片标记位 是 1 就表示我们还没有把所有分片收完后续还有分片。 而如果为 1那么就表示后面没有更多分片了。 该标记位为1有两种情况一种是本来就是完整报文没有被分片那么自然就是没有更多分片。 第二种情况就是这是对方发过来的多个分片中的最后一个既然是最后一个分片那么也是没有更多分片了。
最后就是 13 位片偏移了。进行分片之后我们还需要能够将分片按照一定顺序组装成原始报文这时候就需要知道每一个分片的数据在原始报文(一定是先封装成一个IP报文然后发现IP报文过大才会进行分片)的位置这里的片偏移的单位是8字节这就是片偏移的作用。
比如第一个分片的片偏移为 0 如果第一个分片的大小是1500(算上原始报头),那么第二个分片的片偏移就是 1500/8 ,而如果第二个分片的整个IP报文的大小也是 1500 那么意味着他传送的原始报文的数据大小为 1460那么他的片偏移就是 (15001460)/8 以此类推。 为什么是1460而不是1480呢?因为片偏移的单位是 8 字节那么就要求每一个分片所携带的原始报文的数据大小必须是 8 的整数倍。
那么我们再来解答上面的几个问题 首先是 1 如何判断一个报文是否被分片了其实就是对方的IP层拿到报文之后都会先检测一下报头中的更多分片标记位如果为 1 或者片偏移不为0(因为网络中报文送达的顺序可能和发出的顺序不一样)就代表被分片了这时候就不会直接交给上层而是先将其维护在一个临时的缓冲区中并且设置一个定时器等到后续收掉所有的分片组装成一个完整的原始的IP报文再按照正常流程对这个完整IP报文进行解包与分用。
第3个问题第一个分片的更多分片标记位为1同时片偏移为0. 最后一个分片的片偏移不为0同时更多分片为0。 在这里我们也能区分最后一个分片和 普通的完整IP报文的区别虽然更多分片标记为都为0但是完整的报文片偏移也为 0 。而如何确定是否收全则要跟第 4 个问题一起讨论。
第4个问题就是将分片组装成一个完整报文这里面最重要的就是分片的原始位置其实也很简单了根据每一个分片的片偏移将分片的数据放到缓冲区的指定位置。 如果偏移量为0就代表是第一个分片也就是携带了原始报头的分片那么这个分片有点特殊不会将报头去掉而是会将报头也放到缓冲区。 而其他的分片的报头都跟原始报头无关了都是要先去掉报头只将有效载荷放到缓冲区中进行组装。
而最后一个为标题如何确定报文的完整性 IP层这里只能判断出分片组装完整之后是否有缺失以及通过首部检验和判断报头是否出问题而无法判断报文数据部分内容是否发生变化比如比特位翻转 。不过我们也不需要担心内容是否出问题这个判断是在TCP或者UDP中的检验和来判断的。 分片的过程我们可以用图来描述 分片与组装的原理其实不难。
那么分片好吗
可以肯定的是分片肯定是不好的。分片和组装都只在网络层完成但是对于链路层和传输层来说他们根本不关心是否分片只关心数据是否完整而不关心这一个数据在底层是怎么发送过来的。
可是分片就意味着一个完整报文要分多次发送出去而只要其中任意一个报文丢失在接收方网络层进行组装时是会设置计时器的如果超时了那么就说明有分片在网络中丢失了导致无法组装成完整的报文那么就会将目前已经接收到的分片全部丢弃。 然后什么都不做了。
而接收方的TCP由于根本就没有收到网络层交付的数据根本就不会进行应答那么发送方的TCP协议就会触发超时重传超时重传可不是只对某一个分片进行重传在TCP协议中是没有分片的概念的TCP这时候就会将这份数据重新交给网络层。 而网络层还是需要进行分片。。。
分片会增加丢包概率因为只要一个分片丢了那么就相当于整个报文都丢了这时候需要重传(如果是TCP的需要重传而如果是UDP的话丢了就丢了)。
当然这个 MTU 对TCP协议肯定也会有影响因为我们都知道了分片会增加重传的概率那么TCP既然要可靠高效肯定就需要避免分片下一篇数据链路层协议的文章会提一嘴。