网络层
因特网的网络层提供了单一的服务,称为尽力而为服务(best-effort service),似乎是根本无服务的一种委婉说法,分组间的定时是得不到保证的,分组接收的顺序也不能保证与发送的顺序一致,传送的分组也不能保证最终交付。
网络层的作用是将分组从一台发送主机移动到一台接收主机,为此,需要两种重要的网络层功能:
- 转发:当一个分组到达某路由器的一条输入链路时,该路由器必须将该分组移动到适当的输出链路。
- 选路:当分组从发送方流向接收方时,网络层必须决定这些分组所采用的路由或路径,选路涉及一个网络中的所有路由器,它们经选路协议共同交互。
转发
每台路由器具有一张转发表(forwarding table),路由器通过检查到达分组首部中的一个字段的值,然后使用该值在该路由器的转发表中索引查询来转发一个分组。
IPv4使用32bit作为地址编码,有超过40亿个可能的地址,这么多地址全部存储到一台路由器中显然是不现实的,为此路由器使用最长前缀匹配规则,在该表中寻找最长的匹配项,并向与最长前缀匹配的链路接口转发该分组。
| 前缀匹配 | 链路接口 |
|---|---|
| 11001000 00010111 00010 | 0 |
| 11001000 00010111 0001100 | 1 |
| 11001000 00010111 00011 | 2 |
地址分层的做法与web服务的UrlMapping一样,每个微服务仅负责与自己相关context的请求,Controller内部使用最长前缀匹配。
IP编址
一台主机通常只有一条链路连接到网络,当主机中的IP想发送一个数据报时,它就在该链路上发送,主机与物理链路之间的边界叫做接口。一台路由器连接多条链路,每条链路都有一个接口。一个IP地址在技术上是与一个接口相关联的,而不是与包括该接口的主机或路由器相关联的,所以IP要求每台主机和路由器接口都有自己的IP地址。
在因特网中,每台主机和路由器上的每个接口都必须有一个全球唯一的IP地址(位于NAT后面的接口除外),一个接口的IP地址的组成部分需要由其连接的子网决定。
为了划分网络,提出了子网掩码的概念,比如一个子网地址223.1.1.0/24,其中的/24表示该子网所有IP地址的前24bit值相同。一个子网的IP定义并不局限于连接多台主机到一台路由器接口的以太网网段,同一个路由器的不同接口可以归属不同子网,不同接口可以跨子网路由转发。
IP数据报格式
版本:通过查看版本号,路由器可确定如何解释IP数据报的剩余部分。
首部长度:区别于总长度,是因为这里有一个option选项需要指明其长度。
标识、片偏移:一个链路层帧能承载的最大数据量叫做最大传输单元(Maximum Transmission Unit,MTU),其限制了IP数据包的长度,虽然IP自己用了16bit存储总长度,但在进入链路层传输时也需要进行分片。分片工作可以在路由器中进行,但出于性能考量,I分片的组装工作被放到端系统中,注意这里是端系统的网络层不是运输层,组装失败被视为丢包不会传递给运输层。id标识初始数据报,offset是字节量偏移,flag指明是否到达结尾。
TTL:每当数据报经过一台路由器时,该字段的值减1,减到0时该数据报必须丢弃,用于确保数据报不会永远在网络中循环。
协议:该字段仅在一个IP数据报到达其最终目的地时才会用到,指明了IP数据包的数据部分应交给哪个运输层协议,比如给TCP还是UDP还是ICMP。
首部检验和:这里只对IP首部进行了检验,而TCP/UDP是对整个报文段进行的,另外IP能够携带不一定要传递给TCP/UDP的数据,两者没有绑定关系。
版本号对于协议的升级兼容或者是多版本的过滤匹配有着不可忽视的好处。
HTTP断点续传原理与数据报分片的方式相似,id+offset+flag三板斧近乎完美实现分片。
路由器工作原理
一台路由器有4个组成成分:
- 输入端口:它要执行将一条输入的物理链路端接到路由器的物理层功能,它也要执行需要与位于链路远端的的数据链路层功能交互的数据链路层功能,它还要完成查找与转发功能,以便转发到路由器交换结构部分的分组能出现在适当的输出接口。
- 交换结构:将路由器的输入端口连接到它的输出端口。
- 输出端口:执行与输入端口顺序相反的数据链路层和物理层功能。
- 选路处理器:执行选路协议,维护选路信息与转发表,并执行路由器中的网络管理功能。
输入端口通常有一份转发表的本地拷贝,就可在每个输入端口本地做出转发决策,而无需调用中央选路处理器,这种分散式的转发可避免在路由器中的某个单点产生转发处理的瓶颈。
输入端口一旦通过查找确定了一个分组的输出端口,则该分组可转发进入交换结构,这些分组可能会在进入交换结构时暂时阻塞,这是由于来自其他输入端口的分组当前正在使用该交换结构。交换结构可能使用的交换技术有内存交换、总线交换、互联网络交换,这几种技术都涉及到资源锁定。
当交换结构将分组交付给输出端口的速率超过输出链路速率时,就需要排队与缓存管理。随着这些队列的增长,路由器的缓存空间将会最终耗尽,且会出现丢包。
引入队列来缓冲任务,并发调度也采用了这种方案,比如Java线程池,Golang协程调度器。
NAT
NAT(Network Address Translation)适用于IP复用场景,几个LAN设备仅对外暴露一个WAN IP,这个IP和转换工作均由NAT路由器完成。
路由器从ISP的DHCP服务器得到其WAN IP地址,它再运行一个DHCP服务器,在NAT-DHCP路由器控制的家庭网络的地址空间中为计算机提供LAN IP地址。
如果来自广域网到达NAT路由器的所有数据报都有相同的目的IP地址(特别是对NAT路由器广域网一侧的接口而言),那么该路由器是怎样知道它应将一个给定数据报转发给哪个内部主机呢?技巧就是使用NAT路由器上的一张NAT转换表,并且在表项中包含了端口号和IP地址。当生成一个新的源端口号时,NAT路由器可选择任意一个当前未在NAT转换表中使用的源端口号,端口号字段为16bit,因此NAT协议可支持60000多个并行使用路由器广域网一侧IP地址的连接。
NAT也是存在争议的,有人认为路由器通常应当处理最多第三层的分组,NAT协议违反了所谓端到端原则,即主机间应相互直接对话,节点不应介入修改IP地址与端口号。他们认为我们应使用IPv6来解决IP地址短缺问题,而不是不计后果地用一种诸如NAT之类的权宜之计来修补存在的问题。
选路
那么路由器中的转发表是如何配置的?它揭示了选路和转发间的重要相互作用关系,选路算法决定了插入该路由器的转发表中的值,选路算法可能是集中式的(该算法在某个中心点执行,并向每台路由器下载选路信息),也可能是分布式的(分布式选路算法的各部分运行在每台路由器上)。在任何一种情形下,路由器都接收选路协议报文,并用于配置其转发表。
选路路由算法不在本专栏目标范围内,这里就不进行分析解释。
选路的分布式协议和集中式协议,是一个思考点,参考elsticjob和xxl的区别,还有tidb,选型的主要依据在于中心节点,如果存在高可用中心节点对集群进行调度协调使得整个系统更加高效强大,那就采用集中式协议,然后把能下派到分布式节点的任务尽量下发,减少中心节点压力瓶颈。
数据链路层
在数据链路层,把主机和路由器均称为节点,不特别关心一个节点是一台路由器还是一台主机,把沿着通信路径连接相邻节点的通信信道称为链路。为了将一个数据报从源主机传输到目的主机,数据报必须通过沿端到端路径上的每段链路传输,在通过特定的链路时,传输节点将此数据报封装在链路层帧中,并将该帧发送到链路上,接收节点然后接收该帧并提取出数据报。
链路层协议定义了链路两端的节点之间交互的分组格式,以及当发送和接收分组时这些节点采取的动作。链路层协议交换的数据单元称为帧,当发送和接收帧时,链路层协议所采取的动作包括差错检测、重传、流量控制和随机接入。链路层协议包括以太网、Wifi、令牌环和PPP。链路层的一个重要特点是数据报在不同链路上可能由不同链路层协议所承载。
链路层协议
链路层协议能够提供的可能服务包括:
- 成帧。帧的结构由链路层协议规定。
- 链路接入。媒体访问控制(Medium Access Control,MAC)协议规定了帧在链路上传输的规则。对于在链路的一端有一个发送方、链路的另一端有一个接收方的点对点链路,MAC协议比较简单(或者不存在),即只要链路空闲,发送方都能够发送帧;对于多个节点共享单个广播信息的情况,设计到多路访问问题,MAC协议用来协调多个节点的帧传输。
- 可靠交付。与传输层可靠交付服务类似,链路层的可靠交付服务通过是通过确认和重传实现的。链路层可靠交付服务通常用于易产生高差错率的链路,比如无线链路,而对于低比特差错的链路,链路层协议不提供可靠交付服务,比如光纤、同轴电缆。
- 流量控制。链路每一端的节点都具有有限容量的帧缓存能力,与运输层相似,链路层协议能够提供流量控制,以防止链路一端的发送节点淹没链路另一端的接收节点。
- 差错检测。在因特网中运输层和网络层也提供了有限形式的差错检测(即互联网校验和),链路层的差错检测通常更复杂,并且用硬件实现。
- 差错纠正。差错纠正和差错检测类似,区别在于接收方不仅能检测帧中是否引入了差错,而且能够准确地判决帧中的差错出现在哪里。
链路层提供的许多服务与运输层是非常相似的,区别在于作用域,运输层作用于两个节点进程之间,链路层作用于同一链路的相邻节点。
链路层编址
节点的适配器都有链路层地址,称为MAC地址,其长度为48bit。
适配器的MAC地址具有扁平结构(这与层次结构相反),而且不论适配器到哪里用都不会变化,而IP地址与一个人的邮政地址相似,它是有层次的,无论何时当人搬家时,该地址都必须改变,就像一个人可能发现邮政地址身份证号码都有用一样,一个节点具有一个IP地址和一个MAC地址都是有用的。
当某个适配器要向某些目的适配器发送一个帧时,发送适配器将目的适配器的MAC地址插入到帧中,并将该帧发送到LAN中,如果该LAN是一个广播LAN,这个帧会被该LAN上的所有其他适配器接收和处理,接收到该帧的每个适配器将检查目的MAC地址是否是自己的MAC地址,如果是就提取出封装的数据报将其传递给网络层,否则丢弃该帧。
多路访问协议
有两种截然不同类型的链路层信道:
- 由广播信道组成,这种信道常在局域网(Local Area Network,LAN)接入网中,许多主机被连接到相同的通信信道,需要所谓的媒体访问协议来协调传输和避免碰撞。
- 由点对点通信链路组成,例如两台路由器之间的通信链路或一个住宅的拨号调制解调器与一台ISP路由器之间的通信链路。
节点通过多路访问协议(multiple access protocol)来规范它们在共享的广播信道上的传输行为。因为所有节点都能够传输帧,两个以上的节点可能会同时传输帧,这种情况下所有节点同时接收到多个帧并发生碰撞,碰撞帧的信息纠缠在一起最后丢失掉造成广播信道浪费。
我们能够将任何多路访问协议划分为3种类型之一:信道划分协议、随机接入协议和轮流协议。
以太网
以太网几乎完全占领着现有的有线局域网市场。
- 所有的以太网技术都向网络层提供无连接服务。
- 所有的以太网技术都向网络层提供了不可靠服务,当该帧通过CRC校验时,它不发送确认帧,而当该帧没有通过CRC校验时,它也不发送否定帧,适配器只是丢弃该帧。
20世纪90年代后期,大多数公司和大学使用一个基于集线器的星型拓扑,用以太网安装替代了它们的LAN,集线器(hub)是一种物理层设备,他作用于各个比特而不是作用于帧,当表示一个0或一个1的比特到达一个接口时,集线器只是重新生成这个比特,将其能量强度放大,并将该比特向其他所有接口传输出去,因此,这也是一个广播LAN,如果某集线器同时从两个不同的接口接收到帧,出现一次碰撞,生成该帧的节点必须重新传输该帧。
以太网使用了CSMA/CD多路访问协议,此协议运行在所有适配器中:
- 适配器可以在任何时刻开始传输,这就是说没有时隙的概念。
- 当一个适配器侦听到有某些其他的适配器正在传输,它决不会传输帧,这就是说它使用了载波侦听。
- 一旦传输中的适配器检测到另一个适配器正在传输,就中止它的传输,这就是说它使用了碰撞检测。
- 在尝试重传之前,适配器等待一个随机时间,这个时间通常比传输一帧的时间要短,这里使用了随机指数后退策略。
在21世纪前期,以太网安装继续使用星型拓扑,但是位于中心的集线器被交换机(switch)所替代,交换机采用的是存储转发分组交换,在任何时候决不会向相同的接口转发超过一个帧,此外现在交换机是全双工的,这使得一台交换机和一个节点能够在同时向对方发送帧而没有干扰,因此这时就没有必要使用MAC协议。
交换机的过滤和转发借助于交换机表,此表类似于路由器的转发表,区别是这里存储的是MAC地址。
public void switch(int srcPort, String dstMac, Frame data) {
Integer dstPort = getPortFromTable(dstMac);
if (dstPort == null) {
// 找不到匹配的表项,广播,或许ARP有用,或者传递给级联交换机(扁平结构!)
broadcast(data);
} else if (dstPort != srcPort) {
// 找到匹配的表项,存储到对应缓存进行转发,减少碰撞
forward(data, dstPort);
} else {
// 无效loop,丢弃
discard(data);
}
}
二层互通
好了,让我们把前面的网络层和数据链路层联通起来吧。
数据链路层工作在两个相邻节点的链路上,交换机只是拓扑结构的组成之一,其没有MAC地址,不属于节点,帧只是通过链路流向交换机并被存储转发到目的链路,我们可以狭义地将“相邻节点”视为交换机能够触及到的所有节点范围内,这个范围内的节点只需要通过MAC地址通信即可,这便组成了一个LAN子网。
为了发送数据报,源节点不仅要向它的适配器提供IP数据报,还要提供目的节点的MAC地址,那么发送节点是如何根据目的节点的IP地址确定其MAC地址呢?答案就是ARP,每个节点的ARP模块都在它的RAM中有一个ARP表,这张表包含IP地址到MAC地址的映射关系。通过ARP分组广播可以更新表项。
ARP将一个IP地址解析为一个MAC地址,看着与DNS类似,DNS将主机名解析为IP地址,然而DNS为在因特网中任何地方的主机解析主机名,而ARP只为同一个子网上的节点解析IP地址。
那如果两个节点处于不同子网呢?答案就是网络层路由器,路由器的特殊之处就是拥有多个端口,每个端口有一个独立的IP地址和MAC地址,这些端口可以处于不同子网,所以源节点可以将初始帧目的MAC地址填为与自己同一子网的路由器端口MAC地址,(此时IP地址与MAC地址是不匹配的,ARP入参的IP地址由源节点路由表决定而不是数据报IP源地址,能传递交付就行!),路由器链路层将数据报传递给自身的网络层,网络层使用IP转发表将数据报传递给对应的端口,此端口在自己的链路层再次使用ARP将包传递出去,最终到达目的节点。
这里的工作链路与前面网络层的NAT不冲突,在这里路由器对外表现就是替换了目的MAC地址,NAT对外表现就是替换了源IP地址或目标IP地址,NAT是可选搭配的,由路由器对LAN内主机透明实现。