目录
- 概述
- 物理层
- 数据链路层
- 网络层 (1) 基础知识
- 网络层 (2) IP地址设计和用尽问题
- 网络层 (3) 网络层分组的传输流程
- 网络层 (4) 网络层多播等应用
- 运输层 (1) 可靠传输和TCP/UDP
- 运输层 (2) 拥塞控制和握手挥手
- 应用层 (1) HTTP和万维网
TCP怎样实现可靠传输的
大体上来说就是根据滑动窗口的动态调节。通过什么来调节呢,一个是接收方的确认报文的窗口大小,一个是网络拥塞时的拥塞控制。下面分别介绍这两种方式。
接收方的确认报文窗口
滑动窗口的概念上一节已经说过,它是以字节为单位的。发送方会把发送方的窗口内的数据全部发送出去,并缓存这些数据,知道收到接收方的确认。收到确认会向后滑动窗口,发送窗口内新的数据并丢弃已经收到确认的数据。接收方的窗口表示接收方的接收缓存大小。接收方发送的确认报文中有确认号和一个窗口值,例如确认号是400,窗口是200。就表示400之前的数据已经收到了,并且接收方接收缓存窗口还有200字节的剩余,如果下次发送的比这个窗口大,那么就会丢弃装不下的数据,所以发送方需要调整自己的窗口不能大于接收方的返回的窗口大小。
没有按序到达的分组怎么确认?
最简单的方式就是丢弃,但是这样的效率很低,发送方需要重传没有按序到达的分组。一般是接收方保留这些分组,只对按序收到的数据的最高位序号进行确认。那么如果一个很靠前分组在传输中丢失了,后面的分组都收到了,因为没有按照顺序到达,发送方在超时后就要发送所有的分组数据了。怎么才能单独传送这个丢失的报文呢,需要使用选择确认SACK。
超时重传的时间
怎么选择超时重传的时间,肯定是根据网络状况来看的,TCP采用了一种自适应的算法,会记录一个报文的发出时间和收到确认的时间。两者支持就是往返时间RTT,并计算所有的时间的加权平均值。
窗口是0的处理
如果确认的窗口是0,那么表示接收方没有空间存放新的数据了,发送方需要停止发送。因为窗口是0了,当接收方有了可用的缓存空间时。需要发送一个新的窗口指给发送发,但是有一种情况是这个确认在网络中丢失了,那么双方都不会发送数据,产生了死锁的现象。怎么处理呢,发送方在窗口是0时,需要定期向接收方发送询问新的窗口值。
网路拥塞控制窗口大小
当网络状况很差时,如果还是以原始速度向网络中传输的话,那么这些数据都会被路由器丢弃。循环往复,那么网络的状况只会越来越差。当前的状态是对某一资源的需求超过资源能提供的可用部分,这种情况就叫拥塞。极限状况下,如果没有这方面的控制,数据想怎么传输就怎么传输,那么所有的路由器和节点的缓存都会被填满,整个网络直接瘫痪,没法传输任何数据了。所以一定要对网络拥塞进行控制。 拥塞控制肯定需要产生额外的开销,所以必须权衡得失。原理上怎么实现控制呢,一就是无限增大数据数据提供方的可用资源,二就是减少数据的请求。显然第一个很难做到,只能限制数据的请求。
TCP的拥塞控制方法
涉及到四种算法,慢开始、拥塞避免、快重传、快恢复。这四种策略的结果都会体现在发送窗口的变化上,也叫基于窗口的拥塞控制。主要原则就是如果出现了拥塞,就把窗口变大,传输更多的数据,如果出现了拥塞,就减少窗口的大小。怎么知道有没有产生拥塞呢?只要出现了超时,就说明出现了拥塞。下面详细说下四种算法。
慢开始
当开始发送数据时,发送方并不知道网络的状态,所以不能立即把大量的数据注入到网络中,应该先试探一下,由小到大慢慢的增加窗口值。在收到一个确认报文时,可以把拥塞窗口增加一倍,没经过一个传输轮次,拥塞窗口就会加倍。但是不能一直这么增加下去的,会设置一个满开始门限,超过这个值,就是用拥塞避免算法。
拥塞避免
让拥塞窗口缓慢增大,没经过一个传输轮次,就把窗口值加1。是一个线性增加的过程,而不是像慢开始呈现平方函数增长。当增加到一定值如果出现了超时,说明网络出现了拥塞,那么就调整窗口值是原来的一半,进入满开始阶段,重复上面的两个过程。
快重传
有时网络并没有发生拥塞,只是某一个报文传输被丢失了,这样也会导致发送方认为发生了拥塞,开始了慢开始阶段。这是就要使用快重传算法,他要求接收方在收到没有按序到达的报文时,也要重复发送确认报文,防止发送方认为是网络发生了拥塞,及早的让发送方知道是丢失了报文,而不是发生了拥塞,算法规定如果连续收到3个重复确认,就知道接收方确实没有收到下一个报文。不会让发送方误认为网络拥塞。
快恢复
如果收到了连续的额确认报文,发送方不会执行满开始阶段,因为现在确认是网络产生了丢失的数据,而不是产生了拥塞,这时会执行快恢复算法,恢复到之前误减少的窗口状态。
主动队列管理
路由器的报文丢失策略如果是传统的队列模式,满了就把不能加入的丢弃,会使发送这个报文的发送方超时,执行慢开始,如果很多TCP连接都被丢弃了,就会导致很多个慢开始的同时发生。在网络恢复了以后,又有好多同时的恢复执行,导致网络的通信量急剧增大。 怎么避免这种情况呢,使用主动队列管理,主动就是不是等到了路由器的队列增大到装不下才丢弃,这样太被动了,而是队列到达一个警告值时,主动丢弃数据。
TCP的连接管理
TCP和UDP很大的区别就是TCP需要传输前先建立连接,这种连接有什么用呢,网络传输并不像传统的电话线路那样会提前分配出需要的资源,电话传输需要提前建立连接分配这些资源。那么TCP的建立连接为了什么呢。
- 知道通信双发的存在
- 让双发协商一些传输时使用的参数
- 对传输中使用的实体资源进行分配,如缓存的大小等。
连接建立阶段
大家熟知的3次握手 设A是TCP的客服程序,B是TCP的服务程序。 最初两端的都处于CLOSED状态
- B的TCP服务器进程先创建传输控制块TCB,准备接收客户进程的连接请求,然后服务器状态就是LISTEN状态了,等待客户端的连接请求。
- 第一次握手:A的TCP客户进程创建传输控制TCB,然后向B发送请求报文,首部中 同步位是SYN=1,同时选择一个初始序号,TCP规定,SYN报文段不能携带数据,但是要消耗一个序号,TCP客户进程进入SYN-SEND状态。
- 第二次握手:B收到了这个请求报文,如果同意建立连接,则向A发出确认,在确认报文中把SYN和ACK都设置成1,确认号ack = x+1,这个报文段不能携带数据,TCP服务器进程处于SYN—RCVD状态。
- 第三次握手:TCP的客户进程收到B的确认后,还要向B发出确认。确认的ACK为1,ACK报文可以携带数据,这时TCP连接已经建立了,A进入ESTABLISHED状态。B收到这个确认后,也进入这个状态。
❝「为什么A还要发送一次确认呢?」
❞
这是为了防止已失效的连接请求报文有传到了B,因而产生了错误。A发送一个第一个连接请求时,这个请求可能在网络中阻塞了,因为超时,A可能有发送了一个。这个正常的到达了B。连接建立成功了。第一个网络请求这时传输过来了,这时B收到了这个请求,会向A发出确认,由于A没有连接请求,所以不会发送第三次握手。
连接释放阶段
大家熟知的4次挥手 还是以上面的简称标记传送和接收方
- 现在AB都处于ESTABLIASHED状态,A因为发送完了数据,向B发送连接释放报文,并停止发送数据。主动关闭了TCP连接,并把报文的首部FIN设置成1,需要是u,它等于前面已经传送的数据的最后一个字节序号加1。这时A进入FIN-WAIT-1阶段
- B收到连接释放后发出确认,确认号是v,等于B前面已经传送的数据的最后一个字节加1,现在B进入CLOST-WAIT状态了,这时TCP处于半关闭状态,A已经没有数据要发送了,但B还是有数据的,若B发送这时候A还是要接收的。这个状态会持续一阵,因为B服务器还有数据没有处理完发往A。A收到了B的确认后,就进入FIN-WAIT-2状态,等待B的释放报文。
- 若B发送完所有数据后,其应用进程通知TCP释放连接,B发出一个释放报文段FIN=1,这时B就进入LAST-ACK状态,等待A的确认。
- A收到B的连接释放报文后,必须对此发出确认,在确认中把ACK设置成1,A进入TIME-WAIT状态,这时候TCP连接还没有释放掉,必须经过等待计时器设置的2MSL时间后,A才进入CLOSED状态,时间MSL叫最长报文段寿命。过了这个时间后,就进入了CLOSED状态,A撤销相应的传输控制TCB后,就结束了TCP连接。B收到了这个确认也进入CLOSED状态。
❝「为什么A要等待2MSL的时间呢?」
❞
一是因为保证A发送的连接终止可以到达B,这个ACK又可能丢失,因而使B收不到这个确认,B因为超时就会重新传输请求终止连接的报文,A就可以重传这个报文,而不是不确认是否传输成功就直接关闭连接。二是防止已失效的请求报文继续起作用,A在发送最后一个报文后,再经过2MSL就可以是在本连接持续时间所产生的所有报文都消失。