运输层
提出问题
- 两个实体怎么样才能在一种会丢失或损坏数据的媒体上建立可靠的通信。
是什么
运输层与网络层的关系
网络层提供了主机之间逻辑通信,运输层为运行在不同主机上的进程之间提供了逻辑通信。运输层协议只工作在端系统中,将来自应用进程的报文移动到网络边缘
网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。传输层提供了进程间的逻辑通信,传输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个传输层实体之间有一条端到端的逻辑通信信道。
运行
运输层协议是在端系统当中实现的,在发送端运输层将从发送应用程序进程接收到的报文转换成运输层分组;在发送端系统运输层将这些报文段传递给网络层,网络层将其封装成网络层数据报并发送向目的地。
应用
适用性
- 为运行在不同主机上的应用进程提供直接的通信服务起着至关重要的作用。
- 两个实体怎么样才能在一种会丢失或损坏数据的媒体上建立可靠的通信。
- 控制运输层实体的传输速率以避免网络拥塞,或从拥塞中恢复。
可提供的功能:
- 可靠数据传输。
- 拥塞控制。
缺陷
应用场景
概述
运输层协议为运行在不同主机上的应用进程之间提供了逻辑通信功能。从应用程序的角度看,通过逻辑通信,运行在不同进程的主机好像直接相连一样。应用进程使用运输层提供的逻辑通信功能彼此发送报文,而无需考虑承载这些报文的物理基础设施的细节。
服务模型
最基本的责任是将两个端系统间IP的交付服务扩展到运行在端系统上的两个进程的交付服务,被称为是运输层的多路复用与多路分解。
多路复用与多路分解
进程拥有一个或多个套接字socket(TCP、UDP等),相当于从网络向进程传递数据和进程向网络传递信息的门户,运输层与进程通信,是运输层将数据传递给socket。
多路分解:将运输层报文段中的数据交付到正确的套接字的工作。运输层检查达运输层的报文,标识出接收套接字,将报文段定向到该套接字。
多路复用:在源主机从不同的套接字收集数据块,并未每个数据块装上首部信息,生成报文段传递到网络层。
分解与复用依据:源IP,源端口,目的IP,目的端口(端口号是16比特的)。
UDP套接字:目的IP,目的端口。但是报文依然有着源IP与端口,两个不同源的报文会被定向到同一进程。
TCP:源IP,源端口,目的IP,目的端口。两个不同源的报文会被定向到不同进程。
可靠数据传输机制
对于IP服务,数据报能够溢出路由器缓存而永远不能到达目的地,数据报也可能是乱序到达,而且数据报中的比特可能损坏。由于运输层报文段是被IP数据报携带着在网络中传输的,因此运输层的报文段也会带来这种问题。
- 检验和。用于检测在一个传输分组中的比特错误。
- 定时器。用于超时/重传一个分组,可能因为该分组或其ACK在信道中丢失了。由于当一个分组延时但未丢失,或当一个分组已经被接收方收到但ACK丢失时,可能产生超时事件,所以接收方可能会收到一个分组的多个冗余副本。
- 序号。用于为从发送方流向接收方的数据分组按序号编号。所接收分组的序号间的空隙可使得接收方检测出丢失的分组。具有相同序号的分组可使得接收方检测出一个分组的冗余副本。
- 确认。接收方用于告诉发送方一个分组或一组分组已被正确地接收到了。确认报文通常携带着被确认的分组或多个分组的序号。确认可以是逐个的或累积的,这取决于协议。
- 否定确认。接收方用于告诉发送方某个分组未被正确地接收。否定确认报文通常携带着未被正确接收的分组的序号。
- 窗口、流水线。发送方也许被限制仅发送那些序号落在一个指定范围内的分组。通过允许一次发送多个分组但未被确认,发送方的利用率可在停等操作模式的基础上得到增加。窗口长度可根据接收方接收和缓存报文的能力、网络中的拥塞程度或两者情况来进行设置。
UDP
提供的运输层服务。
最低限度的运输层服务,只是做了运输协议能够做的最少工作,使用UDP即类似直接与IP协议交互:
- 检查报文段首部差错字段而提供完整性检查。
- 数据交付。
即用户数据报协议。
- 属于传输层通信协议。
- 基于
UDP
的应用层协议有TFTP
、SNMP
与DNS
。
特点
- UDP是无连接的。
- UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态(这里面有许多参数),因此可以支持更多的用户。
- UDP是面向报文的(对于应用程序传下来的报文不合并也不拆分,只是添加UDP首部)。
- 因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。
- UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如直播,实时视频会议等)。
- UDP支持一对一、一对多、多对一和多对多的交互通信。
- UDP的首部开销小,只有8个字节,比TCP的20个字节的首部要短。
为什么使用UDP
- UDP关于何时、发送什么数据控制更为精细。只要应用进程将数据传递给UDP,UDP就回将此数据打包金UDP报文并立即将其传递给网络层。
- UDP的效率更高,速度更快;而TCP还有拥堵,TCP依然需要确认目的主机真实收到了报文。
- 实时应用通常要求最小的发送速率,不希望过分地延迟报文段的传送,且能容忍一些数据丢失。
- UDP不引入连接握手,不引入时延。
- 无连接状态。不需要维护过多的状态参数,使用UDP的应用程序一般能支持更多的活跃客户。
- 首部字节开销小。
UDP 首部格式
首部字段只有8个字节,包括源端口、目的端口、长度、检验和。12字节的伪首部是为了计算检验和临时添加的。
- 端口号可以使得目的主机进行多路分解。
- 长度指示了在UDP报文中的字节数(首部加数据)。接收方使用检验和来检查报文段是否出现了差错。
UDP的差错检验:
由UDP检验和提供,发送方的UDP对报文段的所有16位比特字的和进行反码运算。求和时候遇到的溢出进行回卷(最高位换到最低位,低位继续向高位进),对求和得到的结果进行反码,并放在UDP报文段中的检验和字段。
因此在接受方的计算结果与检验和相加的结果应该是1111111111111111,如果有一个0,则出现了差错。
设计原因:端到端原则,某种功能必须基于端到端实现,在低级别设置功能可能是冗余的甚至完全没有价值的。即使网络中间进行了检测,但依然无法确保链路与路由器内存的可靠性。
TCP
即传输控制协议:
- 属于传输层通信协议。
- 基于
TCP
的应用层协议有HTTP
、SMTP
、FTP
、Telnet
和POP3
。
TCP有差错检测、重传、累积确认、定时器以及用于序号和确认号的首部字段。
特点
- TCP是面向连接的。(就好像打电话一样,通话前需要先拨号建立连接,通话结束后要挂机释放连接)。
- 每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的(一对一)。
- TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双方通信的数据。
- TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。
- 面向字节流。TCP中的“流”(Stream)指的是流入进程或从进程流出的字节序列。“面向字节流”的含义是:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。
- TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。
进程将数据发送到缓存当中,TCP在方便时候取出缓存数据发送。
TCP 首部格式
- 序号:用于对字节流进行编号,例如序号为301,表示第一个字节的编号为301,如果携带的数据长度为100字节,那么下一个报文段的序号应为401。
- TCP连接双方可以随机地选择初始序号,可以减少将那些仍在网络中存在的来自两台主机间先前已终止的连接的报文段,误认为是新建立连接所产生的有效报文段的可能性。
- TCP将数据看作是无结构的、有序的字节流。序号是建立在传送的字节流上的,而不是建立在传送的报文段的序列上。一个报文段的序号因此是该报文段首字节的字节流编号。
- 当主机A通过TCP连接向主机B传输一个数据流,A使用的TCP连接将隐式地对数据流中的每一个字节编号。假定有500000字节,MSS为1000,则数据流的首字节编号为0,第二个报文序号为1000。
- 确认号:期望收到的下一个报文段的序号。例如B正确收到A发送来的一个报文段,序号为501,携带的数据长度为200字节,因此B期望下一个报文段的序号为701,B发送给A的确认报文段中确认号就为701。
- TCP是全双工的,因此主机A向主机B发送数据的同时,也许会接收到来自B的数据。A填充进报文段的确认号是主机A期望从B收到的下一字节的序号。
- 数据偏移:指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。由于TCP选项字段的原因,TCP首部的长度是可变的,通常该值为空,即为20字节。
- 确认ACK:当ACK = 1时确认号字段有效,否则无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
- 同步SYN:在连接建立时用来同步序号。当SYN = 1,ACK = 0时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中SYN = 1,ACK = 1。
- 终止FIN:用来释放一个连接,当FIN = 1时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。
- 接收窗口:窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。
- 选项字段:用于发送方和接收方协商最大报文段长度MSS时,或在高速网络环境下用作窗口调节因子使用。
TCP 的三次握手
假设A为客户端,B为服务器端。
- 首先B处于LISTEN(监听)状态,等待客户的连接请求。
- A向B发送连接请求报文,SYN = 1,ACK = 0,选择一个初始的序号seq = x。
- 报文段不包含应用层数据,该特殊报文段也被称为SYN报文段。
- 随机选择初始序号也有一些出于避免一些安全性攻击的考虑。
- B收到连接请求报文,如果同意建立连接,则向A发送连接确认报文,SYN = 1,ACK = 1,确认号为x + 1,同时也选择一个初始的序号y。
- B此时会为该TCP连接分配TCP缓存和变量。但此设计使得TCP容易受到SYN洪泛的拒绝服务攻击。
- 该报文段也不包含应用层数据,有时也被称为SYNACK报文段。
- A收到B的连接确认报文后,还要向B发出确认,确认号为y + 1,序号为x + 1。
- A为该TCP连接分配缓存和变量。
- 此次报文段会负载客户到服务器的数据。
- B收到A的确认后,连接建立。
三次握手的原因
第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。
客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。
为什么要传回SYN
接收端传回发送端所发送的SYN是为了告诉发送端,我接收到的信息确实就是你所发送的信号了。
传了SYN,为啥还要传ACK
双方通信无误必须是两者互相发送信息都无误。传了SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要ACK信号来进行验证。
握手的核心目的
握手的核心目的是告知对方seq,对方回复ack(收到的seq+包的大小),这样发送端就知道有没有丢包了。
数据报
前两个报文段是不承载应用层数据的,而第三个报文段可以承载应用层数据。
TCP 流程
当建立了一条TCP连接,两个应用进程间就可以相互发送数据了。考虑客户进程向服务器进程发送数据的情况,客户进程通过套接字传递数据流,数据一旦通过该门,就由客户中运行的TCP控制了。
TCP将这些数据引导到该连接的发送缓存中,发送缓存是在3此握手初期设置的缓存之一,之后TCP会不时从发送缓存中取出一块数据,RFC规范描述为TCP应该在它方便的时候以报文段的形式发送数据。
TCP可从缓存中取出并放入报文段中的数据数量受限于最大报文段长度(MSS),MSS通常根据最初确定的由本地发送主机发送的最大链路层帧长度(最大传输单元,MTU)。设置该MSS要保证一个TCP报文段(当封装在一个IP数据报中)加上TCP/IP首部长度(一般40字节)将适合单个链路层帧。以太网和PPP链路层协议都具有1500字节的MTU,因此典型的MSS=1460。
TCP为每块客户数据配上一个TCP首部,从而形成多个TCP报文段。TCP连接的每一端都有各自的发送缓存和接收缓存。即在TCP连接的组成包括:
- 两台主机上各自的缓存、变量和进程连接的套接字。
TCP 的四次挥手
以下描述不讨论序号和确认号,因为序号和确认号的规则比较简单。并且不讨论ACK,因为ACK在连接建立之后都为1。
- A发送连接释放报文,FIN = 1。
- B收到之后发出确认,此时TCP属于半关闭状态,B能向A发送数据但是A不能向B发送数据。
- 当B不再需要连接时,发送连接释放报文,FIN = 1。
- A收到后发出确认,进入TIME-WAIT状态,等待2 * MSL(最大报文存活时间)后释放连接。
- B收到A的确认后释放连接。
四次挥手的原因
客户端发送了FIN连接释放报文之后,服务器收到了这个报文,就进入了CLOSE-WAIT状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送FIN连接释放报文。
因此,如果没有还未传送完的数据SYN + FIN一起发送,那就三次了
TIME_WAIT
客户端接收到服务器端的FIN报文后进入此状态,此时并不是直接进入CLOSED状态,还需要等待一个时间计时器设置的时间2 * MSL。这么做有两个理由:
- 确保最后一个确认报文能够到达。如果B没收到A发送来的确认报文,那么就会重新发送连接释放请求报文,A等待一段时间就是为了处理这种情况的发生。
- 等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文。
TCP 可靠传输
可靠数据传输:数据可以通过一条可靠的信道进行传输。借助于可靠信道,传输数据比特就不会受到损坏(由0 -> 1或相反)或丢失,而且所有数据都是按照其发送顺序进行交付。
底层通信是一个不靠谱的点对点通信,例如IP协议。TCP包含确认、定时器、重传、序号机制来保证可靠传输。
TCP提供一种面向连接的、可靠的字节流服务。其中,面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接。在一个TCP连接中,仅有两方进行彼此通信;而字节流服务意味着两个应用程序通过TCP链接交换8bit字节构成的字节流,TCP不在字节流中插入记录标识符。
对于可靠性,TCP通过以下方式进行保证:
数据包校验:目的是检测数据在传输过程中的任何变化,若校验出包有错,则丢弃报文段并且不给出响应,这时TCP发送数据端超时后会重发数据。
对失序数据包重排序:既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。TCP将对失序数据进行重新排序,然后才交给应用层;对于失序且缺失前面序号的数据,TCP将问题交给编程人员,可以保留(对于带宽而言更有效),也可以丢弃。
丢弃重复数据:对于重复数据,能够丢弃重复数据。
应答机制:当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。
超时重发:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
一个报文段从发送再到接收到确认所经过的时间称为往返时间RTT,大部分的TCP实现仅在某个时刻做一次RTT策略,而不是为每个报文段作一次测量,且仅为传输一次的报文段测量RTT。
由于RTT是波动的,每次进行一次新的测量后,则TCP会重新计算RTTS,加权平均往返时间RTTs计算如下:
其中,0 ≤ α < 1
,其参考值为0.125,RTTs随着α的增加更容易受到RTT的影响。
超时时间RTO应该略大于RTTs,否则将导致不必要的重传;并且超时间隔也不应该过大,否则当报文段丢失时,不能很快地重传该报文段导致数据传输时延较大。TCP使用的超时时间计算如下:
其中RTTd为偏差的加权平均值。
流量控制:TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据,这可以防止较快主机致使较慢主机的缓冲区溢出,这就是流量控制。TCP使用的流量控制协议是可变大小的滑动窗口协议。
累积确认:在收到失序的报文段,返回确认号字段为第一个失序号。
TCP 流量控制
流量控制是为了控制发送方发送速率,保证接收方来得及接收。
接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,提示接受方还有多少可用的缓存空间,从而影响发送方的发送速率。将窗口字段设置为0,则发送方不能发送数据。
TCP 滑动窗口
窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过TCP报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。
发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动窗口类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。
接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为{31,34,35},其中 {31} 按序到达,而 {34,35} 就不是,因此只对字节31进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。
TCP 拥塞控制
如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。
拥塞状态:链路容量有限,在较高的拥堵下,排队时延将呈现指数上升,并且可能陷入无限排队,爆掉缓存,吞吐量接近0。
路由器可以向发送方提供关于网络中拥塞状态的显式反馈信息(ATM ABR)。
UDP连接(恒定速率,不会降速)与web的并行TCP连接会使得网络拥塞,对于其他连接不公平。
判断拥塞的原则
一个丢失的报文段意味着拥塞。
一个确认报文段指示网络正在接受,即可增加速率。
带宽探测,给定ACK指示无拥塞,丢包则指示有拥塞。
TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。
发送方需要维护一个叫做拥塞窗口(cwnd,对一个TCP发送方能向网络中发送流量的速率做了限制)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。
为了便于讨论,做如下假设:
- 接收方有足够大的接收缓存,因此不会发生流量控制。
- 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。
1. 慢开始与拥塞避免(必然)
发送的最初执行慢开始,令cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将cwnd加倍,因此之后发送方能够发送的报文段数量为:2、4、8 …。
注意到慢开始每个轮次都将cwnd加倍,这样会让cwnd增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限ssthresh,当cwnd >= ssthresh时,进入拥塞避免,每个轮次只将cwnd + 1。
如果出现了超时,则令ssthresh = cwnd / 2,然后重新执行慢开始。
平均吞吐量:0.75W / RTT。
2. 快重传与快恢复(推荐部分)
在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到M1和M2,此时收到M4,应当发送对M2的确认。
在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个M2,则M3丢失,立即重传M3。
在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令ssthresh = cwnd / 2,cwnd = ssthresh,注意到此时直接进入拥塞避免。
慢开始和快恢复的快慢指的是cwnd的设定值,而不是cwnd的增长速率。慢开始cwnd设定为 1,而快恢复cwnd设定为ssthresh。