zoukankan      html  css  js  c++  java
  • 计网第三章——运输层

    运输层

    概述

    多路分解

    将运输层报文段中的数据交付到正确的套接字。

    比如说:快递员从邮局拿到快递,分发给不同的客户。

    多路复用

    在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息从而生成报文段,然后将报文段传递到网络层。

    比如说:快递员从各个家里取到快递送到邮局的发送。

    套接字

    每个主机上有多个进程,每个进程有一个或多个套接字——从网络向进程传递数据 和 从进程向网络传递数据 的门户。每个套接字都有唯一的标识符,每个报文段里都有特殊的字段表示要交付到的套接字。

    在主机上,每个套接字能够分配一个端口号,当报文段到达主机的时候 检查报文段中的目的端口号并将其定向到响应的套接字。

    UDP套接字

    UDP套接字为二元组——(源端口号,目的端口号),则源和目的端口号相同的报文段会交付给相同的套接字。

    • 主机A中运输层创建一个运输层报文段,其中包括应用程序数据、源端口号、目的端口号等,传递到网络层;
    • 网络层将该报文段封装到一个IP数据报中,并尽力而为交付给接收主机;
    • 报文段到达主机B后,接收主机运输层将检查报文段中的目的端口号并交付给相应的套接字。

    TCP套接字

    UDP套接字为四元组——(源IP地址,源端口号,目的IP地址,目的端口号);

    过程和UDP差不多,但运输层报文段还会包括源和目的IP地址,由四元组来识别每个套接字。

    可靠数据传输原理

    数据都是先从应用层交付到运输层被分为报文段并加上运输层头部,再被交付到网络层加上网络层头部 利用IP协议进行传输;

    就算我们能够保证运输层传输的可靠的(提供校验和等),但由于IP协议是不可靠的,所以数据传输可靠性还是不能得到保证。

    rdt_send() 可靠传输

    udt_send() 不可靠传输

    rdt_rcv() 接收数据

    deliver_data() 向较高层交付数据

    下面状态图中,横线上方是引起变迁的时间,下方是事件发生时采取的动作。

    rdt1.0 完全可靠信道

    所有数据传输都是可靠的,接收方也不需要发送任何响应。

    rdt2.0 具有比特差错信道

    • 这时候底层信道中的分组的比特可能受损,更加符合实际;

    • 接收方收到数据会发送肯定确认ACK,否定确认NAK;

    接收方需要具有以下三种功能来处理比特出错的问题:

    • 差错检查:接收方要可以检查到何时出现了比特差错(通常会添加一些额外的比特用于检测);
    • 接收方反馈:因为发送方与接收方通常运行在不同的主机上,因此接收方要提供明确的反馈信息给发送方;
    • 重传:接收方发送NAK时重传该分组。

    此时发送方有两个状态,一个是等待上层数据的到来,另一个是发送完数据后等待接收方的ACK或NAK。在第二个状态,发送方不能从上层获取更多的数据,也就是说 rdt_send() 事件不会出现,这就是停等协议。

    但是rdt2.0无法处理当ACK或NAK出现错误的情况。比如,当收到意味不明的ACK或NAK时,发送方通常会重传分组,但接收方不知道发送方是否正确收到了ACK或NAK,所以它就无法知道收到的分组是新发送的还是一次重传

    rdt2.1 带有分组信息的信道

    rdt2.1 就是在2.0的基础上,在数据分组中添加一新字段(seq),让发送方对其数据分组编号。这样接收方只需要检查序号即可确认收到的分组是否为一次重传。

    比如,当发送完分组0进入等到ACK或NAK状态时,得到的是NAK的话就会一直在当前状态,得到ACK才会进入等到上层传来分组1的状态。

    rdt2.2 无NAK ACK加上编号

    rdt2.2 相较于 rdt2.1 来说,它没有了NAK否定确认,但是对与每个ACK都加上了分组编号。

    比如,当发送完分组0进入等待ACK 0状态时,若收到了 ACK 1 的话,虽然是ACK 但是当前状态不会改变 还是停留在等到ACK0。

    rdt3.0 具有比特差错的丢包信道

    要处理丢包,肯定要解决何时重传的问题。

    • 发送方选择一个事件值(设置一个定时器),若在该事件段内未收到接收方的相应分组的 ACK,则重传该分组;重传分组可能会有冗余数据分组的问题,但该问题已经在rdt2.2中通过ACK的分组确认解决了;
    • 倒计时定时器的主要工作流程:
      • 发送方每发送一个分组就启动一个定时器;
      • 若收到相应分组的ACK后停止计时器;
      • 若定时间时间到了则重传分组并重新开始计时器。

    流水线 差错恢复

    • 停等协议:每次发送方发送一个分组,必须要等到收到该分组的ACK后才会发送下一个分组;
    • 流水线:每次发送方可以发送多个分组而不需要收到ACK。
      • 必须增加序号范围,因为每个输送中的分组必须有一个唯一的序号,不然每次发送多个分组 也不知道哪个分组出错或者没送到了;
      • 发送方和接收方必须缓存多个分组(后面详细讲);
      • 所需序号范围和对缓冲的要求取决于数据传输协议如何处理丢失。

    发送方利用率

    L为数据分组的长度,R为数据的传送速率,RTT为往返传播时延;

    发送方利用率为 t= (L/R) / (RTT+L/R)

    所以可以从利用率公式看出,若采用停等协议则利用率会比较低,因为每发送一个分组就要等待一个RTT。

    差错恢复——回退N步(GBN)

    允许发送方发送多个分组不需等待确认,但它流水中的未确认分组不能超过某个最大允许数N。

    如下图,[0, base-1]为发送完且被确认的分组;[base, nextseqnum-1]为发送完但未被确认的分组;[nextseqnum, base+N-1]为待发送的分组;大于等于base+N的分组为暂时不能使用的分组;

    这里是N就是窗口长度,GBN协议也通常被称为滑动窗口协议。

    GBN在发送方的事件

    • 上层的调用(上层调用rdt_send()):发送方先检查发送窗口是否已满(即是否已有N个已发送但未被确认的分组),若未满则产生一个分组并发送 同时更新变量,若满了就将数据返回给上层;
    • 收到一个ACK:GBN对序号为n的分组采用累计确认,表明接收方已正确接收到n及n之前所有分组的数据;
    • 超时事件:若出现超时,发送方将重新发送所有已发送但未被确认的分组;
      • 发送一个分组则启动一个定时器,收到一个ACK则重启定时器(若没有要发的分组了的话则终止定时器)

    GBN对于所有失序到达的分组进行简单的丢弃,即若现在收到n-1分组的ACK,但接下来接收方收到了n+1的分组,接收方会将其直接丢弃,直到收到分组n后 发送方又会重新发送n+1分组。这种方法使得接收方的缓存比较简单,只需要维护一个期待的需要 expectedseqnum 即可,若收到不是它的分组则直接丢弃。

    差错恢复——选择重传(SR)

    GBN允许发送多个分组来实现流水线填充,但由于其对于失序到达的分组采用简单丢弃的方式,所以单个分组的差错就能够引起GBN重传大量不必要重传的分组,而选择重传协议让发送方只会重传它怀疑在接收方出错的分组,能够避免不必要的重传。

    SR在发送方的事件

    • 上层的调用(上层调用rdt_send()):和GBN几乎一致;
    • 收到一个ACK
      • 若该分组在窗口内,则将其标记为已接受;
      • 若序号刚好为send_base(即当前窗口中第一个未被确认的分组序号),则窗口基序号移动到具有最小需要的未确认分组处;
      • 若窗口移动了且窗口内有未发送的分组,则发送这些分组;
    • 超时重传:重传这一个超时的分组。

    SR在接收方的事件

    SR接收方将确认一个正确接收的分组 而不管其是否按序到达。失序的分组将被缓存直到所有丢失的分组(即需要更小的分组)全部被接收再传给上层

    • 序号在窗口内([rcv_base, rcv_base+N-1])的分组被正确接收:
      • 接收方发送相应分组的ACK;
      • 若之前未收到过该分组 则进行缓存;
      • 若该分组的序号恰好为rcv_base,则到该分组为止或经过该分组到后面的连续分组被向上交付,且窗口移动到向上交付的分组的后一个;
      • 如:若接收方已经收到了 1, 3, 4, 5, 7分组,则若此时收到了2分组,则向上交付 1, 2, 3, 4, 5 ,窗口基序号变为6。
    • 序号在窗口之前([rcv_base-N, rcv_base-1])的分组被正确接收,产生一个ACK(即使已经被接收过了)。

    上面的第二步是必须的,因为若send_base的ACK未被传回给发送方,发送方将会再次发送send_base;显然接收方已经收到过该分组了,但若接收方不对其再次确认的话发送方的窗口将永远无法向前滑动。

    这就说明了,在SR协议里,接收方和发送方的窗口并不总是一致的。

    UDP

    优势

    • 关于何时、发送什么数据的应用层控制更为精细;UDP中 只要应用进程将数据传递给UDP,UDP就会将数据打包进UDP报文段并立即将其传递给网络层。而TCP由拥塞控制等机制,会遏制运输层发送方。
    • 无需建立连接。TCP在数据传输前要进行三次握手,时间开销比较大;
    • 无连接状态。TCP需要在端系统中维护连接状态——包括接收和发送缓存、拥塞控制参数以及序号和确认号的参数。UDP无需追踪这些参数,一般能够支持更多的活跃用户;
    • 分组首部开销小。TCP报文段由20字节的首部开销,UDP只有8字节。

    报文段格式

    • 源端口:这个字段占据 UDP 报文头的前 16 位,通常包含发送数据报的应用程序所使用的 UDP 端口。接收端的应用程序利用这个字段的值作为发送响应的目的地址。这个字段是可选的,所以发送端的应用程序不一定会把自己的端口号写入该字段中。如果不写入端口号,则把这个字段设置为 0。这样,接收端的应用程序就不能发送响应了。
    • 目的端口:接收端计算机上 UDP 软件使用的端口,占据 16 位。
    • 长度:该字段占据 16 位,表示 UDP 数据报长度,包含 UDP 报文头和 UDP 数据长度。因为 UDP 报文头长度是 8 个字节,所以这个值最小为 8。
    • 校验值:该字段占据 16 位,可以检验数据在传输过程中是否被损坏。

    TCP

    TCP概述

    • TCP连接提供的全双工服务,若一台主机上的进程A和另一台主机上的进程B存在一条TCP连接,则应用层数据可在A和B间双向传播,但TCP只支持一对一
    • TCP发起连接的进程被称为客户进程,等待连接的进程被称为服务器进程;
    • TCP三次握手的前两次不承载数据信息,第三次可以承载数据信息;
    • 一旦建立其一条TCP连接,客户进程就可通过套接字传递数据来,通过套接字后进入运输层 TCP将引导到该连接的发送缓存里,接下来TCP会时不时从发送缓存里取出一块数据进行传输;
    • TCP从发送缓存里取出并放入报文段的数据长度受限于最大报文段长度(MSS)——仅为应用层数据的段长度,不包括即将加上的TCP/IP首部;
    • MSS要保证一个TCP报文段(封装在IP数据报里)加上TCP/IP首部长度将适合单个链路层帧

    TCP报文段格式

    TCP报文段由首部字段数据字段组成,其中数据字段长度受限于MSS。

    • 源端口和目的端口不多说,分别占16比特;
    • 序号(seq):32比特,当前传送的报文段的首字节的字节流编号(由于TCP是面向字节流的协议,因此);
    • 确认号(ack):32比特,期待收到对方下一个报文段的第一个数据字节的序号,因此当前报文段最后一个字节的编号+1即为确认号;
    • 确认ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效;
    • 同步SYN:连接建立时用于同步序号,SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0;
    • 终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接。

    另外,TCP也是采用累计确认TCP只确认该流中第一个丢失字节为止的字节。如:若1535已经收到,在536899收到之前就收到了900~1000的话,则下一个报文的确认字段将会是536而不是1001。

    TCP发送与重传的三个事件

    • 从上层应用程序接收数据:TCP从应用程序接收数据,将数据封装在一个报文段中并将其交给IP。同时启动定时器;
    • 超时重传具有最小序号的未被确认的分组,同时重启定时器;
    • 收到ACK确认:TCP将ACK的y值与sendBase值相比较(sendBase为当前未被确认的最小字节序列号,即sendBase-1及之前的都被确认过了):
      • 若 y > sendBase,说明该ACK在确认之前未被确认了一个或多个报文段,则 sendBase = y,若当前还有未被确认的报文段,则重启定时器。

    其他

    • 超级间隔加倍:当超时事件发生时,TCP会重传具有最小序号的未被确认的报文段。每次重传时都会将下一次的超时间隔设置为先前值的两倍;这是由于,本来就是网络拥塞导致了超时,若现在还是相同的时间间隔重发,会导致网络更加拥塞;
    • 快速重传:冗余ACK就是再次确认某个报文段的ACK。由于TCP不采用否定确认,所以若发送方收到了冗余ACK则说明数据发送了错误;若发送方接收到对相同数据的3个冗余ACK,说明跟在这个ACK之后的报文段已丢失,这时发送方会进行快速重传,即在定时器过期之前重传丢失的报文段。

    TCP连接——三次握手

    • 第一次:客户端->服务器,SYN(同步标志位)被置为1,seq字段里为初始化序列号client_isn,不包含应用层数据;
    • 第二次:客户端<-服务器,SYN和ACK标志位都被置为1,seq字段里为初始化序列号server_isn,ack字段里为当前报文序号加一,即为 client_seq+1,不包含应用层数据;
    • 第三次:客户端->服务器,SYN标志位被置为1,seq为client_isn+1,ack为server_isn+1,此次握手可包含应用层数据。

    至此,客户端和服务器都知道了对方的isn,且确认对方知道了自己的isn,连接建立。

    为什么要三次,而不是两次就可以了?

    客户端和服务器都必须要双方知道自己的初始化序列isn,若没有第三次握手,TCP连接将无法识别当前的握手请求是这一次要建立的还是之前延误的请求(可能会由冗余连接)。

    TCP断开——四次挥手

    • 第一次:客户端->服务器,FIN标志位置为1,标识客户端确认发送完数据且知道服务端已经接受完了,想要关闭发送数据口,seq为当前报文段数据首字节编号;
    • 第二次:客户端<-服务器,ACK标志位置为1,表示服务器已经收到客户端想要断开连接的请求,但这个时候可能还会有数据要发送给客户端,因此不会立即发送FIN;
    • 第三次:客户端<-服务器,FIN和ACK标志位都被置为1,这时服务器数据已经传完了,告诉客户端可以关闭连接了;
    • 第四次:客户端->服务器,客户端收到服务器的FIN报文后,发送ACK报文给服务器,至此可以关闭连接了。

    为什么第二三次挥手不能合并?

    这是由于服务器收到客户端想要断开连接请求的时候,可能数据还未发送完毕,要先将数据发送完毕之后才会发哦是那个FIN表示可以断开连接了。

    最后的等待时间?

    要注意的是,最后一次挥手时,由于客户端无法知道服务器是否准确收到了ACK,会等待2MSL时间,若在这个时间内服务器没有向客户端重传FIN报文(服务器无法收到客户端的ACK报文就会重发FIN报文),说明准确收到了,则彻底关闭连接。

    TCP流量控制

    流量控制:如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失。

    TCP的流量控制是利用滑动窗口机制实现的,接收方在返回的数据中会包含自己的接收窗口的大小,以控制发送方的数据发送。

    • rwnd——接收窗口,用于告诉发送方该接收方还有多少可用的缓存空间;

    • LastByteRead——主机B上的应用进程缓存读出的数据流的最后一个字节的编号;

    • LastByteRevd——从 网络中到达的并且已放入主机B接收缓存中的数据流的最后一个字节的编号;

    • RcvBuffer——接收缓存

    rwdn = RcvBuffer - [LastByteRevd - LastByteRead]

    为什么会出现滑动窗口?

    在确认应答策略中,对每一个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送下一个数据段,这样做有一个比较大的缺点,就是性能比较差,尤其是数据往返的时间长的时候。

    使用滑动窗口,就可以一次发送多条数据,从而就提高了性能。

    TCP拥塞控制

    拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。流量控制是为了预防拥塞

    TCP让每一个发送方根据所感知到的网络拥塞程度来限制其能向连接发送浏览的速率。

    • cwnd——拥塞窗口,对TCP发送方能向网络中发送流量的速率进行了限制。

    • 发送方感知拥塞:超时,3个冗余的ACK

    • ssthresh:慢开始阈值(到达这个阈值进入拥塞避免阶段)

    慢开始

    • TCP在一开始时,将cwnd设置为1 MSS;
    • 发送方通常希望寻找找到可用带宽的数量(找到上限 阈值),因此收到1个ACK就将cwnd加1,因此是指数增长
    • 当达到ssthresh(慢开始阈值)时,进入拥塞避免阶段;
    • 若在增长过程中发生超时时间,则将ssthresh值设置为 cwnd/2 ,并重新进入慢开始阶段。

    拥塞避免

    • 在这个阶段,不再像慢开始一样采用指数增长,而是在一个RTT内,cwnd只增加1MSS(线性增长);
    • 当出现超时或丢包时,将ssthresh值设置为 cwnd/2 ,并重新进入慢开始阶段(和慢开始一样)。

    快重传

    • 要求接收方不要等待自己发送数据时才进行捎带确认,而是立即发送ACK确认;

    • 收到了失序的报文段也要立即发送对这个失序的报文段的重复确认;

    • 当发送方收到三个冗余的ACK,就将相应的报文段立即重传,而不是等该报文段的超时重传计时器超时再重传;

    • 当收到三个重复确认就可以知道,现在只是丢失了个别报文段(这样才会一直对该报文段发送想要的请求),所以不执行慢开始,而是直接进入快读恢复状态。

    快速恢复

    • 快重传出现后不进入慢开始,而是进入快速恢复状态;
    • 发送方将当前的阈值ssthresh调整为原来的一半,cwnd调整为ssthresh+3开始线性增长(就是拥塞避免状态)。

    Reno和Tahoe的区别

    • Reno算法在遇到三个冗余的ACK的情况下,会进入快重传+快恢复阶段;
    • Tahoe算法在遇到三个冗余的ACK时和超时时的处理一样,进入慢开始状态。
  • 相关阅读:
    Linux安全-通过修改/etc/hosts.deny拒绝远程ssh暴力破解
    JS-二进制的处理者:Blob
    JS-二进制数据缓冲区ArrayBuffer
    axio源码分析
    【js】appendChild()具有移动性
    js实现-二进制数据转换为blob
    实现点击下载到本地的功能
    memo、useMemo、useCallback
    memoization缓存优化
    什么是纯函数?
  • 原文地址:https://www.cnblogs.com/TRY0929/p/14676356.html
Copyright © 2011-2022 走看看