TCP协议概述:可靠的传输层协议
一、TCP 协议概述:可靠的传输层协议
大家好,我是小北。
在 UDP 的文章中,我们说 UDP 是不可靠的一种传输协议,主打一个高效,发送方只管不停的发,不会和接收方做任何协商。
而 TCP(Transmission Control Protocol) 恰巧相反,主打一个靠谱(可靠)。
TCP 是一种功能齐全的传输层协议,它有一系列的机制来确保数据包可靠、一致、及时的达到接收方。
它提供大多数应用程序所需的网络传输功能,应用程序可以将字节数据作为字节流通过 TCP 发送,TCP 会自动将它们打包成适当大小的数据段(segment)进行传输。
我们通常所说的 TCP 协议其实就是 RFC793文档,793 是现代 TCP 协议的定义标准,当然,后续还有很多个 RFC 文档分别对 TCP 不同特性进行的补充说明。
TCP 有很多特性,其中有三大特性,是其作为面向连接的传输协议的关键:
我们先简单介绍一下这三大特性都是什么,提供了什么机制,后面我们将会逐步分析 TCP 的各种细节:
- 可靠传输(Reliable Transmission)
- 数据完整性:通过序列号(Sequence Numbers)和确认机制(Acknowledgment),TCP确保数据包按顺序到达,丢失的数据包能够被检测到并重传。
- 错误检测与纠正:TCP使用校验和(Checksum)来检测传输过程中数据是否被篡改。
- 重传机制:如果发送方未收到ACK(确认)信号,TCP会超时重传,确保数据的可靠送达。
- 流量控制(Flow Control)
- TCP通过滑动窗口协议(Sliding Window Protocol)动态调整发送方的发送速度,以匹配接收方的处理能力。
- 避免接收方因处理能力不足而造成的数据丢失。
- 拥塞控制(Congestion Control)
- TCP采用多种算法(如慢启动、拥塞避免、快速重传、快速恢复)来探测和缓解网络拥塞。
- 它根据网络状况调整数据发送速率,防止因过载导致网络性能下降。
二、TCP 头部格式
在 UDP 中我们看到它的头部极其简单,只有源端口、目的端口、长度、校验和,而 TCP 为了实现可靠传输,不仅包含 UDP 的所有字段,还新增了很多控制信息,如下图(真的很多,图都画了好久 orz):
先来看看来自 RFC793 的 TCP header 说明:
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 | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
TCP Header Format
当然,上面那个看起来比较古老,还是看下面这个手绘版本:
TCP 头部是 TCP 协议的核心,它包含了保证可靠传输所需的所有控制信息。
当然正如我在学习计网的开篇所说,学习网络绝不是要让大家死记硬背这些头部的格式和每个字段布局,这没太大作用。
关键还是通过 What-Why-How 的方式来记住 TCP 为了达成他的目标,都有哪些机制,而这些机制又是通过哪些字段来完成的。
接下来小北会用寄快递的例子,来让我们更容易理解和记忆 TCP 头部的结构。
话不多说,进入正题:
一、基础定位字段:门牌号码
1. 源端口和目的端口(各16位)
就像快递必须有发件人和收件人的地址一样,这两个字段标识了通信的双方:
- 源端口:发送方的"门牌号"
- 目的端口:接收方的"门牌号"
- 常见端口:HTTP(80)、HTTPS(443)、FTP(21)等
二、可靠传输字段:包裹追踪号
2. 序列号(32位)
相当于快递包裹的单号:
- 标识当前发送的数据包的序号
- 初始序列号(ISN)是随机生成的
- 后续序列号 = ISN + 已发送的字节数
- 例子:如果ISN=1000,发送100字节,下一个序列号就是1100
3.确认号(32位)
就像快递的签收回执:
- 表示期望收到的下一个数据包的序号
- 确认号 = 收到的序列号 + 收到的数据长度
- 用于告诉对方"我已经收到了哪些数据"
三、控制字段:包裹处理说明
4. 首部长度(4位)
- 告诉我们TCP头部有多长
- 单位是4字节
- 通常为5(表示20字节),最大为15
5. 标志位(6位)
像是快递包裹上的特殊标记:
- SYN(同步):🤝 打招呼 "你好,我要建立连接"
- ACK(确认):👍 点头 "我收到了"
- FIN(结束):👋 挥手 "我要关闭连接了"
- RST(重置):🚫 中止 "出问题了,重来"
- PSH(推送):🏃 急件 "请尽快处理"
- URG(紧急):🚨 特急 "这是紧急数据"
本质上是来操作 TCP 状态机的,双方收到带有特殊标志的 包时会驱动 TCP 的状态做转换。
四、流量控制字段:仓库容量管理
6. 窗口大小(16位)
就像告诉对方"我的仓库还能存多少货物":
- 表示接收缓冲区的剩余容量
- 用于流量控制
- 防止发送方发送过多数据导致接收方处理不过来
五、数据校验字段:包裹完整性检查
7. 校验和(16位)
相当于快递包裹的防伪标签:
- 用于检测数据是否被篡改
- 覆盖TCP头部和数据部分
- 接收方通过重新计算来验证数据完整性
8. 紧急指针(16位)
- 配合URG标志使用
- 指明紧急数据的位置
- 很少使用
TCP 头部每个字段都有它的作用,在上面我们分为了五类,分别起不同的作用:
- 定位:门牌号码(端口号)
- 追踪:包裹编号(序列号、确认号)
- 控制:处理指令(标志位)
- 管理:仓储管理(窗口大小)
- 安全:完整性检查(校验和)
也可以类比把它想象成是一个快递系统:
- 源目端口 = 发件人和收件人地址
- 序列号 = 快递单号
- 确认号 = 签收回执
- 标志位 = 包裹处理标签,说明如何包裹如何处理
- 窗口大小 = 仓库容量
- 校验和 = 防伪标记
三、TCP 的局限
虽然我们上面说了 TCP 提供很多能力,但是实际上也有它自己的局限,这里简单说一下:
- 安全性:
TCP 不提供任何机制来确保其传输的数据的安全性,如果需要信息保密的话,需要在TCP 的上层进行安全加密措施
- 不维护消息边界
TCP 将数据作为连续的字节流发送,而不是离散数据包发送。这就意味着接收方收到的数据是断断续续的字节流,如何把这些字节流归还为原始的数据包(比如一个 User 类),需要应用程序自己指定一条消息的结束位置和下一条消息的开始位置。
这也是非常经典的的一个问题:
TCP 粘包怎么办?
后面我们会单独写篇文章解答这个问题,在这我想简单的说一下,这个本身不是 TCP 的问题!!! 因为 TCP 一开始就是提供的面向字节流的服务,不存在任何包的概念,所以一切的一切都需要应用层去自己去定义消息的边界!
所以我认为这个面试题本身就很有问题!
- 不保证数据一定可靠传过去
这个似乎和 TCP 本身是可靠传输违背?其实不是的, TCP 本身是可靠传输是通过一系列的确认、重传机制做到,但是无论如何这些都是构建在底层网络基础上的,所以当网络底层链路出现问题的时候,TCP 也是没法保证数据传输成功的