文章目录
TCP
TCP 概述
- 点对点
- 一个发送方,一个接收方
- 可靠的、按序的字节流
- 流水线机制
- TCP 拥塞控制和流量控制机制设置窗口尺寸
- 发送方 / 接收方缓存
- 全双工
- 同一连接中能够传输双向数据流
- 面向连接
- 通信双方在发送数据之前必须建立连接
- 连接状态只在连接的两端中维护,在沿途节点中并不维护状态。
- 流量控制机制
- TCP 段结构
- TCP:序列号和 ACK
- 序列号指的是 segment中第一个字节的编号,而不是 segment 的编号,即上图第一个报文段序列号为 0 ,第二个报文段序列号为 1000 ,以此类推。
- ACK
- 希望接收到下一个字节的序列号
- 累计确认:该序列号之前的所有字节均已被正确接收到。
- 接收方如何处理乱序到达的 Segment
- TCP 规范中没有规定,由 TCP 实现者做决策
TCP 可靠数据传输
概述
- TCP 在 IP 层提供的不可靠服务基础上实现可靠数据传输
- 流水线机制
- 累计确认
- TCP 使用单一重传计时器
- 触发重传的事件:
- 超时
- 收到重复 ACK
- 渐进式
- 暂不考虑重复 ACK
- 暂不考虑流量控制
- 暂不考虑拥塞控制
TCP RTT 和 超时
-
定时器的超时时间:
- 大于 RTT,但 RTT 是变化的
- 过短:不必要的重传
- 过长:对段丢失时间反应慢
![在这里插入图片描述]
-
如何估计 RTT:
-
SampleRTT:测量从段发出去到收到 ACK 的时间
-
SampleRTT 变化:测量多个 SampleRTT,求平均值,形成 RTT 的估计值 EstimatedRTT。
E s t i m a t e d R T T = ( 1 − α ) ∗ E s t i m a t e d R T T + α ∗ S a m p l e R T T EstimatedRTT = (1-\alpha) * EstimatedRTT + \alpha *SampleRTT EstimatedRTT=(1−α)∗EstimatedRTT+α∗SampleRTT
α \alpha α 推荐值为 0.125
E s t i m a t e d R T T = ( 0.875 ) ∗ E s t i m a t e d R T T + 0.125 ∗ S a m p l e R T T EstimatedRTT = (0.875) * EstimatedRTT + 0.125 *SampleRTT EstimatedRTT=(0.875)∗EstimatedRTT+0.125∗SampleRTT -
DevRTT:测量 RTT 的变化值,SampleRTT 与 Estimated 的 差值 D e v R T T = ( 1 − β ) ∗ D e v R T T + β ∗ ∣ S a m p l e R T T − E s t i m a t e d R T T ∣ DevRTT = (1-\beta) * DevRTT + \beta * |Sample RTT - EstimatedRTT| DevRTT=(1−β)∗DevRTT+β∗∣SampleRTT−EstimatedRTT∣
β \beta β 的 推荐值为 0.25
-
-
定时器超时时间的设置:
- EstimatedRTT + 余量
- EstimatedRTT 变化大 -> 较大的余量
T i m e o u t I n t e r v a l = E s t i m a t e d R T T + 4 ∗ D e v R T T TimeoutInterval = EstimatedRTT + 4*DevRTT TimeoutInterval=EstimatedRTT+4∗DevRTT
TCP 可靠数据传输
TCP 发送方事件
-
从应用层收到数据:
- 创建 Segment
- 序列号是 Segment 第一个字节的编号
- 开启计时器
- 设置超时时间 TimeOutInterval
-
超时
- 重传引起超时的 Segment
- 重启定时器
-
收到 ACK
- 如果确认此前未确认的 Segment
- 更新 SendBase
- 如果窗口中还有未被确认的分组,重启定时器。
- 如果确认此前未确认的 Segment
TCP 重传
-
由于确认丢失而重传:首先 主机B 发送 ACK 过程中分组丢失,主机 A 发生了 Timeout ,重传了该 8字节 数据,收到后主机 B 发送 ACK = 100,主机 A 收到后,sendBase = 100。
-
由于超时导致重传:主机 A 发送了 Seq = 92,8 bytes 和 Seq = 100,20 bytes,主机 B 依次返回 ACK = 100,ACK = 120,但由于到达 主机A 的时候 Sendbase = 100 —> Sendbase = 120,由于已超时,在此之前主机 A 重传了分组 Seq = 92,由于累计确认机制,主机 B 收到后产生了冗余分组,丢弃 Seq = 92 并再次发送 ACK = 120。
-
TCP 的实现中,如果发生超时,超时时间间隔将重新设置,即将超时时间间隔加倍,导致其很大
- 重发丢失的分组之前要等待很长时间
-
通过重复 ACK 检测分组丢失
- sender 会背靠背地发送多个分组
- 如果某个分组丢失,可能会引发多个重复的 ACK
-
快速重传:在定时器超时之前即进行重传
如果 sender 收到对同一数据的 3 个 ACK,则假定该数据之后的段已经丢失
TCP 流量控制
-
流量控制:发送方不会传输的太多、太快以至于淹没接收方
-
速度匹配机制
-
接收方为 TCP 连接分配 buffer
-
上层应用可能处理 buffer 中数据的速度较慢
-
举例
对主机 A 进行流控,需要滑动窗口并挑中窗口大小。
对主机 A 再次流控
TCP 连接
- TCP 是面向连接的协议,它基于运输连接来传送 TCP 报文段
- TCP 运输链接的建立和释放是每一次面向连接的通信中必不可少的过程。
- TCP 运输链接有以下三个阶段:
- 建立 TCP 连接
- 数据传送
- 释放 TCP 连接
- TCP 的运输连接管理就是使运输连接的建立和释放都能正常地进行。
TCP 连接的建立
-
TCP 的连接建立需要解决一下三个问题:
- 使 TCP 双方能够确知对方的存在
- 使 TCP 双方能够协商一些参数(最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等)
- 使 TCP 双方能够对运输实体资源(缓存大小、连接表中的项目等)进行分配
-
TCP 三报文握手
- TCP 服务器创建传输控制块,进入监听状态。(被动打开连接)
- TCP 客户创建传输控制块,向TCP 服务器进程发送 TCP 连接请求报文段,进入同步已发送状态(SYN - SENT)
- TCP 连接请求首部报文段 的
SYN = 1
seq = x
作为 TCP 客户进程所选择的初始序号。==(主动打开连接)
注意:这个报文段不能携带数据,但消耗序号
- TCP 连接请求首部报文段 的
- TCP 服务器进程收到 TCP 连接请求报文段后,如果同意连接,则向 TCP 客户进程发送 TCP 连接请求确认报文段,并进入同步已接收状态(SYN - RCVD)
- TCP 连接请求确认报文段 首部同步位
SYN = 1
和 首部确认位ACK = 1
seq = y
作为TCP 服务器进程的初始序号 注意:这个报文段不能携带数据,但消耗序号ack = x + 1
是对TCP 客户进程所选择的初始序号的确认。
- TCP 连接请求确认报文段 首部同步位
- TCP 客户进程收到 TCP 连接请求报文段后,还要向 TCP 服务器进程发送 一个普通的 TCP 确认报文段,进入连接已建立状态
- 一个普通的 TCP 确认报文段 该报文段
ACK = 1
seq = x + 1
,因为 TCP 客户端发送的第一个 TCP 报文段的序号是 x,由于这是一个普通的 TCP 报文段且不携带数据,不消耗序号,所以下一个报文序号仍为x + 1
ack = y + 1
这是对 TCP 服务器进程所选择的初始信号的确认
- 一个普通的 TCP 确认报文段 该报文段
- TCP 连接已建立
三次握手中最后一次普通的 TCP 确认报文段是否多余?
假设没有这次握手,三次握手转为二次握手,即 TCP 服务器收到 TCP 连接请求就建立连接。
若发生如下情况:
- 第一个 TCP 连接请求滞留在网络中,引起超时重传
- 第二个 TCP 请求发出
- TCP 服务器收到第二个 TCP 连接请求进入已建立状态
- 数据传输完毕,关闭 TCP 连接
- 第一个 TCP 连接才到达 TCP 服务器
- TCP 服务器会认为是客户端再次请求连接并进入已连接状态
- TCP 客户端不理睬,因为没有建立新的连接
- TCP 服务器等待 TCP 客户端响应
所以第三次握手的普通 TCP 确认报文段并不多余,这是为了防止已失效的连接请求报文段突然又传送到了 TCP 服务器,导致错误。
为什么要三次握手
三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。
-
第一次握手: Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常
-
第二次握手: Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常
-
第三次握手: Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常
所以三次握手就能确认双发收发功能都正常,缺一不可。
例题:
TCP 连接的释放
- TCP 四报文挥手
- TCP 客户进程通知其主动关闭 TCP 连接
- TCP 客户端向TCP 服务器发送 TCP 连接释放报文段,TCP 客户端进入
FIN-WAIT-1
状态- TCP 连接释放报文段 首部终止位
FIN = 1
,确认位ACK = 1
,对之前收到的报文段进行确认 seq = u
,等于 TCP 客户进程之前已传送过的最后一个字节序号加一。规定 TCPFIN = 1
的报文段即使不携带数据也要消耗一个序号- ack = v,等于 TCP 客户进程之前已收到的最后一个字节的序号加一。
- TCP 连接释放报文段 首部终止位
- TCP服务器收到后,TCP 服务器发送 普通的TCP确认报文段,进入
CLOSE - WAIT
- 普通的TCP确认报文段 首部确认位
ACK = 1
seq = v
与上一个 TCP 连接释放报文段的确认号匹配ack = u+1
对 TCP 连接释放的报文段确认,TCP 服务器通知高层应用进程连接要断开。
TCP 客户端收到后,TCP客户端进入FIN-WAIT-2
状态
此时TCP 客户进程到 TCP 服务器进程已经关闭
- 普通的TCP确认报文段 首部确认位
- TCP 服务器向TCP客户端发送 TCP 连接释放报文段,TCP 服务器进入
LAST-ACK
最后确认状态。- TCP 连接释放报文段 首部终止位
FIN = 1
,确认位ACK = 1
,对之前收到的报文段进行确认。与第一步相同 seq = w
在半关闭的状态下,TCP 服务器进程可能又发送了一些数据。ack = u+1
对之前 TCP 客户端向 TCP 服务器的 TCP 连接释放报文段的 重复确认。
- TCP 连接释放报文段 首部终止位
- TCP 客户端收到之后,发送 TCP 普通确认,进入
TIME-WAIT
状态,并等待 2MSL后自动进入CLOSED
状态(MSL 最长报文段寿命,RFC793建议 2分钟)- TCP 普通确认 ,首部
ACK = 1
seq = u + 1
,由于 TCP 客户端上一个报文段虽然不携带数据但仍占用序号
ack = w + 1
这是对所收到的 TCP 连接释放报文段的确认
- TCP 普通确认 ,首部
- TCP 服务器进入
CLOSED
关闭状态,TCP 客户端等待结束进入CLOSED
关闭状态。
- 为什么要设置等待时间?
- TCP 保活计时器
- 一个 TCP 客户端经历的状态序列举例:
- 一个 TCP 服务器经历的状态序列举例:
TCP 拥塞控制
- TCP的拥塞控制
- 四种拥塞控制算法
- 慢开始
- 拥塞避免
- 快速恢复
- 快重传
- 快恢复
下文有如下假设:
- 数据是单方向传送,而另一个方向只传送确认。
- 接收方总是有足够大的缓存空间,因而发送方窗口的大小由网络的拥塞程度来决定。
- 以最大报文段 MSS的个数为讨论问题的单位,而不是以字节为单位
初始状态:
- 慢开始
逐渐增加发送速率,每一轮成功的传输轮次都会使下一轮次发送的报文数量翻倍
- 发送方发送报文段 0,并收到**报文段0 **的确认
- 增大
cwnd == 2
,即能发送 2 个数据报文段,发送报文段 1 ,报文段 2 ,并受到确认。 - 继续增大
cwnd == 4
,即能发送 4 个数据报文段,发送报文段 3,4,5,6,并收到确认。 - 同理继续增大
- 直到 cwnd 增大到慢开始门限值 ssthresh,改为拥塞避免算法
- 拥塞避免
每个传输轮次结束后,拥塞窗口值只能线性加 1
直到出现超时重传:
重新开始:
慢开始和拥塞避免的弊端:
快重传与快恢复(快速恢复)
- 快重传
- 快恢复
例题