功能:将网络层的在两个端系统之间的交付服务扩展到两个不同端系统上的应用层进程之间的交付服务 最基本的功能:数据交付和差错检查
1. 概述和运输层的服务
运输层协议为运行在不同主机上的应用进程之间提供了逻辑通信功能,从应用程序的角度看,通过逻辑通信,运行在不同进程的主机好像直接相连一样。
运输层的两个重要的协议:TCP(传输控制协议)和UDP(用户数据报协议)
1.1 运输层和网络层的关系
- 运输层协议值工作在端系统中,在端系统中,运输层协议将来自应用进程的报文移动到网络边缘。
- 运输协议能够提供的服务常常受制于底层网络层协议的服务模型,如果网络层协议无法为主机之间发送的运输层报文段提供时延或者带宽保证的话,运输层协议也就无法为进程之间发送的应用程序报文提供时延或带宽保证
即使底层网络层协议不能再网络层提供相应的服务,运输层协议也能提供某些服务,例如可靠数据传输,机密性
1.2 因特网运输层概述
运输层为应用侧提供两种截然不同的运输层协议:一种是UDP(用户数据报协议),提供一种不可靠的、无连接的服务,不提供不必要服务,另一种是TCP(传输控制协议),提供一种可靠的,面向连接的服务
- TCP分组称为报文段,UDP分组称为用户数据报
- 网络层简介:网络层协议IP网际协议,IP的服务模型是尽力而为交付服务。
- 尽它最大的努力在通信的主机之间交付报文段。
- 他不确保报文段的交付。不保证报文段按序交付,不保证报文段数据的完整性。
- IP被称为不可靠服务
- 将主机间交付扩展到进程间交付被称为运输层的多路复用和多路分解
2多路复用和多路分解
- 将主机间交付扩展到进程间交付被称为运输层的多路复用和多路分解
- 在接收端,运输层检查运输层报文段的首部字段,表示出接收套接字,进而将报文段定向到具体的套接字。将运输层报文段中的数据交付到正确的套接字的工作称为多路分解。
- 在发送端(源主机端),原主机从不同套接字中手机数据块,并未每个数据快封装上首部信息,从而生成报文段,然后将报文段传递到网络层,所有的这些工作称为多路复用
2.1无连接服务的多路复用于多路分解
- UDP运输层报文段主要包括:应用程序数据、源端口号、目的端口号和两个其他的值
一个UDP套接字是有一个二元组来全面标识的(目的的IP地址和目的的端口号)
TCP套接字是由一个四元组标识的(源IP地址、源端口号、目的IP地址、目的端口号)
在TCP服务模型中需要于四元组全部匹配,才被分配到同一个套接字
2.3web服务器与TCP
当今高性能Web服务器通常只使用一个进程,但是为每一个新的客户连接创建一个具有新连接的套接字的新线程。
3 无连接运输:UDP
UDP从应用进程得到数据,附加上用于多路复用/多路分解服务的源和目的端口号字段,以及两个其他的小字段,然后将形成报文段交给网络层,网络层将该运输层报文段封装到一个IP数据报中。
UDP的优势
关于何时、发送什么数据的应用层控制更为精细。
- 采用UDP,只要应用进程将数据传递给UDP,UDP就会将此数据打包进UDP用户数据报,并立即传递给网络层。
- 而TCP由一个拥塞控制机制,当源和目的主机间的一条或多条链路变得极度拥塞时来遏制TCP发送方。
- 因此UDP对于实时应用程序友好
- 无需连接建立
- TCP在开始数据传输之前要经过三次握手,UDP则不需要。如果DNS运行在TCP之上则会慢很多
- 无连接状态
- TCP需要在端系统中维护连接状态。在此链接状态包括接收和发送缓存、拥塞控制参数以及序号与确认好的参数
- 分组首部开支小
- 二元组
- 维护参数
UDP虽然没有提供可靠数据传输服务,但是UDP应用时可以实现可靠数据传输的,这可以通过应用程序自身建立可靠性机制来实现(可以通过增加确认与重传机制来实现)
3.1UDP报文段结构
3.2 UDP检验和
作用
- 提供了差错检查功能
用于确定当UDP报文段从源到达目的地移动时,其中的比特是否发生了变化(例如,可能由于链路中的噪声干扰或者存储在路由器中时引入问题)
原理
反码(complement):发送方的UDP对报文段中的所有16bit字的和进行反码运算,就是将所有的0换成1,所有的1换成0;接收端计算校验和但不做反码,将结果与发送方校验码相加,如果得到的结果为16个1,那么说明该分组中没有引入差错。
- 回卷(wrap around):遇到最高位进位的时候,就将溢出的那一位与低16位相加。
示例
假定有下面3个16bit的字:
这些比特字的前两个之和是:011001100110000001010101010101011000111100001100
再和第三个字相加,得出:01100110011000000101010101010101————————————————1011101110110101
最高位溢出,回卷:10111011101101011000111100001100————————————————10100101011000001
在发送方,需要进行反码运算,得到:1011010100111101,这就变成了检验和;01001010110000010000000000000001————————————————0100101011000010
在接收方,全部的4个16比特(包括检验和)加在一起,如果该分组没有引入差错,则显然在接收方处该和将是:1111111111111111。如果这些比特之一是0,那么我们就知道该分组中已经出现了差错!为什么UDP提供了检验和
- 尽管许多链路层协议提供了差错检测,但是不能保证从源主机到目的主机之间的所有链路都提供差错检测。
- 即使报文段经链路正确的传输,当报文段存储在某台路由器的内存重视,也可能引入比特差错
- 在既无法保证逐一链路的可靠性,又无法确保内存中的差错检测的情况下,UDP必须在端到端的基础上再运输层提供差错检测,被称为端到端原则。
4 可靠数据传输原理

rdt:可靠数据传输协议 udt:不可靠数据传输协议 可靠数据传输对上一层提供的服务抽象为:数据可以通过一条可靠的信道进行传输:完整、有序
4.1构造一个功能正确的协议
概要:
- RDT1.0:假设底层信道完全可靠
- RDT2.0:考虑底层信道具有比特差错
- RDT2.1:考虑底层信道具有比特差错,并且ACK或NAK分组也有可能受损
- RDT2.2:在具有比特差错的信道上实现的一个无NAK的可靠数据传输协议
- RDT3.0:假定底层信道除了比特受损外,还会丢包!
RDT1.0:底层信道完全可靠
RDT1.0操作较为简单,即底层全部采用rdt传输,上层对运输层调用rdt,运输层向下也是调用rdt;
因此底层的通信信道完全可靠,不需要担心数据出现差错,接收端就不需要提供任何反馈信息给发送方。
RDT2.0:考虑比特差错但不考虑ACK和NAK受损
- 在1.0的基础上考虑分组中的比特可能受损,但仍然假定所有发送的分组将按其发送的顺序被接收
- 为了正确的接收完整的分组,接收端需要向发送端告知:哪些分组被正确接收(肯定确认),哪些报文段接收有误(否定确认),并因此需要重传。在计网中,基于这样的重传机制的可靠数据传输协议称为自动重传请求(ARQ)协议。ARQ需要一下三种协议支持:
- 差错检测
- 一种时接收方可以检测到何时出现了比特差错的机制
- 接收方反馈
- 接收方需要提供明确的反馈信息给发送方
- 重传
- 接收方收到有差错的分组时,发送方将重传该分组文
- 差错检测

- 当发送方处于等待ACK或NAK的状态时,它不能从上层分组获得更多的数据,也就是说,rdt_send() 事件不可能出现,rdt2.0这样的协议被称为停等协议
- 问题:
- 目前还没有添加类似于检验和比特之类的差错检测
- 协议应该怎样纠正ACK或者NAK分组中的差错,
- 如果一个ACK或者NAK受损,发送方无法知道接收方是否正确接收了上一块发送的数据
- ACK和NAK受损的三种可能性:
- 发送方对接收方的确认机制引入二次确认机制,但是这样貌似陷入了一种死循环
- 添加足够多的检验bit。使得接收方可以恢复受损的分组数据
- 发送方收到受损的ACK或NAK分组时,直接进行重传操作。这种方法在信道中引入了冗余分组。接收方因为不知道之前发过去的ACK或NAK有没有被收到,所以也无法知道冗余分组时新分组还是重传分组
- 解决这个新问题的一个简单方法时在数据分组中添加一个新字段,让发送方对其发送的分组编号。即将发送数据分组的序号放在该字段。接收方通过检查该序号即可知道时新分组还是重传分组。引入rdt2.1
RDT2.1:考虑ACK和NAK的比特差错
通过引入标号。即把发送数据分组的序号放在该字段。这样接收方只需要检测序号就能发现是否重传了分组。rdt2.0是”停等协议”,因此只需要一位序号就够了,因为只有接收方和发送方都确认该分组传输完成了,才会传输下一个分组。只要前后两次分组的编号是不同的即可。
接收方会判断收到的分组中的序号。如果因为ACK分组被破坏导致发送方进行了重传,接收方通过序号可以分辨出来,并重新发送一个ACK回去。
RDT2.2:无NAK分组
在RDT2.1的基础上,如果接收到受损的分组时,不发送NAK,而是针对上一次正确接收的分组发送一个ACK,也能实现NAK一样的效果。
此时,发送方收到对同一个分组的两个ACK(冗余ACK(duplicate ACK))后,就知道接收方没有正确接收到跟在被确认两次的分组后面的分组。
下面用图来描述一哈:
RDT3.0:考虑比特差错和丢包
- 怎样检测丢包?一种可行的方法是,发送方等待一个合适的时间以确定分组已经丢失,尽管可能不能确保。
- 如何处理丢包?发送方判定发生丢包之后,只需要重传即可,于是引入了冗余数据分组,RDT2.2协议已经有足够的功能(即序号)来处理冗余分组的情况。
- 如何实现这种机制?为了实现基于时间的重传机制,需要一个倒计时定时器,在一个给定的时间量过期之后,可中断发送方。因此,发送方需要能做到:
- 每次发送一个分组(包括第一次分组和重传分组)时便启动一个定时器
- 响应定时器中断
- 终止定时器
RDT涉及协议总结
- 自动重传请求协议(ARQ):用于接收方向发送方确认是否接收到分组(ACK字段)
- 停等协议:当发送方处于等待ACK或者NAK状态时,发送方不能从上层获得更多的数据,(不能再次调用rdt_send() )
- 比特交替协议:因为序号位0和1交替出现
- 检验和:用于差错检测
- 序号:用于冗余分组检测(只检测前后两个分组是否冗余,并不是用来排序的)0,1
- ACK分组:肯定确认或否定确认
- 重传:当出现分组丢失或者比特差错时,发送方重新向接收方再次发送上一个分组
- 倒计数定时器:在一个给定的时间量过期后,将中断发送方,并重新发送上一个分组

解释为什么停等协议的性能十分糟糕:
假定有两个端系统,它们之间的光速往返传播时延RTT是30毫秒,彼此通过一条发送速率R为1Gbps的信道相连。现在有一个长L为1000字节的分组,发送一个分组进入链路的实际所需时间为:
因此停等协议有着非常低的发送方利用率:
也就是说,发送方只有万分之2.7时间是忙的,或者说发送方在30.008ms内只能发送1000字节,有效吞吐量仅为267Kbps,即使是在1Gbps的链路上也是如此!
停等操作对应的示意图:
流水线发包示意图:
为了支持流水线需要对协议进行如下修改:
- 必须增加序号范围
- 协议的发送方和接收方两端都需要缓存分组
- 发送方至少需要缓存已发送未确认分组
- 接收方需要缓存那些正确接收的分组
流水行差错恢复有两种基本方法:
通过上面的两个定义可以将整个序号空间分成四份
其中:
- [0,base-1]:已发送,已确认
- [base,nextseqnum-1]:已发送未确认
- [nextseqnum, base+N]:可连续发送的序号
=base+N:尚不可发送的序号
N通常被称为窗口长度(Window size),GBN协议也通常被称为滑动窗口协议(sliding window protocol)
发送方相应三个事件
- 上层的调用,当上层调用时,发送方首先检查发送窗口是否已满
- 收到一个ACK
- 超时事件 即timeout

接收方只接受按序到达的分组,并传送给上层,乱序到达的分组会直接被丢弃,下面是一个实例:
选择重传
发送方只重发那些可能丢失的分组。发送方对于每个分组都有一个定时器
序号空间如下:
发送方的逻辑:
- 从上层收到数据:收到数据后,检查下一个可用于该分组的序号。如果该序号位于发送方窗口内,则将数据打包发送
- 超时:超时时重发对应分组,注意在选择重发协议中,每个分组都有自己的逻辑定时器,可以使用单个硬件定时器模拟多个逻辑定时器
- 收到ACK,若该序号分组在窗口内,则将该分组标记位已确认,若该分组等于send_base,则将send_base移动至下一个未确认分组对应的序号,若移动窗口导致有待发送分组的序号落在窗口中则发送对应的分组。
接收方逻辑:
- 序号在[rcv_base, rcv_base+N-1]内的分组被正确接收
- 返回对应的ACK
- 若该分组第一次收到,则缓存该分组
- 若序号等于rcv_base,则将从rcv_base开始连续的已经收到的分组发给上层。并移动窗口到下一个未接收分组对应序号处
- 序号在[rcv_base-N, rcv_base_base-1]内的分组被正确收到:
- 返回对应ACK
- 这个逻辑针对ACK传送失败的问题
- 其余情况忽略该分组
一点总结
可靠数据传输机制及其用途总结:
| 机制 | 用途和说明 |
|---|---|
| 检验和 | 检测在一个传输分组中的比特错误,rdt2.0引入 |
| 定时器 | 用于超时/重传一个分组,可能因为该分组或其ACK在信道中丢失了,超时情况: - 分组丢失 - 分组延时超过设定超时时间 - 接收方已收到分组,但ACK丢失 - rdt3.0 |
| 序号 | rdt3.0用于为从发送方刘翔接收方的数据分组按顺序编号 rdt2.0序号为一位序号0/1 |
| 确认 | 接收方用于告诉发送方一个分组或一组分组已被正确的接收。 |
| 否定确认 | 接收方用于告诉发送方某个分组未被正确的接收 |
| 窗口、流水线 | 发送方也许被限制进发送那些序号落在指定范围内的分组 |
分组重新排序:一个具有序号或确认号x的分组的旧副本可能会出现,即使发送方或接收方的窗口中都没有包含x。
5 TCP
TCP为什么要进行三次握手?
- 《计算机网络》:“为了防止已失效的连接请求报文段突然又传送到了服务端”;
- 解释:client发出的第一个连接请求报文段并没有丢失,而是在某个网络节点长时间的滞留了,以致延误到连接释放以后的某个时间蔡到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意连接建立。假设不采用“三次握手”,那么只要server发出确认,新的连接就会建立。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经确立,并一直等待client发来数据。这样server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止这个问题。例如刚才的情况,client不会向server的确认发送确认。server由于收不到确认,就知道client并没有要求建立连接。
- 这个问题的本质是,信道不可靠,但是通信双方需要就某个问题达成一致。而要解决这个问题,无论你在消息中包含什么信息,三次通信是理论上最小值。所以三次握手不是TCP本身的要求,而是为了满足“再不可靠通信上可靠地传输信息”这一需求所导致的。请注意这里的本质需求,信道不可靠,数据传输可靠。三次达到了,后面你想接着握手也好,发数据也好,跟进行可靠信息传输的需求就没关系了。因此,如果信道是可靠的,即无论什么时候发出消息,对方一定能收到,或者你不关心是否要保证对方收到你的消息,那就能像UDP一样直接发送消息就可以了。这可视为对“三次握手”目的的另一种解答思路

