TCP
本文是学习 TCP (Transmission Control Protocol) 的一些笔记。
1. 概述
The Transmission Control Protocol (TCP) is one of the main protocols of the Internet protocol suite. It originated in the initial network implementation in which it complemented the Internet Protocol (IP). Therefore, the entire suite is commonly referred to as TCP/IP.
TCP provides reliable), ordered, and error-checked delivery of a stream of octets) between applications running on hosts communicating by an IP network. Major Internet applications such as the World Wide Web, email, remote administration, and file transfer rely on TCP. Applications that do not require reliable data stream service may use the User Datagram Protocol (UDP), which provides a connectionless datagram service that emphasizes reduced latency) over reliability.
2. TCP/IP
TCP 作为 TCP/IP 的关键协议为人所熟知,在这里对 TCP/IP 进行简单描述 。
TCP/IP 协议族分为四层:
2.1 链路层
链路层位于 TCP/IP 协议族的最底层,链路层主要有三个目的:(1)为 IP 模块发送和接收 IP 数据报;(2)为 ARP (Address Resolution Protocol 地址解析协议) 模块发送 ARP 请求和接收ARP应答;(3)为 RARP (Reverse Address Resolution Protocol 逆地址解析协议) 发送 RARP 请求和接收 RARP 应答。
2.2 网络层
网络层提供路由和寻址的功能,使两终端系统能够互连且决定最佳路径,并具有一定的拥塞控制和流量控制的能力。由于TCP/IP协议族中的网络层功能由 IP 协议规定和实现,故又称 IP 层。
2.3 传输层
传输层的协议为应用层提供端到端的通信服务,其中最著名的传输层协议就是下面即将详细分析的传输控制协议 (TCP) 以及 用户数据报协议 (UDP)。
2.4 应用层
应用层直接和应用程序接口并提供常见的网络应用服务。
可以用一张图来作为一个 TCP/IP 数据包结构的示例:
3. TCP 包结构
TCP 接收来自数据流的数据,将其分成块,并为其添加 TCP 头部使其成为 TCP 数据包。TCP 数据包稍后会被包裹成 IP 数据包与另一端进行数据交换。
TCP/IP 对每一层的数据包都用不同的英文专业术语来描述,在 Transport Layer 里的 TCP 数据包被称为 Segment、UDP 数据包被称为 UDP Datagram,在 Internet Layer 被称为 IP Datagram,在 Link Layer 被称为 Frame。当然有时亦会被泛称为”Packet”,但中文一般都以一个更泛用的”包”来形容。
一个 TCP 数据包由头部和数据两部分构成。
- 头部由十个必须域以及一个可选域构成
- 数据部分位于头部后面,其内容即为负载的应用层数据。数据部分的长度虽未于头部中描述,但可通过 IP 数据包的长度(Total Length)减去 IP 数据包头部以及 TCP 数据包头部获得
3.1 一些概念
- MTU (Maximum Transmission Unit):是网络层能单次负载的最大单元,网络层协议会根据这个值来决定是否把上层传下来的数据进行分片。
- MSS (Maximum Segment Size):TCP 数据包所能负载的最大体积,单位为字节,出于性能考虑,MSS 应该设得比 MTU 小,以避免 IP 分片
- WS (Window Scaling):滑动窗口,用于提高带宽的利用效率(占位,细节待补充)
- SACK (Selective Acknowledgments):接收端用于确认接收到的不连续块是否正确(占位,细节待补充)
3.2 TCP 头部信息
TCP 头部信息,如下图
- Source port 源端口和 Destination port 目的端口:用于寻找发送端和接收端应用程序,这两个值加上 IP 头部中的发送端 IP 地址和接收端 IP 地址唯一确定一个 TCP 连接
应用程序的端口号和应用程序所在主机的IP地址统称为 socket (套接字),IP:XX, 在互联网上 socket 唯一标识每一个应用程序,源端口+源 IP +目的端口+目的 IP 称为套接字对,一对套接字就是一个连接,一个客户端与服务器之间的连接。
Sequence Number 序号:占32 bit,有两种不同的作用
- 如果 SYN 标记位的值为1 ,那么当前序号就是初始序号(Initial Sequence Number),是接收到的顺序排第一的数据包,回应的确认序号将在此序号上加1
- 如果 SYN 标记位的值为0,那么当前序号就是当前数据通信会话中从初始序号开始累增的序号
Acknowledgment Number 确认序号:占32 bit,如果 ACK 标记位的值为1,那么当前确认序号的值就是发送方所期待的序号。确认先前收到的所有字节,每个端发送的第一个确认序号确定另一端的初始序号
Data offset 数据偏移:占4 bit,最大可表示 15 32 bit / 8 = 60 字节,最小为5 32 bit / 8 = 20 字节,提供了最大为40 字节的 options 部分。数据偏移确定了 TCP 数据部分在 TCP 数据包里的偏移(开始)位置
Preserved 保留:占3 bit ,均为0
Flag 标记位 (又叫控制位)占9 bit
- NS、CWR、ECE:不懂(占位,细节待补充)
- URG:标示 Urgent pointer 紧急指针是重要的
- ACK:标示确认序号 Acknowledgment Number 是重要的。在初始化 SYN 数据包之后发送的数据包都应该将此标记位设为 1
- PSH:发送方使用该标志通知接收方将所有缓冲的数据提交给应用程序
- RST:重置连接
- SYN:同步序号,只有双方发的第一个包应该将此标记位设为 1.
- FIN:标示发送方的最后一个数据包
Window Size 窗口大小:占16 bit,通过告诉对方本端 TCP 接收缓冲区还能容纳多少字节的数据,这样对方就能按需控制发送数据的速度
Checksum 校验和:占16 bit,用于校验,确保报文的准确性和完整性
Urgent pointer 紧急指针:占16 bit,如果设置了 URG 标记位,那么这个16 bit 的域就是紧急数据相对序号 Sequence Number 的偏移量
Options 选项:Options 的长度是由 Data Offset (数据偏移)决定的 (最多40字节)。Options 有三个域,分别是 Option-Kind (1字节), Option-Length (1字节)以及 Option-Data (可变长度),下面是一些 Option-Kind
- 0:选项表结束
- 1:无操作 (NOP – No Operation),用来对齐边界提高性能
- 2:MSS
- 3:WS
- 4:发送端支持并同意使用 SACK
- 5:具体的 SACK
- 8:时间戳 (占位,细节待补充)
Padding :头部填充,用于确保 TCP 头部结束,令数据在32 bit 的边界上开始
下面用 Wireshark 抓到的包和上面说到首部信息进行示例展示
4. TCP 连接的建立
TCP 采用了三次握手的方式来创建一个连接。在客户端尝试主动与服务端建立连接前,服务端必须先被动地监听将要建立连接的端口。以下是三次握手的细节:
- SYN:客户端主动地发送带有SYN 标记位的数据包给服务器,数据包的序号被设为一个随机数值 A
- SYN-ACK:服务端回复 SYN-ACK 作为回应,确定序号设置为接收到的数据包的序号加一,即 A+1 ,这时服务端将把数据包的序号设置为另一个随机数值 B
- ACK:最后,客户端发送 ACK 至服务端,序号设置为接收的数据包的确定序号,即 A+1,确认序号设置为接收的数据包的确定序号加一,即 B+1
完成三次握手后,客户端和服务端都确定了连接的建立。
可以用一张 GIF 图来表示握手:
5. TCP 数据的传输
客户端和服务端完成握手建立连接以后,双方便可以进行全双工(full-duplex)的通信。
TCP 使用序号 (Sequence Number) 来标识数据,数据传输的两端能通过序号重排列传输的数据。
数据传输的一个示例如下图:
当服务端朝客户端传递数据时,客户端会回应设置了 ACK 标记位的数据包,告诉服务端自己收到了相应段的数据。
(占位,传输策略的细节待补充)
6. TCP 连接的断开
TCP 使用了4次握手(four-way handshake) 来断开连接,确保了两端都能独立地断开连接。
当一端 I 想要断开连接,该端会发送设置了 FIN 标记位的数据包给另一端 R ,另一端 R 会返回一个设置了 ACK 标记位的数据包。所以一次典型的连接断开需要每个终端都提供一对带 FIN 和 ACK 标记的数据包,如下图: