面向连接的传输: TCP

TCP:概述

  • 提供的是点对点的服务: 一个发送方,一个接收方
  • 可靠的、按顺序的字节流 : 没有报文边界
  • 管道化(流水线): TCP拥塞控制和流量控制设置 窗口大小
  • 发送和接收:

img

  • 全双工数据 : 在同一连接中数据流双向流动 ,两者可以互相发 ( ==MSS:最大报文段大小 [任意一个网络都有它最大的传输部分,如果应用交互的报文非常长的话,就必须打成一个个MSS的大小。每层都要加上头部信息==])
  • 面向连接: 在数据交换之前,通过握手(交换控制报文) 初始化发送方、接收方的状态 变量
  • 有流量控制: 发送方不会淹没接收方

段结构

TCP报文段结构

img

源端口号:16bit

目标端口号: 16bit

序号: 32bit

TCP序号,确认号:

序号:

  • 报文段首字节 的在字节流的编号

如果初始序号为X, 那么第n个序号就是X + n*MSS

序号具体就是: 上层交互的报文 ,我们按照MSS为单位,切割成为一个个的MSS报文段。 每个TCP都有头部和Body部分, Body部分就是MSS的载荷部分。载荷部分(Body)中的第一个字节就是MSS在整个报文中的偏移量.

确认号:

  • 期望从另一方收到的下一个字节的序号
  • 累积确认

发送方发送, 接收方接收, 假设接收方发送了ACK为555, 那么就说明接收方接收到了554及其之前的所有字节。而且期望发送方从556的序号开始发送

首部长度:

  • 4bit

保留未用:不太清楚

标志位: UAPRSF

接收窗口 : 用于流量控制。 (如果接收窗口为 X, 那么就表示能接收 Xbit的数据)

紧急指针: 不怎么用。

img

img

TCP面临的通信场景(往返延时(RTT)和超时 )

采用自适应的策略和计算。

  1. 怎样设置TCP 超时?

如果比RTT要长 , 但RTT是变化的

如果太短:太早超时 会产生不必要的重传

如果太长:对报文段丢失 反应太慢,消极

  1. 怎样估计RTT?

SampleRTT:测量从报文段发出到 收到确认的时间

如果有重传,忽略此次测量

SampleRTT会变化,因此估计的 RTT应该比较平滑

对几个最近的测量值求平均,而 不是仅用当前的SampleRTT

  1. EStimatedRTT(估计的往返延迟时间)的计算:

EstimatedRTT = (1- a)*EstimatedRTT + a*SampleRTT

  • 指数加权移动平均
  • 过去样本的影响呈指数衰减
  • 推荐值:a = 0.125

往返延迟时间分布:

img

设置超时

平均值越大, 我们设置的超时时间就需要变大

往返延迟的变化越大, 就会越分散 , 超时时间就需要设置的越大。

EstimtedRTT + 安全边界时间

  • EstimatedRTT变化大 (方差大 ) -> 较大的安全边界时间

SampleRTT会偏离EstimatedRTT多远:

img

当前的采样值, 离偏差程度的一个平均值。

可靠数据传输(TCP怎么实现RDT)

我们知道IP提供的是不可靠的服务 ,而TCP向上层提供的确是可靠的服务, 那么这是如何实现的呢 ?

TCP在IP不可靠服务的基础上 建立了rdt

  • 管道化的报文段 • GBN or SR (它实现了两者的混合体)
  • 累积确认(像GBN)
  • 单个超时重传定时器(像GBN)
  • 是否可以接受乱序的,没有规范

通过以下事件触发重传

  • 超时(只重发那个最早的未确认 段:SR)
  • 重复的确认 ( 例子:收到了ACK50,之后又收到3 个ACK50 )

首先考虑简化的TCP发 送方:

  • 忽略重复的确认
  • 忽略流量控制和拥塞控 制

TCP 发送方(简化版)

img

TCP发送方事件:

从应用层接收数据:

  • 用nextseq创建报文段

  • 序号nextseq为报文段首字 节的字节流编号

  • 如果还没有运行,启动定 时器

    • 定时器与最早未确认的报文 段关联
    • 过期间隔: TimeOutInterval

超时:

  • 重传后沿最老的报文段
  • 重新启动定时器

收到确认:

  • 如果是对尚未确认的报 文段确认

    • 更新已被确认的报文序号
    • 如果当前还有未被确认的 报文段,重新启动定时器

img

ACK7: 表示7之前的都发送了并且已经得到了确认。接下来就需要从8号开始

TCP: 重传

img

img

产生TCP ACK的情况及其 建议

接收方的事件 TCP接收方的动作
所期望序号的报文段按序到达。 所有在期望序号之前的数据都 已经被确认 延迟的ACK。对另一个按序报文段的到达最 多等待500ms。如果下一个报文段在这个时 间间隔内没有到达,则发送一个ACK。
有期望序号的报文段到达。 另一个按序报文段等待发送ACK (一次到两个报文) 立即发送单个累积ACK,以确认两个按序报 文段。
比期望序号大的报文段乱序到达。 检测出数据流中的间隔 立即发送重复的ACK,指明下一个期待字节 的序号
能部分或完全填充接收数据间隔 的报文段到达。 若该报文段起始于间隔(gap)的低端, 则立即发送ACK。

快速重传

就是在快速定时器超时之前已经收到了某个段的冗余ACK, 那么就需要在某个段还没有到时的情况下,将这个段快速重新传出去 ,而不是等待它超时了再进行重传。

  • 超时周期往往太长:

    • 在重传丢失报文段之前的 延时太长
  • 通过重复的ACK来检测 报文段丢失

    • 发送方通常连续发送大量 报文段
    • 如果报文段丢失,通常会 引起多个重复的ACK

如果发送方收到同一数据 的3个冗余ACK,重传最 小序号的段:

==快速重传:在定时器过时 之前重发报文段==

它假设跟在被确认的数据 后面的数据丢失了

• 第一个ACK是正常的;

• 收到第二个该段的ACK,表 示接收方收到一个该段后的 乱序段;

• 收到第3,4个该段的ack,表 示接收方收到该段之后的2个 ,3个乱序段,可能性非常大 段丢失了

img

快速重传算法:

1
2
3
4
5
6
7
8
9
10
11
event: ACK received, with ACK field value of y
if (y > SendBase) {
SendBase = y
if (there are currently not-yet-acknowledged segments)
start timer
}
else { //已确认报文段的一个重复确认
increment count of dup ACKs received for y
if (count of dup ACKs received for y = 3) { //快速重传
resend segment with sequence number y
}

流量控制

目的就是防止发送方发送的太快, 而使得接收方得缓冲区溢出。

img

  • 接收方在其向发送方的TCP段 头部的rwnd字段“通告”其空 闲buffer大小

    • RcvBuffer大小通过socket选项 设置 (典型默认大小为4096 字 节)
    • 很多操作系统自动调整 RcvBuffer
  • 发送方限制未确认(“inflight”)字节的个数≤接收 方发送过来的 rwnd 值

  • 保证接收方不会被淹没

img

TCP流量控制

相当于木桶效应, 即使发送方能够发送4096bit得数据。 但是接收方只能接收1080bit的数据, 那么发送方也只能有效发出1080bit的数据。

img

连接管理

连接的本质:

  1. ==双方都知道要和对方通信==
  2. ==双方要为通信连接准备好必要的资源(初始序号等)==
  3. ==控制变量需要做置位(序号、初始化receiveBuffer(接收窗口)大小等都需要告诉对方)==*

在正式交换数据之前,发送方和接收方握手建立通信关系:

  • 同意建立连接(每一方都知道对方愿意建立连接)
  • 同意连接参数

为连接做 准备

img

两次握手建立连接的不可行性

img

  1. 变化的延迟(连接请求的段 没有丢,但可能超时)
  2. 由于丢失造成的重传 (e.g. req_conn(x))
  3. 报文乱序
  4. 相互看不到对方

2次握手失败的场景:

img

Client发送了建立连接的请求, 然后Server收到连接请求, 并且进行了确认, 然后发送给了Client 。Client接收到了Server的连接确认, 表示Client知道Server是活跃的, 但是之后Client并没有继续发送确认信息。 因为握手已经结束, 所以Server并不知道你Client是否活跃,所以这就是所谓的半连接。

TCP 三次握手

基于2次握手的不可行性, 我们通过三次握手来实现解决。

基本方案是 : 变化的初始序号+双方确认对方的序号(3次握手)

img

  1. Client建立起连接 。然后将自己的初始序号, x发送TCP SYN报文。

SYN = 1 就是连接请求, Seq = x 就是告诉对方,我将要从x这个字节开始传输。(x就是初始序号)

  1. Server接收到连接请求 ,发出连接确认。

SYN = 1 表示连接请求, Seq = y 就是告诉Client我要从y这个字节开始传输(y就是server的初始序号)

ACK =1 表示我确认接收到了连接请求,ACKNum 表示我确认接收到了x的请求 ,希望你下次从x+1开始传输

  1. Client接收到了第二次握手的信号,发出第三次握手

接收到SYN = 1 表示Server是活跃的, 发送SYNACK 的ACK表示 该报文可能包含C-S的数据

接收到了Server的初始序号Seq = y, 本次我就要确认Server给的初始序号。

并且发送ACK = 1表示Client确认了这次的请求。ACKNum = y+1 表示我确认接收到了y的请求 ,希望你下次从y+1开始传输

  1. Server接收到Client的确认请求。

ACK(y)表示Client是活跃的。

通常第三次握手跟第一次的数据传递是放在一块的。

3次握手解决:半连接和接收老数据问题

img

因为三次握手首先需要将初始序号(x)告诉对方, 然后收到对方的确认之后, 再进行后续的传输,如果说client本次传输的序号不是(x+1) 那么Server就会refuse。 就不会出现老数据传输

TCP 三次握手 : FSM

img

TCP: 关闭连接

  1. 客户端,服务器分别关闭它自己这一侧的连接【通过发送FIN bit = 1的TCP段 】
  2. 一旦接收到FIN,用ACK回应 【 接到FIN段,ACK可以和它自己发出的FIN段一起发 送
  3. 可以处理同时的FIN交换

img