文章目录
UDP
UDP为调用它的应用程序提供的是不可靠,无连接的服务。这里的不可靠,表示UDP不能保证一个进程发送的数据能够完整无缺,并且无失序地到达目的进程。
UDP是一种无连接的运输层协议,因为在使用UDP时,在发送报文段之前,发送方和接收方的运输层实体之间没有进行握手。所谓的握手,就是发送方和接收方通过发送一些特定的报文段来互相确认,从而为发送做准备.
UDP的报文段结构
UDP检验和:
TCP
TCP是一个可靠的、面向连接的基于字节流的运输层协议。
TCP报文段结构
TCP的首部长度为20个字节,和UDP的首部长度有区别,UDP的首部长度为8个字节,所以UDP分组的首部长度开销更小,这是TCP和UDP的区别之一。
面向连接
TCP是一种面向连接的运输层协议,因为在使用TCP时,在发送报文段之前,发送方和接收方的运输层实体之间需要进行三次握手建立连接。
TCP三次握手建立连接
- 第一次握手:客户端首先发送一个特殊地TCP字段(SYN字段)
- 第二次握手:服务器向客户端发送一个特殊字段(SYN+ACK字段)。
- 第三次握手:客户端向服务端发送一个特殊字段(ACK字段)。
为什么需要经过三次握手建立连接之后才可以进行数据传输,两次握手为什么不可以?四次握手呢?
我们将从三个方面进行分析三次握手的原因:
- 三次握手才可以阻止重复历史连接的初始化(主要原因)
- 三次握手才可以同步双方的初始序列号
- 三次握手才可以避免资源浪费
原因一:避免历史连接
因此在客户端连续发送多次SYN建立连接(例如上图中在第一次发送序号为90的SYN报文建立的连接释放之后,再次请求建立连接)的报文,在网络拥塞的情况下:
- 一个旧SYN报文比最新的SYN报文到达服务端
- 服务端会发送响应报文给客户端
- 客户端会根据自身的上下文,从而判断出这是一个历史连接(序列号过期或者超时),那么客户端就会发送RST报文给服务端,表示终止这一次连接。
如果是两次握手建立连接,无法判断当前连接是否时历史连接,三次握手则可以在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前的连接是否是历史连接:
- 如果是历史连接(序号过期或者超时),那么客户端就会发送RST报文,中止历史连接
- 否则,第三次发送的是ACK报文,通信双方建立来连接。
所以,TCP使用三次握手建立连接的最主要原因是防止历史连接初始化了连接。
原因二:同步双方的初始序列号
在说明原因之前,我们先明白序列号是可靠传输的一个关键因素,它的重要作用:
①接收方判断接收到的分组是一个重复分组(即之前已经接收过这个分组了),如果是重复分组,就丢弃这个分组,直接发送这个分组的ACK分组
②接收方可以根据数据包的序列号按序接收,这个可以在停等协议、回退N步协议、选择重传协议等中有重要的体现。
③可以标识发送出去的数据包,哪些已经被对方收到了,例如在回退N步协议中,发送方接收到序号为K的ACK分组,表示K及之前的分组已经被接收方正确接受了
可见,序列号在TCP连接中有重要的作用,所以当客户端发送携带初始序列号的SYN报文时,需要服务端回应一个ACK报文,标识客户端的SYN报文被服务端成功接收,当服务端发送 初始序列号的的ACK报文给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能保证双方的初始序列号被可靠的同步。
四次握手其实也能够可靠的同步双方的初始序号,(只是将服务端发送的SYN + ACK报文才成两个报文:ACK报文(用于回应客户端已经成功接收了SYN报文)、SYN报文,所以将这两个报文合并起来,就成了三次握手。
而两次握手只能保证一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被正确接收。
原因三:避免资源浪费
如果只有两次握手,那么当客户端发送的的SYN报文1在网络中阻塞,那么客户端因为超时重传SYN报文2,此时服务端接收到客户端发送的SYN请求之后,就会发送ACK报文给客户端,然后进入ESTABLISHED状态,当客户端接收到了ACK报文之后,同样进入ESTABLISHED状态。但是在进行数据传输之前,先前发送的阻塞的的SYN1报文到达了服务端,由于是两次握手,所以服务端收到请求后建立冗余的连接,浪费资源。
因此如果是两次握手建立连接的话,就会导致建立冗余连接或者使服务端一直处于ESTABLISHED状态并不断重传SYN + ACK报文,从而浪费资源,所以不可以使两次握手建立连接。
TCP四次挥手释放连接
TCP报文在数据传输完毕之后,同样需要断开连接,这时候需要通过四次挥手释放连接。
为什么,客户端向服务端发送ACK报文之后不直接进入到CLOSED状态,而是进入TIME_WAIT状态,并等待2MSL时间之后才进入CLOSED状态?
①假设客户端在接收到了FIN报文,向服务端发送ACK报文之后进入CLOSED状态,那么如果客户端向服务端发送ACK报文在中途中丢失了,这时候服务端会因为超时(一直没有接收到发送方的ACK报文)重传FIN报文,由于客户端已经处于CLOSED状态,那么不会接收这个重传的FIN报文,因此导致服务端一直处于LAST_ACK状态而无法断开连接。因此客户端接收到服务端的ACK报文,向服务端发送ACK报文之后进入的是TIME_WAIT状态,而不是CLOSED状态。
②MSL是Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间的报文将被丢弃。因为TCP报文基于IP协议的,而IP头中有一个TTL字段,是IP数据包可以经过的最大路由数,每经过一个处理它的路由器此值就减1,当此值为0时则数据报将被丢弃,同时发送ICMP报文通知源主机。
MSL 与 TTL的区别:MSL的单位是时间,而TTL是经过的路由数,所以MSL应该要大于等于TTL消耗为0的时间,以确保报文已被自然消亡。
TIME_WAIT等待2MSL时间,是因为被动关闭连接(在这里是服务端)没有接收断开连接的ACK报文,就会触发超时重传FIN报文,当另一方接收到FIN后,会重传ACK给被动关闭方,一来一回刚好是2个MSL。
2MSL的时间:从客户端接收到FIN报文之后向服务端发送ACK报文开始计时的。如果在TIME_WAIT时间内,因为客户端的ACK没有被接收到,那么客户端再次接收到服务端重传的FIN报文,此时2MSL时间将重新计时。
可靠传输
因特往的网络层服务(IP服务)是不可靠的。IP不保证数据包的交付,不保证数据包的按序交付,也不保证数据报中数据的完整性。
但是TCP是一个可靠的运输层协议,它在IP不可靠的尽力而为服务之上创建一中可靠数据传输服务。TCP的可靠数据传输服务确保一个进程从其接收缓存中读出的数据流是一个无损坏、无间隙、非冗余和按序的数据流,即该字节流与连接的另一方端系统发送的字节流完全相同。
构造可靠数据传输协议
经完全可靠信道的可靠数据传输: rdt 1.0
经具有比特差错信道的可靠数据传输rdt 2.0
在分析rdt 2.0的FSM之前,我们首先需要弄清自动重传请求协议。很明显,一旦接收端接收到了来自客户端的分组之后,如果这个分组出现了比特差错(接收端通过差错检测得出的),那么就会向发送方发送一个否顶确认NAK,表示没有正确接收分组,之后发送方就会重传分组;否则,接收方接收到的分组是正确的,就会给发送方发送一个正确确认ACK,之后发送方就可以新发一个分组。
rdt 2.0 对应的FSM,该数据传输协议采用了差错检测,肯定确认与否顶确认:
值得注意的是,当发送方处于等待接收方发送的ACK/NAK状态的时候,它不能从上层获取新的数据,这也就是说,rdt_send()事件不会出现,除非在发送方接收到了ACK并离开了该状态(即等待接收方发送的ACK/NAK状态)时,才可以从较高层中接收到新的分组。因此除非发送方确信接收方已经正确接收到了分组之后才会进行发送新的数据,这就是停等协议。
但是我们仔细观察rdt 2.0的FSM,容易想到下面几种情况:
①如果发送方发送的分组丢失了,那么接收方接收不了分组,也就不能向发送方发送确认或者否顶确认分组了,发送方就会一直处于等待来自接收方的ACK/NAK状态了
②如果接收方接收到了分组,然后向发送方发送反馈,这时候如果发送的反馈发生了丢包,那么发送方同样不知道所发送的分组是否已经被接收方正确接收,依旧处于等待来自接收方的ACK/NAK状态
③接收方接收到了分组,向发送方发送反馈,但是发送的反馈发生了损坏,那么发送方接收到了反馈之后,发送方无法知道接收方是否正确接受了发送的数据。
基于上面的①②情况,将在rdt 3.0中进行分析,这里主要讲解第三种情况,因为考虑到了第三种情况,所以就有了rdt 2.1。
在讲解rdt 2.1之前,我们首先考虑对于受损的ACK/NAK处理的可能:
显然我们时通过方法三来解决的,下面我们将来开一下rdt 2.1的FSM:
但是,通过仔细观察上面的图,我们会有这样的疑问:一旦接收的分组不是我们接收方想要的,最后返回的是一个ACK分组?例如上面的图中,接收方希望接收序号为0的分组,但是发送方发送的分组序号为1,最后接收方向发送方发送的是一个ACK分组,这是为什么呢?
原因很简单,以问题中的例子进行解析,接收方希望接收序号为0的分组,而发送方发送的是序号为1的分组,那么如果这种情况下我们发送的是NAK分组,那么接收方接收到这个分组之后,就会重传序号为1的分组,这时候重传的分组依旧不是接收方所希望序号的分组,依旧发送NAK分组,这时候就会进入到了死循环中,明显不合适;反之,如果接收方收到的不是希望序号为0的分组,向发送方发送一个ACK分组,此时接收方的状态并没有发生改变,依旧是希望接收序号为0的分组,这时候发送方接收到的是ACK分组,那么就会进入等待上层数据,并且将数据打包成为序号0的分组,这时候接收方接收到的是希望的分组,此时就会返回ACK分组,并进入希望接收序号为1的状态。
因此接收方接收到的不是希望序号的分组,通过返回一个ACK分组,最后就会接收到希望序号的分组,否则,如果返回的是一个NAK分组,就会陷入死循环中。
而rdt 2.2是在优比特差错信道上实现的一个无NAK的可靠数据传输协议,它对应的FSM下图:
经具有比特差错的丢包信道德可靠数据传输: rdt 3.0
现在假定除了比特受损外,第层信道还会发生丢包,这在今天的计算机网络(包括因特往)中并不罕见。协议现在必须处理另外两个关注的问题:怎样检测丢包以及发生丢包后该做些什么。那么我们将如何解决丢包问题呢?
对应的rdt 3.0的发送方的FSM如下图:
差错恢复
停等协议
上面我们分析的rdt协议都是利用的是停等协议,只有在发送方每次只能发送一个分组,并且发送的分组受到来自接收方的确认分组之后,才可以发送新的一个分组,否则发送方收到的不是确定分组,那么需要重传这个分组。所以利用率极低。
如果RTT远大于TD的时候,那么信道利用率极低,因此为了避免这种情况,有了回退N步,选择重传。
回退N步协议
在回退N步协议中,允许发送方发送多个分组(当有多个分组可用时)而不需要等待确认,但是它也受限于流水线中未确认的分组数不能超过某个最大允许数N,即发送窗口外的分组不可以发送,只能发送发送窗口内的分组。
接收窗口只有一个,所以只有接收方接收到的分组是在接收窗口中才会发送确认分组,否则就会发送一个冗余分组,表示最近接收到确认分组,当接收方接收到多个重复的确认分组,那么就知道了发生了丢包,这时候发送方需要在发生超时之前立刻重传丢包之后的所有分组,否则,一旦发生了超时,那么发送方需要重传发送窗口中的所有分组。
对应的扩展FSM如下图,通过上面的文字部分,再来看下面的图片,会更加容易理解:
回退N步协议的过程分析:
选择重传协议
选择重传协议可以说是回退N步协议的进阶。因为回退N步协议中的接收方窗口尺寸只有1个,那么这时候接收方只能在接收到按序到达的并且没有损坏的分组之后才可以发送ACK分组,否则其他所有情况接收方都要将收到的分组丢弃,即便这个分组没有损坏,但是已经失序了,正因如此,从而导致了许多不必要的重传。因此出现了选择重传协议。
顾名思义,选择重传(SR)协议通过让发送方仅重传那些它怀疑在接收方出错(即丢失或者损坏的)分组而避免了不必要的重传,接收方需要逐个向发送方发送ACK分组。
选择重传的过程分析:
然而,当我们面对有限序号范围的现实时,发送方和接收方窗口缺乏同步会产生严重的后果。例如如果接收窗口过大,那么就会导致无法辨析新、旧分组,即无接收方无法辨析当前接收到的分组是一个新分组还是一次重传。所以需要要求窗口长度比小于等于序号空间的一半。
因此通过上面的协议,我们可以知道要实现可靠传输,至少需要用到以下几种技术:
- 检验和:通过检验和来判断分组是否出现了比特差错。(rdt 1.0)
- 肯定和否定分组技术:在rdt 2.0中应用到,发送方通过接收来自发送方的肯定或者否顶分组来判断是否需要重传分组。
- 序号:rdt 2.0中有着致命的缺陷,如果接收方发送的肯定或者否定分组损坏,那么发送方就无法明确知道接收方是否正确接收了自己发送的分组,因此通过再次发送当前这个分组(冗余分组),而通过发送冗余分组带来的问题是:当接收方收到这个分组的时候,就没有办法知道这个是一次重传还是一个新的分组(因为在rdt 2.0的接收方只有一个FSM,所以只能判断接收到的分组是否损坏,如果损坏就发送NAK分组,否则发送ACK分组),此时如果接收方发送的是一个ACK,而这个ACK分组受到损坏而导致发送方重传当前的数据分组,那么接收方本来都已经接收过这个分组了,是否需要再次接收这个分组呢?答案是否定的,对于重复的分组,我们采用丢弃的方式,所以需要通过序号来判断接收方是否已经接收到了重复的分组,如果接收方得知当前接收到的分组是一个重复分组,那么就丢弃这个分组,然后发送这个分组的ACK分组给发送方。
- 重传: 主要用于解决丢包的问题(rdt 3.0),通过在发送方中设置一个定时器,一旦发生了超时(有可能是因为发生了丢包,或者发送方发送的分组或者接收方发送的ACK/NAK过度延长导致的超时),发送方立刻重新启动定时器,并且重传数据分组给接收方,再次等待来自接收方的ACK/NAK分组,当接收到了ACK分组之后,就终止这个计时器。
- 窗口、流水线: 因为rdt 3.0利用的是停等协议,所以发送方每次只能发送一个分组,并且只有在接收到这个分组的ACK分组之后才可以发送新的分组,信道利用率为(L / R) / (RTT + L/R),信道利用率极低,所以出现了回退N步协议,选择重传协议,允许一次发送多个但未被确认的分组(即发送窗口内的分组),从而提高了信道利用率。
TCP超时重传时间的选择
拥塞控制
一个TCP发送方如何限制它向连接发送流量的速率呢?
有上面图片我们可以知道,TCP接收缓存足够大,那么可以忽略接收窗口的限制,这时候在发送方中没有被确认的数据量仅受限于cwnd。所以通过约束cwnd就可以限制TCP发送方向连接发送流量的速率了。因为在每个往返时间RTT的七十点,发送方向该链接发送cwnd个字节的数据,在该RTT时间时发送方接收到数据的ACK报文,那么该发送方的发送速率大概是cwnd / RTT 字节/秒(将一个丢包和发送时延忽略不计时),因此发送方通过调整cwnd的大小来限制TCP发送方向连接发送流量的速率。
TCP发送方如何感知到它与目的地之间的路径出现了拥塞?
在上面对拥塞控制有一定了解之后,我们来学习拥塞控制的算法:
- 慢开始
- 拥塞避免
- 快恢复
慢开始
拥塞避免:
当检测到拥塞窗口cwnd的值等于慢开始门限(sstreshen)的值时候,那么就需要结束慢开始,转而使用一种更加谨慎的方式增加cwnd,这就是拥塞避免,每个RTT只将cwnd的值增加1,即cwnd = cwnd + 1。
那么什么时候结束拥塞避免的线性增长呢?
快恢复:
流量控制
值得注意的是UDP并不提供流量控制,所以使用UDP的应用程序可以根据其需要一起愿意的任何速率发送数据。这也是TCP与UDP的区别所在之一。
利用滑动窗口机制可以方便地在TCP连接上实现发送方的流量控制:
- TCP接收方利用自己的接收窗口的大小来限制发送方的发送窗口的大小
- 如果TCP的接收窗口变成了0,那么接收方会通知发送方接收窗口rwnd = 0,这时候在发送方接收到报文之后,就会启动持续计时器,向接收方发送只有一个字节数据的报文段(即零窗口探测报文端),而TCP中规定,即便接收窗口为0,也要接收零窗口探测报文段、确认报文段以及携带紧急数据的报文段,所以接收方会接收这个零窗口探测报文端,接收方会针对这个报文端发送一个ACK报文,并且通知自己的接收窗口rwnd为0,然后发送方再次启动持续计时器,重复刚刚的操作,直到接收方发送的ACK报文中告知接收窗口rwnd不为0.
UDP与TCP的区别
-
连接
TCP是面向连接的运输层协议,传输数据之前需要通过三次握手建立连接。而UDP不需要建立连接就可以传输数据。因此TCD只能支持单播,即一对一的两点服务,而UDP支持一对多、一对一、多对多的交互通信。 -
服务对象
TCP是面向字节流的,而UDP面向的是数据报。 -
可靠性
TCP是可靠的运输层协议,能够确保数据无差错,不丢失,不重复,按序到达。
UDP是不可靠的运输层协议,尽最大的努力交付数据,并不能保证报文段的按序交付,不保证报文段中数据的完整性。 -
拥塞避免、流量控制
UDP没有拥塞避免、流量控制这些机制,所以使用UDP的应用程序可以根据其需要一起愿意的任何速率发送数据。
TCP含有拥塞避免、流量控制等机制,所以发送速率会受到约束,避免了数据从缓存中溢出的情况,从而保证了数据传输的安全性。 -
应用场景
TCP是一个可靠数据传输,所以能够保证数据无差错,不丢失,不重复,按序到达。通常用于:①HTTP/HTTPS,②FTP文件传输等。
UDP是无连接的,并且没有拥塞避免、流量控制等机制,所以能够立刻以其愿意的速率发送数据,在多媒体等方面能够容忍少量的分组丢失,所以UDP常用于:①视频、音频等的多媒体通信②广播通信③包总量较少的通信如DNS,SNMP等.
-
分组首部开销
UDP的首部只有8个字节,开销较小,而TCP首部长度较长,有一定的开销,如果首部没有使用选项字段时占20个字节,否则会变长。