上一博客记录了 UDP 协议的关键要点, 这部分记录 TCP 协议的关键要点.
24.3 传输控制协议(TRANSMISSION CONTROL PROTOCOL)
TCP(Transmission Control Procotol )协议是一个面向连接, 可靠的协议. TCP 为了提供面向连接的服务, 专门定义了连接创建, 数据传输, 连接终止阶段. TCP 使用 GBN 和 SR 协议来提供可靠性. 为了实现可靠性这个目标, TCP 使用检验和来进行误差控制, 重传来处理数据包丢失和冲突, 同时还利用了应答和计数机制. 在本节, 首先讨论 TCP 提供的服务, 然后讨论 TCP 的详细特征.
24.3.1 TCP 服务
进程到进程的通讯(Process-to-Process Communication)
像 UDP 一样, TCP 使用端口号提供基于进程到进程的通讯.
流分发服务(Stream Delivery Service)
和 UDP 不同, TCP 是面向流的, 允许发送进程以字节流的方式发送, 然后接收方以字节流的方式接受数据, TCP 创建了虚拟的管道环境, 从而使得发送进程和接收进程连接起来. 图 24.4 表示了双方数据流的传输.
发送和接收缓冲区(Sending and Receving Buffers)
因为接收方和发送方的读写数据的速度不一样, TCP 需要缓冲区进行数据存储. 存在两个缓冲区, 发送缓冲区和接收缓冲区. 缓冲区在 TCP 的流量控制和误差控制发挥作用, 这点稍后将会介绍. 一种 实现缓冲区的方式是使用循环的字节数组, 如图 24.5 所示.
图中展示了数据在缓冲区的流动方向, 对于发送方, 缓冲区可以分成三个部分. 白色的部分表示还没装载数据的缓冲区部分, 由发送进程来填充, 橙色部分存储的是已经发送但还没被接收方应答的字节数据, TCP 发送者会保留该区域的数据直到接收到了接收方的应答. 黑色部分表示将要发送的数据. 橙色部分的数据被应答之后, 可以重新由发送进程来填充.
接收方的接收缓冲区的操作会简单一些, 环形的缓冲区分为两个部分. 白色部分表示还没装载数据的缓冲区部分, 橙色部分表示已经接收, 可供接收进程进行数据读取的缓冲区部分. 当一个字节被接收进程读取之后, 整个缓冲区将会循环, 同时更新白色缓冲区部分.
片段(Segment)
尽管缓冲区可以处理发送方和接收方处理数据不一致的问题, 但是在进行数据发送之前, 还需要做一步. 向 TCP 服务的网络层是以数据包的形式发送数据, 而不是数据流. 在传输层, TCP 会封装一定字节的数据成一个数据包, 叫片段. TCP 将会在每个数据片段加一个头部, 然后将数据提交给网络层进行数据传输. 数据片段在网络层封装成 IP 数据报然后传输, 对于接收进程, 整个过程是透明的. 等会还会介绍接收方接收到无序, 存在丢失和冲突数据以及重新传输的情况, 这些都是由 TCP 接收方接收进程来处理的. 图 24.6 展示了数据片段是如何由发送缓冲区的字节生成的. 需要注意的一点是, 片段的长度不是恒定的.
全双工通讯(Full-Duplex Communication)
TCP 提供全双工通讯, 也就是数据流能同时在两个方向进行传输, 每个 TCP 终端都有一个发送缓冲区和一个接收缓冲区.
面向连接的服务(Connection-Oriented Service)
和 UDP 不同, TCP 是面向连接的的协议, 当 A 端的一个进程想发送数据或者接收数据到 B 端另一个进程时, 以下三个阶段将会出现:
a. 两个 TCP 创建一条逻辑上连通的传输通道
b. 数据在双方双向传输
c. 终止连接
可靠的服务(Reliable Service)
TCP 是一种可靠的传输协议, 使用应答机制来检查数据的传输安全.
24.3.2 TCP 特征(TCP Feature)
计数系统(Numbering System)
尽管 TCP 软件能够追踪片段的传输和发送, 但它不是用片段数来记录数据的发送, 而是用序列号和应答号两个字段来记录传输字节数.
字节数(Byte Number)
TCP 在数据传输过程中记录所有传输的字节数. 计数在接收方和发送方是独立的, 当 TCP 接收字节从一个进程时, TCP 存储数据在缓冲区, 并且记录字节数, 计数并不是从 0 开始的, 而是随机选择一个 0 到 (2^23-1) 之间的数据. 例如, 如果初始数据是 1057, 要发送的数据总长度为 6000 字节, 字节的编号就是 1057 到 7056. 字节计数主要用来进行流量控制和误差控制.
序列号(Sequence Number)
字节编号之后, TCP 将会分配一个系列号到每一个将要发送的片段, 序列号根据以下规则定义:
1. 第一个字段的序列号是初始序列号 ISN(Initial sequence number), 也就是一个随机生成的数.
2. 其他片段的序列号是上一个片段序列号加上上一个片段传输的字节数.
当一个片段携带用户数据和控制信息时, 它需要加上序列号, 当一个片段没有携带数据时, 不需要定义序列号, 序列号字段还是存在的, 只是是无效的. 需要注意的是, 有些片段, 虽然只是携带了控制信息, 却需要序列号来允许接收方来进行数据应答, 这些字段用来进行连接创建, 终止, 中断. 每个这类片段默认是一个数据长度, 但是实际中是没有数据的.
应答号(Acknowledgment Number)
在通讯过程总, 通讯双方都用序列号记录了每个片段携带的第一个数据的编号. 同时, 接收方和发送方还会用应答号来应答接收的数据. 应答号定义了对方下一个要发送的字节的序列号.
24.3.3 片段(Segment)
TCP 中一个数据包叫片段.
格式(Format)
片段的格式如图 24.7 所示, 片段包括一个 20-60 字节的头部, 然后就是数据部分. 如果没有其他选项, 头部 20 字节长度, 否则, 头部是 60 字节长度.
source port address: 定义发送方的端口号.
destination port address: 接收方的端口号.
sequence number : 序列号, 初始序列号 (ISN) 随机产生.
acknowledgment number: 应答号, 定义接收方下一个期望接收的字节数据起始编号, 如果接收方已经成功字节编号为 X 的数据, 应答号就是 X+1.
header length: 头部长度.
control: 该字段包括 6 个不同的控制位或者控制标志, 如图 24.8 所示. 这些位可以用来流量控制, 连接创建, 连接中断, 连接断开. 图 24.8 给出了这些位的含义.
Windows size: 该字段定义了发送数据的窗口大小, 长度为 16 位, 最大数据是 65535 字节. 该值一般用于指接收方的接收窗口大小, 发送方必须考虑接收方的接收窗大小.
checknum: 校验和, 16 位.
封装(Encapsulation)
TCP 片段封装从应用层提交过来的数据, 然后在网络层, TCP 片段被封装成 IP 数据报, 进而在数据链路层被封装成帧.
24.3.4 TCP 连接过程(A TCP Connection)
TCP 连接包括三个阶段: 连接创建, 数据传输, 连接终止.
连接创建(Connection Establishment)
三次握手(Three-Way Handshaking)
TCP 创建连接的过程叫做三次握手, 试图创建连接的应用程序, 一般称为客户端, 等待连接的程序, 一般称作服务器. 图 24.10 给出了使用三次握手进行连接创建过程以及创建连接过程中数据的关键字段. 连接创建过程的三次握手如下:
1. 客户端发送第一个片段, 称作 SYN 片段, 片段中设置了 SYN 位为 1. 该片段用于序列号的同步. 客户端随机生成一个数作为初始序列号(ISN), 然后发送到服务器. 需要注意的是, 该片段没有包括应答号, 也没有定义窗口大小, 当片段中的应答号存在是, 窗口大小定义才有效. SYN 片段为控制片段, 没有携带数据, 但是, 它还是会消耗一个一个序列号, 因为它需要被服务器应答, 实际中可以认为 SYN 片段携带一个虚拟的字节.
2. 服务器发送第二个片段, SYN+ACK 片段, 同时该片段中 SYN 和 ACK 位都设为 1. 该片段有两个目的, 第一是初始化从服务端到客户端的通讯, 该片段初始化了一个序列号, 来记录由服务端发向客户端的的字节号. 第二个目的是应答了客户端发送的 SYN 片段, 同时向客户端说明了下一个要接受的片段的序列号. 因为数据包需要被应答, 所以需要定义接收接收窗体的大小(rwnd). 该片段也需要消耗一个序列号.
3. 客户端发送第三个片段, ACK 片段, 这个仅仅是应答片段, 应答第二个片段的接收. 该字段设置 ACK 位为 1. 需要注意的是, 该片段如果没有携带数据时, 不需要消耗序列号. 该字段允许携带从客户端发送服务端的数据信息, 当携带数据时, 需要消耗序列号.
数据传输(data transfer)
连接创建之后, 就可以进行数据传输了. 需要注意的问题是, 数据传输过程中, 如果发送方设置了 PUSH 位, 意味着接收方要尽快的把数据提交到应用层, 而不需要等待缓冲区满或者更多数据到来.
连接终止(Connection Termination)
交换数据的双方都可以关闭连接, 虽然关闭连接请求往往由客户端初始化. 如今很多 TCP 实现允许两种终止连接方式: 三次握手 (图 24.12) 和半关闭的四次握手(图 24.13).
三次握手(Three-Way Handshaking)
1. 客户端 TCP 在接收到客户端应用的断开连接命令后, 向服务器发送第一个片段, 称作 FIN 片段, 该片段中 FIN 标志位设为 1,FIN 片段可以携带最后发送给服务器的数据信息, 也可以不携带任务信息, 仅仅作为一个控制片段. 如果是控制片段, 就需要消耗一个序列号.
2. 服务器 TCP 接收到 FIN 片段后, 向上层应用通知连接断开, 然后发送第二个数据片段, FIN+ACK 片段, 一方面是中断服务器向客户端发送数据的连接, 另一方面是告知客户端已接收到 FIN 片段. FIN+ACK 片段可以携带最后发送给客户端的数据信息, 也可以不携带任务信息, 仅仅作为一个控制片段. 如果是控制片段, 就需要消耗一个序列号.
3. 客户端 TCP 发送最后一个片段, ACK 片段, 证实接收到了 FIN+ACK 片段, 该片段包括应答号, 但不携带数据, 不消耗序列号.
半关闭(Half-Close)
TCP 中, 一方可以停止发送数据的同时仍然接收对方发送的数据, 这种方式称作半关闭. 服务端和客户端都可以发出半关闭请求. 半关闭发生在接收方需要接受所有数据之后, 交给上层处理, 然后再把数据发送给发送方, 发送方发完数据之后就可以请求半关闭了. 一个比较容易理解的例子就是排序, 客户端向服务器发送要排序的数组, 服务器把要排序的数据全部接收后, 交给上层应用排序好之后, 再把排序好的数据发送给客户端, 客户端发送完数据之后, 就可以发送半关闭的请求了. 图 24.13 就表示了半关闭过程.
来源: https://www.cnblogs.com/mangojun/p/10887057.html