TCP与UDP的区别(上)

欢少的成长之路

共 4851字,需浏览 10分钟

 ·

2022-06-19 16:50

记得点击 "欢少的成长之路", 设为星标

后台点击【联系我】,申请加入优质技术学习社群

大家好,我是Leo。

之前聊了 体系结构,消息流程,TCP粘包 。今天接着粘包继续看看TCP与UDP的区别有哪些。

TCP与UDP区别

  1. TCP面向连接(如打电话要先拨号建立连接)。UDP是无连接的,即发送数据之前不需要建立连接 三次握手,四次挥手
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,也不保证可靠交付; 序列号与确认应答机制
  3. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的;
  4. UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等); 拥塞机制
  5. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信;
  6. TCP首部开销20字节;UDP的首部开销小,只有8个字节; 报文结构
  7. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。

三次握手与四次分手

三次握手

第一次握手:客户端将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给服务端,客户端进入SYN_SENT状态,等待服务端确认。

第二次握手:服务端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务端将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务端进入SYN_RCVD状态。

第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务端,服务端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务端进入ESTABLISHED状态,完成三次握手,随后客户端与服务端之间可以开始传输数据了。

SYN_SENT:代表TCP连接的发起方第一次发给接受方的时候设置成的状态。

SYN_RECV:服务端被动打开后,接收到了客户端的SYN并且发送了ACK时的状态。再进一步接收到客户端的ACK就进入ESTABLISHED状态。

ESTABLISHED:代表已经建立了连接

四次分手

在断开连接之前客户端和服务器都处于ESTABLISHED状态,双方都可以主动断开连接,以客户端主动断开连接为优。

第一次挥手:客户端打算断开连接,向服务器发送FIN报文(FIN标记位被设置为1,1表示为FIN,0表示不是),FIN报文中会指定一个序列号,之后客户端进入FIN_WAIT_1状态。

也就是客户端发出连接释放报文段(FIN报文),指定序列号seq = u,主动关闭TCP连接,等待服务器的确认。

第二次挥手:服务器收到连接释放报文段(FIN报文)后,就向客户端发送ACK应答报文,以客户端的FIN报文的序列号 seq+1 作为ACK应答报文段的确认序列号ack = seq+1 = u + 1。

接着服务器进入CLOSE_WAIT(等待关闭)状态,此时的TCP处于半关闭状态(下面会说什么是半关闭状态),客户端到服务器的连接释放。客户端收到来自服务器的ACK应答报文段后,进入FIN_WAIT_2状态。

第三次握手:服务器也打算断开连接,向客户端发送连接释放(FIN)报文段,之后服务器进入LASK_ACK(最后确认)状态,等待客户端的确认。

服务器的连接释放(FIN)报文段的FIN=1,ACK=1,序列号seq=m,确认序列号ack=u+1。

第四次握手:客户端收到来自服务器的连接释放(FIN)报文段后,会向服务器发送一个ACK应答报文段,以连接释放(FIN)报文段的确认序号 ack 作为ACK应答报文段的序列号 seq,以连接释放(FIN)报文段的序列号 seq+1作为确认序号ack。

之后客户端进入TIME_WAIT(时间等待)状态,服务器收到ACK应答报文段后,服务器就进入CLOSE(关闭)状态,到此服务器的连接已经完成关闭。

客户端处于TIME_WAIT状态时,此时的TCP还未释放掉,需要等待2MSL后,客户端才进入CLOSE状态。

MSL: 报文最大生成时间,它是任何报文在网络上存在的最长时间,超过这个时间的报文将被丢弃。2MSL=60秒

TIME_WAIT:通过这个状态我从网上查了一些,只有主动关闭连接的,才会有TIME_WAIT状态。所以画图的时候画在了左面

为什么分手需要四次

这是由于TCP的 半关闭(half-close) 造成的。半关闭是指:TCP提供了连接的一方在结束它的发送后还能接受来自另一端数据的能力。通俗来说,就是不能发送数据,但是还可以接受数据。

TCP不允许连接处于半打开状态时,就单向传输数据,因此完成三次握手后才可以传输数据(第三握手可以携带数据)。

当连接处于半关闭状态时,TCP是允许单向传输数据的,也就是说服务器此时仍然可以向客户端发送数据,等服务器不再发送数据时,才会发送FIN报文段,同意现在关闭连接。

这一特性是由于 TCP双向通道互相独立所导致 的,也使得关闭连接必须经过四次握手。

等待2MSL的意义

这一块就是介绍一下客户端为什么要等待2MSL之后才进入CLOSE状态。

  1. 保证客户端最后发送的ACK能够到达服务器,帮助其正常关闭。
  2. 防止已失效的连接请求报文段出现在本连接中

2MSL的时间是从 客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没 有传输到服务端,客户端⼜接收到了服务端重发的 FIN 报⽂,那么 2MSL 时间将重新计时。

三次握手的报文流程

  1. 第一个SYN报文:在三次握手时,客户端会随机生成序号,将序号放入TCP的首部的 序列号区域。同时把SYN标志位置于1,接着把第⼀个 SYN 报⽂发送给服务端,表示向服务端发起连接,之后客户端处于 SYN-SENT 状态
  2. 第二个SYN+ACK报文:服务端收到报文后随机生成序号,将序号继续放入 序列号区域,并把客户端的序列号+1放入 确认号区域。SYN和ACK都置为1。最后把该报⽂发给客户端,之后服务端处于 SYN-RCVD状态。
  3. 第三次ACK报文:客户端收到服务端的报文后,还要向服务端回应最后⼀个应答报⽂,⾸先该应答报⽂ TCP ⾸部 ACK 标志位置为1 ,其次服务端的序号+1放入确认号区域,最后把报⽂发送给服务端。

第三次握手的ACK报文可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED 状态。服务器收到客户端的应答报⽂后,也进⼊ ESTABLISHED 状态。

序列号与确认应答机制

TCP通过序列号与确认应答机制实现可靠的数据传输。当发送端将数据发出之后会等待接收端的确认应答。如果有确认应答,说明数据已经成功到达。如果没有确认应答,很有可能就丢包了。

数据包丢失

在一定时间内,还没有收到确认应答包,就会进行重发。所以即使丢包,仍然能够保证数据可靠传输。

特定时间的间隔就是 主机A向主机B两次数据请求的间隔。

确认包丢失

确认应答机制是双向的,所以主机A请求主机B可能出问题,主机B回传主机A也有可能出现丢包的情况,下面我们就用图解释一下。

超时如何确定

超时主要由RTO 确认的,RTO代表往返时间,由下图解释。

  • 当超时时间较大时,下一次重发就会变慢,效率低,性能差。
  • 当超时时间较小时,下一次重发就会过快,以至于并没有丢包就重发了,这样就导致了更多的超时,更多的超时又导致了更多的重发。

解决方案:可以通过TCP建立连接时间的延时时间与参考。通过RTT+DelayACK+抖动时间的算法来计算,而不再需要取一个估计的最小值

半连接队列和全连接队列

介绍

半连接队列是SYN队列,代表服务端接收到客户端的请求后。

全连接队列是SYN+ACK(accept)队列,代表建立连接后。.

在客户端发起第一次连接时,服务端会将其加入到syn队列中,并且响应客户端syn+ack报文,等到客户端发送ack应答报文时,服务端将该连接从半连接队列中取出,并新建一个新的连接,加入到accept队列当中。等待进程调用accept请求时,将该连接取出来

不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,内核会直接丢弃,或返回 RST 包。

SYN攻击

这里也就是我们经常听说的SYN攻击,如果一直对服务端发送syn包,但是不回ack回应包,这样就会使得服务端有大量请求处于syn_recv状态,这就是所谓的syn洪泛,syn攻击,DDos攻击

解决方案

  1. 增大半连接队列
  2. 开启tcp_syncookies功能
  3. 减少ack+syn报文的重传次数

2.开启tcp_syncookies:可以在不使用syn半连接队列的情况下建立连接syncookies在接收到客户端的syn报文时,计算出一个值,放到syn+ack报文中发出。当客户端返回ack报文时,取出该值验证,成功则建立连接

3.因为我们在收到syn攻击时,服务端会重传syn+ack报文到最大次数,才会断开连接。针对syn攻击的场景,我们可以减少ack+syn报文的重传次数,使处于syn_recv状态的它们更快断开连接 修改重传次数:/proc/sys/net/ipv4/tcp_synack_retries

全连接队列溢出

当服务端的全连接队列过小时,容易发生全连接队列溢出。发生全连接队列溢出,后续的请求就会别丢弃。

Linux有个参数可以指定TCP全连接队列满了,会使用什么策略来回应客户端。

丢弃连接只是linux的默认行为,我们还可以向客户端发送RST报文终止连接,告诉客户端连接失败

解决方案

tcp_abort_on_overflow共有两个值分别是0和1

  • 0:如果全连接队列满了,那么服务端丢弃ack报文
  • 1:如果全连接队列满了,那么服务端会想客户端发送RST报文,终止这个握手连接

通常情况下设置为0更好,可以提高效率

  • 如果设置为0的话,此时服务端全连接队列满了,客户端发送过来的ack报文,服务端丢弃。而此时客户端还会继续重传,如果此时服务端的全连接队列有空闲,那么就会接受重传的ack包,这样就能直接建立连接了。
  • 而设置为1的话,还需要重新连接

当全连接队列溢出后,我们需要增大全连接队列的长度,以提高请求容量。

TCP 全连接队列的最大值取决于 somaxconn 和 backlog 之间的最小值,也就是 min(somaxconn, backlog),所以我们需要提高这两个参数的大小才能拿增大全连接队列

报文结构

TCP报文结构

URG:紧急指针(urgent pointer)有效。

ACK:确认序号有效。

PSH:接收方应该尽快将这个报文交给应用层。

RST:重置连接。

SYN:发起一个新连接。

FIN:释放一个连接。

16位窗口大小: 接收缓冲区剩余的空间大小

16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP 首部, 也包含TCP数据部分.

16位紧急指针: 标识哪部分数据是紧急数据;

UDP报文结构

16位UDP长度,表示整个数据报(UDP首部+UDP数据)的最大长度;

如果校验出错,就会直接丢弃。

UDP数据报最大长度64K(包含UDP首部),如果数据长度超过64K就需要在应用层手动分包,UDP无法保证包序,需要在应用层进行编号。

结尾

这一篇从TCP与UDP的区别,逐一展开每一块技术。

  • 三次握手
  • 四次分手
  • 握手报文流程
  • 客户端等待2MSL的意义
  • 通过序列号与确认应答机制实现可靠传输
  • 半连接队列与全连接队列

下一篇将继续TCP与UDP的区别,展开聊一下窗口控制,重发控制,流量控制,拥塞控制。


浏览 64
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报