注册

计算机网络 传输层

计算机网络 传输层


传输层服务


传输层在网络层的基础上,实现了进程到进程的服务。


传输层通过引入端口号来区分进程,在 UDP/TCP 的首部中,一个端口占用 2B, 即 16 bit。



复用与分用


一台主机上的多个进程,利用一个传输层实体,将数据发送出去,这就叫复用。而远端主机的传输层实体,收到了很多数据,然后通过识别端口号,将数据交给具体的进程,这就叫分用(或解复用)。


UDP(无连接传输协议 / 用户数据报协议)


面向报文协议,即一个 UDP 数据报就是应用层交下来一个完整报文。


是一种尽力而为的,非有序的、无连接的一种协议,通常用于流媒体应用、事务性应用,如 DNS 等。


UDP 的报文结构


ec5ae891c76e4888bc6a53481d084782~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp

UDP 会使用校验和对数据做差错控制编码。


将数据按每16位一组相加,最高位相加产生的进位,回卷到最低位去,如此,将最后的和取反码就是最终的校验和,在接收端,按同样的方法求校验和,与接收端的校验和相加,如果结果不是 16 个 1,则肯定出错,如果是 16 个 1 ,说明大概率没有出错


可靠数据传输的基本原理


可靠的传输通常需要实现数据的不丢失、不乱序、无差错等核心功能。


我所看过的传输层相关的书,都是通过递进式的讲解实现可靠的网络传输,这里也已这种方式来记录。但我这里 rdt1.0、rdt2.0、rdt3.0跟书本上的可能不一致,纯粹是按自己理解的来,我觉得这里能够理解其逐渐完善可靠性的过程就OK了。


rdt1.0 停等协议


先假设传输层以下的实现都是可靠的,不会出现数据差错的情况,**那如何实现不乱序呢?**很简单,我们只要保证按顺序传就可以,发送端先发一个,等接收端收到了再发下一个,如此往复直到数据全部发送完。这样的协议叫做停等协议。


f19d8b44096e40fc836de827f1391ba5~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp

rdt2.1 ACK 和 NAK


这里看上去确实实现了按序到达,但这是建立在底层不会出现差错的情况下,一旦出现了一些异常情况,比如**分组在传输过程中出现了比特反转(0被识别为1,1识别为0)**该如何处理?


那需要接收端给发送端一个答复,接收端是否收到了合法的分组。如果收到了正确的分组,接收端回复一个确定(ACK),发送端识别到时ACK,就可以继续发送下一个分组,若收到了错误的分组,则回复一个否定(NAK),那接收端就需要重发当前分组。


99cef05641e849139e18c7c59809a938~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp

如图这里主要有两种异常情况:




  1. 分组在传输过程中出错:


    接收端检测到错误之后,回复 NAK , 发送端收到 NAK 后,需要重传上一个分组。




  2. ACK 或 NAK 在传输过程中出错:


    发送端不能识别这到底是 ACK 还是 NAK,于是就统一按照这是 NAK 来处理,于是就重发上一个分组。而这里重发后,会导致接收端收到重复的分组,接收端需要丢弃这个分组,然后回复 ACK。




当然,如果过程中没有出错,那就一步一步的正常的将数据发送给接收端就OK了。


rdt2.2 序号


在rdt2.2中,我们开始使用序号,给分组进行编号。并且使用上一个分组的编号来替代 NAK ,什么意思呢?如果用户在收到一个分组 P3 时,发现数据出错了,那这时它就不再回复 NAK 了,而是回复 ACK = P2,表示接收端才收到 P2,于是发送端需要重传 P3,这样实现了跟 NAK 一样的效果。这么做为后面的流水协议奠定了基础。



可以看到,这里用分组的序号替代单纯的 ACK/NAK,异常情况跟 rdt 2.1 非常相似。


rdt3.0 超时重传机制


如果分组在传输过程中丢失了,接收端等待下一个分组,发送端等待 ACK,双方如果都这么默默的等待下去,那不就出现死锁了?


我们可以让发送端发送一个分组后,设置一个超时定时器,如果在一段时间后还没有收到 ACK, 触发超时重传机制,重发上一个分组,这里不论是分组丢失还是 ACK 丢失,都可以重发。如果是 ACK 丢失,接收端会收到重复的分组,跟 rdt2.0 的方案类似,将重复的分组丢弃就可以了,并且对当前的分组发送 ACK 。


ae0872ecff7943109d16ccd80f0bd165~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp

由于定时器的设置,不可能百分百的确定分组或 ACK 丢失,可能在超时重传后好一段时间,对前面某个分组的 ACK 慢悠悠的过来了,这是发送端可以什么都不做,无视它就可以了,因为前面已经收到过对这个分组的确认。


超时计时器的时间需要根据RTT的时间来动态设置,如果超时时间设置得过短,会引起不必要的重传,如果设置得过长,会使重传效率变低。


到目前为止,停等协议已经实现了可靠传输,但是它的信道利用率很低


滑动窗口协议(slide window)


停等协议的信道利用率很低,是因为它一次只发一个分组,确认发送完成后才能发另一个,这中间会有很长的等待时间,如何提高信道利用率呢?显然,我们在发完一个分组后,不等 ACK 就在此下一个,看上去就能提高效率。因此,这种连续发送多个未经确认的分组的协议叫做流水线协议


在发送端会有一段缓冲区,可以存储已经发送出去但还没有收到 ack 的分组,用于后面检错重发、超时重发等。而在接收端,也有一段缓存区,主要是为了适应发送端发送速率和接收端接收速率不一致的情况,当发送端速度过快时,可以用缓冲区暂存一下。


发送端缓冲区的发送窗口长度叫做 sw , 接收端缓冲区接收窗口长度叫做 rw 。窗口是缓冲区的一个子集。



  • 当 sw = 1,rw = 1 时,这就是停等协议
  • 当 sw > 1,rw = 1 时,这就是 GBN 协议
  • 当 sw > 1,rw > 1时,这就是 SR (selective repeat)协议

3e6e827a733147ed8dd14155541aa11d~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp

通常发送窗口的后沿指针指向低位的首个已发送但没有确认的分组,前沿指针指向高位第一个已发送的分组,如果发送的分组数量比最大发送窗口长度小,那表明发送端可以继续发送分组,也就是前沿指针可以向前移动。如果低位的未确认的分组收到了确认,则后沿指针可以向前移动,移动到首个未确认的分组。这时,发送窗口被使用的长度就变小了,发送端就可以继续发送分组。


而接收端收到的分组在接收窗口范围内时,可以接收并对其作出确认,若没有比它序号更低的分组等待被确认,则接收窗口的后沿指针可以向前移动,移动到首个未确认的分组。由于窗口指针的移动,接收端又有了更多的空间来接收后续的分组。如果接收的分组超出接收窗口范围,则将其丢弃,因为接收窗口暂时没有足够空间去保存更多的分组。


GBN

GBN 协议特点:rw = 1, 顺序接收,累积确认。


双方正常的流程:发送方可以按序发送多个分组,这些分组有序的到达接收端,接收端对其一一作出确认,接收端每接收一个分组,窗口就向前移动一位,然后接收到下一个分组,再对下一个分组进行确认,然后再往前移动。


双方的异常流程:



  1. 如果接收端收到乱序的分组,比如在等待 P1 的过程中,结果收到一个 P2,那接收端会将其丢弃,并发送其上一个分组,也就是 P0 的确认。发送端收到 P0 的 ACK 后,就会将 P0 之后的所有的分组重发一遍,这也就是 go back N 的意义。总结起来就是,如果低位的分组出错(失序或超时)了,需要从低位开始重传后续所有的分组


SR

SR 协议特点:rw > 1, 可以乱序接收非累积确认,或者叫单独确认,收到哪个分组,就给哪个确认。窗口向前滑动时,滑到从低位开始第一个未确认的分组


双方的正常流程:发送方依次发送多个分组,每个分组要设置一个独立的超时计时器,由于 SR 的接收窗口长度大于 1,因此只要是在窗口范围内的分组,它都可以接收,并单独对其作出确认。发送方收到确认后,取消超时计时器,如果没有比当前更小的需要被确认的分组,那后沿指针就向前移动,发送端继续发送后续的分组。


双方的异常情况:




  1. 发送方的某个分组丢失了,那接收方就无法对其作出确认,接收窗口的后沿指针无法向前移动,一段时间后,发送端的这个分组的超时计时器会触发重传,然后重新设置超时计时器。接收方收到这个分组后,对其作出确认,然后接收窗口的前沿指针得以向前移动,从而可以接收更多的分组。而发送方收到确认后,发送窗口的后沿指针也会向前移动,发送端就可以发送后续的分组。




  2. 接收端的某个分组的ACK 丢失了,这里发送端对应的分组超时计时器会进行重发,重发后接收端收到了重复的分组,而接收端发现这个分组已经被确认过,直接将其忽略即可。如果接收端的某个分组的 ACK 是因为网络拥塞导致超时之后才到达发送端,那发送端发现自己已经重发了这个分组,正在等待确认中,那发送端直接忽略这个ACK就可以了,同理,如果某个分组已经通过超时重传后被确认了,而首次发送的 ACK 才缓缓到达,发送端同样可以忽略这次的确认,因为这个分组已经被确认过了。




GBN & SR 的差别

GBN 使用起来简单,适合在低差错率的网络中使用,使用累积确认(比如,对某个分组进行确认,则表明这个分组及以前所有的分组,都被正确接收了,而之后的分组没有被收到),一旦出错,需要重发之后所有已经发送过的分组,会有比较大的代价。


SR 实现起来复杂些,因为每个分组都有单独的超时计时器,只需要对出现差错的分组单独重传,适合在差错率较高的网络中使用。使用非累积确认。


TCP(面向连接传输协议)


在可靠传输原理中,我们为了方便,是给分组设置了编号,但实际在 TCP 中是对字节流进行编号。


由于我们到了具体的 TCP 协议,通常需要将分组描述为报文段


TCP 采用了前面可靠传输原理中的流水线协议、累积确认、单超时计时器等特点,是 SR 和 GBN 的混合,同时还支持流量控制,拥塞控制。


段结构


6b90657f3be44a57b08a3a5eee6a7d39~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp

TCP 在载荷部分前,会加上自己的控制信息,也就是 TCP 的首部。如果不包含选项部分,首部的长度为 20个字节。


介绍几个主要的字段。


序号:由发送端设置,表示发送端发送报文段的首字节编号。


确认号:由接收端设置,当 ACK = 1时,确认号才有效,表示接收端期望收到字节编号。同时,在累积确认中表示这个编号之前的分组已经收到。


SYN:表示要建立连接。


FIN:表示要拆除连接。


可靠数据传输


TCP 的可靠传输原理大致可以理解为是 SR + 累积确认 + 快速重传。TCP 的发送端不会为每个发出去的报文段设置超时计时器,而是在发送窗口滑动到首个未确认的报文段时启动一个超时计时器。


TCP 对于乱序到达的分组,没有规定该存储还是丢弃,可以自由实现。


快速重传


低位的分组,在超时计时器触发超时之前,如果收到了连续 3 次重复的对当前报文段的确认,可以认为当前报文段已经出现了差错,发送端就可以提前重发这个报文段,而不用等到超时计时器生效。


TCP 的实际实现中,在收到某个分组后,会短暂的等待一会,等收到下一个分组后,再发送确认,这样就可以只发送 1 次确认,这也是利用了累积确认的优点。当然,它不能等待太久,并且等待的分组只能有 1 个,如果下一个分组到来,立刻发送确认。


流量控制


流量控制时为了解决发送端的发送速率和接收端的读取数据不一致的问题,通常是发送端发送的太快,超出了接收端的缓冲区,超出的这些报文段就会被丢弃,那发送端发这么多也就没有了意义。


接收方在发送确认时,可以将自己可用的缓冲区大小放在 TCP 首部的接收窗口字段中,发送端收到这个 ACK 后,就知道是否还能够继续发送数据,可以根据接收端的窗口,调节自己的窗口大小。


连接管理


TCP 是面向连接的,在正式传送数据前,需要做一些准备工作,如:协商起始序号,设置发送窗口、接收窗口等。


87dd3153445247ed8a45d0732f4259d6~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp

连接建立被称为“三次握手”,TCP 的连接建立过程如下:


连接建立前,客户端处于连接关闭状态,服务端处于监听状态。



  1. 客户端发送连接建立请求,SYN=1,同时设置一个随机的初始字节序号x,即 seq=x。
  2. 服务端收到了连接建立请求,设置 ACK=1,表示同意建立请求,同时设置请求号 ack=x+1,TCP 规定 SYN 报文段不能携带数据,但需要消耗 1 个序号,因此这里的 ack=x+1。这部分操作表示服务端同意建立请求。同时,第二次握手还有服务端作为发送方希望与客户端建立连接的请求,所以,这里有跟第一次握手相似的数据,SYN=1, seq=y,y 是服务端随机生成的序号。
  3. 客户端收到了服务端发来的 ack,那客户端作为发送方与服务端的连接就已经建立,这时客户端就已经可以向服务端发送数据了。同时也收到了服务端发来的连接建立请求,因而这次客户端需要回复 ACK=1,ack=y+1, 表示同意建立请求,然后这次不论是否传送数据 seq都是 x+1。

经过上面三次握手,客户端与服务端的双向的连接就建立起来了。


当双方通信完毕后,TCP 需要拆除连接。连接拆除的过程通常叫做“四次挥手”,过程如下:


8de3c55170e64f75b5d9e40fbe8b0fc8~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp

  1. 客户端发送 FIN=1,表示希望拆除连接。
  2. 服务端收到后发送 ACK=1。(客户端收到ACK后,客户端到服务端的连接就拆除了,但服务端到客户端的连接仍然可用,服务端仍然可以向客户端发送数据)
  3. 服务端发送 FIN=1,表示希望拆除连接。
  4. 客户端收到后发送 ACK=1。服务端收到后确认后,连接即拆除。但站在客户端的角度,作为最后一个发送消息的角色,它还不知道自己发送的确认有没有准确的到达服务器,所以在发送完 ACK 后,会等待一段时间,如果服务端没有重传FIN,那客户端作为接收方到服务端的接收连接也就断开了,那到目前为止,所有的连接就都拆除了。

上面第四步客户端会有一个等待时间来防止 ack 出现差错,但这个方案也不是完美的,如果服务端发现这个ack 有差错,重发了一个连接拆除的请求,但这个请求慢悠悠过了好一会才到达客户端(时间大于客户端的等待时间),可是客户端已经关闭了连接,那服务端短时间内就没法收到拆除连接的 ack 了。


因此,作为拆除连接的最后一环,第四次挥手无论怎样都不会完美,只能尽可能的降低出现问题的概率。


为什么要设置 SequenceNum?


防止滞留在网络中的上一次连接中的一些段,被识别位本次连接中的段。SequenceNum 的长度是 32 位,可以编址 4G 的字节,这么大的范围在两次连接中很难出现相近的序号段,一旦序号段差别很大,这个“野“报文段也就很难出现接收窗口中,就会被接收方丢弃。


为什么2次握手不行?



  1. TCP 是全双工的连接,建立连接时,既要有客户端到服务端的连接,也要有服务端到客户端的连接。建立一个方向的连接需要 2 次握手,两个方向就是 4 次,但是在服务端向客户端确认连接时,可以捎带上服务端对客户端的连接建立请求,这样就合并了一次握手,所以有三次。
  2. 如果只有 2 次握手,且服务端的确认出现差错的情况下,客户端就收不到建立连接的确认,这个方向的连接就会失败。而服务端不知道自己的连接确认丢失了,导致服务端到客户端的连接建立了,这样就出现了“半连接”的情况。如果服务端有大量的这种“半连接”,会极大的消耗服务端的性能。如果上一个连接滞留在网络中的报文段到达了接收方,还有可能阴差阳错的被服务端接收。


TCP的拥塞控制


拥塞时指过多的分组被注入到网络中,超出了网络的处理能力,导致大量的分组 ”拥挤“ 在网络中间设备队列中等待转发,网络性能显著下降的现象。


拥塞的直接后果:



  1. 数据分组通过网络的延时显著增加;
  2. 由于中间网络设备的队列满导致大量的分组被丢弃。


拥塞控制就是通过合理调度、规范、调整向网络中发送数据的主机数量、发送速率或数据量,以避免拥塞或尽快消除已发生的拥塞。拥塞控制可以在不同层实现,比较典型的是在网络层和传输层进行拥塞控制。


拥塞控制的基本思想是 AIMD。拥塞窗口的调整主要分为慢启动阶段和拥塞避免阶段。在慢启动阶段,每收到 1 个确认段,拥塞窗口增加 1 个 MSS,每经过 1 个 RTT,拥塞窗口增长 1 倍;在拥塞避免阶段,每经过 1 个 RTT,拥塞窗口才增长 1 个 MSS。



如何检测网络中是否发生了拥塞?



  1. 分组发生了超时
  2. 连续收到了多个对同一分组的重复确认。

作者:Jax__
链接:https://juejin.cn/post/7144759075631267853
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册