运输层
课程笔记
图源谢希仁版计算机网络教材
2022-3-29
网络通信实际上是进程之间的通信,网络数据通过网络层发送到目标的主机后,需要准确发送到目标程序。因为通信是由程序请求和接收的,这就需要通过端口来判断是目的是哪个进程。
运输层的主要内容就是
在这里简单介绍运输层的内容,包括协议内容和端口。
运输层有两个重要的功能,第一个是实现进程间通信,另一个是进行差错检测。
如图能看到,网络之间通信最终的端点是应用程序,最后通过运输层传输到应用层。
另一部分,在网络层,IP数据报只对包头进行检测,而不会检测数据报的内容本身,最终是在运输层实现可靠运输。
这就涉及到运输层两个重要协议:UDP和TCP。
UDP(用户数据报协议)是无连接的协议,双方通信不需要建立连接。而TCP(传输控制协议)是面向连接的,需要建立连接才能通信。
面向连接可以实现可靠传输,同时也增加的许多开销,像确认,流量控制,计时器,连接管理等,增加了包头大小
应用程序的端口号用16为存储,能记录65535个端口号。
端口号分两类,一种是服务器端使用的,其中也分为两类,一种是熟知端口号或叫系统端口号,数值为0-1023。
另一种是客户端使用的端口号,这类端口是客户进程在运行时临时使用的端口号
UDP协议特别简单,仅仅找IP数据报服务上增加了差错检测的功能。
1)无连接。双方不需要建立连接,因此减少了开销和发送的时延、
2)尽最大努力交付。不保证可靠交付,因此计算机不需要维持复杂的连接状态表。
3)面向报文。
4)没有拥塞控制。UDP不会因为网络拥塞而控制流量,这对于实时应用(视频、语音等)很重要,不会导致太大的时延,并且允许丢失部分数据
5)支持多对多交互通信。
6)首部开销小。
当从IP层收到UDP数据报时,根据首部中的端口,将UDP数据报通过端口发送到相应的进程中。
伪首部是用来进行差错检测时临时添加的内容。UDP进行首部检验和时,临时添加伪首部,内容不向上或者向下传递。
TCP协议相对UDP比较复杂,因为它是提供可靠连接的协议,所以设计比较复杂
1)面向连接。应用程序使用TCP协议进行通信的时候,需要建立连接,使用套接字(socket)。
2)一对一。
3)提供可靠的交付服务。
4)全双工通信。通信双方都有一个缓存空间。
5)面向字节流。
序号:是该字节流的顺序编号,指的是本报文段发送数据字节的第一个字节的序号
确认号:期望收到对方下一个报文的第一个数据字节的序号,意味着确认号之前的数据都已经正确收到。这个是由接收方给发送方发送确认报文的时候设置的
数据偏移:当前报文段数据起始距离TCP整个报文段起始处的距离,可以推算TCP报文的首部长度。
紧急 URG:紧急指针。有紧急数据需要传输。发送方将紧急数据插入报文数据段的最前面。配合紧急指针使用
确认 ACK:建立连接后,ACK置1
推送 PSH:两个进程进行交互式通信时,一段进程希望键入某个命令后立即得到相应。此时可以使用PSH操作,对方收到后,尽快交付,不必等到缓存满后再交付
复位 RST:置1时表示发生严重错误,需要重启连接。
同步 SYN:当ACK为0,SYN为1时,表明这是一个请求连接报文,若同意则返回报文中置SYN=1和ACK=1。总而言之,该字段为1时,表面这是连接建立报文。
终止 FIN:用于释放连接
窗口:指报文段的接收窗口。接收方告诉发送方,从本确认号开始,目前允许发送的数据量
检验和:检验和字段
紧急指针:紧急数据的字节数
选项:最初只规定了MSS一种选项,用来设置最大报文段长度。因为当TCP搭载的数据特别小时,每次发送TCP报文都要添加上各层的首部,会导致较多资源浪费,当当搭载数据过多时,IP可能要分成许多子段,可能导致各种错误,耗费时间,所以发送方和接收方之间互相调整这个MSS
后面慢慢加入了窗口扩大,时间戳,选择确认等选项
TCP协议最大的特点是提供可靠传输,这里简单介绍可靠性传输的实现原理。
想要实现可靠传输,有两个重要特点
下面停止等待和滑动窗口都具有同样的功能,停止等待比较简单,滑动窗口更常用。
一句话来说,停止等待就是:发送方发送一个数据后等待,当接收方正确接收后再发送。
以下用几种情况来描述
图a就是最简单的无差错情况,发送方正确发送报文M1后,接收方正确接收后发送一个确认报文,发送方收到这个确认报文后再发送下一个报文M2。
图b就是出现差错的情况。当发送方超过一段时间没有接收到接收方发送的确认报文,则重新发送。
上图b就是出现差错的情况。报文未送达或者未正确送达,接收方都不会发送确认报文给发送方。发送方设置一个超时计时器,超过一定时间就重新发送报文。
为能够正确实现该功能,要需要做几点工作
①:发送方在发送一个分组后,还需要保留已发送的分组的副本。这样才能够正确重传数据
②:分组和确认分组都需要正确编号,才能确定是哪个分组发送错误(但是上面描述是收到上一个报文确认后才发送新报文)
③:重传时间应该长于平均往返时间。
上图a的情况,发送的确认报文丢失了,这时候发送方需要重新发送报文M1,此时接收方会收到一个重复的报文。
接收方需要做两个事情,一个是丢弃该报文,第二个是重新发送一遍确认报文。
图b的情况是发送方超时接收到了接收方发来的确认报文。发送方只需要丢弃即可。
通过上面的分析,我们就做到在不可靠的传输网络中进行可靠的通信。这个传输协议称之为自动重传请求ARQ。
停止等待协议的简单,并且有效,但是信道利用率太低了。
看图,整个信道大部分时间都在RTT的过程中,大部分时间都浪费在往返等待。倘若我们使用流水线传输就可以提高信道利用率,如下图。后面介绍的滑动窗口协议就是连续ARQ协议。
即连续ARP协议。滑动窗口协议就比较复杂,同时也是TCP协议的精髓,要好好理解学习。但是这里仅仅进行简单的介绍,后续将TCP实现可靠性的时候再具体展开介绍。
如上图,发送方维持一个发送窗口,发送窗口内的分组能够连续发送出去,不必等待确认。当收到第一个分组的确认的时,滑动窗口向右移动。
而接收方则采用累计确认的方式,成功收到几个分组后,对最后一个收到的分组进行确认,意味着前面的分组都正确送达了。
这里介绍TCP的可靠性实现,实际上就是连续ARQ协议的讲解。先介绍滑动窗口的实现,然后是超时重传的内容,然后是选择与确认。
如图,一目了然…
接收方发送确认号后,发送方的发送窗口根据接收方发来的发送窗口进行调整,前移,窗口内的数据能够发送出去,不能发送窗口外面的数据。
等下一次接收方发送确认报文后,再移动发送窗口。
要注意的是,发送窗口的前沿TCP协议强烈不建议进行收缩。因为收缩前可能发送方已经把数据发送出去了,会造成一些错误和麻烦。
我们可以观察一下实际的发送状态
我们需要三个指针来维护发送窗口,一个用来指定窗口前沿,一个指定后沿,一个指定可用窗口。
我们介绍到,TCP发送方和接收方都有一个缓存
我们看到,在发送方,已经发送了的端口窗口和缓存是重合的,但是后面未发送的是不重合的。因为我们要留下一个缓存空间,便于窗口的滑动。
接收方则相反。
最后我们需要强调,发送方和接收方的窗口不一定是相同的,因为发送的窗口是根据接收方的接受窗口来设定的,网络发送过程中可能存在延迟。
并且发送方会根据网络拥塞情况来调整发送窗口大小。
重传时间如何选择,是TCP协议最复杂的问题之一。重传时间不能一成不变, 因为网络环境一直在变化,过长或过短都会有不同的弊端。
TCP设计了一种自适应算法。记录一个报文段发出的时间,以及其确认的时间。来确认一个报文段的往返时间RTT。TCP保留一个RTTs,称为平滑的往返时间。RTTs是加权平均,所以值更加平滑。
从公式能看到,倘若参数α接近零,则RRT与旧的RTTs比较相近,和RTT相关不大;反之α接近1,则RTTs主要取决于RTT。
标准建议α应该取值未0.125即1/8.
然后超时重传时间RTO应该略大于计算出来的RTTs
RTTd与RTTs和新的RTT样本的差值有关,标准建议如下图计算RTTd
其中β建议为0.25即1/4。
但是有一个重要的问题,假如发送方超过重传时间后,重新传输了报文,后面接收到的确认,如何确认该确认报文是对哪一个发送报文的确认?这和计算RTTs密切相关。
而后,提出了一个算法,计算RTTs时候,不采用重传报文段的RTT样本。
但是倘若当前时延增大,大量出现重传情况,算法就不适合在这个情况使用了。而后进行了一定修改,报文段重传后,把RTO增大一些,设置为原来的两倍。
实际上不怎么用
流量最主要是管理发送方的流量,正常我们希望数据发送的越快越好。但是发送得太快会导致接收方来不及接收,会导致大量的数据丢失。
流量控制,就是控制发送方的发送速率不要太快,让接收方来得及接收。
过程中,不断调整接收窗口的值,当满了之后,设置为0,不允许再发送数据,发送方需要等重新调整窗口后才能继续发送数据。但是有一个问题,倘若发送方发送的调整窗口值未成功发送,接收方一直在等待,就会发生死锁。
为了避免这种情况,需要在接收方设置一个持续计时器,当接收方收到零窗口通知,就开始计时,到达一定时间后,发送一个零窗口探测报文段,接收方确认后返回现在的窗口值。
网络道路就和马路一样,车辆过多或者出现什么事故容易导致塞车。
如何解决拥塞?可以参考交通是如何处理的。把马路加宽,或者减少车辆出行,还有就是当发生事故的时候,合理处理,不要堵塞后面的车辆。
下面介绍下TCP中,拥塞控制,流量控制的方法。
慢开始、拥塞避免、快重传、快恢复。
TCP的拥塞控制基于窗口。
原理是:当网络没有拥塞,发送方的窗口可以增大一些,当发生拥塞,就减少一些。
如何知道网络发生拥塞?很简单,判断是否出现超时。
而这个拥塞窗口是如何变化?那就是慢开始方法。
发送窗口先从比较小的值开始,逐渐增大,如果没有超时,则增加一倍。
但是我们会发现,这个所谓慢开始,是指从很小的值开始,实际上增长的非常快,为了避免慢开始窗口(cwnd)增长过快导致网络拥塞,还有设置一个慢开始门限ssthresh。
拥塞避免算法原理是让cwnd慢慢增加,每经过一次往返就增加1,不是慢开始算法那样增加一倍。
某些时候,可能是发送过程中,发丢了,实际上并没有出现拥塞,但是出现超市,被误以为拥塞的话,拥塞窗口修改为1,就会大大降低传输速率。因此要让发送方尽快知道什么报文丢失,赶紧发送确认,不要等那么久。当出现丢失,接收方一直发送丢失前的数据报文的确认,到收到重复确认就知道发生丢失,就重传。
当发送方知道只是丢失了部分报文,并非超时,所以不启动慢开始,不将cwnd设为1,而是把门限设置为原来的一半,并把cwnd设置为拥塞门限的值,然后开始拥塞避免算法。
TCP提供可靠的连接,是面向连接。因此,需要一个建立连接的过程
主要解决三个问题
建立连接的过程叫做握手,握手需要在客户端和服务端之间交换三个报文段,也叫三次握手。
第一次握手,客户端发送一个报文,同步位SYN设1,设置一个初始序号seq为x;
第二次握手,当服务端同意后,发送一个报文,同步位SYN为1,确认位ACK也设1,并返回ack确认号(确认收到哪个报文),并自己设置一个序号seq为y;
第三次握手,客户端要给服务端发送一个确认报文,确认位ACK为1,seq序列为x+1,确认号ack为y+1
自此,连接建立完成。
问题来了,为什么要进行第三次握手呢?我们知道网络环境空间很复杂,倘若有一段请求连接未成功发送,超时又发送了一遍,成功建立连接并释放后,该请求发送过去,可能导致异常连接,服务端发送确认同步报文给客户端。
这种情况,客户端不会理他,因为自己根本就没发送请求连接,但是它也不通知服务端,服务端就傻傻的在等待…
因此服务端还需求确认客户端是否确认连接。
当数据发送完毕,就要释放链接。
客户端提出结束请求,发送报文设结束位FIN为1,序列seq为u;
服务器收到该报文后,发送报文确认号ACK为1,序列seq为v,确认号ack为u+1;
这个时候就处于半关闭状态,这时候客户机没有数据要发送,但是可能服务器有数据发送,这时候客户器是要接收的。
当服务器没有数据发送后,发送报文,结束号FIN为1,确认号ACK为1,序列seq为w,确认号ack为u+1,通知释放连接。
然后客户机就发送确认报文,链接正式释放。