TCP三次握手、数据传输与四次挥手

在这里插入图片描述

一、建立TCP连接 —— 三次握手

(1)客户端向服务端发送一个携带客户端随机初始序列号x的SYN报文,进入SYN-send状态。
(2)服务端收到后将其加入到半连接队列,进入SYN-rcvd状态。然后向客户端回复携带服务端初始序列号y的SYN+ACK报文,这里的ack确认应答号就是x+1。
(3)客户端收到后再向服务端发送一个确认应答号为y+1的ACK包,服务端收到后确认建立连接,放入到全连接队列,此时双方都进入了Established状态,表示连接已建立成功,可以进行数据交互了。

在这里插入图片描述

问题1:为什么是三次握手?不是两次、四次?
  • 防止历史重复连接:例如当客户端发了一个SYN包由于网络拥堵没有及时得到回复,于是发了一个新的SYN包,如果是两次握手会重复连接多次,造成资源浪费。
  • 服务端回复的SYN+ACK包可合并一起发送,无需分成两次,因此不需要四次
问题2:第三次握手丢失了,会发生什么?
  • 因为这个第三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传 SYN-ACK 报文,直到收到第三次握手,或者达到最大重传次数。

  • 注意,ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文

问题3:第三次握手可以携带数据吗?

首先前两次握手是不能携带数据的,第一个还没确认链路是否畅通,第二个客户端可能会利用第一次握手发送携带大量数据的SYN报文进行恶意攻击,但是不响应服务端的SYN+ACK报文。

SYN泛洪攻击

那对于第三次握手理论上是可以携带数据,因为此时对于客户端来说连接已经建立成功了。这里面涉及到TCP的一个捎带应答机制:就是在发送一个ACK报文的时候,正好也要发送数据报文,就把这两个报文合并一块发送了。

捎带应答机制

二、数据传输过程

建立连接后双方就可以开始正式的数据通信了

1. TCP数据分片

TCP传输是面向字节流的,数据之间没有边界,当数据量过大时,在TCP层面就会按MSS大小(MTU-IP&TCP头部长度)对数据进行分片,每片数据再构成一个TCP报文交给IP层进行发送,以此避免超过MTU大小导致在IP层又对TCP报文进行分片,导致一片IP报文丢失时就要重传整个较大的TCP报文。

与TCP不同的是,UDP按包进行传输,不会对数据分片,这就是说应用层一次交给UDP多长的数据,UDP就将其组成一个UDP报文交给IP层,若报文过长,则IP层会进行分片,一片丢失则对于接收端整个UDP报文就丢失了,这也是UDP丢包率高的原因之一。

2. 确认应答机制

数据分片发送后那接收端如何重新将其排列为有序的数据流呢,发送端又是如何知道数据包安全抵达了呢?这就依靠TCP头部中的序列号和确认应答号了。

序列号:在建立连接时通信双方就会在SYN包中随机生成一个初始序列号发送给对方,之后发送数据包通信时就从上一次序列号+数据字节数长度作为序列号(连接和断开时的SYN包和FIN包数据长度视为1),这样接收端就可以按包的序号以及数据长度进行排列,解决网络包乱序问题。
确认应答号:接收端在收到数据后会发送一个ACK包,其中包含一个确认应答号,值为接收到的数据包序列号+数据长度,表示说这个序号前面的数据我都收到了,从这个序号开始发数据给我,这样发送端若没有收到对应的应答号,就可以对数据进行重传,解决丢包的问题。

在这里插入图片描述

为了保证传输的高效和可靠,TCP协议还做了很多复杂的机制,如重传机制、滑动窗口、流量控制、拥堵控制等,之后的文章再聊

三、关闭TCP连接 —— 四次挥手

(1)假设客户端要关闭连接,首先向服务端发送一个FIN包,自身进入终止等待1状态
(2)服务端收到后向客户端回复一个ACK包,自身进入关闭等待状态,此时仍可以向客户端发送数据。客户端收到ACK包后进入终止等待2状态
(3)服务端发送完毕后向客户端再发送一个FIN包,进入最后确认状态。
(4)客户端收到FIN包后向服务端发送一个ACK包,进入TIME-WAIT超时等待状态,会等待2MSL时间(MSL:报文最大生存时间)。服务端收到ACK包后就关闭连接,客户端超时完毕后也关闭连接。如果服务端再指定时间内没有收到ACK包会重发FIN包,客户端收到后会重发ACK包并刷新超时时间。

在这里插入图片描述

问题1:服务端出现大量Close-wait状态连接是什么原因?

首先在Close-wait是被动关闭连接方的状态,在服务端发送第二次挥手的ack报文后就会进入Close-wait状态,只有在调用close函数发送第三次挥手的fin报文后才会从Close-wait变为最后确认状态。如果说服务端出现大量Close-wait状态,那么有可能是以下两个原因:

  1. 服务端线程被阻塞了,没有调用到close函数。
  2. 服务端在调用close前有大量耗时的逻辑,导致迟迟调用不到close函数。

参考:
3. https://www.bilibili.com/video/BV1kV411j7hA
4. https://xiaolincoding.com/network/3_tcp/tcp_interview.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值