什么是TCP和UDP协议?

TCP和UDP各自适用的场景

当要求通信数据可靠时,选择TCP协议:

  • 例如文件传输,邮件传输,金融交易,可靠通信,消息队列等。

当要求传输速度,不要求数据可靠时,选择UDP:

  • 适合多媒体信息分发的场景,如直播,视频,语音,实时信息(股票走势)等。

UDP

UDP特点

UDP是无连接的传输层协议。

UDP使用尽最大努力交付,不保证可靠交付。

  • 即不能保证数据在网络中是否丢失。

UDP是面向报文传输的,对应用层传输过来的数据不会进行任何处理(不合并,不拆分,保留原报文的边界)。

UDP没有拥塞控制,因此即使网络出现拥塞也不会降低发送速率。

  • 适用于一些实时场景,如直播、视频会议等。

UDP支持一对一,一对多,多对多的交互通信。

UDP的首部开销很小,总共只有8个字节。

如何保证UDP协议可靠?

模拟TPC的可靠机制来实现,保证四个无即可(无丢失、无失序、无错误、无重复)。

TCP

TCP特点

TCP是面向连接的传输层协议,也就是说,在双方传输数据之前,必须先建立起连接,传输完数据后要结束连接。

每一条TCP连接只能有两个端点,即点对点通信(一对一)。

TCP提供可靠的传输服务,通过TCP连接传送的数据,无差错、不丢失、不重复、按序到达。

TCP提供全双工的通信:

  • TCP允许通信双方的应用进程在任何时候都可以发送数据。

TCP是面向字节流的协议。

TCP段

应用需要传输的数据可能会非常大,当传输层的数据包大小超过 MSSTCP 最大报文段长度)

  • 就要将数据包分块
    • 这样即使中途有一个分块丢失或损坏了,只需要重新发送这一个分块,而不用重新发送整个数据包。

每个分块称为一个 TCP 段(TCP Segment)。

在这里插入图片描述

端口

当设备作为接收方时,传输层则要负责把数据包传给应用,但是一台设备上可能会有很多应用在接收或者传输数据

  • 因此需要用一个编号将应用区分开来,这个编号就是端口

由于传输层的报文中会携带端口号,因此接收方可以识别出该报文是发送给哪个应用。

流量控制

TCP 利用滑动窗口实现流量控制,流量控制是为了控制发送方发送速率,保证接收方来得及接收数据。

接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。

将窗口字段设置为 0,则发送方不能发送数据。

特殊的情况:

当接收方窗口大小为0时,发送方不能发送数据,在接收方把数据处理完之后,向接收方发送rwnd=1000

  • 通知发送方我还可以接收1000个字节的数据。

但是这个字段在传输时丢失了,接收方并没有接收到这个数据,默认接收方当前窗口为0。

可以通过定时器来解决这个问题:

当接收到窗口为0的消息,则启动定时器。

坚持定时器会每隔一段时间发送一个窗口探测报文,来询问接收方当前的窗口大小。

拥塞控制

网络传输过程中,某段时间如果网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就会变坏。

  • 这种情况就叫做网络拥塞

为解决这个问题,TCP中使用了四种拥塞控制算法。

慢开始:

发送方会维持一个拥塞窗口cwnd的状态变量。

  • 拥塞窗口的大小取决于拥塞程度,并且会在收发包过程中动态的进行变化。

核心思想:

  • 指数级由小到大逐渐增加拥塞窗口大小,如果网络出现阻塞,拥塞窗口就减小。

判断出现网络拥塞的依据:

  • 没有按时收到应当到达的确认报文(即发生重传)。

img

拥塞避免算法:

拥塞避免算法是让拥塞窗口缓慢增长,每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。

  • 拥塞窗口按线性规律缓慢增长。

不论是在慢开始期间还是拥塞避免期间:

  • 只要判断网络发生了拥塞ssthresh就设置为当前发送窗口大小的一半,然后重新开始执行慢开始算法。

这样做的目的是迅速减少主机发送到网络中的分组数:

  • 使发生拥塞的路由器有足够的时间把队列中积压的分组处理完毕。

具体过程:

维护一个慢开始门限ssthresh状态变量:

  • cwnd < ssthresh 时,使用慢开始算法。
  • cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
  • cwnd = ssthresh 时,既可以使用慢开始算法,也可以使用拥塞避免算法。

假设发送方拥塞窗口cwnd的值为1,发送窗口swnd等于拥塞窗口cwnd

  • 那么目前发送方只能发送一个报文段,cwnd为几就能发送几个报文段。

接收方收到报文段后发送回复确认,发送方收到确认报文,会将拥塞窗口的值乘2,变为2。

发送方此时一次就能发送两个报文段,接收方收到报文段后返回两次确认报文段,发送方收到之后拥塞窗口再乘2,cwnd=4

知道发送方发送16个报文段都按时收到确认报文,拥塞窗口变为32,但是这一次没有按时收到确认报文。

  • 即有报文需要重传,表示网络发生了拥塞。

这时候设定ssthresh为当前窗口cwnd的一半,即ssthresh = cwnd/2 = 16,重新开始慢开始。

  • 再一次cwnd=16的时候,开启拥塞避免算法。

6424c7d14d7bde967f2b5a7699473034

快速重传

在数据传送过程中,网络有可能不太稳定,个别报文段在网络中丢失了。

  • 但是实际上网络并没有发生拥塞。

这样会导致发送方超时重传,误以为网络上发生了拥塞。

由于有慢开始和拥塞避免机制,发送方错误的启动了慢开始算法,并且把拥塞窗口cwnd又设置为最小值1,因为降低了传输效率。

为解决这个问题,快重传要求接收方在收到一个失序的报文段后立即发出重复确认。

为的是让发送方知道有一个报文丢失了,快速重传算法规定:

  • 发送方只要一连收到三个重复确认就应当立即重传对方还没有接收到的报文段,而不必继续等待设置的重传计时器时间到期。

快恢复算法

快恢复算法适合快重传算法配合使用的。

当发送方连续收到三个重复确认的时候,执行 乘法减小 算法,将ssthresh门限减半(为了预防网络发生拥塞)。

但是接下来不执行慢开始算法:

  • 因为如果网络发生拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。

此时不会执行慢开始算法,而是将拥塞窗口cwnd设置为ssthresh减半后的值。

  • 然后执行拥塞避免算法,让cwnd缓慢变大。

7d4234f94f28de0310e53accec62ad90

四次挥手

第一次挥手:

客户端向服务器发送一个 FIN 数据包(FIN = 1,seq = u)主动断开连接,报文中会指定一个序列号。

告诉服务器:我要跟你断开连接了,不会再给你发数据了。

客户端此时还是可以接收数据的,如果一直没有收到被动连接方的确认包,则可以重新发送这个包。

此时客户端处于 FIN_WAIT1 状态。

第二次挥手:

服务器收到 FIN 数据包之后,向客户端发送确认包(ACK = 1,ack = u + 1)。

把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了。

这是服务器在告诉客户端:我知道你要断开了,但是我还有数据没有发送完,等发送完了所有的数据就进行第三次挥手。

此时服务端处于 CLOSE_WAIT 状态,客户端处于 FIN_WAIT2 状态。

第三次挥手:

服务器向客户端发送FIN 数据包(FIN=1,seq = w),且指定一个序列号。

以及确认包(ACK = 1, ack = u + 1),用来停止向客户端发送数据。

这个动作是告诉客户端:我的数据也发送完了,不再给你发数据了。

此时服务端处于LAST_ACK状态,客户端处于TIME_WAIT状态。

第四次挥手:

客户端收到 FIN数据包 之后,一样发送一个 ACK 报文作为应答。

且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值。

此时客户端处于 TIME_WAIT 状态。

需要过一了一定时间(2MSL)之后,客户端发送确认包(ACK = 1, ack = w + 1)。

此时客户端才会进入 CLOSED 状态,以确保发送方的ACK可以到达接收方,防止已失效连接请求报文段出现在此连接中。

至此,完成四次挥手。

等待计时器(TIME-WAIT):

MSL(Max Segment Lifetime):最长报文段寿命。

等待计时器等待的时间为2MSL,MSL一般设置为2分钟。

为什么需要等待2MSL?

确保发送方发送的第四次挥手ACK报文可以到达接收方。

确保当前连接的所有报文都已经过期。

在四次挥手时,只要发送方发出了第四次挥手的报文之后,发送方就进入了等待状态。

这时最后一个报文其实是并没有确认的,这个等待计时器主要是为了确保发送方发送的第四次挥手ACK报文可以到达接收方。

2MSL是报文在网络中最长可以存活的时间。

在2MSL时间里,如果第四次挥手ACK报文没到达服务端,接收方会重新发送第三次挥手的报文给客户端。

客户端收到之后,就知道之前第四次挥手的 ACK 报文丢失了,然后再次发送 ACK 报文,确保正确地结束这次连接。

另外一个功能是为了确保当前连接的所有报文都已经过期,防止已失效的连接请求报文段出现在本连接中。

因为最后一个报文都已经等待了2MSL时间,所以对于其他报文,肯定也超过2MSL的时间,都是过期的报文。

如何保证有序

TCP保证有序的方式是通过序号和确认应答实现的。

在 TCP 中,每个数据包都有一个唯一的序号,序号的单位是字节(Byte)。

发送方将数据分割成多个数据包,每个数据包都打上序号,并逐个发送到接收方。

接收方会将接收到的数据包按照序号大小顺序进行排序,保证数据按照发送方发送的顺序被接收到。

接受方一旦收到了顺序化的数据,它就将这些数据按正确的顺序重组成数据流传递到高层进行处理。

同时,接收方会向发送方发送确认应答(ACK),表示接收到了哪些数据包。

这样发送方就能够知道哪些数据包已经被接收方正确接收了。

如果发送方没有收到确认应答,就会认为数据包没有被接收方正确接收,会重新发送这些数据包。