Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UDP和TCP #25

Open
dark9wesley opened this issue May 12, 2021 · 0 comments
Open

UDP和TCP #25

dark9wesley opened this issue May 12, 2021 · 0 comments
Labels

Comments

@dark9wesley
Copy link
Owner

dark9wesley commented May 12, 2021

UDP

UDP: User Datagram Protocol,用户数据报协议。

UDP是一个面向无连接的传输层协议。

UDP特点

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

UDP首部

UDP

UDP首部是固定的8字节,由四个字段组成,每个字段都是两个字节。

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

TCP

TCP: Transmission Control Protocol,传输控制协议。

TCP是一个面向连接的,可靠的,基于字节流的传输层协议。

TCP特点

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

TCP首部

TCP

TCP首部包括固定的20字节以及可选项,这里只介绍固定的20字节中的常用字段。

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

TCP三次握手

TCP三次握手的目的是确认通信双方的两样能力:发送能力和接收能力。

TCP

  1. 客户端先发送一个请求连接数据包,其中SYN为1,初始序号seq为一个随机数x。

客户端:在吗?我要和你对话,这个x是我的数据初始序号seq。

  1. 服务端收到该数据包后,会为该TCP连接分配缓存和变量。接着回复一个数据包,其中SYN为1,ACK为1,初始序号seq为一个随机数y,确认号ack为x+1。

服务端:我在,同意建立连接,这个y是我的数据初始序号seq,我希望下一次收到序号为x+1的数据。

  1. 客户端收到该数据包后,会为该TCP连接分配缓存和变量,接着返回一个数据包。其中SYN为0,表示这不是建立连接的请求了,ACK为1,确认号ack为y+1。

客户端:收到,接下来要开始通信了,我希望下一次收到序号为y+1的数据。

总结:
第一次握手能让服务端确认客户端的发送能力
第二次握手能让客户端确认服务端的发送和接收能力
第三次握手能让服务端确认客户端的接收能力

为什么不是两次握手?

根本原因:无法确认客户端的接收能力。

分析:假设两次握手就能建立连接。

  1. 现在客户端发了一个SYN为1的数据包,但由于网络原因这个数据包滞留在了网络中没有送达。客户端迟迟没有收到确认包,就误以为是丢包了,于是重传。这次的数据包送达,服务端返回确认,两次握手建立好了连接。

  2. 当连接关闭后,那个滞留的数据包又到达了服务端,由于现在是两次握手,服务端接收到之后就回复确认,默认建立连接,但此时的客户端已经断开了。

这就带来了连接资源的浪费

为什么不是四次握手?

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

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

第三次握手的时候可以,前两次不可以。

如果第一次能携带数据,那么一旦有人想攻击服务器,就会在第一次握手时携带大量数据,服务端就要耗费大量时间和内存去处理这些数据,增大了服务器被攻击的风险。

至于第二次握手,此时服务端无法确认客户端是否具有接收能力,如果携带数据的话无法保证送达。

第三次客户端已经处于ESTABLISHED状态,并且能确认服务端发送和接收能力正常,所以可以携带数据。

TCP四次挥手

TCP

  1. 客户端先发送一个请求断开数据包,其中FIN为1,序号seq为u,u是最后一次接收到的数据最后一个字节的序列号+1。

  2. 服务端收到该数据包后,返回一个确认数据包。其中ACK为1,确认号ack为u+1,序号seq为v,v由服务端发送给客户端最后一个包的确认号来决定

此时客户端就不能给服务端发信息了,只能接受,但是服务端还可以发。

  1. 当服务端没有可传的信息后,会再发送一个请求断开数据包,其中FIN为1,ACK为1,确认号ack为u+1,序号seq为w,这里的w和上面的v是一个意思,但由于这一步和上一步中间可能还在发数据,所以这个seq可能会变。

  2. 客户端接收到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连接推入半连接队列。

  2. 全连接队列:当三次握手完成时,就会将这个TCP连接推入全连接队列,等待被具体应用取走。

SYN Flood攻击

也称为SYN洪泛攻击,原理就是攻击者伪造大量不存在的IP地址,并向服务端疯狂发送SYN数据包请求连接。当服务端返回ACK数据包后,该攻击者不对其进行再确认。

对于服务端而言,这会导致两个危险的结果:

  1. 处理大量的SYN数据包并返回对应ACK数据包,会导致大量TCP连接处于半连接状态,从而占满整个半连接队列,无法处理正常请求。

  2. 由于是不存在的IP,服务端长时间收不到客户端的ACK确认,就会不断重发数据,直到耗尽服务端的资源。

如何应对SYN Flood攻击

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

TCP快速打开(TFO)

顾名思义,TCP快速打开(TCP Fast Open,即TFO)就是为了解决每次都要三次握手才能建连的问题。

它利用SYN Cookie技术来实现。

TFO的流程

首轮三次握手

  1. 客户端发送SYN数据包给服务端
  2. 服务端接收到,然后会在内部通过计算得出一个SYN Cookie,并放入SYN+ACK数据包的Fast Open选项里,一起发送给客户端。
  3. 客户端接收到数据包,拿到这个SYN Cookie并存起来,然后发送ACK数据包,完成三次握手

后面的三次握手

  1. 客户端将之前缓存下来的SYN Cookie、SYN数据包、HTTP请求一起发送给服务端
  2. 服务端验证SYN Cookie的合法性,不合法直接丢弃,如果是合法的,正常返回SYN+ACK数据包
  3. 然后服务端可以直接发送HTTP响应给客户端
  4. 客户端接收到SYN+ACK数据包,返回ACK数据包,完成三次握手

TCP

注意: 客户端最后握手的 ACK 不一定要等到服务端的 HTTP 响应到达才发送,两个过程没有任何关系。

TFO的优势

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

TCP流量控制

TCP

双方在使用TCP进行通信时,很可能发送方的速率和接收方的速率是不相等的。如果发送方发送的速率过快,而接收方处理又需要时间,这时候接收方只能把处理不过来的数据放入接收缓存区里(失序的数据包也会放入接收缓存区里)。

如果接收缓存区满了,发送方还在一直发送数据,接收方只能把收到的数据包丢掉,这就会产生大量的丢包,浪费网络资源。

因此我们需要根据接收缓存区的大小,动态调整发送方的发送,这就是所谓的流量控制。

滑动窗口

TCP通过滑动窗口的概念来实现流量控制,由于TCP是全双工的传输层协议,因此通信双方都各有两个滑动窗口。

  1. 发送窗口:根据对方接收窗口大小调整自己的大小,控制自己的传输速率,用来发送数据。
  2. 接收窗口:根据缓存区剩余大小调整自己的大小,用来接收数据。

流量控制具体过程

TCP

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

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

当发送方停止发送数据后,该怎样才能知道自己可以继续发送数据?

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

TCP拥塞控制

流量控制与接收方的接收窗口相关联,并没有考虑到整个网络环境的影响。

如果说当前网络特别差,特别拥塞,发送方发出去的数据包都被堵在路上了,迟迟没有到达接收方。发送方迟迟收不到回应,就会以为丢包了,然后进行重传。但这样的结果就是不但浪费了网络资源,还使得网络更加拥塞了,因此,我们需要拥塞控制。

对于拥塞控制来说,每条TCP连接都要维护两个状态:

  1. 拥塞窗口
  2. 慢启动阀值

拥塞控制包含以下几个算法:

  1. 慢启动
  2. 拥塞避免
  3. 快速重传
  4. 快速恢复

拥塞窗口

拥塞窗口指的是目前自己还能传输的数据量大小,用来限制发送窗口。

发送窗口 = min(接收窗口,拥塞窗口)

发送窗口会取两者的较小值。而拥塞控制,就是来控制拥塞窗口的。

慢启动

三次握手结束,刚进入传输阶段的时候,发送方并不知道当前的网络状况如何。如果一开始就快速大量的发送数据包,很可能会导致大量的丢包和网络拥塞。

所以,拥塞控制首先要做的就是采用一种保守算法来慢慢的适应整个网络,这个算法就叫做慢启动。

过程如下:

  1. 首先三次握手,双方宣告自己的接收窗口大小。
  2. 双方初始化自己的拥塞窗口大小。
  3. 在开始传输的一段时间,发送端每接收到一个ACK确认数据包,都会使拥塞窗口翻倍。也就是如果初始拥塞窗口为10,那么每经过一个RTT,就会变成20,40,80...依次类推。

但也不会一直翻倍下去,当拥塞窗口大小到达一个阀值时,就不会涨那么快了,这个阀值就叫慢启动阀值。

到达慢启动阀值后,如何控制拥塞窗口大小呢?

那就是拥塞避免要做的事情了。

拥塞避免

当到达慢启动阀值后,发送方每收到一个ACK确认数据包,拥塞窗口大小会增加1。

也就是说,以前一个RTT下来,拥塞窗口大小翻倍,现在只增加1而已。

当然,慢启动和拥塞避免是一起作用的,是一体的。

快速重传

在TCP传输过程中,如果接收方发现数据包不是按序到达的,也就是发现了丢包,就会重复发送之前的ACK数据包。

比如第5个包丢了,即使6.7个包已到达,也会先存在缓存区里,然后接收方会一律返回第4个包的ACK确认包。

当发送方收到3个重复的ACK数据包时,会意识到丢包了,然后马上进行重传,不用等到一个RTO(重传超时时间)再进行重传。

快速恢复

当发送方收到3个重复的ACK数据包时,发现丢包,就会觉得当前的网络有点拥塞了,自己会进入快速恢复阶段。

在这个阶段,发送方发生以下改变:

  1. 拥塞阀值降低为拥塞窗口大小的一半
  2. 拥塞窗口大小变为拥塞阀值
  3. 拥塞窗口大小线性增长,也就是一个RTT,大小+1

以上就是TCP拥塞控制的经典算法: 慢启动、拥塞避免、快速重传和快速恢复。

参考

TCP协议灵魂之问,巩固你的网路底层基础
前端需要了解的计算机网络知识
通俗易懂讲解TCP流量控制机制

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant