基于 IP 协议的传输层协议不止一种,因此 IP 从下层获取到一个报文并将其解包后,需要知道应将分离后的有效载荷上交给哪一个协议,这会通过 IP 报头中的 8 位协议字段来完成。
IP 报头中的 8 位协议字段,表示的是上层协议的类型,根据该字段就可以判定出应上交的协议。
该字段是发送端的 IP 层从传输层获取到数据后所填充的,假设一份数据是由上层 TCP 交给 IP 的,那么其报文中的 8 位协议字段填充的就是 TCP 协议的编号。
3)源 IP 与目的 IP
IP报头中的 32 位源 IP 地址和 32 位目的 IP 地址,分别表示的是报文的发送端 IP 地址、接收端 IP 地址。
数据在网络传输中会经过一个个的路由器,来进行路由转发,以慢慢趋近于目标主机。路由器在帮助数据进行路由转发时,会提取出该数据 IP 报头中的目的 IP 地址,作为路由转发的重要依据。
当接收端收到了发送端发来的数据后,也可能要给发送端发送数据,因此,发送端在发送数据时不仅需要指明该数据的目的 IP 地址,也需要指明该数据的源 IP 地址,以便接收端后续作出应答或发送其他重要的数据。
特别地,在进行网络编程时,想要一端能发送数据给另一端,必须要指明对端的 IP 地址和端口号,也就是发送数据的目的 IP 地址和目的端口号。这个需指明的目的 IP 地址,其实就是传给网络层的 IP 协议,用于数据在网络中进行路由转发的,而这个需指明的端口号,是给传输层的 TCP协议或 UDP 协议,用于指明该数据应交给应用层的哪一个进程。而由于传输层和网络层都是在操作系统内核中实现的,因此在实际发送数据时,就无须指明发送数据的源 IP 地址和源端口号,数据在贯穿网络协议栈经历封装时,会被操作系统填充对应的源 IP 地址和源端口号。
IP 能够将数据跨网络地从一台主机送到另一台主机,网络传输期间,数据需要经过一个个的路由器并进行路由转发,最终才能到达目标主机。
IP 进行数据跨网络传送的前提是,需要先将数据从一个节点传送到和自己相连的下一个节点,而这实际是由网络层下数据链路层负责的。两个节点直接相连,也就意味着这两个节点在同一个局域网中,因此要探讨两个相邻节点的数据传送,实际探讨的就是局域网通信的问题。
1)MTU 与分片、组装
MAC 帧是数据链路层中最典型的协议,它能将 IP 传下来的数据封装成数据帧,然后发送到网络中。
但 MAC 帧携带的有效载荷,其最大长度是有限制的,也就是说,IP 交给 MAC 帧的报文不能超过某个值,而这个值就是最大传输单元 MTU(Maximum Transmission Unit),一般为 1500 字节。
如果 IP 向下传输的报文超过了 1500 字节,那么就会在 IP 层经历分片,再交给下层的 MAC 帧进行发送;如果数据在发送时,在发送端的 IP 层进行了分片,那么送达对端后,数据也需要在对端的 IP 层进行组装,再向上交付给传输层。
不过,在网络通信中,数据的分片并不是经常要做的,不分片其实才是常态。分片本身会带来一些潜在的问题,例如可能会增加丢包的概率等。此外, 在分片的数据中,每一个分片在 IP 层都会被添加上对应的 IP 报头,而传输层添加的报头只会出现在第一个分片中,因此网络中传输的数据包可能没有传输层的报头。
【ps】数据的分片和组装都由 IP 层完成
数据的分片和组装都是在 IP 层完成的,上层的传输层和下层的链路层其实并不关心。
假设传输层的协议为 TCP 。TCP 将待发送的数据交给 IP 后,并不关心该数据是否会在 IP 层进行分片,换句话说,TCP 并不关心数据具体的发送过程;而当 TCP 从 IP 获取到数据后,也不关心该数据是否在 IP 层经过了组装。
假设链路层的协议为 MAC 帧。 MAC 帧只负责,将数据从一个节点传送到和自己相连的下一个节点。当 IP 将待发送的数据交给 MAC 帧后,MAC 帧并不知道该数据是 IP 经过分片后的某个分片数据、还是一个没有经过分片的数据,它只知道自己一次最多只能发送 MTU 大小的数据,如果 IP 交给自己的数据大于 MTU 字节 ,那自己就无法进行发送。而当 MAC 帧从网络中获取到数据后,也不关心这个数据是否需要进行组装,只需要将该数据的MAC帧报头去掉后,直接上交给上层 IP 。
总得来说,数据的总量和可发送量并不由 IP 层决定的,总量是由 TCP 层决定的,可发送量是由 MAC 层决定的,IP 夹在 TCP 和 MAC 之间,当 TCP 的数据总量超过 MAC 的可发送量,就需要自行决定是否进行数据分片。而 TCP 层和 MAC 层完全不关心 IP 层进行了分片,这就实现了高度解耦。
【ps】不仅源端主机可能会对数据进行分片,网络中的路由器也可能对数据进行分片。这是因为,不同局域网中的 MTU 是不一样的,如果传输路径上,某个网络的 MTU 比源端网络的小,那么该网络中的路由器就可能对 IP 数据报再次进行分片。
2)分片的具体过程
假设,IP 层要发送 4500 字节的数据。由于该数据超过了 MAC 帧规定的 MTU,即 1500 字节,因此 IP 需要先将该数据进行分片,然后再将一个个的分片交给 MAC 帧进行发送。4500 字节的数据,至少需要分为四个分片报文进行发送。
另假设,IP 层添加的 IP 报头不携带选项字段,其长度为 20 字节,且 IP 层将数据分片以下四个分片报文:
在对数据进行分片时,需要记录分片的信息,以便分片报文到达对端的 IP 层后被组装起来。
IP报头中的 16 位标识、3 位标志和 13 位片偏移,就记录了数据分片的有关信息。
16 位标识:唯一标识主机发送的报文,如果数据在 IP 层进行了分片,那么每一个分片报文的 16 位标识是相同的。
当子网中每新增一台主机时,都需要为其分配一个 IP 地址,而当子网中每有一台主机断开网络时,又需要将它的 IP 地址回收,便于分配给新增的主机使用。这使得手动管理 IP 地址非常麻烦,而事实上对于 IP 地址的分配和回收一般不会手动进行,而是采用 DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)技术。
DHCP 是一个基于 UDP 的应用层协议,通常被应用在大型的局域网环境中,其主要作用就是集中地址管理、分配 IP 地址,不仅能使网络环境中的主机动态地获得 IP 地址、Gateway 地址、DNS 服务器地址等信息,还能提升地址的使用率。
此外,一般的路由器也都带有 DHCP 功能,因此路由器其实也可以看作是一个 DHCP 服务器。例如连接 WiFi 时,,路由器需要验证账号和密码,验证通过后,路由器就会动态分配一个IP地址,通过这个 IP 地址就可以进行各种上网活动。
【Tips】IP 地址的分配和回收一般依赖于应用层的 DHCP 协议。
IP 地址作为信标,在数据跨网络传输中起着至关重要的作用。但其实,IP 协议将数据跨网络地从一台主机发送到另一台主机,并不是直接将数据送达目标主机的,而是先将数据送达目标主机所在的网络,再送达目标主机的。
依此方案,要判断一个 IP 地址属于哪一类,只需要遍历 IP 地址的前 5 个比特位,其中第几个比特位最先出现 0 值,那么这个 IP 地址就对应地属于哪一类地址。
但随着网络的飞速发展,这种划分方案很快就显现了局限性。
由于 A 类地址的网络号只占 7个比特位,因此 A 类地址可申请的网络只有 2 ^ 7 个,这使得大多数组织,例如一些学校、公司、实验室等,只好选择申请 B 类地址。而 B 类地址的主机号占 16 个比特位,于是理论上一个 B 类网络中允许有 65536 台主机。但在实际网络架设中,一般一个局域网中不会存在这么多主机,如果存在,也就意味着,大量的 IP 地址会被浪费掉。
为了避免大量 IP 地址被浪费,后来又提出了新的划分方案,被称为 CIDR(Classless Interdomain Routing,无类域间路由)。
CIDR 并没有完全抛弃原先的方案,而是基于原有的五类网络的,继续进行子网划分,这也就意味着,需要借用主机号中的若干位来充当网络号,而为了区分 IP 地址中的网络号和主机号,又引入了子网掩码(subnet mask)的概念。
子网掩码是一个 32 位的正整数,通常用一串 "0" 来结尾。每一个子网都有自己的子网掩码,将 IP 地址与当前网络的子网掩码进行"按位与"操作,就能够得到当前网络的网络号。
随着一个网络被更细粒度地划分成一个个更小的子网,子网中 IP 地址对应的主机号也越来越短,使得子网中可用 IP 地址的个数也就越来越少,由此就避免了大量 IP 地址被浪费的情况。
由于 IP 地址(以 IPv4 为例)是一个 4 字节、32 位的正整数,因此共有 2 ^ 32 个 IP 地址,也就是将近 43 亿个IP地址。
TCP/IP 协议规定,每个主机都需要有一个 IP 地址。
但现在不仅电脑需要 IP 地址,还有其他设备也需要 IP 地址。例如也可以上网的智能手机,现在全世界人口已经有 70 多亿了,就算只有一半人拥有智能手机,算下来也有 30 多亿了,而它们都需要一个唯一的 IP 地址。随着科技的发展,还有智能手表、智能冰箱、智能洗衣机等设备,它们如果要入网,就也需要 IP 地址。另外,一个主机可能还需要多个 IP 地址,更别谈还有很多组网的路由设备,也需要IP地址。
所以,43 亿个 IP 地址其实早就不够用了,因此才提出了 CIDR。
CIDR 对已经划分好的五类网络继续进行子网划分,其目的既是为了减少 IP 地址的浪费,也是解决 IP 地址不够用的问题。虽然这的确在一定程度上缓解了 IP 地址不够用的问题,但 IP 地址的绝对上限并没有增加。
【补】解决 IP 地址不足的其他方案
动态分配 IP 地址:只给接入网络的设备分配 IP 地址。由此,同一个 MAC 地址的设备,每次接入互联网中,得到的IP地址不一定是相同的,这就避免了 IP 地址强绑定于某一台设备。
NAT 技术:让不同局域网中同时存在两个相同的IP地址。NAT 技术不仅能解决 IP 地址不足的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。
IPv6:IPv6 用 16 字节 128 位来表示一个 IP 地址,能够大大缓解 IP 地址不足的问题。不过,IPv6 并不是 IPv4 的简单升级版,它们其实是互不相干的两个协议,彼此并不兼容,而目前 IPv6 尚未普及。
5)特殊的 IP 地址
其实,并非所有的 IP 地址都能够作为主机的 IP 地址,有些 IP 地址本身是具有特殊用途的。
将 IP 地址中的主机地址全部设为 0,就成为了网络号,代表这个局域网。
将 IP 地址中的主机地址全部设为 1,就成为了广播地址,用于给同一个链路中相互连接的所有主机发送数据包。
特别的,127.* 的 IP 地址用于本机环回(loop back)测试,通常是 127.0.0.1。
主机号为全 0 代表的是当前局域网的网络号,主机号为全 1 代表的是广播地址,这两个 IP 地址都不能作为主机的 IP 地址。也就是说,在某个局域网中,最多能存在的主机个数其实是 2 ^ 主机号位数 - 2 。
当数据到达 IP 层需要继续向下交付时,如果是环回程序,那么 IP 输出函数会将该数据放入到 IP 输入队列当中,然后再由IP输入函数读取上去。而 IP 输入函数读取的本应该是链路层交付上来的数据,因此,该数据后续就会被当作从网络中读取的一样看待,各层协议都会对该数据依次进行解包和分用。
如果不是环回程序的话,就会判断该数据的目的 IP 地址是否为广播或多播地址,或者是否与本主机的 IP 地址相同。是,则也会将该数据放入到 IP 输入队列中,等待 IP 输入函数将其读走。只有程序不是环回程序,且也不是广播或多播、或发给本主机的数据,才会用ARP 获取该数据目的主机的以太网地址,并进行数据发送的操作。
6)私网 IP 和公网 IP
如果一个组织内部组建局域网,IP 地址只用于局域网内的通信,而不直接连入广域网中,则称这个 IP 地址是私有的。
LAN 口(Local Area Network):表示连接本地网络的端口,主要与家庭网络中的交换机、集线器或 PC 相连。
WAN 口(Wide Area Network):表示连接广域网的端口(一般指互联网)。
其中,LAN 口的 IP 地址叫做 LAN 口 IP,或子网 IP,而 WAN 口的 IP 地址叫做 WAN 口IP,或外网 IP。
自用的电脑、家用路由器、运营商路由器、广域网以及我们要访问的服务器之间具有如下关系:
不同的路由器的子网 IP ,其实都是一样的(通常是 192.168.1.1),子网内的主机的 IP 地址不能重复,但子网之间的 IP 地址可以重复。
每一个家用路由器,也是运营商路由器的子网中的一个节点,这样的运营商路由器可能会有很多级,最外层的运营商路由器的 WAN 口 IP 就是一个公网 IP 了。
如果希望自己实现的服务器程序,能够在公网上被访问到,就需要把程序部署在一台具有外网 IP 的服务器上。
私网 IP 不能出现在公网中。这是因为,不同的局域网中主机的 IP 地址可能是相同的,私网 IP 在广域网中无法唯一标识一台主机,也就不能让私网 IP 出现在公网上;且 IP 地址天然不足,也就不能让主机直接使用公网 IP,而得让主机使用可重复的私网 IP,在不同的局域网中使用相同的 IP 地址,以缓解 IP 地址的不足;运营商要收取网费,数据包必须要经过运营商的路由器才能发送到公网,而不能直接发送到公网。
不同局域网中的主机要进行通信,必须跨公网。这是因为,一个主机要将数据发送给另一台主机,前提是得先知道另一台主机的 IP 地址,但即便知道了,也有可能两台主机的 IP 地址是一样的,且都是私网 IP 地址,如果一台主机发送数据时,将目的 IP 地址填成和自己相同的IP地址,系统就会认为这个数据就是要发给自己的,就不会向外进行发送了。也就是说,数据要从一个局域网到另一个局域网,不可能不经过公网。
于是,子网内的主机在和外网进行通信时,路由器会不断将数据包的 IP 报头中的源IP地址,替换成路由器的 WAN 口 IP,如此逐级替换,最终数据包中的源IP 地址会变成一个公网 IP,这种技术就称为 NAT(Network Address Translation,网络地址转换)。
但实际上也存在一些技术,能够使数据包在发送过程中不进行公网 IP 的替换,而将数据正确地送到目标主机。这种技术叫做内网穿透,或 NAT 穿透。
五、路由
1)数据路由
数据在网络传输中,会经历路由,也就是路径选择,从而到达目的主机。
数据路由的过程,其实就是一跳一跳(Hop by Hop)"问路"的过程。所谓"一跳",就是数据链路层中的一个区间,具体在以太网中指,从源 MAC 地址到目的 MAC 地址之间的帧传输区间。
数据在网络传输中,会遇到很多路由器,这些路由器会帮助数据进行路由转发。每收到一个数据包,路由器都会查询路由表,找到该数据的目的 IP 地址,并告知该数据下一跳应该往哪跳。
【Tips】路由器查询路由表的可能结果
得知该数据下一跳应该跳到哪一个子网。
没有发现匹配的子网,此时路由器会将该数据转发给默认路由。
得知该数据的目标网络就是当前所在的网络,此时路由器就会将该数据转给当前网络中对应的主机。
2)路由表查询的具体过程
每个路由器内部会维护一张路由表。
指令 route 可以查看云服务器上对应的路由表。
其中表项的含义为:
Destination:代表的是目的网络地址。
Gateway:代表的是下一跳地址。
Genmask:代表的是子网掩码。
Flags:U 标志表示此条目有效(可以禁用某些条目),G 标志表示此条目的下一跳地址是某个路由器的地址。没有 G 标志的条目,表示目的网络地址是与本机接口直接相连的网络,不必经路由器转发。