传输层——可靠性传输原理

传输层协议给运行在不同主机上的应用进程提供逻辑通信服务。逻辑通信,以应用层的视角,可以认为通信对端主机上运行的进程是直接连接的,但事实上,主机之间可能连接有许多路由器。应用进程使用传输层提供的逻辑通信服务进行信息传递。

传输层协议有两种:TCP与UDP,每种协议均为上层应用提供里不同的服务。

1 传输层复用与解复用

网络层为主机提供逻辑通信,UDP和TCP为运行在主机上的进程提供逻辑通信。将主机之间的信息传递扩展至进程之间的信息传递称为复用与解复用。

在目标主机,传输层从网络层接收到段。传输层负责将这些段发送到主机合适的进程上。例如在电脑上同时运行两个telnet进程,传输层需要将网络层传上来的段准确传输到对应的进程。一个进程(可以认为是一种网络应用)可能有一个或多个sockets,sockets可以理解为网络层到应用的门,数据从网络层上传到应用层必须经过这个门,反之亦然。

每个传输层的段有一个字段用来确定该字段将会被传输到哪一个socket。在接收主机侧,传输层检查字段确定接受的socket并将数据段定向至该socket。解复用的含义就是发送传输层数据段到正确的socket(网络层→传输层→应用层);反之将不同socket的数据块收集并赋予头部信息组成数据段,并将这些数据段传递至网络层成为复用(应用层→传输层→网络层)。

wanghao

图1-1 传输层的复用与解复用

上图中间主机P1或P2进程必须解复用从网络层上传的段,即就是将网络层上传的段准确定向至相应进程的socket。如果中间主机要向外传递信息,需要将应用层数据复用为传输层数据段,然后将这些数据段传输给网络层。

将数据传递到指定进程的socket需要两个条件(1)sockets有惟一的id;(2)每个数据段有特殊字段,该字段用以指明数据段被传输至哪一个socket。

在这里插入图片描述

图1-2 特殊字段

特殊字段中有目标端口号和源端口号,另外针对tcp与udp协议的不同还有其他字段。端口号范围是0~65535,0~1023端口号是知名端口号,如http 的端口号为80等。

1.1 udp复用与解复用

udp在创建socket时会自动分配一个端口号(1024~65535),或者也可以指定端口号

sockfd = socket(AF_INET, SOCK_DGRAM, 0);// 创建socket
bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr))// 绑定指定端口

在这里插入图片描述

图1-3 端口号转换

假设主机A分配端口号19157,主机B分配端口号46428。主机A传输层将数据组包为数据段,数据段包括源端口字段与目的端口字段,然后将数据段传递给网络层(复用过程)。当数据段到达主机B的时候,通过目标端口号将数据段定向至主机B的指定socket(解复用过程)。如果两个段的目标端口号和目标IP相同,那么这两个段会定向至相同的socket。

特殊字段中的源端口的作用:上图中主机A从端口19157将数据发送至主机B的端口46428,此过程19157是源端口;如果主机B发送至主机A那么19157就可以作为源端口。

1.2 TCP的复用与解复用

TCP的复用功能不同于udp,这是因为TCP定向段至指定socket的条件不同于UDP,TCP将段定向至socket需要以下四个要素:source IP,source port number,destination IP 和 destination port。当一个TCP段经由网络层到达对端主机需要利用这个四个要素将该段定向至合适的socket,这个过程就是TCP的解复用。如果两个TCP段的source IP或者source port number不同会被定向到不同的socket,这一点与UDP不同。

2 可靠数据传输原理

网络层(IP层)向传输层提供的是不可靠的传输服务。数据的可靠性需要上层协议来保证,此处上层协议可以是传输层或应用层,本文只针对传输层提供可靠的传输协议做讲解。传输层有两种协议,UDP协议是不可靠的数据传输,TCP协议是可靠的数据传输协议。
在这里插入图片描述
图2-1 可靠数据传输服务模型

上图(a)表明传输层提供了可靠的数据传输通道,数据通过这个通道不会被破坏或者丢失,但这是一个抽象的概括;图(b)是真实的可靠性数据服务过程。发送方调用应用层接口rdt_send()使用可靠性协议将数据传递到网络层,网络层的传输是不可靠,调用的接口为udt_send();在接收方,数据通过网路层到达传输层,然后调用接口deliver_data()将数据上传给应用层。

2.1 建立可靠性数据传输协议的过程

2.1.1 完美的可靠性数据传输:rdt1.0

图2-1使用有限状态机定义里rdt1.0的发送方和接收方。有限状态机里面的箭头表示状态的切换,图2-1中发送和接收双方均只有一种状态,故箭头指向自身。横线上方是导致状态转换的事件,横线下方是状态转变之后的动作,如果没有事件或动作就用三角符号表示。带有箭头的虚线为有限状态机初始状态。

在这里插入图片描述

图2-2 rdt1.0 – A protocol for a completely reliable

发送方从上层收到数据,通过rdt_send()事件完成,然后通过动作make_pkt()将数据发送出去;接收方从底层通过rdt_rcv()接口从网络层收到数据,并将数据通过deliver_data()接口将数据传递至上层。

rdt1.0版协议是最简单也是最理想的协议,发送方发出的数据没有任何的破坏或者丢失,完整的被接收方拿到。

2.1.2 数据传输过程出现位错误:rdt2.0

rdt2.0协议是更趋向于真实情况的协议,在数据传输过程中,数据包里面的某些位发生错误,比如从0变成1或者从1变成0。该协议的前提是所有发送的包都能被完成收到,当然包括其中位出错的包。

为解决此种位出错的情况,引入了应答机制。如果接收方收到正确的包就回复ACK (positive acknowledgments);收到错误的包就回复NAK **(positive acknowledgments)。**发送方收到NAK,就会重新发送上一个包,在计算机网络中这种重传被称为ARQ (Automatic Repeat reQuest)协议。

ARQ协议中还需要处理位错误的三种功能:

错误检测:接收方需要一种机制检测合适出现位错误。

接收反馈:发送方需要收到接收方的反馈,如果接收方收到的是正确的包,就回复ACK,反之回复NAK。

重传机制:接收方收到错误的包之后,发送方能重传。

在这里插入图片描述

图2-3 rdt2.0 - A protocol for a channel with bit errors

rdt2.0发送端状态转换过程:

  1. wait for call from above:
  • 上层协议调用下层服务接口(rdt_send(data))切换至另一状态wait for ACK or NAK。
  1. wait for ACK or NAK:
  • 收到NAK(isNAK(rcvpkt)),停留在此状态。
  • 收到ACK(isACK(rcvpkt))后切换至另一状态wait for call from above。

当发送方等待ACK或者NAK状态时,不能从上层应用接收数据,即rdt_send(data)事件不会出现,只有收到ACK或者NAK时才能发生切换状态的事件。rdt2.0协议因此被称为停止等待(stop-and-wait)协议。

rdt2.0接收端状态转换过程:

  1. wait for call from below:
  • 收到网络层传递的包,如果包错误,回复NAK(make_pkt(NAK)),停留在该状态;
  • 收到网络层传递的包,如果包正确,回复ACK(make_pkt(ACK)),停留在该状态。

rdt2.0看上去解决了位错误的情况,但是它有很严重的缺点:如果ACK或者NAK本身出现了问题,发送方不知道上一包数据是否被接收方正确接收。

如果发送方收到错误的ACK或者NAK,处理方法可能有:

  1. 增加足够的校验位,使发送方可以检测差错,还能够回复差错,对于产生差错但是数据包没有丢失的数据传输通道,可以直接解决问题。
  2. 当发送方收到错误应答时,即重传当前包。但是可能会导致接收方收到冗余分组(duplicate packet)。

解决冗余分组有一个简单的方法:在数据包中增加一个新字段,发送方用该字段进行编号。接收方只需要检查序号就可以确定是否收到冗余分组,针对rdt2.0,只需要一个bit来表述序号就行,因为接收方知道收到的是否为新的数据包,发送反只需要根据收到ACK或者NAK包的序号来发送数据包就行。

在这里插入图片描述

图2-4 rdt2.1 - sender

rdt2.1在rdt2.0的基础上增加了序列号,同时接收方和发送方的状态也是rdt2.0的两倍。

rdt2.1发送端状态转换过程:

  1. wait for call 0 from above:
  • 上层协议调用下层服务接口(rdt_send(data))切换至另一状态wait for ACK or NAK 0。
  1. wait for ACK or NAK 0:
  • 收到的包错误(corrupt(rcvpkt))或者收到NAK(isNAK(rcvpkt)),停留在此状态。
  • 收到的包正确并且收到ACK(notcorrupt(rcvpkt)&& isACK(rcvpkt))切换至下一状态wait for call 0 from above。

后续两者状态转换与前两者类似。

在这里插入图片描述

图2-5 rdt2.1 - receiver

rdt2.1接收端状态转换过程:

  1. wait for 0 from below:
  • 收到错误(corrupted)的包时,无论是sn0(sequence number 0)还是sn1(sequence number 1)都停留在此。
  • 收到sn1正确(notcorrupted)的包时停留在此,即使此时收到的是乱序的包,但是此时的回应仍然是ACK。
  • 收到sn0正确(notcorrupted)的包时,切换到状态wait for 1 from below。在此状态下,只有收到sn0正确的包才能切换到另一状态。
  1. wait for 1 from below:与wait for 0 from below状态类似。

rdt2.1中接收方使用ACK与NAK回复发送方。当收到乱序的包时,接收方回复ACK;当收到受损的包时,接收方回复NAK。其实在引入序号之后,回复收到上一个包的ACK与回复收到当前包的NAK效果是一样的。假设发送方发送第一个包之后收到接收方的ACK,发送方发送第二个包时,收到的仍旧是第一个包的ACK,说明接收方没有收到第二个包。这种方法需要对ACK的序号进行标记。图2-5与2-6解释了这种无NAK的方式,称为rdt2.2。

在这里插入图片描述

图2-6 rdt2.2 - sender

rdt2. 2 发送端状态转换过程:

  1. wait for call 0 from above:
  • 上层协议调用下层服务接口(rdt_send(data))切换至另一状态wait for ACK 0。
  1. wait for ACK 0:
  • 收到错误的包或者ACK1((corrupt(rcvpkt) || isACK(rcvpkt,1))则停留在此状态。
  • 收到正确的包且ACK0(notcorrupt(rcvpkt) && isACK(rcvpkt,0)),即切换至wait for call 1 from状态。

在这里插入图片描述

图2-7 rdt2.2 - receiver

rdt2. 2接收端状态转换过程:

  1. wait for 0 from below:
  • 收到错误(corrupt(rcvpkt))的包或者乱序(has_seq1(rcvpkt))的包,停留在此状态;然后回应ACK(sndpkt=make_pkt(ACK,1,checksum))。
  • 收到正确并且正序的包(notcorrupt(rcvpkt)&& has_seq0(rcvpkt)),切换至wait for 1 from below状态。
  1. wait for 0 from below:
  • 与1类似。

2.1.3 位错误并且有数据包丢失:rdt3.0

在网络传输过程中可能会出现丢包的情况,出现丢包的情况会引入两个问题:1. 怎么确定包丢掉了;2. 包丢掉之后如何处理。问题2很好解决,前面描述的协议可以用来解决问题2。但是问题1很复杂,发送方不知道发送的包是否被接收方接收,又或者发送的包被接收方接收,但是又不能收到ACK。如果发送方不介意等,那么可以等到确定包已经丢掉后直接重发,但这种情况几乎没有。

对于发送方来说,重传是万能的。发送方不知道是数据包丢失,还是ACk丢失,又可能是数据包或ACK过度延时。为了避免长时间等待的情况,发送方可以设置一个定时器,发送方在发送数据包时启动定时器,如果超时没有收到ACK,就启动定时中断(采取合适的动作),然后停止计时。图2-7为rdt3.0发送方的FSM,rdt3.0是针对出错和丢包的可靠传输协议。

在这里插入图片描述

图2-7 rdt 3. 0 sender

rdt3.0发送方相对于rdt2.2增加了超时处理。


版权声明:本文为weixin_42369760原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。