网络层中,IP协议首部和有效载荷组成的完整数据称为数据报。
前面本喵讲解了传输层的TCP和UDP协议,它们主要是用来控制数据在网络中的传输的,像滑动窗口,超时重传,确认应答,拥塞控制等等,都是在控制数据的传输。
在TCP/IP模型中,传输层的下一层是网络层,传输层封装好的数据段并不是直接交给了网络,而是给到了网络层,也就是我们今天要讲解的IP协议。
- IP协议的作用:定位目标主机,具有将数据报从A主机跨网络送到B主机的能力。
IP层是有这样的能力,但是有能力就一定能办到吗?不一定,只能说有非常大的概率可以办到。
既然是概率问题,就可以通过一些策略让其成为百分之百的事情,比如没有传成功时就重传,直到传送成功,这样一来就保证了IP协议传送数据的可靠性。
- TCP协议就是给IP协议提供数据传送策略的。
TCP协议又叫传输控制协议,它通过多种机制来保证数据能够可靠的传送到对端主机,但是它并不是执行者,真正的执行者是IP协议,TCP协议只是提供相关的策略让IP协议执行,进而保证数据的可靠传送。
和讲解TCP协议的时候一样,主要字段的作用穿插在后面具体内容中讲解。
4位TOS分别表示:最小延时,最大吞吐量,最高可靠性,最小成本。这四者相互冲突,只能选择一个。对于ssh/telnet这样的应用程序,最小延时比较重要,对于ftp这样的程序,最大吞吐量比较重要。
上面本喵提到的字段理解起来并没有压力,没有提到的字段,在后面的具体内容中会详细讲解。
解包分用:
任何一个协议都必须考虑解包和分用,IP协议也不例外,要考虑对端的IP层是如果将报头和有效载荷分离的,又是将数据交给传输层的哪个协议的?
在IP首部中,有4位首部长度,根据这个字段中的内容可以得到当前数据报报头的长度,首部中还有16位总长度,可以得到整个数据报的长度,二者做差得到的就是有效载荷。
知道有效载荷后,只需要读取固定字节数的有效载荷即可分离报头。
在IP首部中,还有一个8位协议号,这里的编号就是要将数据交给的上层(传输层)协议的编号,如TCP或者UDP等,所以实现分用非常容易。
- 主机:配有IP地址,可以进行网络通信的设备,比如电脑,手机等等。
- 路由器:配有IP地址,又能进行路由控制,也就是将数据从一个节点传送到另一个节点。
- 节点:主机和路由器的统称。
上面说了,IP协议具有将数据从A主机跨网络可靠的送到D主机的能力,那么IP协议是怎么从主机A找到主机D的呢?
如上图所示,IP协议报头中有两个IP地址字段,32位源IP地址标识的是主机A所在的位置,32位目的IP地址标识的是主机D所在的位置。IP地址就像生活中的家庭住址等地址一样,都具有唯一性。
- IP地址 = 网络号 + 主机号
IP地址采用的是点分十进制,IPv4的格式形如192.168.1.1
这样的形式,共有四个字节,字节和字节之间用点隔开,每个字节的数字范围是0~2555
,所以IP地址理论上的范围是0.0.0.0~255.255.255.255
,这个范围中有232个IP地址,也就是42亿多,是不够全球网民使用的。
网段划分:
假设你现在从你的电脑上用微信发送了一条消息,这条消息第一步肯定是到腾讯的微信服务器上,服务器也是一个计算机,也是一个节点,它也有自己的IP地址。
此时你电脑上的数据也知道服务器的IP地址,但是怎么找到呢?你的电脑和服务器之间有无数个节点,难道要把42亿多个IP地址遍历一遍吗?肯定不是的,这样效率太低了。
所以要进行子网的划分:
如上图所示,本喵按照国家,省市区为单位来划分子网,实际上的划分并不是这样的,而是按照人口区域来划分的,本喵这样划分只是为了好理解。
世界上有众多国家,每一个国家对应IP地址中4字节中第一个字节里的一个数字,如中国就是10.x.x.x
,日本是1.x.x.x
这样。
每个国家里又划分为多个行政区,拿中国来说,有34个省(自治区,直辖市,特别行政区),将IP地址中的第二个字节分配给这些省,第一个字节保持不变,如山东就是10.0.x.x
,上海就是10.2.x.x
这样。
上海市又分为多个区,将IP地址中的第三个字节分配给这些区,第一个和第二个字节保持不变,如浦东就是10.2.0.x
,黄埔就是10.2.3.x
。
每个区中又有多台主机,也就是有多个节点,包括服务器也是一个节点,将IP地址中的第四个字节分配给这些节点,如张三的电脑就是10.2.3.2
,腾讯的微信服务器就是10.2.3.4
。
假设你现在在日本,用微信发了一个消息,当数据到了IP层被封装以后,报头如上图所示,源IP地址是1.1.1.1
,目的IP地址是10.2.3.4
。
数据被发送出去以后,首先到达的是你所在区子网的路由器,路由器发现目的IP中的前三个字节是10.2.3
,而整个日本子网的IP地址中第一个字节是1
,所以判断这不是发给你所在国家的子网的,就将数据交给了上层路由器,最后到了日本的入口路由器。
- 每个子网都有一个入口路由器,该路由器是子网内部所有路由器的上层路由器,也叫做默认路由器。
日本的入口路由器和其他国家的入口路由器处于一个子网内,所以它肯定知道IP地址第一个字节是1
属于哪个国家的,所以就将这个数据报直接发给了中国的入口路由器。
- 每一个入口路由器都横跨两个子网,一个是它所在的子网,还有一个是它下一层的子网。
中国的入口路由器横跨所有国家入口路由器所在的子网,和中国内部所有省份所在的子网,所以它清楚的知道每个省份的IP地址中第二个字节的数字是多少。
中国的入口路由器拿到日本入口路由器发来的数据报以后,发现目的IP是10.2.3.4
,前两个字节是10.2
,对应的是上海市,所以就将数据报直接发送到了上海市的入口路由器。
上海的入口路由器拿到这个数据报后,发现目的IP中的前3个字节是10.2.3
,对应的是黄埔区,所以就将数据报直接发送到了黄浦区的入口路由器。
黄埔区的入口路由器发现IP地址是10.2.3.4
,对应的是微信服务器,所以就将数据直接发送给了微信服务器的路由器。此时,数据报就被送到了目标地址。
上诉过程中,每一层子网只关注IP地址中的一个字节,越往下层,被确定的字节个数就越多,目标也就越明确,所查找的范围也就越小。
通过子网划分的方式,让数据报每传送一次就能排除大量的IP地址,比起遍历的方式,效率提升的不是一星半点。
可以看到,在数据报传送的过程中,根据IP地址中的前3个字节,可以将数据报通过多个路由器送到目标主机所在子网的入口路由器,此时也就确定了目标主机所在的子网络,所以前三个字节被叫做网络号。
目标主机所在子网的路由器再根据主机号,也就是IP地址中的最后一个字节来确定具体是哪个主机,然后将数据报发送到目标主机上。
- IP地址中的前3个字节被叫做网络号,最后一个字节被叫做主机号。
如上图所示,不同网段(子网)的网络号必须不同,但是主机号可以相同,只要保证同一个网段内的所有主机号不同即可。
网络号保证了两个相连的子网具有不同的标识,主机号保证了同一子网内不同主机有不同的标识。
补充知识:
- IP地址中,主机号为0,如
192.168.128.0
就代表网络号,等价于前三个字节192.168.128
,所以这个IP地址是不能绑定主机的。- IP地址中,主机号为1,如
192.168.128.1
代表当前子网的默认路由器(入口路由器),所以这个IP地址也不能绑定主机。
网段划分是经过设计的,所以说互联网是一个被设计过的世界,那么是谁设计的呢?答案是运营商,在我们国家就是移动,电信,联通三大运行商,其他国家也有自己运营商。
运营商不仅设计了互联网的网段划分,而且搭建了物理层中的通信设备,使得网络数据能够在一根根网线中跑,进行网络通信。
所以说,运营商是底层网络的设计者,我们在使用网络的时候,需要向运营商交钱,因为我们使用了人家的底层设计和设备。
不同的子网其实就是把网络号相同的主机放到一起,如果在子网中新增一台主机,则这台主机的网络号和这个子网的网络号一致,但是主机号必须不能和子网中的其他主机重复。
通过合理设置主机号和网络号,就可以保证在相互连接的网络中,每台主机的IP地址都不相同。
那么问题来了,手动管理子网内的IP,是一个相当麻烦的事情。
我们在日常生活中连接家里的wifi时,第一次连接需要输入账号和密码,这其实就是在向路由器申请IP地址,在没有联网的时候,你的设备是没有IP地址的。
过去曾经提出一种划分网络号和主机号的方案,把所有IP 地址分为五类, 如下图所示:
其中网络号和我们前面网段划分中是一样的,也是用来标识不同的子网的,只是不同类型IP地址网络号的范围不一样,主机号的范围也不一样。
某一个子网会领取几类IP地址去使用,其中用的最多的是B类IP地址,随着网络的飞速发展,这种划分方案的局限性很快显现出来,大多数组织都申请B类网络地址,导致B类地址很快就分配完了,而A类却浪费了大量地址。
因为有组织已经申请了一个A类IP地址,即使主机数没有那么多,没有用完,但是别的组织也不能申请这个A类的IP地址了,因为网络号是唯一的。
此时就导致A类IP地址存在大量的浪费,而B类IP地址严重不足。
- 引入一个额外的子网掩码(subnet mask)来区分网络号和主机号。
- 子网掩码也是一个32位的正整数,通常用一串 “0” 来结尾。
- 将IP地址和子网掩码进行 “按位与” 操作,得到的结果就是网络号。
- 网络号和主机号的划分与这个IP地址是A类、B类还是C类无关。
子网掩码的存在就是为了区分网络号和主机号的,可以通过IP地址和子网掩码按位与高效的得到网络号。
子网掩码和IP地址做“与”运算,分离出IP地址中的网络地址和主机地址,用于判断该IP地址是在本地网络上,还是在远程网络网上。
如上图所示,一个路由器收到了一个IP数据报,其中目的IP地址是140.252.20.68
,该路由器所处网段的子网掩码是255.255.255.0
,将IP地址和子网掩码按位与后得到的网络号是140.252.20.0
。
然后与自己所处网段的网络号进行比较,如果相同,直接将数据给到本网段的子网中,如果不同,则需要跨网段,则将数据给到上一层路由器去处理。
- 得到的网络号所标识的子网中,节点的地址范围是
140.252.20.0~140.252.20.255
。- 也就是主机号从全0到全1。
再看一个例子,如上图所示,此时的子网掩码是255.255.255.240
,按位与以后得到的网络号是140.252.20.64
,作用和上面一样,也是用来判断目的IP是否位于本网段。
P地址和子网掩码还有一种更简洁的表示方法,例如140.252.20.68/24
,表示IP地址为140.252.20.68
,子网掩码的高24位是1,也就是255.255.255.0
。
第一个例子中,网络号是前3个字节,第二个例子中网络号是前3个字节加4个比特位,剩下的才是主机号。
- 数据在传送的过程中,网络号是变化的。
继续拿前面的国家和省市之间网络拓扑结构来说,从日本发送出来的微信消息现在已经交到了中国入口路由器,假设中国网段的子网掩码是255.0.0.0
。
- 每个路由器中都维护着一张路由表,里面存放着下层所有子网的网络号和子网掩码。
中国网段入口路由器将IP地址与它所知道的子网掩码遍历,进行按位与,最后发现该IP地址和上海的子网掩码255.255.0.0
按位与后得到的网络号和上海的网络号相同,然后将该数据交给上海子网的入口路由器。
上海入口路由器同样进行上面的操作,最后发现该IP地址和黄浦区的子网掩码255.255.255.0
按位雨后得到的网络号和黄浦区的网络号相同,然后将该数据交给黄埔区入口路由器。
黄浦区入口路由器根据IP地址中的主机号找到微信服务器,至此,数据完成传送。
特殊的IP地址:
140.252.20.0
。140.252.20.255
。127.*
用于本机环回测试,通常是127.0.0.1
,只能在本主机上进行网络通信。IP地址的数量限制:
我们知道,IP地址(IPv4)是一个4字节32位的正整数,一共只有 232个IP地址,大概是43亿左右。而TCP/IP协议规定, 每个主机都需要有一个IP地址,难道这意味着,一共只有43亿台主机能接入网络么?
实际上,由于一些特殊的IP地址的存在,数量远不足43亿。另外IP地址并非是按照主机台数来配置的,而是每一个网卡都需要配置一个或多个IP地址。
CIDR(Classless Interdomain Routing) 虽然在一定程度上缓解了IP地址不够用的问题(提高了利用率, 减少了浪费, 但是IP地址的绝对上限并没有增加),仍然不是很够用,这时候有三种方式来解决:
如果一个组织内部组建局域网,IP地址只用于局域网内的通信,而不直接连到公网上,理论上使用任意的IP地址都可以,但是RFC1918规定了用于组建局域网的私有IP地址范围:
10.*
,前8位是网络号,后24位是主机号,所以共有16777216个地址。127.16
到127.31
,前12位是网络号,后20位是主机号,所以共有1048576个地址。192.168.*
前16位是网络号,后16位是主机号,所以共有36636个地址。在这个范围中的IP地址都称为内网IP(私有IP),其余的都称为公网IP(全局IP)。
如上图所示,路由器横跨两个子网,那么它必然配有两个IP地址,一个是WAN
口IP,位于上层子网,就像前面网络拓扑结构中,上海市的入口路由器和其他省路由器组成的子网中,WAN
口IP就是该路由器在这个网段中的IP地址。
另一个IP地址是LAN
口IP,位于当前子网,上海的入口路由器和上海所有区组成的子网中,LAN
口IP就是该路由器在这个网段中的IP地址。
- 和路由器
LAN
口连接的主机,都属于当前这个路由器的子网中。- 子网内的主机IP地址不能重复,但是子网之间的IP地址就可以重复了。
上图中,所有家用路由器的子网IP(LAN
口IP)都是192.168.1.1/24
,这个IP地址是用来和家里与这个路由器相连的所有设备进来通信的,每一个家用路由器和与它相连的设备组成了一个局域网。
192.168.1.1
就是唯一的。但是左边的两个家用路由器的WAN
口IP不同,因为它两和运营商路由器的LAN
口IP组成一个子网(局域网),该子网中的IP地址都是唯一的。
上图中,两个运营商路由器的子网IP又是相同的,都是10.1.1.1/24
,因为这个LAN
口IP只用于该从属该路由器的子网中通信,在这个子网中也是唯一的。
两个运营商的WAN
口IP是不相同的,而且是122.*
,这个IP地址是公网IP,所以这两个WAN
口IP地址是用来在公网之间通信的。
- 局域网中:一个子网中所有节点的IP地址都不能相同,要具有唯一性。不同子网中的IP地址可以相同。
- 公网中:IP地址必须具有唯一性,不论属于哪个子网。
本喵前面画的网络拓扑结构中,是公网网段划分理想示意图,没有加入任何内网,实际上在到达某个省或者某个市就开始采用内网(局域网)的方式了。
就像我们平时在微博,抖音等软件里的评论归属地一样,只能看到所在省,无法看到再具体的信息了,因为省使用的还是公网IP,非常好识别,再网下使用的就是内网IP,就比较复杂了,好识别。
既然不同的内网中可以有相同的IP地址,那么就出问题了:
假设我现在从我的电脑上发送了一份数据到服务器上,IP报头如上图所示,源IP地址是192.168.204/24
,这是一个内外IP地址,目的IP地址是122.77.241.3/24
,这是一个公网IP地址。
你将数据从你的电脑上发送出去后,首先交给了家用路由器,家用路由器用子网掩码按位与后发现不是发送到本网段的,然后交给了运营商路由器。
运营商同样判断出不是发送给本网段的,而是要发送给公网中的服务器的,所以运营商路由器将数据发送给了服务器。
服务器收到数据以后通过特定进程处理,处理完进行响应,但是响应数据段到了IP层以后,发现目的IP是192.168.1.1
,这是一个内网IP,填入到IP报头中的目标IP地址字段里,然后发到公网中。
但是由于这是一个内网IP,所有运营商路由器都不认识,并且它们的子网中都有这个IP地址,所以就会导致服务器的响应无法传送到我的电脑上。
- 内网中的主机需要和公网网进行通信时,路由器将IP首部中的目的IP地址进行替换(替换成
WAN
口IP),这样逐级替换,最终数据包中的目的IP地址成为一个公网IP。- 这种技术称为NAT(Network Address Translation,网络地址转换),具体详细内容本喵以后会讲解。
如上图所示,在从你的电脑将数据发送到家用路由器时,IP报头中的目的IP是192.168.201/24
,是一个内网IP地址。
家用路由器将数据发送给运营商路由器的时候,将IP报头中的目的IP地址替换成了自己的WAN
口IP10.1.1.2/24
,同样是一个内网IP地址,但是属于你电脑的上一层。
运营商路由器在将数据发送到公网中给服务的时候,将IP报头中的目的IP地址替换成了自己的WAN
口IP122.77.241.4/24
,这是一个公网IP,在全网都具有唯一性。
此时服务器在处理完数据做出响应时,构建的IP数据报中目的IP就填运营商的WAN
口IP地址122.77.241.4/24
,将数据发到公网中后可以准确发回运营商路由器,然后再由运营商路由器发给它子网的路由器,最终到了你电脑上。
- 路由:就是在复杂的网络拓扑结构中找出一条通往终点的路线。
通俗来说,一跳就是两个节点相连之间的路径。路由的过程就如上图那样一跳一跳的问路的过程。
当IP数据报到达路由器以后,由路由器决定这个数据包是直接发送给目标主机还是需要发给上一层路由器。如果目标IP地址在本网段内就直接发送给目标,否则就发送给上一层路由器。
那么路由器是如何判断当前这个IP数据报该发送到哪里呢?这就要靠每个路由器(节点)内部维护的路由表了。
如上图所示,在Linux上,使用route
指令可以查看当前机器上的路由表,我们的计算机也有路由功能,也是可以看作一个路由器的。
使用上图所示的路由表来讲解,本喵删除了Metric
和Ref
两列,因为现在不用关心它是什么意义。
这台主机有两个网络接口,一个网络接口eth0
连到192.168.10.0/24
网络,另一个网络接口eth1
连到192.168.56.0/24
网络。
路由表的Destination
是目的IP地址,Genmask
是子网掩码,Gateway
是下一跳地址,也就是下一个路由器IP地址。Iface
是发送接口,是物理上存在的接口,该接口通过网线和对应网络相连。
Flags
中的U
标志表示此条目有效(可以禁用某些条目),G
标志表示此条目的下一跳地址是某个路由器的地址,没有G
标志的条目表示目的网络地址是与本机接口直接相连的网络,不必经路由器转发。
192.168.56.3
跟第一行的子网掩码做与运算得到192.168.56.0
,与第一行的目的网络地址不符,再跟第二行的子网掩码做与运算得到192.168.56.0
,正是第二行的目的网络地址,因此从eth1
接口将数据报发送出去。
由于192.168.56.0/24
正是与eth1
接口直接相连的网络,因此可以直接发到目的主机,不需要经路由器转发。
202.10.1.2
依次和路由表前几项进行对比,发现都不匹配,说明目的IP不在本网段,按缺省路由条目(最后一行),从eth0
接口发出去,发往IP地址为192.168.10.1
的路由器,由192.168.10.1
路由器根据它的路由表决定下一跳地址。
总的来说,当一个数据报文到来节点后,会经过四步来完成数据报文发送:
Iface
接口发出报文。
如上图红色框所示,IP协议报头中有一个字段叫做8位生存时间(TTL),该字段的值表示跳数。
在整个网络拓扑结构中,存在非常多的节点,数据报在发送的过程中会经过这些节点,如果某个或者某些路由器发生故障,则有可能导致数据报在网络中的传送路径变成环状,在固定路由器之间循环,始终到不了目标IP地址。
发送端迟迟得不到应答,就会触发超时重传机制,然后新发的数据报又会陷入到循环中,导致网络中数据越来越多,最后就拥塞了。
此时8位的生存时间就派上用场了,假设生存时间字段的值是10,那么数据报在网络中每进行一跳该值就会减一,如果减到0这个数据报就消亡了。
- 8位生存时间的最大值是255,也就是说一个数据报最多进行255跳。
- 255跳可以到达整个网络中的任何一台主机了,不会存在还没有到达数据就消亡了,除非路由出现了问题。
- 在数据传送的过程中,类似于多叉树的模型,每一跳就在向下遍历,能排除相当多的主机。
为什么要分片?
我们知道,在TCP/IP模型中,网络层的下一层是数据链路层,网络层中的IP协议并不是把数据直接发送到了网络中,而是交给了数据链路层,数据链路层才是真正将数据发送到网络中。
数据链路层中使用的是MAC
帧协议,该协议规定自己的有效载荷不能超过1500个字节。虽然可以修改,但是这里我们不考虑,就按默认情况来说。
如上图所示,IP层位于TCP层和数据链路层之间,IP层发送的数据是TCP层交付下来的,交付多少就向数据链路层发多少。
此时TCP层交付给IP层3000个字节的数据,IP层向下交付的时候,数据链路层说它最多只要1500个字节,否则就不要。
所以此时IP层就非常委屈,上面是领导要求发送3000个字节,下面员工只能处理1500个字节,面对这个矛盾,IP层只能自己想办法,所以就将数据分片,分成多个数据包,每个数据包不超过1500个字节,然后分多次交付给数据链路层。
同样的,当这些分开的数据报发送到对端后,对端的IP层需要进行组装,将分片的数据拼装成一个完整的数据。
- 分:自己的IP层完成。
- 组:对端的IP层完成。
- TCP层和数据链路层完全不关心IP层进行了分片,实现了高度解耦。
MTU
(1500个字节) IP数据报就会丢弃报文。第三位表示更多分片,如果该数据报被分片了的话,这个第三位就是1,最后一个分片由于不会再分了,所以是0。
如上图所示,将3000字节的数据报分片为两个1500字节的数据报,每个数据再加一个IP报头,其中第一个数据报是3000字节的前半部分,它被分片了,所以报头中的第三个标志位是1。
第二个数据报是3000字节的后半部分,它没有再分片,所以IP报头中的第三个标志位是0。
继续看前面分片的图片,第一个数据报是从头开始分片的,所以片偏移量就是0,第二个数据报是从1500字节处开始分的,所以片偏移量值*8就是1500。
除了最后一个报文之外, 其他报文的长度必须是8的整数倍(否则报文就不连续了),本喵按照1500字节拆分就不是8的整数倍。
数据链路层不关心IP层是否分片了,它只管发,所以对端的IP层收到数据报后需要知道这是一个被分片了的报文。
由于16位标识符的存在,对端收到的数据报中,标识符相同的数据报组合起来就是一个完整的数据报。
更多分片标志位是1,片偏移量是0,说明这是第一个分片。更多分片标志位是0,片偏移量大于0,说明这是最后一个分片。
将所有分片的片偏移量按照升序排序,就可以知道哪个分片在前,哪个分片在后。
当前分片的起始位置+自身数据长度,如果等于下一个分片中的片偏移量,说明这两个分片是挨着的,如次反复判断,便能判断出是否收全了,只要有一次判断结果不相等,那么就说明有数据片丢了。
假设传送一个报文的成功率是99%,将一个长的数据报分片成两个以后,两个数据报的传送的成功率就是99%2 = 98.01%,这个大数据报传送的成功率就降低了。
一个数据报被分片成多个,任意一个丢失就会导致对端IP层组装失败,从而导致发送发TCP层超时重传。
- 分片和组装并不好,而且这种情况也并不是经常的,并不是主流方式。
IP协议中,最重要的字段就是IP地址,IP协议是专门负责路由的,所以要重点理解整个网络的拓扑结构,包括子网划分,公网和内网(局域网)的划分和路由。