2024年Android最全学习网络编程No,2024年最新腾讯Android开发面试记录

最后

一线互联网Android面试题含详解(初级到高级专题)

这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率

如果设置门槛,很多开发者朋友会因此错过这套高级架构资料,错过提升成为架构师的可能。这就失去了我们的初衷;让更多人都能通过高效高质量的学习,提升自己的技术和格局,升职加薪。

最后送给大家一句话,望共勉,永远不要放弃自己的梦想和追求;

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

在学习网络层之前,简简单单把传输层回顾一下。在之前理解传输层时我们将传输层比作是一个驿站,说它具备控制数据转发的能力,然后我们认识了两种它控制数据转发的方式。一种实时效率高一种可靠效率也不低,我们分别学习了它们的报头并从代码的角度理解报头的实现以及具体的封装和分用过程。并且我们通过一些场景和补充知识进一步了解了数据的转发方式,特别是在理解传输层如何保证可靠传输方式上,我们花费了大量的时间和精力进行了深度的剖析。从而对于传输层可靠传输我们有了自己的心得体会,实现可靠传输最关键的步骤就是确认应答,而想要实现确认应答,那么对数据包做序号编号以及有序接收就必不可少。因此对于实现一个可靠数据传输最重要的步骤就是确认应答加编号和有序接收。至于还有相关非常优雅的提高数据传输效率和安全性的策略,这里我们不再详谈。下面我们再把有关传输层与连接和字节流的知识进行进一步理解,完善上篇博客。

1.传输层字节流问题
上篇博客在理解TCP面向字节流的概念时,我们将字节流的概念做了一个很好的铺垫,明确整个计算机使用的数据传输方式都是字节流,而之所以使用字节流好处不言而喻。而在字节流传输带来各种好处的同时挑战也随之降临,也就是我们上篇博客结尾处提到的粘包问题。因此当应用层想要从TCP缓冲区中读取数据,首先需要解决的就是粘包问题,也就是需要对字节流数据按照特定格式进行解析过程。所以对于一个应用层程序而言,在对数据进行反序列化之前最重要的步骤就是将数据从TCP缓冲区中完整的读取。而如果想要将数据从TCP缓冲区完整的读取到应用层,那么对于应用层而言它在发送数据时或者说序列化数据时,就需要对数据进行特定的格式控制。所以再次明确解决粘包问题就是在解决发送数据时的序列化问题,只有解决了发送数据时的序列化问题我们才能确定接收数据时的粘包问题如何解决。其中对于发送数据时的序列化问题而言,通常我们可以采用定长报头表示有效载荷长度的方式或者分隔符的方式或者报头加分隔符的方式。而当我们明确了应用层数据使用的是何种方式进行序列化,那么在接收端读取缓冲区数据时,我们就再也不担心粘包问题了,因为我此时就可以按照序列化格式进行数据读取,如用定长报头表示有效载荷长度,那么在读取数据时我就可以先读取定长字节获取报头,然后根据报头获取有效载荷长度,从而继续向后读取固定大小字节的数据,从而获取到一个我想要的完整的数据。当然由于recv/read在读取数据时是将任意字节的数据读取到应用层缓冲区中,所以本质我们应该是从应用层缓冲区中截取(substr)固定字节的数据。因此最后再次强调粘包问题就是序列化问题,而对于解决序列化问题不同的场景就有不同的解决方案,并不是所有场景都适用同一种解决方案,所以我们需要根据具体的需求来选择具体的方案,也就是根据需求进行序列化过程,以提供一种更加灵活可靠的解决粘包问题的方法。

2.传输层连接问题
对TCP字节流问题有了更深的理解之后,虽然上篇博客我们已经对TCP的三次握手四次挥手过程进行了深度解析,但仍然还有几个点需要我们明确。首先最重要的一个概念就是明白连接一定是应用层在控制,虽然建立和关闭连接的能力都是来源于传输层,但这并不意味着传输层拥有自主决断的能力。也就是明确无论是网路协议栈还是操作系统本质都是在为应用层服务,听从应用层的指挥。所以对于处于正常状态下的连接,只有应用层能决定是否要断开该连接,当然具体为什么应用层要断开正常状态下的连接,我们先不谈。我们此时要先明白,虽然TCP协议不支持对正常连接做管理,但是对于那些非正常的连接TCP协议就需要有对应的策略来处理。这当然也是TCP协议可靠的体现嘛!以我们对四次挥手的理解,我们明白无论是进程终止还是调用close接口,在网络正常的情况下,四次挥手过程都能顺利进行。但若是一个客户端和一台服务器已经建立连接之后,客户端突然网络出了问题或者说断网了,客户端自己的操作系统肯定是能识别到当前网络不可用,并且在采取了一定措施之后发现自己还是连接不上网络,那么它就会将自己管理的连接资源释放。那么在这种情况下,对于服务端而言它如何处理呢?当然可能有的服务器为了追求极致的效能,在进行应用层开发的时候,它就意识到了这个问题的存在从而有对应的处理方法,但是并不是所有的服务器都有这种能力,毕竟这会导致开发成本变高。所以若是没有对应处理方法的服务器碰到了这种问题,也就是客户端建立连接之后因为断网无法发送FIN报文导致服务端无法知道具体的连接状态一直浪费资源维护对应连接的问题。那么单单对于服务器而言,在没有主动发送数据的前提下它是不可能知道客户端的连接情况的,更别说主动断开连接了。因此TCP协议就需要出手,在TCP协议中就存在一个叫做保活的策略,也就是TCP协议会定时给那些长时间没有进行数据交互的连接发送特定的报文来判断它们的连接是否异常,若是异常那么此时TCP协议或者说操作系统就会将对应的资源释放也就是关闭连接。当然这种保活策略还有一个好处就是如果客户端突然又从断网状态恢复过来,那么就能提示客户端当前连接状态不一致,从而使客户端发送RST重置连接报文,保证客户端与服务端之间连接的准确性。明白了这点之后,再次证明TCP协议就是为了给应用层提供一个可靠的服务。当然对于应用层为什么要断开正常连接的问题,此时我们就心中有答案啦!原来服务器为了提高自己资源的利用率,从而增加产能、效能。它就不允许那些长期霸占连接资源但是不进行数据交互的用户存在。因此若是服务器检测到长时间处于不活跃状态的客户端,那么服务器就会直接发送一个RST连接重置报文,从而使得两端的连接关闭。注意:服务器在发送RST报文的时候同时就已经在进行资源释放的工作,不需要等待类似四次挥手一样的应答,而对于客户端收到RST报文的同时就表示着资源要被释放。而当后续你再次活跃之后,客户端就又会重新向服务端发送三次握手请求。当然具体因为客户端和服务端都是同一套设计理念,所以这个过程你肯定是感受不到的,但是它肯定是存在的。最后再明确,一个连接时间的长度也就是一个连接可以不活跃的最长时间一定是由应用层根据需求和场景来设计的,好比我们常使用的聊天软件就一定存在这种策略。而TCP在这其中扮演的角色就像是一个健康检测员或者守护者或者清理工的角色。

3.理解listen的第二个参数
首先明确,对于理解listen的第二个参数本质是一个与连接有关的问题。在当时学习套接字网络通信时我们说想要实现TCP套接字通信,对于应用层而言最关键的就是需要建立连接,当然这也是TCP版本的套接字通信与UDP版本最大的不同。因此在实现TCP套接字网络通信时我们首先就需要在客户端使用connect接口告诉操作系统应用层此时想要与某主机的某进程建立连接,所以当我们调用connect接口之后操作系统就会对目标主机进行三次握手过程,当然前提是此时服务端套接字创建绑定成功并正处于监听状态,也就是调用listen接口成功。而只有当上述工作完成,也就是服务端监听到客户端的连接请求并将其存储在监听队列之中后,服务端才能使用accept接口从底层也就是传输层将该连接获取到应用层。所以对于listen接口的本质理解就是让操作系统在传输层维护一个队列用于存储那些已经完成三次握手也就是已经与我建立连接但还未被应用层处理的连接请求。当然至于应用层为什么要从传输层中获取连接与传输层为什么要建立连接这两个问题的答案肯定是不同的。谈传输层为什么要建立连接,当然准确的是TCP协议为什么要建立连接,这个问题在之前我们谈TCP面向连接时我们就总结过了,我们说TCP协议面向连接就是为了建立一个可靠的通信信道方案,也就是确保两端已经准备好进行数据传输且具备数据发送和接收的能力的同时验证对方目前的身份和状态。而之所以说面向连接就能够保证一个可靠的通信信道方案,本质就是三次握手的过程嘛!三次握手能成功,我就认为此时这个通信是可靠的并且经过了三次握手双方在通信前的状态也就得到了保证。那么应用层为什么要从传输层获取连接呢?这个问题说难不难但是容易混淆和遗漏,首先同理上述connect接口所说调用该接口就是想要与某主机的某进程建立连接,那么很容易我们能知道connect接口肯定是通过对应的参数将目标主机的IP地址和端口告诉操作系统的,然后操作系统获取到对应的IP地址和端口之后,它才能发送连接请求。所以对于一个连接请求来说它除了可以定位目标主机,它肯定也可以定位本主机。因此应用层获取连接本质就是在获取对端主机的定位。所以这就是应用层获取传输层连接的原因。这也就是TCP协议下或者说建立连接下应用层可以直接使用recv/read、send/write接口的本质原因,而不是像UDP版本的套接字一样需要使用recvfrom/sendto这种需要给定IP地址和端口号才能使用的接口。因此这也就是应用层获取连接的好处以及为什么要获取连接。扯远了但是好像又不得不扯,反正此时我们就知道就算你应用层不使用accept接口,底层也能将连接建立完成。因此对于listen接口的第二参数我们就认为是传输层所维护队列中可以建立最大连接个数减一,也就是若我们将listen接口的第二个参数设置为1,那么传输层维护队列最多只能同时存在2个连接。具体如下示意图所示:
在这里插入图片描述
当我们的服务器不使用accept接口将连接获取到应用层并且将listen接口的第二个参数设置为1,而当我们使用三台客户端机器去连接该服务器时,在使用netstat -natp指令查看对应服务器的网络状态时,如上图所示我们就可以发现只有两对连接能够建立成功,还有一对连接无法建立成功,也就是该连接中服务器处于SYN_RECV状态而不是ESTABLISHED状态。而之所以它无法成功建立连接的本质就是因为此时TCP维护的监听队列满了,也就是服务端无法再受理客户端发送的连接请求,但值得注意的是此时服务端依然会将自己的SYN+ACK响应给客户端从而维持在SYN_RECV状态,但如果在接下来收到客户端发送的最后一次应答时监听队列还处于过满的状态,服务端就不再会维持SYN_RECV状态,而是将该连接忽略。而对于服务端处于SYN_RECV状态时,对应收到的客户端连接请求同理就会被存放在一个传输层维护的半连接队列之中。所以对于监听队列而言它也被称为全连接队列,只有处于全连接队列当中的连接才能被上层处理,并且明确对于该全连接队列而言,因为其内部的连接是需要被应用层获取的,所以它的最大连接个数并不等于上层可以同时处理的连接个数,也就是上层可以不断的将该全连接队列中的连接获取走,当全连接队列中的连接被上层获取走之后就又有新的存储位置存储新的连接。因此对于该全连接队列而言它不不要太长,具体只需要根据场景来决定它的长度应该为多少。但这个全连接队列一定不能没有,因为如果没有的话,就是导致应用层获取连接的效率变慢,从而导致资源利用率变低。

正式开始学习网络层

对于网络层,当时我们在学习网络基础时对其有了一定的认识,简单的认为网络层的功能就是地址管理和路由选择,然后探讨了一下路由器工作在网络层的说法。而如今凭借我们对应用层以及传输层的学习我们知道谈网络协议栈本身就是在谈协议,所以想要真正把网络层以及相关知识搞懂,首当其冲的就是学习IP协议。首先我们要明确,虽然在学习传输层TCP时我们谈到了各种保证数据可靠传输的策略,但是TCP协议并没有数据传输的能力。好比我们之前将传输层比作是一个快递驿站,然而快递驿站并没有运送快递的能力,拥有运送快递能力的是那些需要根据地址将快递送货上门的快递小哥。而同理驿站作为快递小哥的上级,传输层作为网络层的上级,所以此时我们就明白IP协议为我们提供了一种能力,一种将数据从A主机送到B主机的能力。而因为驿站也就是TCP协议采取了各种保证传输可靠的策略,此时TCP协议就能让IP协议的能力百分百的发挥出来,从而让数据在网络正常的情况下百分百到达目标主机。当然之所以IP协议具有这种能力,原因有二,其一是因为IP协议中包含源IP、目的IP字段,另一是因为路由器在路由过程中使用的是IP协议。如下图所示,我们直接通过IP报头来学习IP协议。
IP报头示意图
首先明确,当我们学习完了传输层见识了UDP报头和TCP报头,对于报头的认识除了从代码角度的结构体或者位段之外,更重要的是我们明确报头中的字段大部分都是辅助对应协议实现协议特性的关键。好比TCP协议中的各种标志位、序号、确认序号、通告窗口等。因此此时我们直接用倒推的形式来学习IP报头,先明确IP协议的特性以及各种功能,再将对应的报头字段代入,从而更加清晰明白每个报头字段的作用以及存在的原因。对于IP协议的特性来说除了与其它协议相似的封装、解包和交付过程之外,它最重要的一个特性就是它能提供一种具有服务类型的能力,也就是IP协议能够根据发送数据包场景的不同来选择不同的发送方式。而对于发送方式或者服务类型来说,其中有优先级、延迟、吞吐量、可靠性以及成本的高低等选项。而从IP协议的功能来看,其最关键的两个功能就是数据发送和分片重组。所以当我们明确了IP协议的特性以及功能,此时我们就可以结合报头来好好理解一下上述所说。从IP协议的封装、解包和交付特性来看与我们之前学习的协议确实非常相似,都是通过先读取定长20字节获取4位首部长度解决是否包含选项问题,再通过16位总长度实现报头和有效载荷的分离。只不过此时对于IP协议的交付过程而言,由于上层不再是应用层,因此不能直接使用端口号字段进行交付,并且因为上层传输层具有多种数据传输协议,因此也不能采用默认交付。所以此时就需要设计一个与端口号类似能够区分上层不同进程的字段,当然此时是区分不同的传输协议。所以对于IP报头中的8位协议字段我们就能明确其表示的含义了,如该字段为6表示表示上层使用TCP协议或者说操作系统应使用TCP协议对该IP报文解包,若为17则用UDP协议进行解包,当然还可能是其它传输层协议。而当谈到IP协议的另一个重要特性,明确该特性本质应与IP协议转发数据的能力结合,该特性只是对IP转发数据能力的一个完善,以便于解决一些特定场景的数据发送问题。而因为路由器是根据IP协议制定的规则进行数据转发,也就是路由器需要读取IP报头数据,因此我们控制一个数据包发送过程的本质应是对路由器行为进行规则制定,而我们想要控制路由器此时就必须从IP报头入手,所以8位服务类型字段就诞生了。当然对于该最字段而言,具体如何表示我们不关心,也就是该字段状态码为多少表示高优先级、低延迟我们不关心,我们只需要明确路由器可以通过该字段不同的状态码来进行不同的处理动作以及该字段在应用层应该如何被设置和使用。所以如果在特定的场景下我们需要实现特定的数据发送方式或者说数据发送服务,此时我们就需要使用套接字API进行设置,具体如何设置不同的服务类型我们应参考setsockopt接口的使用说明。明白了IP协议的基本特性之后,对于IP报头中的大部分字段我们就认识了,对于什么16位首部校验和我们同理只需要明确它是为了保证数据准确性,当然因为此时是首部检验和所以它保证的只是首部也就是IP报头的准确性,当然为什么不是整个报头的准确性,本质也就是因为此时是网络层它只能识别网络层协议,有关有效载荷的准确性只能通过上层报头中对应的检测机制搞定。而对于4位版本而言,本质是对IPV4和IPV6的区分,因为两者在IP地址上最本质的不同,导致整个IP协议的格式不同,所以当路由器或者操作系统在识别该IP报文时最先识别的就是该字段,同理不同的协议使用不同的解包过程或者说不同的代码执行机制。当然为什么要有IPV6后续当我们谈到IP地址数量问题时再讲。IP报头目前我最后想谈的一个字段就是8位生存时间,首先明确对于生存时间而言我们此时可以理解成一个IP报文在网络中的最大转发次数。明白这点,那么对于该字段而言它的本质就是一个计数器参数,每当一个路由器读取该报文就会对该字段进行减减操作,直到该字段为0停止转发直接丢弃。当然本质也就是一个设计理念,重要的我们要知道为什么要有该字段,本质很简单好比是一个定期清理机制,IP协议为了防止网络中某些转发设备出现问题或者说因为某些网络设备在设计时出现问题,比如程序不小心死循环之类的,导致数据包在网络中被多个路由器循环发送出现资源被占用网络瘫痪。所以为了避免该问题,任何出现在网络中的数据包都必须有最大生存时间。最终,当我们明确了上述知识之后,IP报头中的版本、首部长度、服务类型、总长度、生存时间、协议类型和校验和字段我们全部认识并明确。而当我们明确了大部分IP报头之后,此时我们直接进入IP协议最关键两个功能数据转发和分片重组的学习,通过两个问题搞定。

为什么说IP协议具有数据转发的功能?
这个问题可能有点奇怪,但是我们得明确。谈到数据转发路由器不得不说,因为在网络层的物理层面而言只有路由器具有数据转发的能力,而我们之所以说IP协议具有将数据从A主机发送到B主机的能力,本质我们强调的并不是IP协议的数据转发能力,我们强调的是IP协议将数据从A主机转发到B主机的能力。本质也就是一种设计理念,为了完成这种设计理念我们就需要有路由器的存在。所以对于IP协议具有数据转发的能力最正确的理解应该是IP协议具有指挥路由器将数据从A主机发送到B主机的能力。因此对于上述问题我们就可以理解成为什么IP协议拥有指挥路由器的能力呢?从代码角度理解定制协议就是设计一个可以运行的程序,而使用协议就是一个执行程序的过程。而又因为在IP协议中路由器必不可少,那么路由器使用IP协议或者说执行对应程序肯定是必然的。所以,之所以IP协议具备指挥路由器的能力,本质是路由器执行了IP协议的程序。而之所以执行了IP协议的程序路由器就有将数据从A主机发送到B主机的能力,这也就是协议中各种策略的设计。如IP报头中包含了32位源IP地址和32位目的IP地址字段,让路由器在执行程序时能够从报头中获取到目标主机的IP地址,然后再根据程序中的其它代码解决该地址如何使用、如何转发或者说如何寻址的问题,最后根据一定的算法实现路由功能、定制路由规则,从而具备通过程序指挥路由器实现数据转发的能力。最后明白,IP协议具有发送数据的能力就是因为其定义了寻址、路由规则以及数据包结构格式,而路由器只是用于实现IP协议规则的一个网络设备。

IP协议如何进行分片和重组呢?
首先明确对于分片重组而言本身我们搞清楚分片问题,重组问题不言而喻。而想要明确分片问题,此时我们最大的疑惑不是分片是什么,而是为什么要分片?谈到为什么要分片,此时我们就不得不引出一个概念叫网络最大传输单元(MTU)。我们知道,数据想要从一台主机发送到另一台主机,它除了需要经过各种封装解包当然也就是各种代码执行机制之外,更重要的是它还需要经过各路由器之间的跨网段传输,所以对于一个数据的网络传输过程一定需要耗费时间,也就是我们俗称的网络延迟。当引出了网络延迟的概念,为什么会有网络最大传输单元我们就清楚了,本质也就是MTU和网络延迟或者说网络传输时间存在着一定的关系,MTU越大网络传输时间越长网络延迟就越高。因此我们就明白,MTU或者说数据链路层允许通过的最大数据单元或者说网卡一次发送的最大数据它一定是一个动态变化、较为合理的值。而因为我们不清楚实时网络状态,所以这个值默认被设置成了1500(单位/字节),也就是说MTU为1500是保证数据传输效率和网路延迟之间的最优值。明白了这点之后,对于网络层为什么要分片或者说网络层为什么需要有分片的能力就非常好理解了。因为它要处理上层数据的同时还要对下层负责。所以网络层如何完成分片工作呢?IP协议说我会出手当然本质还是设计理念。此时就不得不谈谈IP报头中剩余的三个字段:16位标识、3位标志、13位片偏移。这三个字段也就是网络层分片重组的关键,换一个说法若是将这三个字段设置在其它协议的报头中那么效果不言而喻,只是站在设计者的角度之所以设置在网络层自有他自己的考量。当然之所以需要设置这三个字段,更多的是考虑重组问题,因为对于分片问题本质很简单,就是对超过MTU数据做拆分再封装的过程,较为麻烦的是接收端重组过程。而当在IP报头中设置了这三个字段之后重组过程就有了抓手。其中对于16位标识而言,本质就是一个16位的整数或者说一个用于区分IP报文和IP报文之间的号码。当然对于这个号码它可以用于区分IP报文,但区分不同的IP报文操作系统一般会使用源IP地址,而这个号码更重要的作用是用于区分分片之后来自同一IP地址的数据,保证重组的准确性。而对于3位标志而言这种比特位位数少的一般的处理方式是进行位操作,而不是根据状态进行判断。其中3位标志的第一个比特位(从右到左)暂时保留并默认设置为0,若第二个比特位为1则表示禁止分片,反之则允许分片,当然如果一个数据超过了MTU并且设置了禁止分片,那么在其想要通过数据链路层发送到网络中时该数据就会被丢弃。那么问题就来了,这个数据我都发不出去,我设置禁止分片的意义在哪里呢?首先设置禁止分片一定是因为某些特定场景的需要,如对实时通信要求极高的场景,因为分片重组会导致增加延迟,所以在特定场景下为了保证数据的实时性和完整性就会将其设置成禁止分片。当然若是该特定数据的大小超过MTU不能被发送,它也能起到MTU探测的功能,同理上述所说MTU是一个动态变化的值,通过设置禁止分片可以起到调整实时数据的大小,从而尽可能的将一个完整的最大的数据发送到网络中,提高特定场景下的实时性。当然对于应用层而言,想要对该标志位进行设置同样使用的还是setsockopt接口,首先定义一个变量作为参数提供给操作系统 int optval = 1; 再使用套接字设置接口 setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG, &optval, sizeof(optval)),可以发现setsockopt接口在应用层网络传输中功能还是非常多的,具体有待深入参考其使用说明。对于3位标志位的最后一个比特位或者说最左比特位来说,它肯定就是重组的关键标志位。其表示的含义为更多分片,也就是如果该标志位为1那么表示该IP报文是一个分片报文且还存在其它分片报文,而如果该标志位为0则表示该IP报文不是分片报文或者该IP报文是分片报文的最后一个报文。最后对于重组而言最关键的字段16位片偏移,其表示的就是每个分片报文在原报文中相对于起始位置的偏移量。因此当我们理解了上述IP报头中有关分片重组的字段之后,此时假设我们有一个4500字节的TCP报文需要发送,此时我们就可以将其分片成如下四个报文所示:

分片报文分片报文总字节IP报头字节数据字节16位标识(整形)3位标志(比特位)13位片偏移(整形)
1150020148012341000
215002014801234100185
315002014801234100370
48020601234000555

所以此时我们明白若是一个报文为4500字节,它并不是就被分成3片每片1500字节,而是需要考虑到IP报头20字节的长度,因此分成四片且每片标识相同。其中每个报文的三位标志与我们预期相同,前三个报文的最左比特位被置1,最后一个报文为0。而最后13位片偏移是以64字节为单位,所以对应的片偏移不是0/1480/2960/4440。而当我们具备了这些可以提供给对端操作系统使用的信息之后,对端就有能力将一个分片报文给重新组装了。同理因为这些字段是在IP报头中,所以组装过程一定是发生在网络层。明确只有当接收端网络层收到的IP报头中3位标志的最左位为0且片偏移为0才表示该报文是一个独立IP报文,可以跳过重组过程直接交付给上层。反之其它情况全为分片报文需要进行重组步骤,而对于重组步骤操作系统同理根据这三个字段来进行。读取13位片偏移判断其是否为0,若为0则通过位操作获取最左处比特位判断是否为0,为0同理为1则表示该报文是某分片报文的第一片报文,提取16位标识字段。而片偏移若是大于0,同理读取3位标志的最左位若为0则表示该报文为某分片报文的最后一片,为1则表示该报文为某分片报文的中间报文,为中间报文则读取片偏移根据顺序对报文进行重组。所以当我们有了这三个字段之后,就可以通过序号区分来自同一主机的不同被分片报文,通过标志和片偏移结合区分该分片报文的首部报文、中间报文、尾部报文,最后通过获取首部报文有效载荷长度得知下一个报文偏移量大小的方式实现按序组装。而在组装过程中无非就是两种情况,一种是组装成功,另一种则是组装失败。对于组装失败而言,常见失败方式有两种,一种是组装过程中数据乱序或者是数据错误,而想要检测出数据是否因为组装过程出错,最简单的方式就是使用协议报头中的校验和字段。另一种则是组装报文发生丢包导致组装无法完成。而对于分片对包问题我们通过下述对MSS的学习来深入理解。

理解什么是MSS(Max Segment Size)
想要理解MSS,我们首先从上述谈到的分片丢包问题来看,因为IP协议的能力是数据转发,TCP协议的能力才是传输控制,只有TCP协议具有超时重传的能力,所以对于分片丢包问题TCP协议并不关心,它关心的只是能够收到应答。因此若发生分片丢包采取的策略并不是对丢包分片进行重传,而是在超时之后TCP重新对整个报文进行超时重传。所以不用想,这样带来的资源浪费或者说效率问题是非常严重的。当然对于数据进行分片传输还有一点不好的地方就是它会增大丢包的概率或者说增大重传的概率,因为同理上述所说对于TCP而言无论是一整个包丢失还是丢失其中一个分片采取的措施都是一样的。所以站在TCP的角度看分片重组策略我看不到任何优势,所以TCP协议必须做出优化。因此最大段尺寸或者说最大报文段长度(MSS)的概念就有了,可以这么说如果TCP不采用MSS的概念,它就不配与UDP相提并论了。而之所以需要设计出MSS,本质也就是设计者从问题的角度出发,我们不能改变下层最大传输单元的大小,那么我们就只能改变上层发送数据的大小,当然此时只是针对于TCP协议。通过TCP协议控制MSS的大小或者说传输层发送数据的大小,当然也就是交给网络层数据的大小,来避免网络层的分片重组问题或者说超时重传问题和丢包问题。上述谈到的可以说是什么是MSS或者说为什么要有MSS,但是这些我们都不怎么关心。更重要的应该是MSS的来源,明确MSS的计算方式一定是通过MTU得到的,而同理我们上述说MTU是动态的,不同的网络设备这个值可能是不同的,所以对于TCP想要完成网络数据传输而言,它就必须知道两端的MSS大小或者说MTU大小。而对于这个知识我们肯定不陌生,比如我们可以通过三次握手过程得知对端的接收能力,那么同理我们就也可以通过三次握手过程得知对端的MSS大小,当然当时学习TCP时之所以没有提到其一是因为场景不合适,其二是因为MSS的大小是通过TCP报头选项获取。所以此时我们明确,TCP滑动窗口一次能发送的最大数据量不仅仅只有通告窗口大小和拥塞窗口大小二者决定,此时还要包括两端MSS的最小值共同决定。而对于默认情况也就是MTU为1500的情况,MSS的大小一般为1460,也就是减去IP报头和TCP报头的长度。所以当TCP协议有了MSS机制之后,它就如虎添翼、重振雄风,再也不要担心UDP的挑衅了。明确MSS只适用于TCP协议,UDP协议并不适用,当然这也就是为什么网络层IP协议中分片重组的功能还存在的重要原因之一,当然我并没有说TCP协议就不需要使用IP的分片重组功能,只是TCP正常情况不用而已,但在某些特殊情况下,就算是TCP协议它也需要进行分片,当然本质还是因为MTU是可变的,不同网络设备有可能不同。

理解网段划分

在理解什么是网段划分之前,首先我们从网络设备入手,只要具有IP地址我们就可以将其认为是一台主机,而若是配有多个IP地址能进行跨网段或跨子网转发我们就可以认为其是路由器,而对于交换机而言目前网络层我们就将其理解成是子网内数据转发的设备。在网络层此时我们重点学习路由器,因此对于路由器而言我们首先明确什么叫跨网段转发什么叫跨子网转发。而想要明确网段和子网的概念,此时我们举一个生活中的例子。可以以国家为例也可以以公司为例也可以以学校为例,此时我们以学校为例,学校为了更好的管理每一个学生,所以需要将不同专业的学生分配到不同的学院,而学院为了更好的管理学生需要将同一专业的学生分配到不同的班级。并且每一个同学在学校都有属于自己的学号,并且不同专业的学生学号的前几位一定不同,同一专业的学生学号前几位一定相同,但后几位一定不同。这样无论是学校还是学院都可以通过学号快速定位到任何一位同学,通过学号的前几位快速定位学院,再通过后几位快速定位班级或个人。所以对于这种分层管理的方式它不仅适用于上述所说场景,在网络中也非常适用。本质也就是因为这种分层管理方式的两大优点便与管理与快速定位和网络数据转发完美契合。所以对于我们一直在谈的IP地址而言,它此时就不再仅表示某一台主机的地址,更重要的是它还能表示该地址对应所处的网络号,因此明确IP地址由网络号和主机号构成。当然可以理解IP地址之所以这样设计本质就是因为需要或者说想要进行网络管理。当然也可以理解成是因为IP地址这样设计,所以我们可以进行网络管理。如下图所示,此时我们就可以进行网段划分。

网段划分示意图
如上图此时我们能发现非常多的知识点,首先明确之所以说路由器具备跨网段或者说跨子网转发能力的前提条件一定是对应路由器也包含于对应网段或子网,也就是对应路由器一定拥有对应网段的IP地址或子网IP地址。而具体路由器是进行跨网段转发还是跨子网转发需要根据具体场景来看,本质也就是因为子网属于网段的一部分,包含于网段之中,也就是我们可以说一个网段就是一个子网,也可以说一个网段被划分成了多个独立的子网。因此对于一个路由器具体是跨网段转发还是跨子网转发我们需要根据参照网络来看待。并且从上图中我们可以发现两个网段它们的网络号一定不同,但主机号可以相同。因此也就是说每一个网段都有属于自己的网络号,每一个网段中的主机号都不相同,在这样的网络管理之下我们就实现了IP地址的唯一性原则。结合上述我们对网段划分所带来好处的理解,此时我们明确对于全世界不同的地区而言,不同的地区一定需要有不同的网络号从而构建出自己的网段,从而达到网络预期快速定位和管理的效果。所以对于网络号或者说IP地址的分配而言,它并不是随意分配,而是由世界各地的网络管理者或者运营商来分配的。所以此时就出现了许许多多的网络号分配方案,如下图所示经典的IP五类分配法:

在这里插入图片描述
如上图所示将32位IP地址分为五类,但其中只有A/B/C三类IP地址被广泛用于标识网络中的主机,而D/E两类地址则被用于特定的设计以实现某些使用目的。目前对于E类而言我们就简单将其理解为保留,而对于D类地址而言,在目前我们的生活中还是有具体的使用场景,我们简单了解一下。顾名思义,多播也被称为组播,它允许数据包发送到一组特定的主机,而不再是单个主机,因此被广泛用于网络直播、视频会议、网络游戏等场景。因此针对多播的概念来看,我们此时知道一般只有服务器会申请并绑定多播地址,而此时与该服务器绑定的客户端想要实现多播通信,在客户端的内部就需要使用对应的套接字API完成特定的工作,如加入多播组并发送特定的数据给路由器告诉路由器特定的IP地址想要实现多播通信,而当服务器发送数据时就会将目的IP地址设置为多播组中对应的IP地址,当路由器接发现了多播组中的IP地址时,就会将数据包转发给对应的客户端。以上知识基于自己实现服务器和客户端的认识简单了解就行。重点是除D/E两类之外的A/B/C三类IP地址的理解。首先明确A类一般用8个比特位作为网络号,B类一般用16个比特位作为网络号,C类一般24个比特位作为网络号。并且A/B/C三类最大的区别是A类第一个比特位一定为0,B类的第一个比特位一定不能为0但第二个比特位一定为0,C类的第一个和第二个比特位一定不能为0第三个比特位一定为0(从左到右),从而以这种方式区分出了三类不同的IP地址。至于这三类IP地址它们能取IP的范围是多少我们不关心,本质也就是通过全0到全1计算。我们更关心的应该是CIDR方案,也就是子网掩码的策略。

全方位理解CIDR策略
上述我们将IP分为了五类,但是真正提供给所有主机使用的IP只有三类,其中因为A类的网络号一般只有8个比特位,所以它最大只能构建一个00000000 ~ 01111111也就是0 ~ 127的网段,而因为0和127一般不作为网络号,所以在该网段只能有126台网络设备,注意目前我说的是该网段,并没有对该网段进行进一步的子网划分。而当其构建出网段之后,我们可以发现该网段中的每一个网络号都可以拥有24个比特位的使用权,也就是可以拥有2的24次方个主机。因此对于这种A类地址,由于其能自由划分的比特位最多,所以它被用于全世界各个地区的网段划分,可以是不同国家也可以是不同的洲,如美国和中国使用的是A类地址中的不同的网络号,美国/中国的网络号可能是00000001/00000010或者也可能是亚洲和美洲使用的是A类地址中不同的网络号,亚洲/美洲的网络号可能是00000001/00000010。明确了这些之后,对于B/C类地址而言一模一样,只不过B/C类它们能自由使用的比特位没有A类多,B类只有16个比特位能自由使用,C类只有8个比特位,因此B类和C类一般被使用于某些公司或者学校。通过上述对A/B/C类地址的进一步认识,此时我们可以发现两个问题。其一,对于B类和C类IP地址而言我们发现它们由于网络号占用的比特位位数多,所以相较于A类126个网络号而言它们的网络号个数多的离谱。因此我们发现如果网络中B类网络或者C类网络的IP地址多了,毕竟对于B类网络和C类网络而言它们允许有2的14次方减2和2的21次方减2个网络号,此时就会导致一个很严重的问题也就是路由器跨网段转发的效率将非常低下。其二,对于A/B类网络而言,我们可以发现它们由于主机号占用的比特位位数多,所以在该网段内允许存在的主机数量相较于C类就多的离谱,此时就有可能会导致主机号或者说IP地址大量浪费的问题。因此为了解决上述问题,CIDR的方案就诞生了。引入子网掩码的概念,对A/B/C三类地址在原先基础之上进行网络号再次划分。注意我说的是原先基础之上,也就是A类地址默认8比特位为网络号,你不能划分成小于该比特位的网络号,只能划分成更大的网络号。如你可以通过子网掩码255.255.0.0让A类网络以16个比特位为网络号,以子网掩码的方式动态的分配网络号和主机号,以此解决IP地址浪费问题。因此当有了子网掩码的概念之后,就不存在所有人使用的都是A/B/C三类中的一类IP地址,也就不存在大量同一类型网络号出现的问题,因为我们可以对某一个网络号通过子网掩码的方式进行子网划分,本质也就是进行了子网划分所以我们不需要使用同一类型的网络号。因此CIDR方案或者说子网掩码的策略就帮我们解决了IP地址浪费以及路由器效率低的问题。并且明确我们还可以通过子网掩码与IP地址按位与的方式快速得到对应IP地址的网络号,具体如下图所示:

IP地址140.252. 20.68 / 140.252. 30.688C FC 14 44 / 8C FC 1E 44
子网掩码255.255.255.0 / 255.255.255.240FF FF FF 00 / FF FF FF F0
网络号140.252.20.0 / 140.252.30.648C FC 14 00 / 8C FC 1E 40
子网地址范围140.252.20.0 ~ 140.252.20.255 / 140.252.30.64 ~ 140.252.30.798CFC1400 ~ 8CFC14FF / 8CFC1E40 ~ 8CFC1E4F
IP地址数量限制问题

明确了上述有关网段划分、IP地址类型以及子网掩码的概念,此时我们结合运营商的理解来谈谈IP地址数量限制的问题。通过上述对IP地址类型以及子网掩码等知识的理解,此时对于整个网络具体如何管理我们就能有一定的认识。所以接下来我们就站在全球的角度来看一看网络是如何被管理的或者说网络是如何被划分的。首先有两种说法,一种是假设型一种是真实型。其中对于假设型而言,此时我们针对国家就可以将32位的IP地址的前8个比特位作为每个国家的网络号划分出一个可以拥有126个网络号的网段,如美国的网络号为0000 0001/8、中国为0000 0010/8、德国为0000 0011/8、俄罗斯为0000 0100/8,当然此时与这些网络号对应肯定存在网络设备,通过对这些网络设备进行设置此时我们就构建出了一个真正意义上的公网,而其中对于路由器而言它就被称为国际路由器,也就是实现国内数据包转发到国际网络或者说公网的出口路由器。也就是此时该路由器就将国外和国内划分成了两个不同的网段。然后每个国家再对其特定的网络号进行子网构建,如此时我们在中国境内对00000010网络号依据CIDR原则对其进一步进行网段划分,如因为中国一共有34个省级行政区所以此时我们以省级行政区为单位进行网段划分,那么我们就需要6个比特位才能实现每一个省级行政区都能分配到对应的网络号,如福建省的网络号为0000 0010 0000 01/14、浙江省为0000 0010 0000 10/14、广东省为0000 0010 0000 11/14、江苏省为0000 0010 0001 00/14,当然同理再对每一个网络号进行网络设备的搭建,此时我们就在中国境内构建出了一个拥有34台网络设备或者省级路由器的网段。然后同理每个省级行政区再对辖区内的地级市进行网段划分,如福建省内一共有9个地级市我们就可以用4个比特位划分,则三明市网络号为0000 0010 0000 0100 01/18,福州市为0000 0010 0000 0100 10/18,厦门市为0000 0010 0000 0100 11/18,泉州市为0000 0010 0000 0101 00/18,最后形成了一个拥有9台市级路由器的网段。具体如下图所示:
子网划分假设示意图
如上图所示,站在全球的视角下我们就完成了网段划分或者说网络管理,这样我们无论是想要进行内网数据转发还是公网数据转发就都能通过上述网段划分的形式快速定位到对应网络号,从而将数据发送到世界各地。当然上述网段划分的形式是便于我们理解假设出来的,而真正的全球网段划分需要考虑各种因素如人口、地理位置、需求等。因此对于全球性的网段划分一定是按照地区来划分的,可能是美洲地区、亚洲地区、欧洲地区、非洲地区等,而对于这些不同的地区我们可以将其称为区域互联网注册机构。当然对于这些机构而言可以说它们的能力来源于国际互联网地址分配机构,准确点来说也就是国际互联网地址分配机构提供了IP资源和管理IP资源的权限给区域互联网注册结构。若是结合上述我们假设过程中对网段划分的理解,就可以将一个一个的区域互联网注册机构理解成是一个一个的国家它们拥有特定网段的网络号,也就是公网网络号。所以这样当各个国家想要与其它国家进行通信,那么它就要向区域互联网注册机构申请对应的IP地址,此时这个IP地址结合上述假设过程理解,就是公网网段中某个网络号的子网IP地址。所以假如此时是中国向区域互联网注册机构申请了IP地址,而因为每个国家都有自己的网络管理者,所以对于中国而言中国互联网信息中心就诞生了。因此也就可以理解成是中国互联网信息中心与区域互联网注册结构合作,从而获取到对应的IP资源,然后在中国境内对IP资源进行再次网段划分,当然此时中国互联网信息中心肯定不是按照我们上述假设以省级行政区来划分,一定也是根据各种因素分为不同的地区进行划分。如什么华北地区、长江地区、西部地区、南部地区等。谈到这里此时我们就会发现一个问题,如果你没有发现那么我再通过一个假设来进行着重强调一下。通过我们对地址类型以及CIDR的理解,我们知道A类地址类型对于网络号的限制是最小的,换个说法也就是它能灵活使用的比特位是最多的。所以我们假设中国分配到的IP资源是一个A类地址类型也就是8位网络号的IP地址,所以此时该IP地址允许存在2的24次方减2台主机,先不说这个数量的IP地址中国的14亿网民够不够分,在够分的情况下也不允许你这种使用网络号。同理网段划分的原因要管理要快速查找,当然如果真的按照这种方式配置主机号,那么中国网民访问公文的速度一定是很快,但是若是访问内网那么肯定是极其不合理的。因此在中国内部就需要不断进行网段划分,假设以4个比特位作为不同地区的网络号,那么依然会导致一个网络号内存在2的20次方减2台主机,仍然不合理。所以我们会发现当我们使用子网掩码对网络号进行不断细分,最后能够使用的主机号一定会越来越少。当然这个现象是合理的是我们期望的,因为主机号少了就表示子网越来越多,这样不同主机通信时路由器跨子网转发的效率就越高。本质也就是子网越多,子网内部的主机因为不断被划分就越来越少,一台主机查找另一台主机的效率就越来越高。所以最后我们发现如果想要网络管理又想要IP地址够用这是不可能的。好比中国14亿网民,我们把A类网络24位主机号中的23位主机号全部拿来子网划分也就是全部拿来到网络号,只留1位作为主机号,也只有2的23次方减2个网络号也就是八百多万,发现远远不够。当然正常这个问题也就是24个比特位在划分子网的情况下能容纳最多的主机数量。应该是16个比特位为网络号8个比特位为主机号时会最大,也就是最大能容纳一千六百多万,当然反正就是不够,不管你怎么分都是不够。因此运营商的概念就有了,私有IP地址和公网IP地址的概念也就有了。

总结

Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

上面分享的字节跳动公司2020年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

就先写到这,码字不易,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包阅读下载,最后觉得有帮助、有需要的朋友可以点个赞

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

csdn.net/topics/618156601)**

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值