TCP 协议 - 学习笔记

一、TCP 协议是什么

TCP(Transmission Control Protocol):全称为传输控制协议,是实现运输层的一种协议

TCP 协议特点

(1)TCP 是面向连接的运输层协议

(2)TCP 协议只能是一对一的

(3)TCP 提供可靠的服务,通过 TCP 传送的数据,无差错、不丢失、不重复并且按序到达

(4)TCP 提供全双工通讯,双方应用均可以同时发送或者接收数据

(5)TCP 是面向字节流的,对应应用传递的数据,可能会进行分组发送,因此接受方应用程序必须有能力识别收到的字节流

TCP 连接

TCP 是面向连接的传输协议,TCP 的许多特性均与连接有关

每一条 TCP 连接均有两个端点,每个端点叫做套接字(socket)或者插口,其定义为:ip:port,一组(ip,port)即构成一条 TCP 链接

二、TCP 协议报文格式

TCP 协议报文格式如下

TCP 报文协议由首部数据部分组成,首部由 20 个字节的固定长度和 40 个字节的可变长度(选项部分)组成,下述主要说明 TCP 首部的组成部分

(1)源端口与目标端口:源端口占 2 字节,代表发送发端口号。目的端口占 2 字节,代表接收方端口号。

(2)序号:占 4 字节,取值范围为 [0, 232-1]。TCP 是面向字节流的,每一个字节都按顺序编号,首部中的序号字段,代表本次报文数据部分的第一个字节的编号

(3)确认号:占 4 字节,由接收方返回,取值范围为 [0, 232-1]。确认号用于告诉发送方,期望收到的下一个报文段的第一个数据字节的编号

(4)数据偏移:占 4 位,最大值为 15,数据偏移代表 该 TCP 报文段的数据起点位置距离 TCP 报文段的起点位置的长度,即也代表了首部的长度,它的单位是 4 字节,所以 TCP 首部的最大长度为 4 * 15 = 60,因此选项部分(可变长度)最长为 40 个字节

(5)保留:占 6 位,为保留数据,今后使用,目前都为 0

(6)URG(urgent):紧急位,当 URG = 1 时,表示告诉接收者该 TCP 报文中存在紧急数据,发送方会把紧急数据放到本段报文数据部分的最前面,这时需要与首部中的紧急指针字段配合使用

(7)ACK(acknowledge):确认位,当且仅当 ACK = 1 时,确认号字段才有效

(8)PSH(push):推送位,当 PSH = 1 时,表示发送方希望立刻就能收到对方的响应。当 PSH 置为 1 时,会立即创建一个报文段发送出去,接收方收到该报文时,尽快的交付给上层应用,而不等到缓存满了再交付。

(9)RST(reset):重置位,当 RTS = 1 时,表示 TCP 连接中出现了严重错误,需要立即断开连接,然后再重新建立连接。

(10)SYN(synchronization):同步位,用于建立连接,当 SYN = 1,ACK = 0 时,表示发送发请求建立连接,接收方如果同意连接,则在报文中使 SYN = 1,ACK = 1。因此 SYN = 1 就表示这个是一个请求建立连接报文或者连接接受报文。传输过程中,SYN = 0,ACK = 1。

(11)FIN(Finish):终止位,当 FIN = 1 时,表示发送方的数据已经发送完毕,请求施放连接。

(12)窗口:占 2 字节,窗口字段用于告诉发送方,从本段报文的确认号起,当前应用可以接受的最大字节数,窗口值将作为发送方设置起发送窗口的一个依据。

(13)校验和:与 UDP 用法一致,会增加一个 12 字节的伪首部(用于增加双方 IP 信息)来进行校验计算,计算范围:伪首部 + 首部 + 数据部分(计算中,会将校验和字段置为 0,同时按偶数字节计算,如果计算范围为奇数字节,则会增加一个全 0 字节)

(14)紧急指针:占 2 字节,与 URG = 1 时配合使用,表示本段报文数据中紧急数据部分的字节数(紧急数据之后为普通数据)

(15)选项:范围可变,长度 [0, 40] 字节,用于可选的属性字段,当选项长度为 0 时,该报文首部为 20 字节长度。

选项中有以下几个可选属性:

  • MSS(Max Segment Size):最大报文长度,该长度指的是最大数据部分的长度,默认值为536,作为发送报文数据长度的一个参考
  • 其他的还有时间戳(10 字节)、窗口扩大(3 字节,用于扩大窗口字段范围,用于延迟和带宽都很大的场景)、确认选择(SACK,可用于连续 ARQ 协议的优化)

 

三、TCP 可靠传输

TCP 数据传输时可靠的,可靠性体现在传输的数据,无差错、不丢失、不重复、按序到达,这种特性由 TCP 协议的可靠性机制保证

3.1 可靠传输原理

TCP 协议的可靠性由以下几个方面原理保证

(a)停止等待协议

提示:停止等待协议不针对网络栈中的任何一层,在此将每次传输的数据简称为一个分组,存在一个发送方 A 和一个接收方 B

停止等待协议的基本规则是:发送方发送一个分组后就停止,等待接收方确认,收到确认后则开始发送下一个分组数据,流程如下:

但是发送数据的过程和确认数据的过程,会发生异常,所以有以下几种情况需要考虑

1、发送分组丢失了

发送分组丢失了,意味着接收方没有获取到数据,也不会发送确认,发送方将一直等待,在这种情况下,可靠性传输会增加一条规则:如果在一定时间内没有收到确认信息,则重新发送该分组,即超时重传,流程如下

2、确认消息丢了

这个时候,发送发也无法获取确认消息,因此会进行超时重传,并重新发送分组,但是,此时接收方将会获取到重复分组数据,如何应对重复分组数据呢?可以再增加一条规则:忽略重复的分组数据,并重新向发送方发送确认消息,流程如下:

3、确认消息延迟到达了,此时发送发已经重复发送了分组,接收方也再次进行了确认,此时可以按如下规则处理:直接丢弃延迟的确认消息,流程如下

由以上规则构成的可靠性传输协议叫做 ARQ(Automatic repeat request)自动重传确认协议

(b)连续 ARQ 协议

连续 ARQ 协议是实现滑动窗口的理论部分,示意图如下:

3.2 可靠传输实现

(a)滑动窗口协议

滑动窗口是一种逻辑上的数据窗口,以字节为基本单位,用于发送和接收数据,因此存在发送窗口接收窗口两种类型的滑动窗口,下面为这两种类型的示意图:

发送窗口包括两类数据:

  • 已经发送但是未收到确认的数据
  • 未发送的数据

接收窗口包括两类数据:

  • 有序到达的数据
  • 无序到达的数据

如果不考虑网络拥塞的情况,那么窗口的大小和起始位置由接收方报文中窗口字段值和确认号字段值决定

如果有网络拥塞的情况,窗口的大小可能会小于接收方报文中窗口字段值

滑动的含义是指:

  • 对于发送窗口:当收到确认报文后,会根据确认号字段值向前移动,并根据窗口值,调整大小
  • 对于接受窗口:当都到有序数据后,发送确认报文,根据已收到数据的最大位置向前移动,并根据接收缓空间可用容量调整窗口大小

(b)超时重传的选择

对于从发送数据到确认数据的时间,把它叫做报文段的往返时间 RTT 

对于超时重传的时间(RTO RetransmissionTime-Out)设定,是一个比较复杂的问题,计算公式如下:

RTTs 是指加权往返时间,计算公式如下,α 默认取值 0.125:

RTTD 是指 RTT 的偏差的加权平均值,β 默认取值 0.25,计算公示如下:

对于报文往返时间(RTT)的计算,在超时重传的情况下计算会存在问题(会有重复接收到确认报文的情况,而无法选择那一个来计算),因此优化的逻辑如下:

  • 对于重传情况下,忽略当前报文 RTT 的计算
  • 同时取新的重传时间为旧的重传时间的两倍

(c)确认选择 SACK

对于接受方,如果收到的数据是无序的,有没有一种方式,只通知发送发重发未收到的数据?

可以使用保温首部中的 SACK 确认选择字段,它会将未收到数据端的区间信息返回给发送方。当然,由于 TCP 官方文档没有指明如何处理 SACK 字段,因此大多数操作系统的 TCP 实现还是重传所有未确认数据。

四、TCP 流量控制

使用滑动窗口进行流量控制

流量控制(Flow Control):是指控制发送方发送数据的速率,使得接收方来得及接收数据

在 TCP 中使用滑动窗口来对流量进行控制,流程示意图如下:

其中 seq 代表发送方的序列号,ACK 代表接收方的确认位,ack 代表接收方的确认号,rwnd 代表收方的窗口大小。

图中,接收方分别对窗口进行了3次调整,rwnd = 300,rwnd = 100,rwnd = 0,对于 rwnd = 0,表示接收方缓存已满,无法再接受数据,通知发送方暂停发送。

持续计时器

对于 rwnd = 0,如果发送方没有获得新的窗口调整消息,或者窗口调整消息丢失了,那么将一直等待下去,进入死锁状态,在 TCP 中,使用持续计时器来解决这个问题。

持续计数器:是指当一方接收到了 rwnd = 0,启动一个计时器,当计时器结束还没有收到新的报文时,则向对方发送一个探测报文,对方根据收到的探测报文再次进行回复。如果探测报文一直未得到确认,那么代表连接出现了严重错误,将断开连接。

TCP 的传输效率

如何控制 TCP 发送报文的时机,也是一个比较复杂的问题,一般采用 Nagle 算法。

Nagle 算法:先发送较少数据的报文,等收到确认后再发送较多数据的报文。只有当收到前一个报文的确认后,才发送下一个报文段数据。

五、TCP 拥塞控制

拥塞控制是指:根据网络拥塞情况来调整发送方数据发送速率。

与流程控制不同,流量控制是端到端的控制,只涉及到发送方和接收方,而拥塞控制是全局的,需要考虑到整个网络的情况。

TCP 拥塞控制算法

拥塞控制算法有:慢开始(slow start)、拥塞避免(congestion avoidance)、快速重传(fast retransmit)、快恢复(fast recovery)四种

先列举一下算法中的概念定义:

  • cwnd(congestion window):拥塞窗口状态变量
  • SMSS(Sender Maximum Segment Size):最大报文段大小
  • ssthresh:慢开始门限

慢开始算法

由小到大逐渐增大发送窗口(即由小到大逐渐增大 cwnd 的值),每当收到 1 个报文段的确认后,拥塞窗口增大 1 个 SMSS 大小。由于报文段通常都是连续发送的,如果连续发送 N 个报文段,收到确认后,拥塞窗口也会增大 N * SMSS 大小,示例图如下

一般把同时发送的多个报文段,叫做一个传输轮次,拥塞窗口不能无限扩展,否则会导致网络拥塞,因此引入了变量 ssthresh 慢开始门限,当 cwnd >= ssthresh 时使用拥塞避免算法。

拥塞避免算法

当 cwnd >= ssthresh 时,每个传输轮次确认后,拥塞窗口增大 1 个 SMSS 大小(而不是 N 个 SMSS 大小)

当发生 TCP 报文超时重传时,ssthresh 降低为原来的 1/2,同时 cwnd 变为 1,重新进入慢开始算法

快速重传算法

由于TCP 报文超时重传时,ssthresh 降低为原来的 1/2,同时 cwnd 变为 1,这会严重降低传输速率,并且,一个传输轮次中,可能只是某一个报文丢失,是否可以快速重传该报文呢?

快速重传算法思路为:当接收方收到不连续报文时,快速重传上一次的确认报文,当接收方连续收到 3 相同确认报文时,就知道是有一个报文丢失了,快速重传该报文即可,无序等待 RTT 时间到来

快恢复算法

思路为:当发生快速重传情况后,将 ssthresh 置为 cwnd 的 1/2,同时 cwnd = ssthresh,然后进入拥塞避免算法阶段

六、TCP 连接管理

TCP 的整个数据传输过程分为三个阶段,连接建立、数据传输和连接释放,连接管理就是用于连接建立阶段和连接释放阶段。

TCP 连接的建立采用客户服务端模式,发起连接建立的一方叫客户端(client),被动等待连接建立的一方叫服务端(server)

6.1 连接建立(三次握手)

核心定义:TCP 连接建立的过程叫做握手,建立过程需要交换三个 TCP 报文段,简称为三次握手

三次握手的示意图如下:

初始阶段:客户和服务器均处于连接关闭状态,服务器会首先启动进程,打开对端口(套接字)的监听

第一次:客户端首先发送连接请求报文,报文中 SYN(同步位) = 1,seq(序号) = x。

  • SYN = 1,ACK = 0 时就代表该报文是连接请求报文
  • seq = x,是指客户端选择发送数据的初始序号值,同时 TCP 协议规定,请求报文不携带数据,但是消耗 1 个序号(这就是第二次握手中 ack = x + 1 的原因)

第二次:服务端收到连接请求报文后,如果同意建立连接,则发送同意连接报文,报文中 SYN = 1,ACK(确认位) = 1,seq = y,ack = x + 1

  • SYN = 1,ACK = 1 时就代表该报文是同意连接报文
  • seq = y,是指服务端选择发送数据的初始序号值,同时 TCP 协议规定,请求报文不携带数据,但是消耗 1 个序号(这就是第三次握手中 ack = y + 1 的原因)
  • ack = x + 1,意思是告诉客户端,下一个请求发送的数据号是 x + 1

第三次:当客户端收到同意连接报文后,再次进行答复,ACK = 1,seq = x + 1,ack = y + 1,发送再次答复报文后,客户端状态变更为已连接状态,该条报文可以携带数据,如果不携带数据,则不消耗序号。

当服务端收到再次答复报文后,更改连接状态为已连接状态

核心问题

当发送了请求建立,并且服务端已经同意建立,为什么还需要第三次确认呢 ?

答案:如果报文能正常发送和接收,2 次握手已经够了,但是报文发送会发生这种异常情况:客户端发送了连接请求报文,但是因为网络问题,延迟到达,在延迟的过程中,客户端超时重起了请求,并且完成数据传输,然后关闭了连接,这个时候第一次发送的连接请求报文才到达服务端,服务端会误以为是再次建立连接,从而答复同意(这时候客户端对于这个答复不做处理),同时建立起了连接,这样就处于空等状态,浪费了资源。第三次握手就是解决了这样的问题。

6.2 连接释放(四次放手)

TCP 连接释放需要经过 4 个报文交换,简称四次放手,示意图如下:

客户端、服务端均可以发起连接关闭,不过通常来说都是从客户端发起

第一次:客户端发送连接关闭报文,报文中 FIN = 1,ACK = 0,seq = u,此时客户端进入 FIN-WAIT-1(停止等待-1)状态

  • FIN = 1 就代表这是一个连接关闭报文
  • seq = u,u 表示发送数据的最后一个序号 + 1,TCP 协议规定连接关闭报文不携带数据,但是消耗 1 个序号

第二次:服务端收到连接关闭报文后,发送确认报文,进入(CLOSE-WAIT)关闭等待状态

  • 此时服务端还可以向客户端发送消息
  • 客户端收到确认报文后进入FIN-WAIT-2(停止等待-2状态),此时客户端进入了半关闭连接状态,此时会通知应用已关闭客户 -> 服务器方向的连接,但此时还可以接受数据,然后等待服务端发送连接关闭报文

第三次:服务端没有更多数据要发送后,即从服务端发起一次连接关闭报文,报文中 FIN = 1,ACK = 2,指明这是第二次连接关闭报文,然后进入 LAST-ACK (最后确认)状态

第四次:客户端接收到来自服务端的连接关闭报文后,发送确认报文,然后进入 TIME-WAIT(时间等待)倒计时状态,计时 2MSL(最长报文段寿命),服务端接收到确认报文后,连接立即关闭。客户端的关闭时间要比服务端晚一些。

为什么要有时间等待状态呢?

2MSL 为 4 分钟

主要解决 2 个问题:(1)保证最后一次确认报文可以被服务端收到,由于确认报文可能丢失,这个时候服务端会超时重传连接关闭报文,客户端收到后可以重新发送确认报文。(2)解决 TCP 连接中,连接请求报文延迟到达的问题,在 4 分钟的时间内,延迟的报文足够被拒绝处理了。

 

名词解释:

RTT(Round Trip Time):TCP 报文段往返时间

 

参考:

《计算机网络》(第七版)第五章
什么是 TCP :https://blog.csdn.net/armlinuxww/article/details/104581606

 

posted @ 2024-11-14 09:38  lenbkan  阅读(20)  评论(0编辑  收藏  举报