1. 基本概念
网络层解决的问题
将数据从一台主机送到另一台主机,也就是数据的路由(路径选择问题)。

保证数据可靠的从一台主机送到另一台主机的前提
发送方要有将数据送到对方主机的能力。
如果发送方没有这个能力,可靠性就没有意义了;在网络层拥有将数据发送到对方主机的能力时,虽然网络层不能保证每次都能将数据成功送到对方主机,但在TCP提供的可靠性策略的保证下,最终网络层就一定能够将数据可靠的发送到对方主机。
这与我们前面的理论也相吻合,TCP是策略,真正传数据的是网络层和数据链路层。
网络层解决的是主机到主机的问题,传输层解决的是进程到进程的问题。
路径选择

这里所说的路径选择就是靠路由器来完成的,这里需要注意"数据"本身的不认识路的,但是路由器就犹如有经验的向导,不断引导"数据"到达目的地,数据路由就像我们旅游一样,当确定了要到达的目标主机后,就需要寻找最短的路径到达该目的地。
主机和路由器
-
主机:配有IP地址,但是不进行路由控制的设备。但实际现在几乎不存在不进行路由控制的设备了,就连你的笔记本也会进行路由控制。
-
路由器:既配有IP地址,又能进行路由控制。实际现在主流的路由器已经不仅仅具有路由的功能了,它甚至具备某些应用层的功能,一般来说一个路由器上至少配有两个IP。
-
节点:主机和路由器的统称。
2. IP协议格式

下面我们来一一介绍这些字段:
4位版本:指定IP协议的版本(IPv4/IPv6),对于IPv4来说,就是4。
4位首部长度:表示IP报头的长度,以4字节为单位。
16位总长度:IP报文(IP报头+有效载荷)的总长度,用于将各个IP报文进行分离。
16位标识:唯一的标识主机发送的报文,如果数据在IP层进行了分片,那么每一个分片对应的id都是相同的。
3位标志字段:第一位保留,表示暂时没有规定该字段的意义。第二位表示禁止分片,表示如果报文长度超过MTU,IP模块就会丢弃该报文。第三位表示"更多分片",如果报文没有进行分片,则该字段设置为0,如果报文进行了分片,则除了最后一个分片报文设置为0以外,其余分片报文均设置为1。
13位片偏移:分片相对于原始数据开始处的偏移,表示当前分片在原数据中的偏移位置,实际偏移的字节数是这个值× 8得到的。因此除了最后一个报文之外,其他报文的长度必须是8的整数倍,否则报文就不连续了。
8位生存时间(TTL):数据报到达目的地的最大报文跳数,一般是64,每经过一个路由,TTL -= 1,一直减到0还没到达,那么就丢弃了,这个字段主要是用来防止出现路由循环。
8位协议:表示上层协议类型。
16位首部检验和:使用CRC进行校验,来鉴别数据报的首部是否损坏,但不检验数据部分。
32位源IP地址和32位目的IP地址:表示发送端和接收端所对应的IP地址。
选项字段:不定长,最多40字节。
IP报头在内核当中本质就是一个位段类型,给数据封装IP报头时,实际上就是用该位段类型定义一个变量,然后填充IP报头当中的各个属性字段,最后将这个IP报头拷贝到数据的首部,至此便完成了IP报头的封装。
IP如何将报头与有效载荷进行分离?
这里其实与TCP类似,TCP那里也有4位首部长度,所以当IP从底层获得一个报文后,首先拿到其前20字节,然后提取其中的4位首部长度字段,得到整个报头大小size,如果size大于20,那么就说明选项字段还有字节,其大小就为size-20,除去基本报头和选项,剩下的就是有效载荷了。
IP就是通过这种"定长报头+自描述字段"的方式进行报头和有效载荷的分离的。
IP如何决定将有效载荷交付给上层的哪一个协议?
基于IP的传输层协议不止一种,所以IP报文在向上交付的时候,一定要明确交付给上层哪一个协议。
8位协议字段,该字段表示的就是上层协议的类型。该字段是发送方的IP层从上层传输层获取到数据后填充的,比如是上层TCP交给IP层的数据,那么该数据在封装IP报头时的8位协议填充的就是TCP对应的编号。
32位源IP地址和32位目的IP地址
IP报头当中的32位源IP地址和32位目的IP地址,分别代表的就是该报文的发送端和接收端对应的IP地址。
在发送报文时,每个报文都需要带上源IP和目的IP,目的IP是为了告诉路由器自己要去哪里,源IP的意义在于,接收端也会给发送端发消息或者对报文进行响应,所以需要知道这个报文是从哪里来的。
理解socket编程:
- 在进行socket编程的时候,当一端想要发送数据给另一端时,必须要指明对端的IP地址和端口号,也就是发送数据的目的IP地址和目的端口号。
- IP地址就是给网络层的IP用的,用于数据在网络传输过程中的路由转发,而这里的端口号就是给传输层的TCP或UDP用的,用于指明该数据应该交给上层的哪一个进程。
- 我们调用send发送数据时,不需要指定IP和端口,因为这些工作OS底层会自动封装。
3. 分片与组装
最大传输单元 MTU
MAC帧作为数据链路层的协议,它会将IP传下来的数据封装成数据帧,然后发送到网络当中。但MAC帧携带的有效载荷的最大长度是有限制的,也就是说IP交给MAC帧的报文不能超过某个值,这个值就叫做最大传输单元(Maximum Transmission Unit,MTU),这个值的大小一般是1500字节。
在Linux下使用ifconfig命令可以查看对应的MTU。

由于MAC帧无法发送大于1500字节的数据,因此IP层向下交付的数据的长度不能超过1500字节,这里所说的数据包括IP的报头和IP的有效载荷。
分片与组装
如果IP层要传送的数据超过了1500字节,那么就需要先在IP层对该数据进行分片,然后再将分片后的数据交给下层MAC帧进行发送。

如果发送数据时在IP层进行了分片,那么当这些分片数据到达对端主机的IP层后就需要先进行组装,然后再将组装好的数据交付给上层传输层。

数据的分片和组装完全是由IP协议自己完成的,传输层和链路层不必关心也不需要关心。
4. 网段划分
IP地址的构成
IP地址由网络号和主机号构成:
网络号:保证相互连接的两个网段的不同弄标识。
主机号:同一网段内,主机之间具有相同的网络号,但是必须有不同的主机号。

DHCP协议
主要作用就是集中地址管理、分配IP地址,使网络环境中的主机动态获得IP地址、Gateway地址、DNS服务器地址等信息,并能够提升地址的使用率。
该协议是基于UDP的应用层协议,一般路由器都有DHCP功能,所以路由器也可以看成DHCP服务器。
我们连接无线网的时候,需要输入Wifi账号和密码,本质就是路由器在进行身份验证,验证通过就会为你动态分配一个IP地址,你就可以拿着这个IP地址去上网了。
先找目标网络,再找目标主机
路由时的第一目的并不是找到目标主机,而是找到目标网络所在的网络,然后再在目标网络当中找到目标主机。
这样做也是为了提高效率,如果一开始就指着主机去找,那么全网中成千上万的主机,找起来效率就太低了。
如果一开始先以找目标网络为目的,那么在查找过程中就能一次排除大量和目标主机不在同一网段的主机,这样就可以大大提高检索的效率。
所以为了提高路由的效率,我们需要进行网段划分。
网段划分

这是之前提出的一种划分方法,
A类:0.0.0.0到127.255.255.255。
B类:128.0.0.0到191.255.255.255。
C类:192.0.0.0到223.255.255.255。
D类:224.0.0.0到239.255.255.255。
E类:240.0.0.0到247.255.255.255。
子网划分
随着网络的发展,上面这种划分方案会导致IP地址的分配不均,于是又提出了新的划分方案,称为CIDR。
在原有的五类网络的基础上继续进行子网划分,这也就意味着需要借用主机号当中的若干位来充当网络号,此时为了区分IP地址中的网络号和主机号,于是引入了子网掩码(subnet mask)的概念。
每一个子网都有自己的子网掩码,子网掩码实际就是一个32位的正整数,通常用一串"0"来结尾。
将IP地址与当前网络的子网掩码进行"按位与"操作,就能够得到当前所在网络的网络号。
通过这种方法,我们可以进一步细分网络,主机号就会变短,这也就避免了IP大量浪费的情况。
比如在某一子网中将IP地址的前24位作为网络号,那么该网络对应的子网掩码的32个比特位中的前24位就为1,剩下的8个比特位为0,将其用点分十机制表示就是255.255.255.0。
假设该子网当中有一台主机对应的IP地址是192.168.128.10,那么将这个IP地址与该网络对应的子网掩码进行"按位与"操作后得到的就是192.168.128.0,这就是这个子网对应的网络号。
IP与子网掩码进行"按位与"操作的本质就是保留了前24个比特位的原貌,将主机号清零了,所以得到的就是网络号。
需要注意的是,子网划分不是只能进行一次,我们可以在划分出来的子网的基础上继续进行子网划分。
5. 特殊的IP地址
-
将IP地址中的主机地址全部设为0,就成为了网络号,代表这个局域网。
-
将IP地址中的主机地址全部设为1 ,就成为了广播地址 ,用于给同一个链路中相互连接的所有主机发送数据包。
-
127.*的IP地址用于本机环回(loop back)测试,通常是127.0.0.1。
6. IP地址的数量限制
IP地址数量不足问题
我们知道,IP地址(IPv4)是一个4字节32位的正整数,因此一共有2^32个IP地址,也就是将近43亿个IP地址。但TCP/IP协议规定,每个主机都需要有一个IP地址。
随着时代的发展,网络设备越来越多,全世界的网络也越来越多,所以43亿个IP就不够用了,所以上面我们提到了子网划分,本质就是为了减少IP地址的浪费,但是这种方法治标不治本,没有增加IP地址的绝对数量。
如何解决IP地址不足问题
-
最重要就是NAT技术:这种技术可以实现不同局域网内可以出现相同的IP,这样就从根源上解决了IP地址不足的问题,还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。
-
动态分配IP地址:只给接入网络的设备分配IP地址。
-
IPv6技术:IPv6技术使用16位1228比特位来标识IP,能够大大缓解IP地址不足的问题。但是遗憾的是,IPv6与IPv4有本质差异,所以无法普及到全球。
7. 私网IP地址和公网IP地址
私网IP地址的种类
- 10.*,前8位是网络号,共16,777,216个地址。
- 172.16.*到172.31.*,前12位是网络号,共1,048,576个地址。
- 192.168.*,前16位是网络号,共65,536个地址。
这些网段中的IP地址都是私有IP,其余的则称为公网IP(或全局IP)。
我们连接云服务器时,连接的这个IP地址就是云服务器的公网IP地址。

我们可以通过ifconfig命令来查看我们这台机器的私网IP,其中网络接口lo(loop)代表的是本地环回,而eth0代表的就是我这台机器的网络接口,可以看到我的私网IP地址是192.168.2.76。

我们为什么要给运营商交网费?
有人会有疑惑,我们享受的是互联网公司的服务,为什么需要给运营商交网费?
这个问题其实很简单,因为网络基础设施都是运营商搭建的,比如我们访问抖音服务器,不是我们直接访问的,而是我们需要经过运营商的路由,才能访问到目标服务器,没有运营商提供的这些基础设施,就不会诞生所谓的互联网公司,因为互联网公司是诞生在网络通信基础之上的。
所谓的网段划分、子网划分等工作实际都是运营商做的,所以我们交的网费本质就是买了入网许可。
数据是如何发送到服务器的
路由器是连接两个或多个网络的硬件设备,在路由器上有两种网络接口,分别是LAN口和WAN口:
LAN口(Local Area Network):表示连接本地网络的端口,主要与家庭网络中的交换机、集线器或PC相连。
WAN口(Wide Area Network):表示连接广域网的端口,一般指互联网。
我们将LAN口的IP地址叫做LAN口IP,也叫做子网IP,将WAN口的IP地址叫做WAN口IP,也叫做外网IP。
我们使用的电脑、家用路由器、运营商路由器、广域网以及我们要访问的服务器之间的关系大致如下:

这里我们可以注意到,我们家用的网络,其实就是由我们的家用路由器构建起来的一个子网,属于局域网,在每一个子网中,IP具有唯一性的,但是在全网内IP地址是可以重复的,这就得益于NAT技术。
NAT技术具体是干什么的呢?
这里我们需要知道,私有IP是不能出现在公网中的,所以在我们访问公网时,路由器会将我们报文中的源IP地址不断替换成路由器的WAN口IP,比如上面图中,我们从主机192.168.200/24发出一个报文,经过家用路由器时,源IP地址192.168.200会被替换成家用路由器的WAN口IP10.1.1.1/24,经过运营商路由器时,刚才的IP就再次被替换成运营商的WAN口IP,这样我们的报文就顺利地进入到公网了。
为什么私网IP不能出现在公网当中?
-
不同局域网中的IP可能是相同的,如果私有IP出现在公网中,那么报文从公网回到内网的时候,就无法确认唯一主机。
-
还有一点原因其实就是如果私有IP可以出现在公网,那么运营商就没有意义了,那我们也就不用交网费了,这显然是不现实的。
两个局域网当中的主机必须跨公网进行通信
数据要从一个局域网到另一个局域网,必须要经过公网,因为服务器基本都在公网上,我们的数据要先到服务器,然后才能发到对端。
不过实际确实存在一些技术能够使数据包在发送过程中不进行公网IP的替换,而将数据正确送到目标主机,这种技术叫做内网穿透,也叫做NAT穿透。
8. 路由
数据"问路"的过程
数据在路由的过程中,实际就是一跳一跳(Hop by Hop)"问路"的过程。所谓"一跳"就是数据链路层中的一个区间,具体在以太网中指从源MAC地址到目的MAC地址之间的帧传输区间。

IP报文在传输过程中会遇到很多路由器,每个路由器都会根据其目的IP地址告知它下一跳去哪里。
路由器的查找结果可能有以下三种:
- 路由器经过路由表查询后,得知该数据下一跳应该跳到哪一个子网。
- 路由器经过路由表查询后,没有发现匹配的子网,此时路由器会将该数据转发给默认路由。
- 路由器经过路由表查询后,得知该数据的目标网络就是当前所在的网络,此时路由器就会将该数据转给当前网络中对应的主机。

Destination代表的是目的网络地址。
Gateway代表的是下一跳地址。
Genmask代表的是子网掩码。
Flags中,U标志表示此条目有效(可以禁用某些条目)G标志表示此条目的下一跳地址是某个路由器的地址,没有G标志的条目表示目的网络地址是与本机接口直接相连的网络,不必经路由器转发。
Iface代表的是发送接口。
当IP数据包到达路由器时,会将IP报文中的目的IP地址依次与路由表中的子网掩码 Genmask
进行"按位与"操作,然后与Destination进行对比,如果匹配则说明该数据包下一跳就应该跳去这个子网,此时就会将该数据包通过对应的发送接口Iface
发出。
如果没有找到匹配的目标地址,那么就走默认路由,也就是图中的default,这其实是另一台路由器。
数据包不断经过路由器路由后,最终就能到达目标主机所在的目标网络,此时就不再根据该数据包目的IP地址当中的网络号进行路由了,而是根据目的IP地址当中的主机号进行路由,最终根据该数据包对应的主机号就能将数据发送给目标主机了。