网络与 IP

TCP 全称是什么?传输控制协议的特点、首部与工作过程

TCP 的全称是 Transmission Control Protocol,中文叫传输控制协议,是 TCP/IP 协议簇里负责可靠传输的传输层协议。它先建立连接,再用序号、确认和重传保证数据不丢、不乱、不重,并用流量控制和拥塞控制调节发送速度;网页、SSH、远程桌面和数据库都跑在它上面。

作者 顶联产品团队发布于 9 分钟阅读

TCP 全称是 Transmission Control Protocol,中文译为传输控制协议。它是 TCP/IP 协议簇中工作在传输层的协议,封装在 IP 包里,IPv4 头部用协议号 6 标识它。TCP 最早在 1981 年由 RFC 793 标准化,2022 年发布的 RFC 9293 把此后几十年的修订合并成新的标准文本,取代了 RFC 793。用一句话回答“TCP 是什么协议”:它在两台主机的两个程序之间,提供面向连接、可靠、按序的字节流传输。服务器上的网站(HTTP、HTTPS)、SSH、远程桌面、MySQL 这类数据库,都依赖 TCP 把数据完整地送到对端。下面依次讲它的特点、首部字段、一次连接的完整过程、流量控制与拥塞控制,以及服务器运维中最常碰到的 TCP 报错。

TCP 是什么协议:在 IP 之上补上“可靠”

IP 只负责“尽力而为”地把包送到目标主机:路上可能丢包,可能因为走了不同路径而乱序,可能因为重发而重复,也可能在传输中出错。应用程序如果直接面对这些问题,每个程序都得自己写一遍重传和排序。TCP 的作用就是在操作系统内核里统一处理掉这些问题,让程序只需要“写进去、读出来”。

IP 层可能发生的情况 TCP 用什么机制解决
包丢了 接收方对收到的数据发确认,发送方超时没收到确认就重传
包乱序到达 每个字节都有序号,接收方按序号重新排好再交给程序
包重复到达 按序号识别出已经收过的数据,直接丢弃
内容在传输中出错 校验和不对的报文段直接丢弃,靠重传补上
接收方处理不过来 流量控制:接收方在每个报文里通告自己还能收多少
网络本身拥堵 拥塞控制:发送方根据丢包和延迟主动降速

有两件事 TCP 不管:一是不加密,数据在线路上是明文,HTTPS、SSH 的加密由 TLS 或 SSH 协议自己完成;二是不保证时效,它保证数据最终完整到达,但丢包重传时,后面的数据要等前面的补齐才能交给程序,延迟会上升。

TCP 只存在于通信的两端。路由器和二层交换机转发时只看 IP 和 MAC,不维护 TCP 状态;防火墙、负载均衡、NAT 设备会读取 TCP 头部,按连接状态做判断,这也是它们能拦截或打断 TCP 连接的原因。

TCP 协议的特点:五个关键词分别指什么

特点 具体含义 带来的代价或注意点
面向连接 传数据之前先握手,双方交换初始序号,告知各自的最大报文段长度(MSS),并协商窗口缩放、SACK 等选项;连接由源 IP、源端口、目的 IP、目的端口四元组唯一确定 多一个往返才能开始传数据;每条连接在两端都占用内存和状态
可靠传输 序号按字节编号,确认号表示“下一个期望收到的字节”;超时未确认就重传,收到 3 个重复确认会提前重传(快速重传) 丢包时延迟上升;Linux 的重传超时最小约 200 毫秒
字节流 程序写入的数据被看作连续的字节,按 MSS 切分发送,不保留每次写入的边界 应用层要自己划分消息,也就是常说的“粘包”问题
全双工 两个方向各有独立的序号和窗口,可以同时收发;一方关闭发送后,仍可继续接收(半关闭) 关闭连接时两个方向要分别关闭
流量与拥塞控制 发送量同时受接收方窗口和发送方估算的拥塞窗口限制 长距离、有丢包的线路上,单条连接往往跑不满带宽

另外,TCP 只支持一对一通信。广播、组播这类一对多的场景用不了 TCP,只能用 UDP。

“面向连接”中的四元组值得多说一句:服务器上的 Nginx 只监听一个 443 端口,却能同时服务几万个客户端,因为每个客户端的 IP 和源端口不同,四元组各不相同,内核据此把报文分发到各自的连接上。

TCP 首部格式:20 字节固定字段

TCP 首部固定部分 20 字节,后面可以跟最多 40 字节的选项,所以首部长度在 20 到 60 字节之间。按每行 4 字节排列:

字节 0–3    | 源端口(16 位)                | 目的端口(16 位)               |
字节 4–7    | 序号(32 位)                                                   |
字节 8–11   | 确认号(32 位)                                                 |
字节 12–15  | 首部长度(4 位)| 保留(4 位)| 标志位(8 位)| 窗口(16 位)       |
字节 16–19  | 校验和(16 位)                | 紧急指针(16 位)               |
字节 20 起  | 选项(0–40 字节,长度为 4 的倍数)                              |
字段 长度 作用 运维中在哪里见到
源端口 16 位 发送方的端口,客户端一般由系统从临时端口范围里分配 Linux 默认临时端口范围 32768–60999,sysctl net.ipv4.ip_local_port_range 查看
目的端口 16 位 接收方程序监听的端口 防火墙规则里的 --dport 443
序号 32 位 本报文段第一个数据字节的编号;SYN 报文里是随机生成的初始序号 tcpdump 输出的 seq
确认号 32 位 期望收到对方的下一个字节编号,ACK 标志置位时有效 tcpdump 输出的 ack
首部长度 4 位 以 4 字节为单位,取值 5–15,对应 20–60 字节 抓包工具里的 Header Length
保留 4 位 置 0,留作扩展 —
标志位 8 位 CWR、ECE、URG、ACK、PSH、RST、SYN、FIN tcpdump 的 Flags [S.]
窗口 16 位 接收方还能接收多少字节,需乘以握手时协商的缩放因子 win 值长时间为 0 表示接收方处理不过来
校验和 16 位 覆盖伪首部(源 IP、目的 IP、协议号、长度)、首部和数据 本机发出的包在抓包里显示校验和错误,多半是网卡校验和卸载,不是故障
紧急指针 16 位 URG 置位时指明紧急数据的位置,现在几乎不用 —

八个标志位里,日常排障用得最多的是这几个,括号里是 tcpdump 的显示符号:SYN(S)发起连接、ACK(.)确认、FIN(F)正常关闭、RST(R)强制复位、PSH(P)提示对方尽快把数据交给程序。ECE(E)和 CWR(W)用于显式拥塞通知(ECN),URG(U)用于紧急数据。

常见选项都只在握手阶段协商一次:MSS 告诉对方自己能接收的最大数据段,以太网 MTU 1500 时通常是 1460 字节;窗口缩放把 16 位窗口左移最多 14 位,使窗口上限从 65535 字节扩大到约 1 GB;SACK 允许接收方告诉发送方“哪几段已经收到”,丢包时只重传缺的部分;时间戳用于更准确地测量往返时间,并防止序号回绕带来的混淆。

一次 TCP 连接的完整过程:从握手到挥手

在服务器 198.51.100.23 上执行 curl http://203.0.113.10/,另开一个窗口抓包:

tcpdump -nn -i eth0 host 203.0.113.10 and tcp port 80

整个连接大致由下面这些报文组成。为了便于阅读,双方的初始序号都记为 0、后面用相对值表示(tcpdump 在握手报文里显示真实的随机序号,之后才换算成相对值),数据大小为示例:

序 方向 标志 序号与确认号 内容 阶段
1 客户端 → 服务器 S seq 0 携带 MSS 1460、SACK、窗口缩放等选项 握手
2 服务器 → 客户端 S. seq 0,ack 1 服务器的初始序号和选项 握手
3 客户端 → 服务器 . ack 1 连接建立 握手
4 客户端 → 服务器 P. seq 1:79 78 字节的 HTTP 请求 传数据
5 服务器 → 客户端 . ack 79 确认收到请求 传数据
6 服务器 → 客户端 . seq 1:1449 第一段响应,1448 字节 传数据
7 服务器 → 客户端 P. seq 1449:2897 第二段响应,1448 字节 传数据
8 客户端 → 服务器 . ack 2897 一次确认两段数据 传数据
9 客户端 → 服务器 F. seq 79 curl 收完响应,主动关闭 挥手
10 服务器 → 客户端 F. ack 80 确认对方的 FIN,同时发出自己的 FIN 挥手
11 客户端 → 服务器 . ack 2898 确认服务器的 FIN 挥手

从这张表能读出 TCP 的几个关键行为:

  • 握手只做协商,不传业务数据。前三个报文的长度都是 0,真正的请求从第 4 个报文才开始。SYN 和 FIN 各占用一个序号,所以第 3 个报文确认的是 1,第 10 个报文确认的是 80。
  • 确认是累计的。第 8 个报文的 ack 2897 一次确认了第 6、7 两段,不必逐段确认;Linux 还会稍微延迟发送确认,等着和要发的数据合并。
  • 数据段大小是 1448 而不是 1460。双方启用了时间戳选项,每个报文段要拿出 12 字节放选项,MSS 1460 减去 12 就是 1448。
  • 挥手常常只有三个包。理论上关闭连接要四个报文,两个方向各一对 FIN 和 ACK;服务器收到 FIN 时如果程序也马上关闭,内核就把 ACK 和自己的 FIN 合并成一个报文发出。
  • 先关闭的一方进入 TIME_WAIT。这里是 curl 先发 FIN,所以 TIME_WAIT 留在客户端,Linux 上持续 60 秒,用 ss -tan state time-wait 能看到。

流量控制和拥塞控制有什么区别

两者都是“控制发送速度”,但保护的对象不同:

对比项 流量控制 拥塞控制
保护谁 接收方,防止它的缓冲区被灌满 网络,防止路径上的链路和设备过载
依据 接收方在每个报文的窗口字段里通告的接收窗口(rwnd) 发送方自己估算的拥塞窗口(cwnd),不写在报文里
信号来源 接收缓冲区还剩多少空间 丢包、往返时间变长、ECN 标记
调节方式 接收方调大或调小通告窗口,处理不过来时通告 0 慢启动、拥塞避免、遇到丢包时降低窗口
常见问题 窗口太小,长距离传输跑不满带宽 线路有丢包,窗口一再被压低

发送方任何时刻在途(已发出但未确认)的数据量,不超过 rwnd 和 cwnd 中较小的那个。由此有一个很实用的估算:单条连接的速度上限约等于窗口除以往返时间。

  • 不启用窗口缩放时,窗口最大 65535 字节。往返时间 150 毫秒的跨境线路上,上限约为 65535 × 8 ÷ 0.15 ≈ 3.5 Mbit/s,带宽再大也用不上。
  • 反过来,要在往返时间 150 毫秒的线路上跑满 100 Mbit/s,窗口至少要 100,000,000 × 0.15 ÷ 8 ≈ 1.9 MB。

拥塞窗口从小开始增长。Linux 的初始拥塞窗口是 10 个报文段,约 14 KB;慢启动阶段每经过一个往返大约翻一倍。假设往返时间 30 毫秒、带宽 100 Mbit/s,需要的窗口约 375 KB,即 259 个 1448 字节的报文段,从 10 翻倍到这个量级要 5 个往返左右。这解释了一个常见现象:下载几十 KB 的小文件时,连接还没走完慢启动就结束了,测出的速度远低于带宽,这不是线路问题。

遇到丢包时,拥塞控制会把窗口压低:经典的 Reno 算法减半,Linux 默认的 CUBIC 降到原来的约 70%,之后再逐步恢复。BBR 则主要依据测得的带宽和往返时间来定速,不把丢包作为主要信号。在服务器上查看和观察:

sysctl net.ipv4.tcp_congestion_control              # 当前算法,多数发行版默认 cubic
sysctl net.ipv4.tcp_available_congestion_control    # 内核已加载、可以切换的算法
ss -tin state established '( sport = :443 )'        # 查看本机 443 端口上每条连接的内部参数

ss 的 -i 输出里,cwnd:10 是当前拥塞窗口(单位是报文段),rtt:3.5/1.2 是平滑往返时间和波动(毫秒),retrans:0/3 是当前未恢复的和累计的重传次数,wscale:7,7 是双方的窗口缩放因子。某个客户反映“下载慢”时,看他那条连接的 rtt 和 retrans,就能初步判断是距离远、线路丢包,还是服务器自身的问题。

服务器上哪些服务用 TCP,连接报错对应什么

服务 默认端口 为什么用 TCP
HTTP / HTTPS 80 / 443 网页、接口的内容一个字节都不能错;HTTP/3 改用 UDP 443 上的 QUIC 是例外
SSH、SFTP 22 命令和文件必须完整、按序到达
远程桌面 RDP 3389 认证和会话建立必须走 TCP,画面传输可以额外用 UDP
MySQL / PostgreSQL / Redis 3306 / 5432 / 6379 查询和结果不能丢失或乱序
SMTP、IMAP 25、465、587 / 143、993 邮件投递要求完整送达
FTP 21,另有数据端口 控制连接和数据连接都是 TCP
BGP 179 路由协议借用 TCP 的可靠性,自己不再实现重传
DNS 区域传送和大应答 53 普通查询走 UDP,数据量大时改用 TCP

正因为几乎所有核心服务都依赖 TCP,服务器上很多“连不上”“断开了”的问题,本质是 TCP 层发生了某个具体事件。程序报出的错误信息可以直接对应到报文:

程序报错 TCP 层发生了什么 常见原因
Connection refused 发出 SYN 后立即收到 RST(或 ICMP 端口不可达) 端口上没有程序监听;或防火墙用 REJECT 拒绝,回了 TCP 复位或 ICMP 端口不可达
Connection timed out SYN 重传多次都没有任何应答 安全组或防火墙静默丢弃;主机不在线;回程路由有问题
Connection reset by peer 连接建立后收到 RST 对端程序崩溃或强制关闭连接;中间设备的会话已超时被删除
Broken pipe 向已经被对端关闭或复位的连接写数据 对端已经关闭;长连接空闲太久被中间设备断开
No route to host 收到 ICMP 主机不可达 路由缺失;或防火墙以 ICMP 方式拒绝,firewalld 默认的拒绝就是这种

最后一类问题在机房里很常见:空闲连接被中间设备悄悄断开。状态防火墙和 NAT 设备会删除一段时间没有报文的连接记录,而 Linux 的 TCP 保活(keepalive)默认要空闲 7200 秒才开始探测,远长于很多设备的超时时间。SSH 空闲一会儿就卡死、数据库连接池里的连接用的时候才发现已失效,都是这个原因。处理办法是让应用层更频繁地发保活:SSH 客户端在 ~/.ssh/config 里设置 ServerAliveInterval 60,或在服务器的 sshd_config 里设置 ClientAliveInterval 60;数据库连接池开启空闲检测。

在管理系统里怎么落地

服务器的 SSH 和远程桌面都建立在它自己的 TCP 协议栈上:网卡配置写错、防火墙把 22 或 3389 挡住、系统卡死,TCP 连接就建立不起来,再多的重试也没用。Toplink DCIM 的 IPMI 远程管理 在浏览器里同时提供 SSH、RDP 和 VNC 控制台:前两者走服务器操作系统的通道,日常使用;VNC 经由 BMC 连接,不依赖服务器系统里的网络和 TCP 服务,系统层面的连接出问题时,仍然能看到画面进去修复。网络设备一侧也离不开 TCP:交换机管理 默认通过 SSH 或 Telnet 下发端口、VLAN 等配置,这两种都是 TCP 上的会话,每一条命令都要可靠送达;SNMP 只用于只读采集。

常见问题

TCP 端口和 UDP 端口是同一套吗?

不是。TCP 和 UDP 各有一套 0–65535 的端口号,TCP 53 和 UDP 53 是两个独立的端口,同一个端口号可以分别被两个程序占用。防火墙和安全组放行时要按协议分别写规则。

TCP 粘包是什么意思?

TCP 是字节流,不保留消息边界:程序连续写入的两条消息,对端可能一次读到,也可能一条消息分两次才读完。这不是故障,而是应用层要自己定边界,常用固定长度、长度前缀或分隔符三种办法。

TCP 一定比 UDP 慢吗?

不一定。TCP 建连多一个往返,丢包时要等重传;但在稳定的线路上传大文件,TCP 同样能跑满带宽。实时音视频、游戏这类宁可丢数据也不愿等重传的业务,才更适合 UDP。

想看看在你的机房里怎么用?

从一次产品演示开始,梳理你的业务链路。

查看价格
服务热线 400-112-2951

让我们聊聊你的 IDC 业务

扫码添加企业微信,预约产品演示或申请试用。

Toplink 企业微信二维码

长按保存或使用企业微信 / 微信扫描

也可致电
400-112-2951
Telegram
@TopLink88