临时抱佛脚!面试恶补计算机网络知识?这篇TCP知识总结请笑纳!(1)

如何成为Android高级架构师!

架构师必须具备抽象思维和分析的能力,这是你进行系统分析和系统分解的基本素质。只有具备这样的能力,架构师才能看清系统的整体,掌控全局,这也是架构师大局观的形成基础。 你如何具备这种能力呢?一是来自于经验,二是来自于学习。

架构师不仅要具备在问题领域上的经验,也需要具备在软件工程领域内的经验。也就是说,架构师必须能够准确得理解需求,然后用软件工程的思想,把需求转化和分解成可用计算机语言实现的程度。经验的积累是需要一个时间过程的,这个过程谁也帮不了你,是需要你去经历的。

但是,如果你有意识地去培养,不断吸取前人的经验的话,还是可以缩短这个周期的。这也是我整理架构师进阶此系列的始动力之一。


成为Android架构师必备知识技能

对应导图的学习笔记(由阿里P8大牛手写,我负责整理成PDF笔记)

部分内容展示

《设计思想解读开源框架》

  • 目录
  • 热修复设计
  • 插件化框架设计

    《360°全方面性能优化》
  • 设计思想与代码质量优化
  • 程序性能优化

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

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

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

底层实现:socket

前面讲到,最简单的运输层协议,就是提供进程之间的独立通信 ,但底层的实现,是socket之间的独立通信 。在网络层中,IP地址是一个主机逻辑地址,而在运输层中,socket是一个进程的逻辑地址;当然,一个进程可以拥有多个socket。应用进程可以通过监听socket,来获取这个socket接受到的消息。

举个例子来理解socket。如下图

每一个主机可以创建很多个socket来接收信息。如主机A的微信进程,想要发送给主机B的微信,那么他只需要发送给主机B的socketC,主机B的微信就会从socketC中取到消息。(当然实际的流程不是这样的,我们的消息需要经过微信后台服务器,这里只是举例子)

同理,主机B的QQ,想要发送消息给主机A的QQ,那么只需要把消息发送给socketB,主机A的QQ就可以拿到消息了。

socket并不是一个实实在在的东西,而是运输层抽象出来的一个对象。运输层增加了端口这个概念,来区分不同的socket。端口可以理解为一个主机上有很多的网络通信口,每个端口都有一个端口号,端口的数量由运输层协议确定。

不同的运输层协议对socket有不同的定义方式。在UDP协议中,使用目标IP+目标端口号来定义一个socket;在TCP中使用目标IP+目标端口号+源IP+源端口号来定义一个socket。我们只需要在运输层报文的头部附加上这些信息,目标主机就会知道我们要发送给哪个socket,对应监听该socket的进程就可获得信息。

运输层协议

运输层的协议就是大名鼎鼎的TCP和UDP。其中,UDP是最精简的运输层协议,只实现了进程间的通信;而TCP在UDP的基础上,实现了可靠传输、流量控制、拥塞控制、面向连接等等特性,同时也更加复杂。

当然除此之外,还有更多更优秀的运输层协议,但目前广为使用的,就是TCP和UDP。UDP在后面也会总结到。

TCP协议首部


TCP协议,表现在报文上,就是会在应用层传输下来的数据前附加上一个TCP首部,这个首部附加了TCP信息,先来整体看一下这个首部的结构:

这张图是来自我大学老师的课件, 非常好用,所以一直拿来学习。最下面部分表示了报文之间的关系,TCP数据部分就是应用层传下来的数据。

TCP首部固定长度是20字节,下面还有4字节是可选的。内容很多,但其中有一些我们比较熟悉的:源端口,目标端口。嗯?socket不是还需要IP进行定位吗?IP地址在网络层被附加了。其他的内容后面都会慢慢讲解,作为一篇总结文章,这里放出查阅表,方便复习:

| 头部参数 | 字节数 | 作用 |

| — | — | — |

| 源端口和目的端口字段 | 各占两字节 | socket是通过端口号和IP号来进行定义,这里表示发出消息的主机端口以及接收消息的目标主机端口 |

| 序号字段 | 4 字节 | TCP 连接中传送的数据流中的每一个字节都编上一个序号。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号。长度4字节,所以序号的范围是【0,2^32 - 1】 |

| 确认号字段 | 4字节 | 是期望收到对方的下一个报文段的数据的第一个字节的序号。 |

| 数据偏移(即首部长度) | 4位 | 指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。“数据偏移”的单位是 32 位(以 4 字节为计算单位) |

| 保留字段 | 6位 | 保留为今后使用,但目前应置为 0 |

| 紧急 URG | 1位 | 当 URG =1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据) |

| 确认 ACK | 1位 | 只有当 ACK=1 时确认号字段才有效。当 ACK = 0 时,确认号无效。当收到报文需要向发送方发送确认报时设置该标志位为1。 |

| 推送 PSH | 1位 | 接收 TCP 收到 PSH = 1 的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。 |

| 复位 RST | 1位 | 当 RST =1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。 |

| 同步 SYN | 1位 | 同步 SYN = 1 表示这是一个连接请求或连接接受报文。 |

| 终止 FIN | 1位 | 用来释放一个连接。FIN = 1 表明此报文段的发送端的数据已发送完毕,并要求释放运输连接 |

| 窗口字段 | 2字节 | 发送方接收缓存区剩下的字节 数,注意单位是字节。 |

| 检验和 | 2字节 | 检验和字段检验的范围包括首部和数据这两部分。在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部。主要是检验报文是否发生了错误,如某个‘1’变成了‘0’。 |

| 紧急指针字段 | 2字节 | 指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面) |

| 选项字段 | 长度不定 | TCP 最初只规定了一种选项,即最大报文段长度 MSS。MSS 告诉对方 TCP:“我的缓存所能接收的报文段的数据字段的最大长度是 MSS 个字节。” |

| 填充字段 | 不定 | 这是为了使整个首部长度是 4 字节的整数倍。 |

选项字段中包含以下其他选项:

| 选项 | 作用 |

| — | — |

| 窗口扩大选项 | 占 3 字节,其中有一个字节表示移位值 S。新的窗口值等于 TCP 首部中的窗口位数增大到 (16 + S),相当于把窗口值向左移动 S 位后获得实际的窗口大小 |

| 时间戳选项 | 占 10 字节,其中最主要的字段时间戳值字段(4 字节)和时间戳回送回答字段(4 字节),主要是用于计算数据报在网络中传输的往返时间。 |

| 选择确认选项 | 接收方收到了和前面的字节流不连续的两个字节块,需要告诉发送方目前已经接收到的数据报范围。每一个段需要两个边界,一个边界需要4字节来表示,选项字段最长是40字节,所以最多可以表示4个已接收的字段。 |

讲完下面内容,再回来看这些字段就熟悉了。

TCP面向字节流特性


TCP并不是把应用层传输过来的数据直接加上首部然后发送给目标,而是把数据看成一个字节 流,给他们标上序号之后分部分发送。这就是TCP的 面向字节流 特性:

  • TCP会以流的形式从应用层读取数据并存放在自己的发送缓存区中,同时为这些字节标上序号

  • TCP会从发送方缓冲区选择适量的字节组成TCP报文,通过网络层发送给目标

  • 目标会读取字节并存放在自己的接收方缓冲区中,并在合适的时候交付给应用层

面向字节流的好处是无需一次存储过大的数据占用太多内存,坏处是无法知道这些字节代表的意义,例如应用层发送一个音频文件和一个文本文件,对于TCP来说就是一串字节流,没有意义可言,这会导致粘包以及拆包问题,后面讲。

可靠传输原理


前面讲到,TCP是可靠传输协议,也就是,一个数据交给他,他肯定可以完整无误地发送到目标地址,除非网络炸了。他实现的网络模型如下:

对于应用层来说,他就是一个可靠传输的底层支持服务;而运输层底层采用了网络层的不可靠传输。虽然在网络层甚至数据链路层就可以使用协议来保证数据传输的可靠性,但这样网络的设计会更加复杂、效率会随之降低。把数据传输的可靠性保证放在运输层,会更加合适。

可靠传输原理的重点总结一下有:滑动窗口、超时重传、累积确认、选择确认、连续ARQ

停止等待协议

要实现可靠传输,最简便的方法就是:我发送一个数据包给你,然后你跟我回复收到,我继续发送下一个数据包。传输模型如下:

这种“一来一去”的方法来保证传输可靠就是停止等待协议(stop-and-wait)。不知道还记不记得前面TCP首部有一个ack字段,当他设置为1的时候,表示这个报文是一个确认收到报文。

然后再来考虑一种情况:丢包。网络环境不可靠,导致每一次发送的数据包可能会丢失,如果机器A发送了数据包丢失了,那么机器B永远接收不到数据,机器A永远在等待。解决这个问题的方法是:超时重传 。当机器A发出一个数据包时便开始计时,时间到还没收到确认回复,就可以认为是发生了丢包,便再次发送,也就是重传。

但重传会导致另一种问题:如果原先的数据包并没有丢失,只是在网络中待的时间比较久,这个时候机器B会受到两个数据包,那么机器B是如何辨别这两个数据包是属于同一份数据还是不同的数据?这就需要前面讲过的方法:给数据字节进行编号。这样接收方就可以根据数据的字节编号,得出这些数据是接下来的数据,还是重传的数据。

在TCP首部有两个字段:序号和确认号,他们表示发送方数据第一个字节的编号,和接收方期待的下一份数据的第一个字节的编号。前面讲到TCP是面向字节流,但是他并不是一个字节一个字节地发送,而是一次截取一整段。截取的长度受多种因素影响,如缓存区的数据大小、数据链路层限制的帧大小等。

连续ARQ协议

停止等待协议已经可以满足可靠传输了,但有一个致命缺点:效率太低。发送方发送一个数据包之后便进入等待,这个期间并没有干任何事,浪费了资源。解决的方法是:连续发送数据包。模型如下:

和停止等待最大的不同就是,他会源源不断地发送,接收方源源不断收到数据之后,逐一进行确认回复。这样便极大地提高了效率。但同样,带来了一些额外的问题:

发送是否可以无限发送直到把缓冲区所有数据发送完?不可以。因为需要考虑接收方缓冲区以及读取数据的能力。如果发送太快导致接收方无法接受,那么只是会频繁进行重传,浪费了网络资源。所以发送方发送数据的范围,需要考虑到接收方缓冲区的情况。这就是TCP的流量控制 。解决方法是:滑动窗口 。基本模型如下:

  • 发送方需要根据接收方的缓冲区大小,设置自己的可发送窗口大小,处于窗口内的数据表示可发送,之外的数据不可发送。

  • 当窗口内的数据接收到确认回复时,整个窗口会往前移动,直到发送完成所有的数据

在TCP的首部有一个窗口大小字段,他表示接收方的剩余缓冲区大小,让发送方可以调整自己的发送窗口大小。通过滑动窗口,就可以实现TCP的流量控制,不至于发送太快,导致太多的数据丢失。


连续ARQ带来的第二个问题是:网络中充斥着和发送数据包一样数据量的确认回复报文,因为每一个发送数据包,必须得有一个确认回复。提高网络效率的方法是:累积确认 。接收方不需要逐个进行回复,而是累积到一定量的数据包之后,告诉发送方,在此数据包之前的数据全都收到。例如,收到 1234,接收方只需要告诉发送方我收到4了,那么发送方就知道1234都收到了。

第三个问题是:如何处理丢包情况。在停止等待协议中很简单,直接一个超时重传就解决了。但,连续ARQ中不太一样。例如:接收方收到了 123 567,六个字节,编号为4的字节丢失了。按照累积确认的思路,只能发送3的确认回复,567都必须丢掉,因为发送方会进行重传。这就是GBN(go-back-n) 思路。

但是我们会发现,只需要重传4即可,这样不是很浪费资源,所以就有了:选择确认SACK 。在TCP报文的选项字段,可以设置已经收到的报文段,每一个报文段需要两个边界来进行确定。这样发送方,就可以根据这个选项字段只重传丢失的数据了。

可靠传输小结

到这里关于TCP的可靠传输原理就已经介绍的差不多。最后进行一个小结:

  • 通过连续ARQ协议与发送-确认回复模式来保证每一个数据包都到达接收方

  • 通过给字节编号的方法,来标记每一个数据是属于重传还是新的数据

  • 通过超时重传的方式,来解决数据包在网络中丢失的问题

  • 通过滑动窗口来实现流量控制

  • 通过累积确认+选择确认的方法来提高确认回复与重传的效率

当然,这只是可靠传输的冰山一角,感兴趣可以再深入去研究(和面试官聊天已经差不多了[狗头])。

拥塞控制


拥塞控制考虑的是另外一个问题:避免网络过分拥挤导致丢包严重,网络效率降低

拿现实的交通举例子:

高速公路同一时间可通行的汽车数量是一定的,当节假日时,就会发生严重的堵车。在TCP中,数据包超时,会进行重传,也就是会进来更多的汽车,这时候更堵,最后导致的结果就是:丢包-重传-丢包-重传。最后整个网络瘫痪了。

这里的拥塞控制和前面的流量控制不是一个东西,流量控制是拥塞控制的手段:为了避免拥塞,必须对流量进行控制。拥塞控制目的是:限制每个主机的发送的数据量,避免网络拥塞效率下降。就像广州等地,限制车牌号出行是一个道理。不然大家都堵在路上,谁都别想走。

拥塞控制的解决方法是流量控制,流量控制的实现是滑动窗口,所以拥塞控制最终也是通过限制发送方的滑动窗口大小来限制流量 。当然,拥塞控制的手段不只是流量控制,导致拥塞的因素有:路由器缓存、带宽、处理器处理速度等等。提升硬件能力(把4车道改成8车道)是其中一个方法,但毕竟硬件提升是有瓶颈的,没办法不断提升,还是需要从tcp本身来增加算法,解决拥塞。

拥塞控制的重点有4个:慢开始、快恢复、快重传、拥塞避免。这里依旧献祭出大学老师的ppt图片:

Y轴表示的是发送方窗口大小,X轴表示的是发送的轮次(不是字节编号)。

  • 最开始的时候,会把窗口设置一个较小的值,然后每轮变为原来的两倍。这是慢开始。

  • 当窗口值到达ssthresh值,这个值是需要通过实时网络情况设置的一个窗口限制值,开始进入拥塞避免,每轮把窗口值提升1,慢慢试探网络的底线。

  • 如果发生了数据超时,表示极可能发生了拥塞,然后回到慢开始,重复上面的步骤。

  • 如果收到三个相同的确认回复,表示现在网络的情况不太好,把ssthresh的值设置为原来的一半,继续拥塞避免。这部分称为快恢复。

  • 如果收到丢包信息,应该尽快把丢失的包重传一次,这是快重传。

  • 当然,窗口的最终上限是不能无限上涨的,他不能超过接收方的缓存区大小。

通过这个算法,就可以在很大程度上,避免网络拥挤。

除此之外,还可以让路由器在缓存即将满的时候,告知发送方我快满了,而不是等到出现了超时再进行处理,这是主动队列管理AQM。此外还有很多方法,但是上面的算法是重点。

面向连接


这一小节讲的就是无人不晓的TCP三次握手与四次挥手这些,经过前面的内容,这一小节其实已经很好理解。

TCP是面向连接的,那连接是什么?这里的连接并不是实实在在的连接,而是通信双方彼此之间的一个记录 。TCP是一个全双工通信,也就是可以互相发送数据,所以双方都需要记录对方的信息。根据前面的可靠传输原理,TCP通信双方需要为对方准备一个接收缓冲区可以接收对方的数据、记住对方的socket知道怎么发送数据、记住对方的缓冲区来调整自己的窗口大小等等,这些记录,就是一个连接。

在运输层小节中讲到,运输层双方通信的地址是采用socket来定义的,TCP也不例外。TCP的每一个连接只能有两个对象,也就是两个socket,而不能有三个。所以socket的定义需要源IP、源端口号、目标IP、目标端口号四个关键因素,才不会发生混乱。

假如TCP和UDP一样只采用目标IP+目标端口号来定义socket,那么就会出现多个发送方同时发送到同一个目标socket的情况。这个时候TCP无法区分这些数据是否来自不同的发送方,就会导致出现错误。

既然是连接,就有两个关键要点:建立连接、断开连接。

建立连接

建立连接的目的就是交换彼此的信息,然后记住对方的信息。所以双方都需要发送彼此的信息给对方:

但前面的可靠传输原理告诉我们,数据在网络中传输是不可靠的,需要对方给予我们一个确认回复,才可以保证消息正确到达。如下图:

机器B的确认收到和机器B信息可以进行合并,减少次数;而且发送机器B给机器A本身就代表了机器B已经收到了消息,所以最后的示例图是:

步骤如下:

  1. 机器A发送syn包向机器B请求建立TCP连接,并附加上自身的接收缓冲区信息等,机器A进入SYN_SEND状态,表示请求已经发送正在等待回复;

  2. 机器B收到请求之后,根据机器A的信息记录下来,并创建自身的接收缓存区,向机器A发送syn+ack的合成包,同时自身进入SYN_RECV状态,表示已经准备好了,等待机器A 的回复就可以向A发送数据;

  3. 机器A收到回复之后记录机器B 的信息,发送ack信息,自身进入ESTABLISHED状态,表示已经完全准备好了,可以进行发送和接收;

  4. 机器B收到ACK数据之后,进入ESTABLISHED状态。

三次消息的发送,称为三次握手。

断开连接

断开连接和三次握手类似,直接上图:

  1. 机器A发送完数据之后,向机器B请求断开连接,自身进入FIN_WAIT_1状态,表示数据发送完成且已经发送FIN包(FIN标志位为1);

  2. 机器B收到FIN包之后,回复ack包表示已经收到,但此时机器B可能还有数据没发送完成,自身进入CLOSE_WAIT状态,表示对方已发送完成且请求关闭连接,自身发送完成之后可以关闭连接;

  3. 机器B数据发送完成之后,发送FIN包给机器B ,自身进入LAST_ACK状态,表示等待一个ACK包即可关闭连接;

  4. 机器A收到FIN包之后,知道机器B也发送完成了,回复一个ACK包,并进入TIME_WAIT状态

TIME_WAIT状态比较特殊。当机器A收到机器B的FIN包时,理想状态下,确实是可以直接关闭连接了;但是:

  1. 我们知道网络是不稳定的,可能机器B 发送了一些数据还没到达(比FIN包慢);
  1. 同时回复的ACK包可能丢失了,机器B会重传FIN包;

如果此时机器A马上关闭连接,会导致数据不完整、机器B无法释放连接等问题。所以此时机器A需要等待2个报文生存最大时长,确保网络中没有任何遗留报文了,再关闭连接

  1. 最后,机器A等待两个报文存活最大时长之后,机器B 接收到ACK报文之后,均关闭连接,进入CLASED状态

双方之间4次互相发送报文来断开连接的过程,就是四次挥手

现在,对于为什么握手是三次挥手是四次、一定要三次/四次吗、为什么要停留2msl再关闭连接等等这些问题,就都解决了。

UDP协议


运输层协议除了TCP,还有大名鼎鼎的UDP。如果说TCP凭借他完善稳定的功能独树一帜,那UDP就是精简主义乱拳打死老师傅。

UDP只实现了运输层最少的功能:进程间通信。对于应用层传下来的数据,UDP只是附加一个首部就直接交给网络层了。UDP的头部非常简单,只有三部分:

  • 源端口、目标端口:端口号用来区分主机的不同进程

  • 校验码:用于校验数据包在传输的过程中没有出现错误,例如某个1变成了0

  • 长度:报文的长度

所以UDP的功能也只有两个:校验数据报是否发生错误、区分不同的进程通信。

但,TCP的功能虽然多,但同时也是要付出相对应的代价。例如面向连接的特性,在建立和断开连接的时候会有开销;拥塞控制的特性,会限制传输的上限等等。下面来罗列一下UDP的优缺点:

UDP的缺点
  • 无法保证消息完整、正确到达,UDP是一个不可靠的传输协议;

  • 缺少拥塞控制容易互相竞争资源导致网络系统瘫痪

UDP的优点
  • 效率更快;不需要建立连接以及拥塞控制

  • 连接更多的客户;没有连接状态,不需要为每个客户创建缓存等

  • 分组首部字节少,开销小;TCP首部固定首部是20字节,而UDP只有8字节;更小的首部意味着更大比例的数据部分

  • 在一些需要高效率允许可限度误差的场景下可以使用。如直播场景,并不需要保证每个数据包都完整到达,允许一定的丢包率,这个时候TCP的可靠特性反而成为了累赘;精简的UDP更高的效率是更加适合的选择

  • 可以进行广播;UDP并不是面向连接的,所以可以同时对多个进程进行发送报文

UDP适用场景

UDP适用于对传输模型需要应用层高度自定义、允许出现丢包、需要高效率的场景、需要广播;例如

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。

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

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

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

高度自定义、允许出现丢包、需要高效率的场景、需要广播;例如

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。

[外链图片转存中…(img-x7iYJiSY-1715310775461)]

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值