UDP和TCP

2022-08-20·12min
type
Post
summary
status
Published
category
tags
slug
date
Aug 20, 2022
password
icon

UDP

UDP: User Datagram Protocol,用户数据报协议。
UDP是一个面向无连接的传输层协议。

UDP特点

  1. 面向无连接:UDP不需要建立连接就能发送数据包。
  1. 不可靠性:UDP会尽自己最大努力传输数据包,但不保证一定能送达。
  1. 面向报文:应用层给UDP多长的报文,UDP就原样发送,即一次发送一个完整报文。

UDP首部

notion image
UDP首部是固定的8字节,由四个字段组成,每个字段都是两个字节。
  • 源端口号。可选字段,需要对方回信时选用,不需要时全部置0。
  • 目的端口号,一定要有。
  • UDP长度。指首部+数据的长度,比如数据是2B,首部固定是8B,那么UDP长度就是2+8 = 10B。
  • UDP校验和。用来检测UDP数据报在传输中是否有错,有错则丢弃。该字段是可选的,当源主机不想计算校验和,可以令该字段全为0。

TCP

TCP: Transmission Control Protocol,传输控制协议。
TCP是一个面向连接的,可靠的,基于字节流的传输层协议。

TCP特点

  1. 面向连接。TCP的三次握手,四次分手,针对的都是连接。
  1. 可靠性。TCP提供无差错,不丢失,不重复,按序到达的可靠交付。
  1. 面向字节流。TCP会将应用层的数据切分成一个个数据包。

TCP首部

notion image
TCP首部包括固定的20字节以及可选项,这里只介绍固定的20字节中的常用字段。
  • 源端口号。两个字节,发送方的端口号。
  • 目的端口号。两个字节,接收方的端口号。
  • 序号。四个字节,表示当前发送数据第一个字节的序列号。
    • 序号有两个作用。1.在SYN为1的数据包中交换彼此的初始序列号。 2.保证数据包按照正确顺序组装。
  • 确认号。四个字节,用来告知对方下一次期望收到的序列号,若确认号为N,则小于N的所有数据都已经正确收到。
  • 标记位。
    • SYN:为1时表示用来发起一个连接。
    • ACK:为1时表示确认号合法,为0的时候表示数据段不包含确认信息。
    • FIN:为1时表示准备断开连接。
    • RST:为1时表示用来强制断开连接。
    • PSH:为1时表示告知对方这些数据包收到后应该马上交给上层的应用,不能缓存。

TCP三次握手

TCP三次握手的目的是确认通信双方的两样能力:发送能力和接收能力。
notion image
1.客户端先发送一个请求连接数据包,其中SYN为1,初始序号seq为一个随机数x。
客户端:在吗?我要和你对话,这个x是我的数据初始序号seq。
2.服务端收到该数据包后,会为该TCP连接分配缓存和变量。接着回复一个数据包,其中SYN为1,ACK为1,初始序号seq为一个随机数y,确认号ack为x+1。
服务端:我在,同意建立连接,这个y是我的数据初始序号seq,我希望下一次收到序号为x+1的数据。
3.客户端收到该数据包后,会为该TCP连接分配缓存和变量,接着返回一个数据包。其中SYN为0,表示这不是建立连接的请求了,ACK为1,确认号ack为y+1。
客户端:收到,接下来要开始通信了,我希望下一次收到序号为y+1的数据。
总结: 第一次握手能让服务端确认客户端的发送能力 第二次握手能让客户端确认服务端的发送和接收能力 第三次握手能让服务端确认客户端的接收能力

为什么不是两次握手?

根本原因:无法确认客户端的接收能力。
分析:假设两次握手就能建立连接。
  1. 现在客户端发了一个SYN为1的数据包,但由于网络原因这个数据包滞留在了网络中没有送达。客户端迟迟没有收到确认包,就误以为是丢包了,于是重传。这次的数据包送达,服务端返回确认,两次握手建立好了连接。
  1. 当连接关闭后,那个滞留的数据包又到达了服务端,由于现在是两次握手,服务端接收到之后就回复确认,默认建立连接,但此时的客户端已经断开了。
这就带来了连接资源的浪费

为什么不是四次握手?

三次握手的目的是确认双方发送和接收的能力,当然再多次也可以,但是没有意义。

三次握手过程中可以携带数据么?

第三次握手的时候可以,前两次不可以。
如果第一次能携带数据,那么一旦有人想攻击服务器,就会在第一次握手时携带大量数据,服务端就要耗费大量时间和内存去处理这些数据,增大了服务器被攻击的风险。
至于第二次握手,此时服务端无法确认客户端是否具有接收能力,如果携带数据的话无法保证送达。
第三次客户端已经处于ESTABLISHED状态,并且能确认服务端发送和接收能力正常,所以可以携带数据。

TCP四次挥手

notion image
1.客户端先发送一个请求断开数据包,其中FIN为1,序号seq为u,u是最后一次接收到的数据最后一个字节的序列号+1。
2.服务端收到该数据包后,返回一个确认数据包。其中ACK为1,确认号ack为u+1,序号seq为v,v由服务端发送给客户端最后一个包的确认号来决定
此时客户端就不能给服务端发信息了,只能接受,但是服务端还可以发。
3.当服务端没有可传的信息后,会再发送一个请求断开数据包,其中FIN为1,ACK为1,确认号ack为u+1,序号seq为w,这里的w和上面的v是一个意思,但由于这一步和上一步中间可能还在发数据,所以这个seq可能会变。
4.客户端接收到FIN=1的数据包之后,会返回一个确认数据包,其中ACK为1,seq=u+1,ack为w+1。发送完之后,客户段会等待两个MSL(Maximum Segment Lifetime,报文最大生存时间),如果这段时间内客户端没收到服务端重发的FIN数据包,那么挥手结束。

为什么要等待两个MSL(报文最大生存时间)?

因为客户端最后一个ACK数据包可能在传输时丢失,然后服务端迟迟没收到确认包就会重传FIN数据包。
如果客户段不等待两个MSL,就不会收到这个重传的FIN数据包,也就不会再重发ACK数据包,服务器就会因为收不到ACK数据包无法正确关闭。

为什么不是三次挥手?

因为服务端在接收到FIN数据包后,往往不会立即返回FIN数据包,而是先返回一个ACK确认包,告知对方自己已收到,然后将所有信息都发送完毕后,才会发送FIN数据包。
这样将ACK数据包与FIN数据包分开发送,就导致了四次挥手。
如果将ACK数据包合并到FIN数据包里,缩减为三次挥手。由于FIN数据包要等到所有数据发送完毕,所以可能有会延时,从而导致客户端迟迟收不到确认,然后不断重发FIN数据包。

半连接队列和全连接队列

三次握手前,服务端状态由CLOSED变为LISTEN,会同时在内部创建两个队列:半连接队列和全连接队列
  1. 半连接队列:当客户端发送SYN数据包到服务端,服务端收到后回复ACK+SYN数据包后,就会将这个TCP连接推入半连接队列。
  1. 全连接队列:当三次握手完成时,就会将这个TCP连接推入全连接队列,等待被具体应用取走。

SYN Flood攻击

也称为SYN洪泛攻击,原理就是攻击者伪造大量不存在的IP地址,并向服务端疯狂发送SYN数据包请求连接。当服务端返回ACK数据包后,该攻击者不对其进行再确认。
对于服务端而言,这会导致两个危险的结果:
  1. 处理大量的SYN数据包并返回对应ACK数据包,会导致大量TCP连接处于半连接状态,从而占满整个半连接队列,无法处理正常请求。
  1. 由于是不存在的IP,服务端长时间收不到客户端的ACK确认,就会不断重发数据,直到耗尽服务端的资源。

如何应对SYN Flood攻击

  1. 增加半连接队列的容量
  1. 减少ACK+SYN数据包的重试次数,避免大量的超时重发。
  1. 利用SYN Cookie技术,在服务端接收到SYN数据包后不立即分配连接资源,而是根据这个SYN计算出一个Cookie,连同第二次握手回复给客户端,在客户端回复ACK的时候带上这个Cookie值,服务端验证Cookie合法之后才分配连接资源。

TCP快速打开(TFO)

顾名思义,TCP快速打开(TCP Fast Open,即TFO)就是为了解决每次都要三次握手才能建连的问题。
它利用SYN Cookie技术来实现。

TFO的流程

首轮三次握手
  1. 客户端发送SYN数据包给服务端
  1. 服务端接收到,然后会在内部通过计算得出一个SYN Cookie,并放入SYN+ACK数据包的Fast Open选项里,一起发送给客户端。
  1. 客户端接收到数据包,拿到这个SYN Cookie并存起来,然后发送ACK数据包,完成三次握手
后面的三次握手
  1. 客户端将之前缓存下来的SYN Cookie、SYN数据包、HTTP请求一起发送给服务端
  1. 服务端验证SYN Cookie的合法性,不合法直接丢弃,如果是合法的,正常返回SYN+ACK数据包
  1. 然后服务端可以直接发送HTTP响应给客户端
  1. 客户端接收到SYN+ACK数据包,返回ACK数据包,完成三次握手
notion image
注意: 客户端最后握手的 ACK 不一定要等到服务端的 HTTP 响应到达才发送,两个过程没有任何关系。

TFO的优势

TFO的优势在于后面的握手,在拿到客户端的SYN Cookie并验证通过以后,服务端可以直接返回HTTP响应,充分利用了1个RTT的时间提前进行数据传输,积累起来还是一个比较大的优势。

TCP流量控制

notion image
双方在使用TCP进行通信时,很可能发送方的速率和接收方的速率是不相等的。如果发送方发送的速率过快,而接收方处理又需要时间,这时候接收方只能把处理不过来的数据放入接收缓存区里(失序的数据包也会放入接收缓存区里)。
如果接收缓存区满了,发送方还在一直发送数据,接收方只能把收到的数据包丢掉,这就会产生大量的丢包,浪费网络资源。
因此我们需要根据接收缓存区的大小,动态调整发送方的发送,这就是所谓的流量控制。

滑动窗口

TCP通过滑动窗口的概念来实现流量控制,由于TCP是全双工的传输层协议,因此通信双方都各有两个滑动窗口。
  1. 发送窗口:根据对方接收窗口大小调整自己的大小,控制自己的传输速率,用来发送数据。
  1. 接收窗口:根据缓存区剩余大小调整自己的大小,用来接收数据。

流量控制具体过程

notion image
  1. 接收方每次收到数据,回复ACK确认数据包的时候,都会告知对方自己的接收窗口大小,也就是缓存区还有多少是空闲的。
  1. 发送方收到ACK确认数据包,便会调整自己的发送速率,也就是发送窗口的大小。当接收方表示接收窗口没有剩余空间了,发送方就会停止发送数据,防止出现大量丢包。

发送方何时再继续发送数据?

当发送方停止发送数据后,该怎样才能知道自己可以继续发送数据?
当发送方收到接受窗口win = 0时,就停止停止发送数据包,并且同时开启一个定时器,每隔一段时间就发个测试报文去询问接收方,打听是否可以继续发送数据了,如果可以,接收方就告诉他此时接受窗口的大小;如果接受窗口大小还是为0,则发送方再次刷新启动定时器。

TCP拥塞控制

流量控制与接收方的接收窗口相关联,并没有考虑到整个网络环境的影响。
如果说当前网络特别差,特别拥塞,发送方发出去的数据包都被堵在路上了,迟迟没有到达接收方。发送方迟迟收不到回应,就会以为丢包了,然后进行重传。但这样的结果就是不但浪费了网络资源,还使得网络更加拥塞了,因此,我们需要拥塞控制。
对于拥塞控制来说,每条TCP连接都要维护两个状态:
  1. 拥塞窗口
  1. 慢启动阀值
拥塞控制包含以下几个算法:
  1. 慢启动
  1. 拥塞避免
  1. 快速重传
  1. 快速恢复

拥塞窗口

拥塞窗口指的是目前自己还能传输的数据量大小,用来限制发送窗口。
发送窗口会取两者的较小值。而拥塞控制,就是来控制拥塞窗口的。

慢启动

三次握手结束,刚进入传输阶段的时候,发送方并不知道当前的网络状况如何。如果一开始就快速大量的发送数据包,很可能会导致大量的丢包和网络拥塞。
所以,拥塞控制首先要做的就是采用一种保守算法来慢慢的适应整个网络,这个算法就叫做慢启动。
过程如下:
  1. 首先三次握手,双方宣告自己的接收窗口大小。
  1. 双方初始化自己的拥塞窗口大小。
  1. 在开始传输的一段时间,发送端每接收到一个ACK确认数据包,都会使拥塞窗口翻倍。也就是如果初始拥塞窗口为10,那么每经过一个RTT,就会变成20,40,80...依次类推。
但也不会一直翻倍下去,当拥塞窗口大小到达一个阀值时,就不会涨那么快了,这个阀值就叫慢启动阀值。
到达慢启动阀值后,如何控制拥塞窗口大小呢?
那就是拥塞避免要做的事情了。

拥塞避免

当到达慢启动阀值后,发送方每收到一个ACK确认数据包,拥塞窗口大小会增加1。
也就是说,以前一个RTT下来,拥塞窗口大小翻倍,现在只增加1而已。
当然,慢启动和拥塞避免是一起作用的,是一体的。

快速重传

在TCP传输过程中,如果接收方发现数据包不是按序到达的,也就是发现了丢包,就会重复发送之前的ACK数据包。
比如第5个包丢了,即使6.7个包已到达,也会先存在缓存区里,然后接收方会一律返回第4个包的ACK确认包。
当发送方收到3个重复的ACK数据包时,会意识到丢包了,然后马上进行重传,不用等到一个RTO(重传超时时间)再进行重传。

快速恢复

当发送方收到3个重复的ACK数据包时,发现丢包,就会觉得当前的网络有点拥塞了,自己会进入快速恢复阶段。
在这个阶段,发送方发生以下改变:
  1. 拥塞阀值降低为拥塞窗口大小的一半
  1. 拥塞窗口大小变为拥塞阀值
  1. 拥塞窗口大小线性增长,也就是一个RTT,大小+1
以上就是TCP拥塞控制的经典算法: 慢启动、拥塞避免、快速重传和快速恢复。

参考

> cd ..