TCP

[TOC]

前言

参考: TCP 系列协议

该文档系 RFC-9293 的阅读笔记,以简略的形式翻译原文,
仅对原文部分段落进行摘要

概述

TCP 全称是 传输控制协议(Transmission Control Protocol)
该文档指定 TCP协议,该协议属于 协议栈(Internet protocol stack) 中的 传输层(transprot-layer),并且它在随着因特网的发展而不断更新.
TCP 最初被记录在 RFC793,作为一部分零星的记录
而该 9293文档 将 一系列文档(详情看官网) 进行了 更新、整合,视自身需要进行查阅

版权声明

版权隶属于 2022 IETF Trust 和 文档的作者 所有
该文档是 BCP78 的主题,在该文档中 提取的组件(Components extracted) 必须包含修订后的 BSD 许可证 ,详情见 第四段落的例子

该文档可能包含一些 来自于的IETF文档、IETF发布贡献(Contributions published) 以及在 2008.11.10 以前公开发表的内容,某些材料的 版权控制 可能没能得到 IETF的许可,使得他们不被允许在 IETF 标准化进程 (IETF Standards Process) 之外进行修改
在未能获得这些材料的 版权控制 时,该文档可能不被允许在 IETF Standard Process 之外进行修改,它的衍生物可能不能在 IETF Standard Process 外被创建,除非是将它的 RFC出版物 翻译为其他语言时

目的和范围

在 1981年,RFC793 被发布,并替换了当时早期的 TCP规范
自那时,TCP被广泛地实现,作为 传输协议 大量用于 Internet 中的应用

几十年后,RFC791 添加了若干文档,并组合到 TCP 的核心规范中,参考 RFC7414
随着时间推移,RFC793有了一些勘误,以及一些已经得到解决的 在安全、表达 以及其他方面的不足之处,这些增强随着时间推移被发表为若干分散的文档,但一直没有对基本规范进行更新

该文档的目的是将 IETF Standard Track 的更改 以及其他 对于TCP的澄清 进行统合,作为一个新版本的规范

一些 相关(companion) 文档定义了一些 TCP中 用到的重要算法(例如 阻塞控制(congestion control) ),他们没有被完整地包含在该文档内,这是有意而为的,因为基本规范可能被多种附加算法使用,而开发者可以根据自己的需要单独进行查阅,
而该文档仅关注于 在TCP实现中必须对 互操作性(interoperate) 支持的 公共基本部分,
由于一些额外的 TCP特性 其内部十分复杂(例如 advanced loss recovery阻塞控制(congestion control)),在未来这部分内容也可能考虑单独剥离出去

互操作性,可以理解为:为对外暴露接口,并且自身能调用其他目标的接口

另外,该协议规范还描述了 TCP段的格式(TCP segment format)、产生(generation)、处理规则(processing rules) 的代码实现, RFC793 和 其他更新 也包含了一些有助于理解 协议涉及、操作 的信息描述,
该文档并不打算改变或者更新这些信息,而是仅关注于更新对 协议规范的规范(normative protocol specification)
该文档保留了 原文档中对 重要解释(important explaneations)、原理(rationale) 的引用
该文档计划用于 审查已有TCP实现、以及编写新的实现

介绍

RFC 793 包含一些对 设计目标的讨论 以及提供了一些操作实例,包括:

  • 连接建立(connection establishment)
  • 连接终止(connection termination)
  • 包的重发(packet retransmission)
  • 修复丢失(repair losses)

该文档讨论 现代 TCP实现 中需要的基本功能,并且替换 RFC793 中的协议规范
它不会 复制 或尝试更新 RFC793 中 第1、2 段落的内容
其他文档是 操作理论的实现(explanations of the theory)、基本原理、设计决策的细节讨论
该文档仅关注协议的规范行为

TCP Roadmap – RFC7414 中提供了一些 TCP定义、重要算法 相关的引导,TCP Roadmap 文档中有一些强烈建议实现的东西,它能在本文档提到的TCP基本功能外进行增强
例如,实现 congestion control 是一个 TCP 的需求,但它本身是一个复杂的话题,在本文档中没有展开讲,因为它有很多 options 可能并不会影响 该文档的核心目标 —— 基本的互操作性(basic interoperability)
同样的,大多数 TCP实现 都包含一些表现 出色的扩展(RFC7323),但这没有严格要求、也没有在本文档中奥伦
另外,TCP中的 Multipath 相关问题也需要被留意,参考:TCP Extensions for Multipath Operation with Multiple Addresses

本文档对于 RFC793 的修订包含在 段落5

要求词汇

关于 MUST MUST NOT REQUIRED SHALL SHOLD SHOLD NOT … 这些词汇,是RFC文档中的一个通用词汇,
可以查阅:Key words for use in RFCs to Indicate Requirement Levels – RFC2119
这里不再赘述

关键的 TCP 概念

TCP 为 应用(applications) 提供的服务包含以下三个基本特性:

  • 可靠的(reliable)
  • 有序的(in-order)
  • 字节流(byte-stream)

应用 的 字节流 在网络中以 TCP segments 的形式传播,
每个 TCP段(TCP segment) 都将会作为 IP协议的 数据报(datagram) 发送

TCP 的可靠性包括:

  • (通过 sequence numbers) 检测 数据包(packet) 的丢失
  • (通过每个 segment 的校验和)错误校验
  • 重传修正

TCP 支持数据 单播传输(unicast delivery). 应用程序可以不加修改而使用 任播(anycast),但这在底层协议转发时由于 行为(behavior) 变化而可能产生一些风险,参考:Architectural Considerations of IP Anycast – RFC7094

TCP 是 面向连接(connection oriented) 的, 尽管它并不自带 liveness 检测能力。

这里,指的是目标断线时可能得不到及时通知,需要更高层使用心跳检测之类的机制进行辅助  

数据流支持在 TCP 连接中双向传输,也可以仅仅单向发送数据.

TCP 使用 端口号(port numbers) 来识别 应用服务(application services)
并且在不同主机之间 多路复用(multiplex) 不同的流

更多 TCP 和其他传输协议的区别可以在 RFC8095 第三段落找到,而更进一步的关于 TCP 的开发动机 以及在 协议栈(Internet protocol stack) 中的角色定位可以在 RFC793 第二段落找到

功能规范

头部格式

在翻译原文前,先提一下 封装
从 应用层往下到物理层,每一层协议都会以自己的方式去处理数据,为了让对等的另一端协议在处理数据时能更有效地沟通,每一层协议都会将上一层给到的数据视为完整的 user data,不进行干预,而在此基础上追加一部分当前层协议需要的信息,这被称为头部(header),
在发送端,协议栈从上往下每一层会层层叠加header,然后在接收端反过来一层层剥离
这种追加头部方式称为 封装,关于 IP头的封装,参考 IP Encapsulation within IP

TCP segments 会(使用下层IP协议)作为 Internet Datagram 进行发送.
IP header 包含若干信息字段,包含 源、目标的主机地址.
而一个 TCP header 跟在 IP headers 后,以提供 TCP 的特殊信息.

这种划分方式允许在 TCP 之外存在其他的 主机级别协议(host-level protocols). 而在早期的 网络协议簇(Internet suite of protocols) 的开发中,IP header 直接作为 TCP 的一部分

该文档使用 TCP headers 来对 TCP 进行描述

一个 TCP header 后紧跟该 segment 中任何的 user data, (注,这里的 user 仍然是一抽象概念,不用深究)
这种格式可以参考:Describing Protocol Data Units with Augmented Packet Header Diagrams

     0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |       |C|E|U|A|P|R|S|F|                               |
   | Offset| Rsrvd |W|C|R|C|S|S|Y|I|            Window             |
   |       |       |R|E|G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           [Options]                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               :
   :                             Data                              :
   :                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   注意,一个 | 表示一个 bit 

   该图示整体为一个 segment, 而 TCP 要发的消息可能被拆成多个 segment

其中:

Source Port: 16 bits,
源端口,映射 0 ~ 65535(2^16)

Destination Port: 16 bits,
目标端口,映射 0 ~ 65535(2^16)

这也是主机的端口范围,

Sequence Number:32 bits,
译作 序列号 ,用于标记每个 segment 中 数据部分八位字节的起始序号

这是一个非常重要的值,并且理解起来会比较抽象,
首先 TCP segment 的单位是 octet,本文中均以 八位字节 进行指代
因为 TCP 是可靠的传输,会保证每个八位字节 有序、完整、不重复 地安全送达,否则进行重传
因此,当 TCP 开始某次连接时,会通过握手机制(见下文)使得双方进入一个可以安全传输的状态
而当开始传输某部分数据时,它会在 segment 的 header 中标记本次传输的起始位置,也就是序列号(Sequence Number)

header 中携带的 Sequence Number 在文档文常常缩写为 SEQ,见术语表,
segment 携带的数据部分,以 八位字节为单位,而每个八位字节都有一个序列号,为了和 header 中的值区分开,用小写的 sequence number 表示,它是基于 SEQ 依次递增的,
根据所指定的 SEQ 能得到第一个八位字节的 sequence number,并推断出 segment 中整个数据部分每个八位字节拥有的 sequence number

比如 SEQ=100,则表示正在向对方发送一个segment,它携带一段数据,并且数据的第一个八位字节的序列号是100,
而对方在接收到数据后,视情况返回一个 ACK(见下文),反馈你它已经接收到了本地给的数据,并且指定希望接下来本地能给对方提供的数据

SYN(control bits) 标志被指定时,意味着处于连接初始化阶段,需要给定一个初始值,这个初始值为 ISN,而第一个八位字节的序列号是 ISN + 1(只有 SYN 和 FIN 才会占用 1 个位置,使得序列号 + 1,详情见下文关于 SYN 的保护机制)
而 SYN 未被指定时,则是使用当前 segment 携带数据的第一个八位字节应有的值,这个值由前面流程累积得到.

从我个人的推断上来说,TCP 会一直尝试重传失败的部分,因此在处理时是近乎连续的,某种意义上来说,一个待传输数据在连接建立时(ISN被确定),每个八位字节都已经拥有了一个理论上的序列号
而这个待传输数据在稍后将分若干segment进行传输
换句话说,Sequence Number 是在告诉对方,我当前给你的是什么内容

Acknowledgment Number: 32 bits,
是一个和 sequence number 息息相关的值
ACK(control bits) 被设置时,该字段包含 segment 发送者 需要的 下一个 sequence number

常在该文档内简称为 ACK,并且有歧义,
它表示 ACK(控制位) 时,意思是确认收到了对方的数据,并且希望指定下一个传过来的数据,因此启用 ACK(序号)
而 ACK(序号) 则表示希望对方下次发的 segment 的 Sequence Number 的值,也就是说希望对方接下来要同步给自己的值

在 TCP 传输中是双向的,ACK 和 SEQ 常常一起被发出、接收,
对于(两端的)发送方:用发出的 SEQ 表示自己传的是哪部分数据,用发出的 ACK 表示自己希望要哪部分数据
对于(两端的)接收方:用收到的 SEQ 确认自己接收到的是什么数据,用收到的 ACK 确认自己应该提供哪部分数据给对方

相当于告诉对方,我接下来需要什么

Data Offset (DOffset): 4 bits,
TCP header (甚至包括 options) 的长度是 32 bits 的整数倍
而该值则表示在 TCP header32-bit words 的数量,
它用于指示 data 的起始位置
因为虽然部分值的长度是固定的,但有些内容的长度不定,因此需要借助这个偏移值来确认 user data 的实际位置

Control bits
control bits(控制位) 也称为 flags(标记)
由 IANA 管理,参考:Transmission Control Protocol (TCP)Parameters

Reserved (Rsrvd): 4 bits,
一组预留的 control bits , 在生成 segments 必须设为 零值,
如果 发送 或者 接收方 的主机,没有在未来实现相应的功能,则必须被忽略

而剩下的几个已经存在的 (图示中 Rsrvd 右侧,竖起来的那几个) 是:

  • CWR:减少阻塞Window,参考 RFC3168
  • ECE:ECN-Echo,参考 RFC3168
  • URG:启用 Urgent Pointer
  • ACK:启用 Acknowledgment
  • PSH:push function ,参考 本文档段落 3.9.1
  • RST: 重置连接
  • SYN: 同步序列号
  • FIN: 发送者不存在更多数据

每个 control bit 不是三言两语能说清楚的,所以这里只是简单提一下:
URG、ACK 在 TCP header 中有一个对应的值,当这两个控制位被表达时,这两个对应的值才有意义
RST 用于重置连接(到 LISTEN) 参考状态机
而 SYN 表示连接建立之处,需要对 Sequence Number 进行交换时,TCP 中某一端为了请求同步而携带的控制位
FIN 则相反,表示即将结束连接

Window 16 bits,
Window – RFC9293
3.8.6. Managing the Window – RFC9293

该 segment 的发送者 希望接收 的,以 确认序号(acknowledgment) 字段中指示的字节开头的,数据字节(data octets) 的数量.
该值会因为 window scaling extension 而发生偏移,参考 RFC7323

window 的 大小必须被视为 无符号数,否则可能导致 负的 window 或者 TCP 不工作的情况
推荐在实现中,预留 32 bit 字段为 发送、接收者的 window size 用作 连接记录(connection record),并将所有 window 组合为 32 bits

在参考后续文档后可以完善对这个东西的认知,    
window 实际是一个数值,也代表一个区域,指代需要的 八位字节 的数量,    
而这些八位字节,也可以视为一个 window 区域,这个概念比较抽象,但是不难理解  
相当于告诉对方,我希望你基于 ACK 为起点,接下来给我发多少东西

Checksum:16 bits

  TODO: 校验和,参考原文

Urgent Pointer:16 bits
该字段将 紧急指针(urgent pointer) 当前的值,作为 segment 的序列号的正向偏移值(positive offset)
紧急指针 指向 紧跟 urgent data 的 octet 的 序列号
该字段只能在设置了 URG(control bit) 时被解释

Options

[TCP Option]; size(Options) == (DOffset-5)*32; 
present only when DOffset > 5. 
Note that this size expression also includes any padding trailing the actual options present

options 可能占据 TCP header 末尾 8 bits (1 octet) 倍数长度 的空间,
所有 options 都被包含在校验和(checksum) 中
一个 option 可能始于任何 字节区间(octet boundary),对于 option 格式有两种情况:

  • 一个单独的 option-kind octet
  • 一个 option-kind octets,以及作为 option-data octets 的 option-length octets注意,options 列表可能比 Data Offset 字段暗示的值要短,
    在 header 内容中超出 option list 的部分,必须以 零值 进行填充
    一个 TCP implementation,可以选择性地定义 options,但该实现必须支持所有 options
Kind Length Meaning
0 End of Option List Option.
1 No-Operation.
2 4 Maximum Segment Size.

这些选项的细节在 本文档 段落 3.2

一个 TCP implementation 必须 无错误地 忽略所有未实现的 TCP Option,假设 option 包含一个 length 字段
除了 EOL(End of Option)NOP(No-Operation) 外的 所有 options ,必须有 length 字段
所有 TCP implementation 必须准备处理一个不合规的 length (比如 零值),建议的处理方式是 重置连接、并且记录错误原因日志

注意,对于 TCP Options 可用空间的扩展正在不断推进(ongoing work),参考TCP Extended Data Offset Option

Data
当前 TCP segment 携带的 数据实体(user data)

总结
TCP 发送数据时,可能拆分为若干个 segment,
每个 segment 都有一个 header,称为 TCP header,
而 segment 在稍后会附上 IP header,这被称为 封装技术
在 IP协议中,该数据将作为 datagram 进行发送,无关TCP本身内容. 当数据被对等侧接收方解析时,TCP 为保证数据的可靠性,会进行测序、错误校验、重发 等等

每个 TCP header 都有固定要求的格式(见上表)
每个 TCP header 以 4个 八位字节(octets) 为单位 (也就是说 TCP header 的长度是 32bit 的倍数),基于对 Data Offset 这部分内容进行填充,保证字节对齐
每个 TCP header 之后紧跟(由上层协议传来的)数据部分(user data),其单位为 八位字节,并且每个八位字节都有一个可以通过特定规则得到的理论序号

基于 TCP segment 的数据交换,核心就是确认双方数据交换的进度, 也就是 sequence number(序列号)
单个 segment 通过携带一个 sequence number 来指示当前从什么地方开始进行交换,
而 Acknowledgment(确认序号) 则指示当前已经交换到了什么程度,
而每次数据交换时,可以基于给定的 window 值同步多个数据字节,这个 window 的数值在逻辑上就覆盖了区间,在作为序列时拥有边界(最小值和最大值,或者说左值和右值)
这三个值是理解 TCP 数据交换的核心,需要对他们有一定程度的认识

而 另一个非常重要的值是 控制位(control bits),它们是一系列由 IANA 管理的特殊标记,每个标记都是一个 bit,也就是 1/0 的开关,当某个控制位的标记被打开时,某些事情变得允许执行(比如 ACK 开启时,Acknowledgment 启用)
这些标记多用于TCP连接的状态切换,详情参考下面的 状态机 章节

特殊选项定义

TCP options 中强制要求以下选项:

  • End of Option List Option, 表示 Option List 的终止
  • No-Operation Option, 用于在Option间进行标记,避免Option在边界处被分割时被提前解析
  • Maximum Segment Size Option. 指示发送该 segment 时最大能从 TCP endpoint 接受到的 segment 数量

其他选项参考文档

TCP 术语

TCP Terminology Overview,完整的术语表在 文档第四段

TCP 进行连接时,存在多种状态,在下文会进一步介绍状态机相关事项。
有一些变量用于控制这些连接状态,而他们被储存在一个被称为 TCB(传输控制块,Transmission Control Block) 的连接记录中.

该记录块保留在主机内,存储在其中的变量包括:
本地和远程 IP 地址和端口号、IP 安全级别和连接隔间(参见 附录 A.1)、指向用户发送和接收缓冲区的指针、指向 重传队列(retransmit queue) 的指针 以及 指向当前 segment 的指针.

此外,TCB 还储存一些和 发送、接收 sequence numbers 相关的变量

发送 sequence numbers 相关:
Variable
Description
SND.UNA send unacknowledged
SND.NXT send next
SND.WND send window
SND.UP send urgent pointer
SND.WL1 segment sequence number used for last window update
SND.WL2 segment acknowledgment number used for last window update
ISS initial send sequence number
接收 sequence numbers 相关:
Variable
Description
RCV.NXT receive next
RCV.WND receive window
RCV.UP receive urgent pointer
IRS initial receive sequence number

文档在这里提出一个名词交 sequence space(序列空间),根据语境,指的多半是内存空间

以图解的形式来帮助理解的话,发送序列的空间:

                   1         2          3          4
              ----------|----------|----------|----------
                     SND.UNA    SND.NXT    SND.UNA
                                          +SND.WND

        1 - 已确认的旧序列号
        2 - 未确认数据的序列号
        3 - 被允许的新数据传输的序列号
        4 - 尚未允许传输但即将到来的数据的序列号

于是 send window 就指的是 序列空间 的一个部分
而这里的图解,从我的理解上,这些 | 像是内存空间中的一个地址,
而 SND.UNA 这样的符号就表示指针,是指向该处的值,它们根据序列关系向后不断累积

而 接收序列空间:

                       1          2          3
                   ----------|----------|----------
                          RCV.NXT    RCV.NXT
                                    +RCV.WND 
另外,还有一些当前片段的参数:
Variable
Description
SEG.SEQ segment sequence number
SEG.ACK segment acknowledgment number
SEG.LEN segment length
SEG.WND segment window
SEG.UP segment urgent pointer

总结一下就是
TCP协议 连接时 会借助 TCB(Transmission Control Block) 来储存一些 “状态”、“缓存”,
这个 TCB 根据语境推测是在主机上缓存,而不是包含在传输数据头,
而发送 TCP segment 时,TCB 内以一些被定义的变量来同步传输状态,这些变量分为三类 “SND” “RCV” “SEG”,分对应其名词表达的用途
而 TCB 中会为 发送、接收 各自指定一块 sequence space , 来使用这些变量

状态机

一个连接的生命周期会经过若干状态,它包括:

  • LISTEN: 等待来自任何 远端对等TCP(remote TCP peer)端口(port)连接请求(connection request)
  • SYN-SENT:在发送一个连接请求后,等待一个 匹配连接请求(matching connection request)
  • SYN-RECEIVED:在 发送、接受连接请求后,等待 确认链接请求(confirming connection request)承认(acknowledgment)
  • ESTABLISHED:连接打开,被接收的数据可以交付给用户.是数据传输阶段的常规状态(normal state).
  • FIN-WAIT-1:等待一个远端对等TCP的 连接终止请求(connection termination request) ;或者是一个之前发送的连接终止请求确认序号(acknowledgment)
  • FIN-WAIT-1:仅等待一个远端对等TCP的 连接终止请求
  • CLOSE-WAIT:收到来自 本地用户(local user)连接终止请求
  • CLOSING:等待远端对等TCP发送 连接终止请求确认序号
  • LAST-ACK: 等待来自 先前发往远端对等TCP的请求 的 确认序号 (发往远端的请求中已经包含了,从远端发往本地时携带的 确认序号)
  • TIME-WAIT:等待足够时间以确保远端对等TCP能够受到发过去的 连接终止请求的 确认编号,避免 新连接 和先前连接中 延迟发送的 segments 冲突
  • CLOSED:表示不再拥有任何连接状态. 它是一种 虚构的状态(fictional),意味着 TCB 中不再拥有状态参数,因此也不再拥有连接(connection).

这里的每个状态,单独看可能比较抽象,需要结合后面的文章 建立可靠的连接

TCP连接程序(TCP connection progresses) 从一种状态切换到另一种状态,称为 响应事件(response to events)
这些 响应事件 主要分三类:

  • 用户调用,包含:OPEN, SEND, RECEIVE, CLOSE, ABORT,以及 STATUS.
  • 传入的 segments,特别是那些包含 SYN, ACK, RST, FIN 控制位的
  • 还有超时的响应(timeouts)

OPEN 调用会根据连接是否成立,而选择 主动执行,或者 被动等待
被动 OPEN 请求,意味着程序希望接收一个来自外部的请求;而主动的 OPEN 则是尝试初始化一个连接.

下图用一种简单的形式描述了基本的状态变换,注意它并没有包含完整的规则,比如判定错误条件、状态转换时未连接 等等.
后面的章节会介绍更多对于 TCP实现中 响应事件(events) 的反应细节.
另外,该图中一些名字用了 缩写、连字符,和文档中其他地方没有保持一致.
这些都是为了绘图的美观.

                            +---------+ ---------      active OPEN
                            |  CLOSED |                -----------
                            +---------+<---------      create TCB
                              |     ^                   snd SYN
                 passive OPEN |     |   CLOSE           
                 ------------ |     | ----------          
                  create TCB  |     | delete TCB            
                              V     |                         
          rcv RST (note 1)  +---------+            CLOSE    |    
       -------------------->|  LISTEN |          ---------- |     |
      /                     +---------+          delete TCB |     |
     /           rcv SYN      |     |     SEND              |     |
    /           -----------   |     |    -------            |     V
+--------+      snd SYN,ACK  /          snd SYN          +--------+
|        |<-----------------           ------------------>|        |
|  SYN   |                    rcv SYN                     |  SYN   |
|  RCVD  |<-----------------------------------------------|  SENT  |
|        |                  snd SYN,ACK                   |        |
|        |------------------           -------------------|        |
+--------+   rcv ACK of SYN         /  rcv SYN,ACK       +--------+
   |         --------------   |     |   -----------
   |                x         |     |     snd ACK
   |                          V     V
   |  CLOSE                 +---------+
   | -------                |  ESTAB  |
   | snd FIN                +---------+
   |                 CLOSE    |     |    rcv FIN
   V                -------   |     |    -------
+---------+         snd FIN  /          snd ACK         +---------+
|  FIN    |<----------------          ------------------>|  CLOSE  |
| WAIT-1  |------------------                            |   WAIT  |
+---------+          rcv FIN                            +---------+
  | rcv ACK of FIN   -------   |                          CLOSE  |
  | --------------   snd ACK   |                         ------- |
  V        x                   V                         snd FIN V
+---------+               +---------+                    +---------+
|FINWAIT-2|               | CLOSING |                    | LAST-ACK|
+---------+               +---------+                    +---------+
  |              rcv ACK of FIN |                 rcv ACK of FIN |
  |  rcv FIN     -------------- |    Timeout=2MSL -------------- |
  |  -------            x       V    ------------        x       V
    snd ACK              +---------+delete TCB          +---------+
     -------------------->|TIME-WAIT|------------------->| CLOSED  |
                          +---------+                    +---------+

图表中有一些 备注(note):

  • 备注1: SYN-RECEIVED 到 LISTEN 的连线,需要主动 OPEN 并到达 SYN-RECEIVED 后才能进行
  • 备注2:FIN-WAIT-1 到 TIME-WAIT 的连线,在 FIN 被接收,并且本地的 FIN 被确认时,省略连线.
  • RST 可以从任何状态 通过相应连线 连到 TIME-WAIT 状态 ^REF1. 这个该连线并不准确,否则这个图的可读性会变得很差。同样的,RST 也可以在任何连接到 LISTEN 或者 COLOSED 的状态中接收,这也是为了图解的易读性。
不难看出,在连接时,这些状态的切换主要通过表达特定 控制位标记 而完成

序列号 Sequence Numbers

3.4. Sequence Numbers – RFC9293

基本概念

在TCP连接中,发送数据的每个 八位字节(octet) ,都拥有一个 序列号(sequence number) .
这使得每个 八位字节 都能被编排顺序,由此进行 确认(acknowledged).

确认机制(acknowledgment mechanism) 在被使用时是 累积的(cumulative),因此,
sequence number X确认序号(acknowledgment) 指代的是所有 直到 X 但不包括 X 的、已经接收的 八位字节

也就是说,序列号 会保证每个 八位字节 都有一个用于测序的编号. 结合前文 TCP header 中对于 序列号 的描述,序列号 只会表示 segment 中单个 八位字节 的编号,而其余 八位字节 的编号由特定规则进行推断.
当某个 八位字节 被发送到远端,双方通过一系列状态同步机制了解到这个 八位字节 已经被正确发送,那么称,这个 八位字节确认(acknowledged),而储存在 TCB 块中的证明被称为 确认序号(acknowledgment number),并且在文档中经常被简称为 确认序号(acknowledgment)
利用这种机制,可以简单地对 重传(retransmission) 时进行 重复检查(duplicate detection).

在 segment 中,八位字节 的编号方式如下:

  • 紧跟在 TCP header 后的数据块中的第一个 八位字节,使用最小编号
  • 在那之后的八位字节,基于第一个编号,使用连续编号

序列号 的取值范围在 0 ~ $2^{32}$ – 1,这是一i个非常大的范围,但它仍然是有限的.
因此,所有算法在处理 序列号 时,必须取 $2^{32}$ 的模(使其在取值范围内循环,避免溢出), 而计算机取模时有一些微妙的地方,请务必小心.

类比

TCP实现(TCP implementation) 中,必须的、典型的 序列号 的类比有:

  • 确认(acknowledgment),指一些 已经发送但尚未 被确认(acknowledged)序列号
  • segment 占用的所有序列号是 已确认的(acknowledged)
  • 一个 传入(incoming) 的 segment 包含的序列号,称为 除外的(expected)
文档会用这些说法,来描述功能  
并且要求所有 TCP 实现都遵从这套类比方式  

大概的用法是:
local --> remote  | 发送 确认序号(acknowledgment)
local <-- remote  | 序列号已经确认(acknowledged)
                  | 已确认的序列号被排除(expected)

作为对发送数据的响应,TCP endpoint 会接收 所有确认序号(acknowledgments),处理该过程时需要了解以下类比:

发送时

  • SND.UNA = oldest unacknowledged sequence number
  • SND.NXT = next sequence number to be sent
  • SEG.ACK = acknowledgment from the receiving TCP peer (next sequence number expected by the – receiving TCP peer)
  • SEG.SEQ = first sequence number of a segment
  • SEG.LEN = the number of octets occupied by the data in the segment (counting SYN and FIN)
  • SEG.SEQ + SEG.LEN – 1 = last sequence number of a segment

一个新的 acknowledgement (称为 “acceptable ack” ) 使得以下不等式成立:
SND.UNA < SEG.ACK < SND.NXT

未确认序列号 < 接收中的确认序号 < 下一个即将发送的序列号  

对于一个 segment 的 重传序列(retransmission queue),如果它的 序列号 和 长度 的和,小于等于 传入 segment 中 确认序号 的值,
则称它被 完全确认(fully acknowledged)

根据定义,segment 最后一个序列号的值,是 首位序列号 + 八位字节长度 - 1  
这句话换个意思就是:若传入的 确认序号 超出了 序列号的覆盖范围,则意味着该 segment 的序列被完全确认

接收时

  • RCV.NXT = next sequence number expected on an incoming segment, and is the left or lower edge of the receive window
  • RCV.NXT + RCV.WND – 1 = last sequence number expected on an incoming segment, and is the right or upper edge of the receive window
  • SEG.SEQ = first sequence number occupied by the incoming segment
  • SEG.SEQ + SEG.LEN – 1 = last sequence number occupied by the incoming segment

如果满足以下条件之一,则判定该 segment 占用了(TCB 内存中)可用的 接收序列空间(receive sequence space)

  • RCV.NXT =< SEG.SEQ < RCV.NXT + RCV.WND
  • RCV.NXT =< SEG.SEQ + SEG.LEN – 1 < RCV.NXT + RCV.WND
第一部分表示,测试segment的开头是否位于 window 覆盖的区间内,而第二部分表示测试尾部
如果首位均被 window 覆盖的区间包含,则该数据位于 window 区间内
不过实际情况比这要复杂,因为存在 window 或者 segment 的 长度(不要忘了这个指的是数据部分的八位字节数) 为 0 的情况,对于传入 segment 可以接受以下四种情况:
Segment Length
Receive Window Test
0 0 SEG.SEQ = RCV.NXT
0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
>0 0 not acceptable
>0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT + RCV.WND
或者
RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT + RCV.WND

Table 5: Segment Acceptability Tests

注意,当接收到的 window 值为 0 时,不应该接受除 ACK segments 以外的任何 segments.
因此,一个TCP实现可能会在 传输数据、接收 ACKs 时,维持一个 window = 0 接收序列空间. (这样就不会接收到其他 segment)
一个 TCP 接收者,必须处理所有传入 segments 中的 RST 和 URS 字段,即使 window = 0.

我们也用编号方案来保护某些 控制信息(control information).
这是在 序列空间 隐式包含一些 控制位 来实现的,这使得它们在 重传、确认 时不会被混淆. (比如,有且仅有一个控制副本会被执行).
控制信息 并没有物理包含在 段的数据空间(segment data space).
因此我们必须采纳一种规则,将 sequence number 隐式分配为 控制位(control) .
而 SYN 和 FIN 是唯二需要这种保护机制的 控制位(controls), 并且这些 控制位 仅在连接开启、关闭时被使用.
出于序列号的目的,SYN 被认为出现在它 实际数据的 第一个八位字节 之前,而 FIN 被认为出现在它实际数据的 最后一个八位字节 之后.
segment length (SEG.LEN) 包括了 数据 和 序列空间占用控制位.
当 SYN 被表达时, SEG.SEQ 是 SYN 的序列号.

按我的理解,
它利用编号方案来将 control bit 中的部分控制位 隐式分配一个序列号,加以保护,  
而使用这种机制的只有 SYN 和 FIN,并且仅在开启、关闭连接时  
SYN 的序列号从位置看,是在数据的 第一个八位字节 之前  

换句话说,在 sequence number 中的介绍中,有提到如果 SYN 被表达时,序列号的值位 ISN,而第一个数据字节为 ISN + 1  
这意思就是,ISN 指的是控制位的序列号,因为第一个数据字节前面还有个隐式存在的东西,它并不是数据字节,而是这个控制位 SYN   

初始化序列号选择

一个 TCP 连接 被定义为一对 sockets 1. 连接是可以被拒绝的.

新的连接实例会被定义为该连接的 实体(incarnations).
在某些情况下(特别是连接成功地快速 开启、关闭 时,或者连接在丢失内存的前提下中断、然后重新连接)会引发一个问题:TCP 如何从先前的 连接实体 中区分出那些重复的 segments ?
为了解决这个问题, TIME-WAIT 状态会限制连接的重用的比例(从宏观上降低该问题发生的概率)
而下面描述的关于 初始化序列号 ISN(initial sequence number) 的选择,进一步对 传入数据包应该选择的连接实体 进行了消歧.

为了避免(实体)混淆,我们必须防止新实体连接使用时,相同的序列号仍然在早先的实体中表达.
我们希望对事件进行确认,哪怕 TCP endpoint 丢失了所有正在使用的已知序列号.
当新的连接被创建时, ISN生成器 会被委派选择一个新的 32位初始化序列号.
此时存在一个安全问题,如果 初始化序列号 的值可以被预测、猜测,则可能遭到非常规路径的攻击

TCP中的初始化序列号,由一种大致被称为 “clock” 的组件生成,它会让数字单调递增直到循环,以此生成初始化序列号.
clock 是一个 32位的计数器,约 4微秒(microseconds) 增加一次,它不是真实时间、并且重启(reboots)后不会持续存在.(理论计算出的上限值是 $4 2^{32}10^{-6}$ 约等于 4~5小时)
clock 组件 的循环周期大概在 4.55 小时,远大于 MSL(Maximum segment Lifetime),而 clock 组件旨在确保在单个循环周期内,每个ISN在自身MSL内都是独一无二的.
值得注意的是,现代网络支持较高的数据传输速率,这使得 序列号可能递增非常频繁,使得这些序列号各自的 MSL 存在重叠. 如果需要实现时间戳,请参考 Section 3.4.3.

一个 TCP 实现,必须使用上述的 clock 组件来选择 ISN.
并且,应该使用该公式进行表达:
$$
ISN = M + F (localip, localport, remoteip, remoteport, secretkey)
$$
其中,M表示 4微秒 计时器, F() 是一个和 识别参数(localip, localport, remoteip, remoteport) 以及 加密关键字(secretkey) 相关的 伪随机函数(pseudorandom function PRF)
F() 不允许能被外部计算,否则攻击者可以由 ISN 猜测序列号并用于其他连接,
PRF 可能被实现作为 TCP连接参数 以及一些加密数据 相关的 hash 加密,
有关特定hash算法的选择以及 密钥数据的管理,参考 Section 3

每个连接都会收发序列号. 初始化发送序列号(initial send sequence number, ISS) 用于向对等TCP进行发送;而初始化接收序列号(initial receive sequence number, IRS) 则是在 connection-establishing(连接建立) 时进行学习.

一个连接 建立、初始化 时,两个对等 TCP 必须同步彼此的 ISN.
这通过在进行 connection-establishing(连接建立) 时,在交换的 segment 中携带 SYN 控制位、ISN 来完成. 这些 segment 携带的所有 SYN 在文档中也简称为 SYNs.
而该解决方案需要一种合适的机制来挑选 ISN,这里需要稍微涉及到 握手(handshake) 的概念来进行 ISN 的交换.
图解:

    1) A --> B  SYN A:我的序列号是 X
    2) A <-- B  ACK B:你的序列号是 X
    3) A <-- B  SYN B:我的序列号是 Y
    4) A --> B  ACK A:你的序列号是 Y

由于 2、3 步可以合并,

    1) A --> B  SYN A:我的序列号是 X
    2) A <-- B  ACK B:你的序列号是 X, 而我的序列号是 Y
    3) A --> B  ACK A:你的序列号是 Y

因此也称为 三路握手,three-way (or three message) handshake (3WHS).

由于序列号并非是由网络中全局的 clock 组件绑定,一个 TCP实现可能用不同的机制去获取ISN,因此一个基于三路握手的序列号交换是必要的(而不是自己各算各,然后认为算出来的是对方持有的值)
接收者仅靠收到的第一个 SYN 无法分辨该 segment 是否过时,除非上一个序列号在连接使用时被记忆(这是少数情况),因此它必须询问发送者来验证这个 SYN. (这样便是一次“握手”行为)
三路握手 以及 在ISN选择时以clock进行驱动的方案,可以参考:附录:69

阶段总结一下,每个 segment 的 header 需要指定 序列号,以表示 数据部分八位字节的序列的开始  

序列号在生成时,利用 clock 组件的机制,使得每个 segment 用到的 序列号都是理论上唯一的

在连接建立之初,双方必须知道彼此的初始序列号,这样才能同步传输的完成度  

为了交换初始序列号,需要在连接建立的时候,以 SYN 控制位为契机进行序列号交换

双方以 SYN 交换序列号,最少需要三步,才能确认双方正确持有的序列号,这称为 3WHS(3 way handshake)

建立可靠的连接

三路握手(three-way handshake, 3WHS), 是一种用在可靠连接的步骤.
该通常步骤由 TCP对等点 启动(initiated) ,由另一个 TCP对等点 进行回执. 也可以同时在两端启动.
当同时打开时,每个 TCP对等点 在发送 SYN 后接收到一个没有 确认编号(acknowledgment) 的 SYN segment.
当然,有可能会收到旧的重复 SYN segment,使得接收者认为连接的初始化正在同时进行.
而正确使用 “reset” segments 可以消除这里的歧义.

下面有几个关于 连接启动 的例子,虽然它们在发segment时没有携带数据,但只要在TCP endpoint认为数据明确有效之前,不要发送数据给用户,就是合理的。 (例如,数据在接收者的缓冲区内,直到该连接抵达 ESTABLISHED 状态,使用三路握手减少连接失败的可能性). 为这种检测提供信息,是基于 内存、消息 之间的权衡.

下图简单展示了一个 3WHS.
每行都有编号供参考. 右箭头(–>) 表示 TCP segment 从 A 离开到达 B. 左箭头反过来.
省略号 (…) 表示 segment 仍然在网络中(被延迟了,正在传输)
图中括号里的是注释.
TCP 连接状态,表示 segment 在离开或者到达后的状态(这些内容展示在每一行中间).
segment 的内容会用一些缩写,这里不赘述.

由一边发起

    TCP Peer A                                           TCP Peer B
1.  CLOSED                                               LISTEN
2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               --> SYN-RECEIVED
3.  ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED
4.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK>       --> ESTABLISHED
5.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK><DATA> --> ESTABLISHED

这个图说得很形象,

  1. 首先 A 发送一个 SYN segment,并且告诉对方自己的序列号是从100开始,
  2. 然后 B 表达 ACK(控制符),以确认收到上一条内容;也发送 SYN(控制符) 告诉对方自己也需要进行序列号的同步;
    并且发送 ACK(确认序号) 告诉 A 自己接下来想要 A 的第一个八位字节的内容 101(见下文的补充说明);同时发送 SEQ 告诉对方,自己的序列号是从 300 开始.
    当 A 收到从 B 发过来的回执,则 A 能确认自己消息有正确发出去,此时 A 自身进入 ESTABLISHED 状态,它能确定自己的消息是能可靠发出去的;但 B 这边尚未收到 A 的回执,所以 B 仍然在等待后续确认步骤.
  3. 之后 A 这边也同样地,回执一个 ACK(控制符) 作为确认,并将对方需要的 SEQ 发出,然后同样地让 ACK 为 301 发出;
    当 B 收到以后,B 也进入 ESTABLISHED 状态,此时一个三路握手就完成了

这三个用于握手的 segment,不携带用户数据
在这之后,可靠连接被建立,后续流程原则上应该发送数据,否则在描述上认为是一个 “空的 segment”

在最后一行,A 向 B 发送了一些数据,但序列号、确认序号都没有改变,
在握手时,SEQ 和 ACK 之所以改变,是因为 SYN 会使得 ISN + 1(这是一种传输时的保护机制,相当于占用了序列号空间的一个位置,参考序列号的定义),而正常情况下序列号是不会变的(因为 ACK 并不会占用序列号空间);除非对面发过来新的 ACK 序号要求你发新的内容给它

两边同时发起

两边同时启动的情况只有一些微小的差别.
在下图中,每个 TCP对等点 之间的连接状态,从 CLOSED | SYN-SENT | SYN-RECEIVED | ESTABLISHED 之间循环

    TCP Peer A                                       TCP Peer B
1.  CLOSED                                           CLOSED
2.  SYN-SENT     --> <SEQ=100><CTL=SYN>              ...
3.  SYN-RECEIVED <-- <SEQ=300><CTL=SYN>              <-- SYN-SENT
4.               ... <SEQ=100><CTL=SYN>              --> SYN-RECEIVED
5.  SYN-RECEIVED --> <SEQ=100><ACK=301><CTL=SYN,ACK> ...
6.  ESTABLISHED  <-- <SEQ=300><ACK=101><CTL=SYN,ACK> <-- SYN-RECEIVED
7.               ... <SEQ=100><ACK=301><CTL=SYN,ACK> --> ESTABLISHED

TCP实现必须支持这种同时打开的尝试
TCP实现必须追踪 SYN-RECEIVED 是从 积极 还是 被动的 OPEN 进入的.

3WHS 的主要原因是 防止旧的重复连接启动 而引起混乱. 为了解决这个问题,会指定一个特殊的控制信息 reset .
如果接收中的 TCP peer 是非同步状态(比如 SYN-SENT, SYN- RECEIVED),它会在接收到可用的 reset 后返回到 LISTEN 状态.
如果 TCP peer 是某种 已同步状态(ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT), 在接到 reset 后会终止连接并且通知用户。
这会在后面的 half-open 连接 中进一步讨论.

通过总结两种情况,对于单个 TCP peer,它的状态会像这样:

  • 发出 SYN 同步序列号,自身进入 SYN-SENT 状态,
  • 若成功收到来自对方的 SYN 请求,自身进入 SYN-RECEIVED 状态,
  • 若发出 SYN 后,成功收到对方的 SYN、ACK,则进入 ESTABLISHD 状态,
  • 双方进入 ESTABLISHED 状态时,握手完成

而根据是否握手完成,分为 同步状态(synchronized state) ,以及非同步状态(non-synchronized state)
正常单边发起的握手,只需要三次;但两方同时发起的握手,视情况可能会变成四次。

从旧的重复SYN中进行恢复

    TCP Peer A                                           TCP Peer B
1.  CLOSED                                               LISTEN
2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               ...
3.  (duplicate) ... <SEQ=90><CTL=SYN>               --> SYN-RECEIVED
4.  SYN-SENT    <-- <SEQ=300><ACK=91><CTL=SYN,ACK>  <-- SYN-RECEIVED
5.  SYN-SENT    --> <SEQ=91><CTL=RST>               --> LISTEN
6.              ... <SEQ=100><CTL=SYN>               --> SYN-RECEIVED
7.  ESTABLISHED <-- <SEQ=400><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED
8.  ESTABLISHED --> <SEQ=101><ACK=401><CTL=ACK>      --> ESTABLISHED

在该图中,一个新的 SEQ=100 的 SYN segment 被发往B,而早前从 A 发往 B 的 SEQ = 90 的 SYN segment 也被送达
B 此时看不出来 SEQ=90 的是旧的重复 segment,它将其视为正常的 SYN segment,并返回一个 ACK=91
而 A 自己能看出来这个 ACK 是不正确的请求,因此返回一个 RST(reset),并且把这个 SEQ 给带上使其可信.
此时 B 收到 RST,重新进入 LISTEN 状态
之后,A 再次重新用正确的 SEQ = 100 尝试同步,这个时候流程就正常了.
而如果第六行的 segment 又比 第五行的早到,则这里的交换会变得更加复杂……

半开连接 以及 其他异常情况

TODO

关闭连接

Segmentation

术语 segmentation 指TCP执行的一种行为 —— 将application 发送的 字节流(stream of bytes) 打包 并填充到 TCP segments
单个 TCP segment 通常并不会和 来自application的 单个 发送(或 socket 写入)调用 一一对应(one-for-one) .
应用程序可以能以上层协议的消息粒度(granularity of message) 进行写入,
但 TCP 保证: TCP segment 在 发送、接收 时的边界(boundaries) 、application 读/写 时缓冲区的边界 是无关的,
在一些特殊的协议中,比如 RDMA(Remote Direct Memory Access)DDP(Direct Data Placement)MPA(Marker PDU Aligned Framing ,当 TCP segment 和 application data units 之间的关系可以被控制时,这些表现可能得到优化;并且MPA包含一些特殊的机制来 发现、验证 TCP segmentsapplication message data structures 之间的关系,但这在 applications 中是特殊的,就像 RDMA 一样. 通常,TCP segments 的 大小(sizing) 会受到 TCP implementation 多个目标(multiple goals) 的影响

发送较大 segment 的原因包含:

  • 减少网络中传输的 数据包(packets) 数量
  • 通过启用较少数量的中断和层间交互来提高处理效率和潜在性能。
  • 限制 TCP headers 的开销

注意,发送更大的 segments 的优势,可能随着其大小的增加而变低,并且可能存在优劣颠倒的临界值(boundaries)
例如,在有些实现架构中,1025 bytes 在 segment 可能表现比 1024 bytes 更加糟糕,纯粹是因为 复制 操作上的数据对齐.

发送较小 segment 的原因包含:

  • 避免发送的 TCP segment 使得 IP datagram 变得很大,让 IP 在网络路径中传输时 MTU 尽可能最小,避免任何 packet 丢失 或者 触发 fragmentation. 更糟的是,一些 防火墙(firewalls) 或者 middleboxes 可能会丢弃 触发 fragment 的 数据包(packets) 或者与 fragmentation 相关的 ICMP 相关消息
  • 避免 application 数据流 的延迟,尤其是 TCP 在等待 application 生成更多 数据,或者一个对等的 为了生成更多数据的 事件、输入 时
  • 在 TCP segments 和 底层数据单元中 进行 fate sharing (例如,在底层IP协议中,用于那些 单元(cell) 或者 frame sizes 小于 IP 中 MTU 值 的 链路

总的来说,TCP包含若干机制,

  • Maximum Segment Size Option(最大segment大小)
  • Path MTU Discovery(路径最大传输长度)
  • Nagle algorithm
  • IPv6 Jumbograms

这些在下面的章节进一步展开了说明

TODO

数据沟通 Data Communication

当连接被确立(established),数据的沟通 将通过 segments 之间的交换来实现.
因为 segments 可能因为 error(校验测试失败) 或者 network congestion(网络拥堵) 而丢失, TCP 使用 重发(retransmission) 机制确保每个 segment 都能被正确发送.
而 segments 可能从 网络、TCP的retransmission 获得重复数据. 在前面 段落 3.4 介绍了 sequence number,TCP implementation 会对 segments 中的 序列(sequence)承认证明值(acknowledgment numbers) 进行特定的测试,以验证他们的合理性(可接受性, aceptability)

数据的发送者追踪下一个 sequence number,以用在(use in)变量 SND.NXT 中.
数据的接收者追踪下一个 sequence number,以要求(expect in) RCV.NXT.
数据的发送者追踪 变量 SND.UNA 中最早的 unacknowledged sequence number.
如果 数据流(data flow) 暂时 idle, 并且所有 data sent 已经被 acknowledged,此时三个变量将相等.

当发送者创建 segment 并且传输时,发送者 增加(advances) SND.NXT,
当接收者收到 segment,它 增加(advances) RCV.NXT,并且发送 acknowledgment.
当发送者收到 acknowledgment 回执,它 增加(advances) SND.UNA
这些变量的区别,由他们在 communication 中的延迟而度量
而这些变量 被增加(advanced) 的值是 segment 中 SYN/FIN 标志 以及 数据的长度
注意,在 ESTABLISHED 状态,所有 segments 必须携带当前的 acknowledgment 信息

CLOSE 用户调用 暗示一个 push 函数(参考 3.9.1, 与传入的 segment 中的 FIN 控制标志 一致

接口

Interface – RFC9293
该章节定义了一些 TCP implementation中 面向 user 的抽象的功能性描述命令,每种操作系统中会有不同的实现
最后,我们必须警告读者,不同的TCP implementation 可能有不同的 用户接口(user interface)
不过,所有 TCP implementation 都必须提供 主要、公共的服务 以保证所有 TCP implementations 都能支持一些相同的协议等级,
该段落指出了这些 所有TCP implementations 都需要的功能接口

在正式阅读前,
3.1. Primitives Provided by TCP – RFC8303 提供了 TCP的一些 原语(Primitives) 以供参考
以下段落是 用户/TCP 接口 的功能表征,他们默认你实用 高级语言(hight-level languages)程序(procedure) 或者 方法调用(function calls),但这不意味着排除 陷阱类服务调用(trap-type service calls)
以下用户命令描述的中的基本方法,TCP implementation 必须实现他们以支持进程间的通信. 而个人的实现必须定义他们自身额外的格式、以及在单个功能调用中可能要提供的 组合 或者 子集. 在一些 TCP实现 中,可能会希望在连接时 第一次 SEND 或者 RECEIVE 发出后自动进行 OPEN.
在提供进程间通信的功能时,TCP实现 不能只是接收命令,而是必须返回它执行服务进程的一些相关信息,包括:

  • 连接相关的常规信息(比如,中断、远端关闭、绑定一个不被期待的socket 等等)
  • 对特殊用户命令进行回复,以指示成功结果,或者多种类型的失败

其中,这些主要的功能集,包含:
包含:

  • OPEN Call
  • SEND Call
  • RECEIVE Call
  • CLOSE Call
  • ABORT Call
  • STATUS Call
  • SEGMENT ARRIVES
  • Timeouts

而他们在以下每种状态下可能存在不同表现:

  • CLOSED STATE (i.e., TCB does not exist)
  • LISTEN STATE
  • SYN-SENT STATE
  • SYN-RECEIVED STATE
  • ESTABLISHED STATE
  • FIN-WAIT-1 STATE
  • FIN-WAIT-2 STATE
  • CLOSE-WAIT STATE
  • CLOSING STATE
  • LAST-ACK STATE
  • TIME-WAIT STATE

他们在某些状态下,比如 TIME-WAIT STATE,会反一个错误,本文不再赘述这些情况

Open

CLOSED STATE:
创建一个新的 TCB(传输控制块),以维持连接状态相关的信息
填写:

  • local socket identifier
  • remote socket
  • Diffserv field
  • security/compartment
  • user timeout information

注意,remote socket 的一部分内容可能不希望在 被动的OPEN 中出现,而是被传入的 SYN segment 的参数填充
需要该 user 的 安全性(security)、差异值(Diffserv value) 得到验证,否则返回错误”Diffserv value not allowed” 或者 “error: security/compartment not allowed”

如果是被动的监听,进入 LISTEN 状态,并且返回
如果激活,并且远端socket未指定,则返回 “error: remote socket unspecified”

如果激活,并且远端socket指定,则提出一个 SYN segment. 此时,一个 ISS(Initial send sequence unmber) 被选择. 然后该 SYN segment 被发送,它处于一个形如 <SEQ=ISS><CTL=SYN> 的表.
将 SND.UNA 设置为 ISS,SND.NXT 设置为 ISS+1,然后进入 SYN-SENT 状态,并返回

另一方面,如果调用者没有 本地指定socket 的访问权限,则返回 “error: connection illegal for this process”
如果没有位置(room)去创建新的连接,返回”error: insufficient resources”

LISTEN STATE:
如果 OPEN 调用被执行,并且远端socket被指定,此时将连接从被动状态交替为主动状态,并选择一个 ISS
发送 SYN segment,设置 SND.UNA 为 ISS,SND.NXT = ISS+1
进入 SYN-SENT 状态.

在进入 ESTABLISHED 状态后,和 SEND 相关的数据可能会和 SYN segment 一起发送,或者为传输进行排序
紧急比特位(urgent bit) 必须和 data segments 一起发送,作为该命令的结果
如果没有 room 来入队该亲求,返回 “error: insufficient resources”
如果 远端socket 未指定,返回”remote socket unspecified”

其他请参考原文档


  1. 注意,这里说的 sockets 指的是一个习惯用语(首字母小写),你不必把他当成 API 中使用的 Sockets,只需要把他当成一个很多地方都会使用的名词,并且在TCP这里也有特定含义。这个词最初源自阿帕网,详情可以参考 Sockets 相关文档 

Leave A Reply

Your email address will not be published. Required fields are marked *