5.运输层
运输层
概述
物理层,数据链路层以及网络层共同解决了将主机通过异构网络互联起来所面临的问题,实现了主机到主机之间的通信
实际上在计算机网络中进行通信的实际是位于通信两段主机中的进程
运输层直接为应用进程间的逻辑通信提供服务
运输层向高层用户屏蔽了下面网络核心的细节,它使应用进程看见的就好像是在两个运输层实体之间有一条端到端的逻辑通信信道
根据应用需求不同,因特网的运输层为应用层提供了两种不同的运输协议,即面向连接的TCP和无连接的UDP,这两种协议是重点内容
端口号和复用和分用
为了使运行在不同操作系统的计算机进程之间能够进行网络通信,就必须使用统一的方法对TCP/IP体系的应用进程进行标识
TCP/IP体系的运输层采用端口号来区分应用层的不同应用进程
端口号使用16比特表示,取值范围0~65535
- 熟知端口号:0~1023,IANA把这些端口号指派给了TCP/IP体系中最重要的一些应用协议,例如FTP使用21/20,HTTP使用80
- 登记端口号:1024~49151,为没有熟知端口号的应用程序使用,使用这类端口号必须在IANA按照规定登记,防止重复
- 短暂端口号:49152~65535,为客户进程选择暂时使用,当服务器进程收到客户进程的报文时,就知道客户进程所使用的动态端口号。通信结束后,这个端口号可供其他客户进程以后使用
端口号只具有本地意义,即端口号只是为了标识本计算机应用层中各进程,在因特网中,不同计算机中的相同端口号没有联系
发送方的某些应用报文在运输层使用UDP协议进行封装,这叫做UDP复用,而另一些应用报文,使用TCP协议封装,叫做TCP复用,运输层使用端口号来区分进程
无论是UDP的用户数据保,还是TCP的报文,在网络层要通过IP协议封装,这叫做IP复用
接收方的网络层收到了IP数据报后进行IP分用,根据协议字段的取值来上交给运输层的UDP和TCP协议,然后TCP和UDP协议通过解析端口号将数据上交给不同的应用
TCP/IP体系的应用层常用协议所使用的运输层熟知端口号
这里的端口号指的是服务器上的端口号,比如:HTTP协议的端口号为80的意义是 服务器的端口号为80,客户端访问服务器时一般使用临时的端口号
UDP协议
Internet协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol,UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法
UDP只在IP数据报服务上增加了很少功能,即复用分用和差错检测功能
首部格式
UDP长度指的是数据报的整个长度,UDP校验和检测整个UDP数据报是否有错,错就丢弃
分用时,如果找不到对应的目的端号,就丢弃报文,并给发送方发送ICMP“端点不可达”差错报文
检验过程
UDP数据报的首部会添加一个“伪首部”以校验
伪首部只有在计算校验和时才出现,不向下传送也不向上递交
校验和是可选的
TCP协议
首部格式
为了实现可靠运输,TCP采用了面向字节流的方式
但TCP在发送数据时,是从发送缓存取出一部分或全部字节并给其添加一个首部使之成为TCP报文段进行发送
- 一个TCP报文段由首部和数据载荷两部分构成
- TCP的全部功能都体现在它首部中各字段的作用
源端口:占16比特,写入源端口号,用来标识发送该TCP报文段的应用进程
目的端口:占16比特,写入目的端口号,用来表示接收该TCP报文段的应用进程
序号:占32个比特,取值范围$[0,2^{32}-1]$,序号增加到最后一个后,下一个序号就又回到0
序号指出本TCP报文段数据载荷第一个字节的序号
确认号:占32比特,取值范围$[0,2^{32}-1]$,确认号增加到最后一个后,下一个确认号就又回到0
指期望收到对方下一个TCP报文段的数据载荷的第一个字节的序号,同时也是对之前收到的所有数据的确认
若确认号=n,则表明到序号n-1为止的所有数据都已正确接收,期望接收序号为n的数据
确认标志位ACK:取值为1时,确认号字段才有效,取值为0时,确认号字段无效
TCP规定,在连接建立后所有传送的TCP报文段都必须把ACK置1
数据偏移:占4比特,并以4字节为单位
用来指出TCP报文段的数据载荷部分的起始处距离TCP报文段的起始处有多远,这个字段实际上是指出了TCP报文段的首部长度
首部的固定长度为20字节,最大长度为60字节,若不是4的倍数,则填充到4的倍数
窗口:占16比特,以字节为单位,指出发送本报文段的一方的接收窗口
窗口值作为接收方让发送方设置其发送窗口的依据,以接收方的接受能力来控制发送能力,称为流量控制
- 同步标志位SYN:在TCP连接建立时用来同步序号
- 终止标志位FIN:用来释放TCP连接
- 复位标志位RST:用来复位TCP连接,当RST=1时,表明TCP连接出现了异常,必须释放连接,然后重新连接
- 推动标志位PSH:接收方的TCP收到该标志位为1的报文段会尽快上交应用进程,而不必等到接收缓存都填满后再向上交付
- 紧急标志位URG:取值为1时紧急指针字段有效,取值为0时紧急指针字段无效
紧急指针:占16比特,以字节为单位,用来指明紧急数据的长度
当发送方有紧急数据时,可将紧急数据插队到发送缓存的最前面,并立刻封装到一个TCP报文段中发送、紧急指针会指出本报文数据载荷部分包含了多长的紧急数据
校验和:占16比特,检查范围包括TCP报文段的首部和数据载荷两部分
在计算校验和时,要在TCP报文段的前面加上12字节的伪首部
扩展部分扩展了TCP报文的功能
- 最大报文段MSS选项:TCP报文段数据载荷的最大长度
- 窗口扩大选项:为了扩大窗口
- 时间戳选项:用来计算往返时间RTT,用来处理序号超范围的情况,又称为防止序号绕回PAWS
填充:由于选项的长度可变,因此使用填充来确保报文段首部能被4整除
TCP和UDP的对比
名称 | 用户数据报协议UDP(User Datagram Protocol) | 传输控制协议TCP(Transmission Control Protocol) |
---|---|---|
连接方式 | 无连接 | 有连接 |
传输方式 | 支持单播,多播以及广播 | 只支持单播 |
面向对象 | 面向应用报文 | 面向字节流 |
服务类型 | 不可靠服务 | 可靠服务 |
首部类型 | 仅8字节 | 最小20字节,最大60字节 |
校验和 | 可选 | 必需 |
流量控制
流量控制(flow control)就是让发送方的发送速率不要太快,让接收方来得及接收,采用流量控制以同步设备速度以便发送数据
TCP采用滑动窗口机制来很方便的在TCP连接上实现对发送方的流量控制
TCP接收方利用自己的接收窗口的大小来限制发送方发送窗口的大小
接收方可以在确认报文的时候调整接收窗口的大小,从而限制发送方发送窗口的大小,其他的协议细节,与选择重传协议类似
考虑这样一种情况
此时A会一直等待B发送的非0窗口的通知,B也一直等待A发送的数据,若不采取措施,这种互相等待而形成的死锁局面会一直持续下去
为了解决这种情况,当发送方的发送窗口被调控到0的时候,就会启动一个持续计时器,若持续计时器超时,就发送一个零窗口探测报文,仅携带一个字节的数据,而对方在确认这个探测报文段时,给出自己的接收窗口值,如果接受窗口值仍然是0,那么收到这个报文段的一段就会重新启动持续计时器。
拥塞控制
在某些阶段,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这种情况就叫做拥塞(congestion)
若出现拥塞而不进行控制,整个网络的吞吐量将随输入负荷的增大而下降
TCP有4种拥塞控制算法
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
发送方要维护一个叫做拥塞窗口cwnd的状态变量,其值取决于网络的拥塞程度,并且动态变化
- 拥塞窗口cwnd的维护原则:只要网络没有出现拥塞,拥塞窗口就在增大一些,但只要网络出现拥塞,拥塞窗口就减少一些
- 判断出现网络拥塞的依据:没有按时收到应当到达的确认报文(发生超时重传)
发送方将拥塞窗口作为发送窗口swnd,即swnd=cwnd
发送窗口=MIN[拥塞窗口,接收窗口]
维护一个慢开始门限ssthresh(slow start threshold)状态变量:
- 当cwnd<ssthresh时,使用慢开始算法
- 当cwnd>ssthresh时,停止使用慢开始算法而改用拥塞避免算法
- 当cwnd=ssthresh时,既可以使用慢开始算法,也可以使用拥塞避免算法
慢开始算法
开始设定cwnd=1,ssthresh=16
当cwnd没有到ssthresh时,cwnd呈指数增长,这就是慢开始算法
拥塞避免
当cwnd超过了ssthresh时,开始使用拥塞避免算法,此时cwnd不再指数增长,而是每次加1,直到出现拥塞情况
当重传计时器超时,则判断网络很可能出现了拥塞,进行以下工作:
- 将ssthresh值更新为发生拥塞cwnd值的一半
- 将cwnd减少为1,并重新开始执行慢开始算法
- 慢开始是指一开始向网络注入的报文段少,并不是指拥塞窗口cwnd增长速度慢
- 拥塞避免并非完全能够避免拥塞,而是指在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞
达到慢开始门限后,每次都增加1个窗口,这和拥塞没有关系
快重传
有时候个别报文段会在网络中丢失,但实际上网络并未发生拥塞,这会导致发送方的超时重传,并误认为网络发送了拥塞,这样会启动慢开始算法,降低效率
采用快重传算法可以让发送方尽早知道发生了个别报文段的丢失
所谓快重传,就是使发送方尽快进行重传,而不是等超时重传计时器超时在重传
- 要求接收方不要等待自己发送数据时才进行捎带确认,而是立即发送确认
- 即使收到了失序的报文段也要立即发出对已收到报文段的重复确认
- 发送方一旦收到了3个连续的重复确认,就将相应的报文段立即重传,而不是等待超时重传计时器
快恢复
发送方一旦收到了3个重复确认,就是现在只是丢失了个别的报文段,于是不启动慢开始算法,而执行快恢复算法
- 发送方将慢开始门限ssthresh值和拥塞窗口cwnd值调整为当前窗口的一半,开始执行拥塞避免算法
超时重传时间的选择
TCP超时重传的时间的选择是一个很复杂的问题
如果将重传时间RTO的值设置的比RTT0小,那么会导致不必要的重传,使网络负荷增大
如果将重传时间RTO的值设置的比RTT0大,那么会导致网络的空闲时间增大,降低传输效率
所以超时重传时间RTO的值应略大于往返时间RTT
问题在于RTT会随着不同网络环境而发生变化,所以不能使用某次测量得到的RTT样本来计算超时重传时间RTO
利用每次测量得到的RTT样本,计算加权平均往返时间$RTT_s$(又称为平滑的往返时间)
$RTT_{S1}=RTT_1$
新的$RTT_S=(1-α)×旧的RTT_S+α×新的RTT样本$
在上式中,$0≤α<1$
若α接近0,则新RTT样本对$RTT_s$的影响不大
若α接近1,则新RTT样本对$RTT_s$的影响较大
RFC6298推荐的α值为1/8,即0.125
可靠传输的实现
TCP基于以字节为单位的滑动窗口来实现可靠传输
接收方会发送自己的接受窗口尺寸以及希望收到下一个字节的序号
如果要描述发送窗口的状态,可以使用3个指针P1,P2,P3分别指向相应的字节序号
虽然发送方的发送窗口是根据接收窗口设置的,但在同一时刻,发送方的发送窗口并不总是和接收方的接收窗口一样大
- 网络传送窗口需要经历一定的时间滞后,并且时间不确定
- 发送方还可能根据网络当时的拥塞情况适当减少自己的发送窗口尺寸
对于不按序到达的数据应如何处理,TCP并无明确规定
- 如果接收方把不按序到达的数据一律丢弃,那么接收窗口管理比较简单,但是会浪费网络资源
- TCP通常对不按序到达的数据临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程
TCP要求接收方必须有累计确认和捎带确认机制,这样可以减少传输开销
- 接收方不应过分推迟发送确认,否则会导致不必要的超时重传
- 捎带确认实际上不会经常发生,因为大多数应用程序很少同时在两个方向上发送数据
TCP的通信是全双工通信,通信中的每一行都在发送和接受报文段,因此,每一行都有自己的发送窗口和接收窗口
连接建立
TCP是面向连接的协议,基于运输连接来传送TCP报文段
TCP运输连接有以下3个阶段
- 建立TCP连接
- 数据传送
- 释放TCP连接
TCP使用“三报文握手”建立连接
一开始,两端TCP进程都属于关闭状态
TCP服务器进程首先开始建立传输控制块,用来存储一些重要信息
之后TCP服务器开始进入监听状态,等待TCP客户发起连接,由于TCP服务器是等待客户建立连接,所以是被动打开
然后TCP客户同样建立传输控制块,然后向TCP服务器发送TCP连接请求报文段,并进入同步已发送状态,报文段首部中同部位SYN被设置为1,序号字段seq设置为x
当TCP服务器接收到报文后,则向TCP客户进程发送TCP连接请求确认报文段,并进入同步已接收状态
报文中的SYN和ACK都设置为1,表示这是一个TCP连接请求确认报文段,序号字段seq被设置了一个初始值y,作为TCP服务器进程所选择的初始序号,ack=x+1,这是对TCP客户进程所选择的初始序号的确认
TCP客户进程在收到TCP连接请求确认报文段后,还要向TCP服务器发送一个普通的TCP确认报文段,并进入连接已建立状态,ACK=1,表示这是普通的确认报文段,seq=x+1,因为第一个报文段的序号为x,ack=y+1,是对TCP服务器进程所选择的初始序号的确认
三次挥手中的第三次挥手,已经可以传递信息了
TCP采取三握手连接是为了防止已失效的连接请求报文段突然又传送到了TCP服务器,因而导致错误
连接释放
TCP通过“四报文挥手”来释放报文段
如果是TCP客户想要发送释放TCP连接,则会发送TCP连接释放报文段,并进入终止等待1状态,该报文段中的FIN和ACK都会设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认,序号seq报文段的值设置为u,等于TCP客户进程之前已传送过的数据的最后一个字节序号加1,ack=v,等于TCP客户之前收到的数据的最后一个字节加1
此时的报文段虽然Seq为u,但是并不携带任何数据
TCP服务器在收到TCP连接释放报文段后,会发送一个普通的TCP确认报文段并进入关闭等待状态,ACK=1表示这是一个普通的TCP确认报文,seq=v,等于TCP服务器进程之前已传送过的数据的最后一个字节,ack=u+1,是对TCP连接释放报文段的确认
TCP服务进程此时会通知高层进程,TCP客户进程要断开与自己的TCP连接,此时,从TCP客户进程到TCP服务器进程这个方向上的连接就释放了,此时TCP连接属于半关闭状态,因为TCP客户已经没有数据要发送了,但TCP服务器进程如果还有数据要发送,TCP客户进程仍要接收
这个状态可能要持续一段时间,当TCP客户收到了TCP确认报文段后,会进入终止等待2状态
当TCP服务器没有数据要发送了,应用就会通知服务器释放连接
TCP服务器进程发送TCP连接释放报文段并进入最后确认状态,报文段中的FIN和ACK设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认,假定seq=w,这是在半关闭状态下,TCP服务器进程可能又发送了一些数据,确认号ack的值为u+1,这是对之前收到的TCP连接释放报文段的重复确认
TCP客户进程收到了TCP连接释放报文段后,必须针对该报文段发送普通的TCP确认报文段,之后进入时间等待状态,ACK=1,表示这是不同的TCP确认报文段,seq的值为u+1,确认号ack的值设置为w+1,是对收到的TCP连接释放报文段的确认
TCP服务器进程在收到后就进入关闭状态,而TCP客户进程还要经过2MSL后才能进入关闭状态
MSL(Maximum Segment Lifetime)意思是最长报文段寿命
设置2MSL的意义在于,加入最后TCP客户发送的普通的TCP确认报文段丢失了,TCP服务器发送的TCP连接释放报文段会超时重传,如果不设置2MSL,那么TCP客户关闭后,就无法接受到超时重传的消息,就会导致TCP服务器一直在最后确认阶段
如果TCP双方在建立连接后,TCP客户方突然出现了故障,显然,TCP服务器就不会再收到TCP客户的数据,应该有措施让TCP服务器进程不要再白白等待下去
为了避免这种情况,TCP服务器每收到一次TCP客户进程的数据,就重新设置并启动保活计时器
若保活计时器定时周期内未收到TCP客户进程发来的数据,则当保活计时器到时候,TCP服务器进程就向TCP客户进程发送一个探测报文段,以后每隔75秒发送一次探测报文段,若一连发送10个探测报文段仍无TCP客户进程的响应,TCP服务器进程就会认为TCP客户端进程故障,则会关闭这个连接