vlambda博客
学习文章列表

TCP协议 十大特性详解(上)

TCP协议

TCP全称为 “传输控制协议(Transmission Control Protocol”). 人如其名, 要对数据的传输进行一个详细的控制。


TCP协议段格式


源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去;

32位序号/32位确认号: 后面详细讲;

4位TCP报头长度: 表示该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 = 60

6位标志位:

URG: 紧急指针是否有效

ACK: 确认号是否有效

PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走

RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段

SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段

FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段


16位窗口大小: 后面再说


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


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


40字节头部选项: 暂时忽略;


选项:选项中的内容不确定,因此就必须用首部长度来区分选项的具体长度。


TCP的十个特性

确认应答(ACK)机制(可靠传输的重要机制)

正常传输

TCP协议 十大特性详解(上)

所谓应答报文,本质上就是ACK字段为1的报文,此时,报头中的“确认序号”字段才是生效的。

TCP通过确认应答(ACK)实现可靠传输。当发送端将数据发出后会等待对端的确认应答。如果有确认应答,说明数据已经成功到达对端。反之,则数据丢失可能性较大。


数据包丢失

TCP协议 十大特性详解(上)


确认应答丢失

TCP协议 十大特性详解(上)

上边我们发现主机B反复收到相同的数据。因而为了对上层应用提供可靠的传输,必须放弃重复的数据包。为此就必须引入一种机制,它能够识别是否已经接收数据,又能够判断是否需要接收。然而这些功能可以通过序列号来实现,就这样我们通过序列号和确认应答号,TCP就可以实现可靠传输。


TCP将每个字节的数据都进行了编号. 即为序列号:

TCP协议 十大特性详解(上)

每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。


超时重传机制

主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B;

如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发;

但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了

那么, 如果超时的时间如何确定?


最理想的情况下, 找到一个最小的时间, 保证 “确认应答一定能在这个时间内返回”.

但是这个时间的长短, 随着网络环境的不同, 是有差异的.

如果超时时间设的太长, 会影响整体的重传效率;

如果超时时间设的太短, 有可能会频繁发送重复的包;

TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间.


Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制,

每次判定超时重发的超时时间都是500ms的整数倍.


如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.


如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.


累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接.


连接管理机制

TCP连接的建立和断开(三次握手 四次挥手)


TCP协议 十大特性详解(上)

建立连接(三次握手)

为啥要建立连接?

1、更好的保证可靠性:建立连接的过程就是再让通信双方验证各自的发送能力和接收能力是否正常;

2、协商一些重要参数。

TCP协议 十大特性详解(上)

建立连接的过程,相当于通信双方各自给对方发送SYN,在各自给对方发送ACK。只不过中间ACK和SYN合二为一了,于是最终是三次握手。

SYN 同步报文段,尝试和对方建立连接。Java Socket API中,客户端 new Socket (ip,port),内核就会发起这样的SYN 请求。


建立连接三次握手握2次可以吗?

答:不可以,如果没有最后一个ACK, 此时主机B 是无法知道自己的发送能力和对方的接收能力是否正常。

建立连接三次握手握4次可以吗?

答:可以,但是没有必要,中间的SYN 和ACK 是同一时机触发的,两个包合并在一起比较高效。

TCP协议 十大特性详解(上)


几个重要的状态:


[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中,并向客户端发送SYN确认报文.

[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态,可以进行读写数据了.

[ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close), 服务器会收到结束报文段,服务器返回确认报文段并进入CLOSE_WAIT;

断开连接(四次挥手)

断开连接,双方各自向对方发起断开连接的请求,再给对方回应,只不过中间的FIN 和 ACK 不一定能合并在一起。


建立连接过程中,ACK和SYN都是内核决定的,时间上是同一时刻发送的,所以就可以合并在一起。但是断开连接中,主机B发送ACK是内核自动完成的,内核在收到FIN 后直接发送ACK,但是主机B的接收缓存区中仍然可能有一些待处理数据,需要等到主机B处理完毕后,调用Socket 的close方法的时候,才会发送FIN。所以按理来说,ACK和FIN不能合并,但是有特殊情况,后面介绍到“延时应答”时候会解释。从而四次挥手挥三次也是可以的。


TIME_WAIT

想一想, 为什么是TIME_WAIT的时间是2MSL?


MSL是TCP报文的最大生存时间,

因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,

可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);

同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了,

但是TCP连接还在, 仍然可以重发LAST_ACK);

CLOSE_WAIT

一般而言,对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题.


注意:三次握手和四次挥手过程中,一旦丢包,就会触发超时重传。保持时间会一直持续到对方不会重传了,就会释放连接,真正进入CLOSED状态。

————————————————

版权声明:本文为CSDN博主「Josvin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_45532227/article/details/107214393