0x01 协议概述
1.1 TCP协议
TCP是一种面向连接、可靠传输的传输层协议,它在通信开始前需要建立连接(三次握手),在数据传输过程中保证数据的正确性和顺序,到最后通过四次挥手断开连接,广泛应用于对数据可靠性要求高的场景中,例如:网页浏览(HTTP/HTTPS)、文件传输(FTP)、电子邮件(SMTP/POP3/IMAP)等;
应用场景:
适用于对可靠性要求高的数据传输,例如:
- Web服务(HTTP/HTTPS)
- 文件传输(FTP/SFTP)
- 邮件服务(SMTP/POP3/IMAP)
- 远程终端访问(SSH/telnet)
1.2 UDP协议
UDP是一种无连接、不保证可靠性的传输层协议,它不建立连接,数据以“数据报”的形式直接发送,因而速度快,适合对实时性要求高、但对数据可靠性要求不高的场景。
应用场景:适合对速度敏感但容错性高的应用,例如:
- 实时视频/音频传输
- 在线游戏
- DNS查询
- SNMP网络管理
- DHCP自动获取IP地址
0x02 协议特点
2.1 TCP协议特点
2.1.1 可靠传输
TCP被称为可靠协议,是因为它通过一整套机制确保数据的完整性、有序性和准确到达,具体包括:
- 确认应答机制(ACK):每当接收方收到数据之后,都会发送确认应答,如果发送方在一定时间内没有收到ACK,则重发数据包(超时重传机制)。
- 序列号与重排序:每个字节都有序列号(
Sequence Number),接收方根据它进行乱序重组,保证数据的有序。 - 重传机制:包括超时重传和快速重传(3个重复ACK触发),确保丢包能被恢复。
- 校验和(
Checksum):用于检测数据是否在传输中损坏。 - 窗口机制:与流量控制结合,确保接收端不会被淹没。
TCP的可靠传输不是因为“它不出错”,而是 即使出错,它也能检测并具备修复功能。
2.1.2 面向连接
- 三次握手(
Three-Way Handshake):在数据传输前,TCP要通过三次握手来建立连接,交换初始序列号和能力(窗口大小等),为双方通信准备状态。 - 状态维护:TCP的通信双方会维持连接状态(例如
ESTABLISHED、FIN_WAIT、CLOSE_WAIT等),知道连接生命周期的每个阶段。 - 四次挥手关闭连接:用于优雅断开连接,确保数据完整传输。
TCP需要建立连接,是因为它需要维护一个 “传输上下文”,包括序列号、确认号、窗口、重传计时器等,用来支持其可靠传输能力。
2.1.3 慢启动
为什么会有 慢启动 机制?
- 网络带宽不是无限的,TCP不知道网络的承载能力。
- 如果一开始发送太多的数据,可能导致网络出现拥塞,引发丢包和重传。
机制原理:

- 慢启动通过一个变量 拥塞窗口(
Congestion Window, cwnd)控制每次可以发送的数据量,该变量是发送方维护的变量,和接收方的接收端口(rwnd)共同决定发送速率。 - 实际的发送限制是:
有效窗口 = min(cwnd, rwnd) - 初始时 cwnd=1 MSS(最大报文段),每收到一个ACK,cwnd翻倍(指数增长)。
每个ACK到来的时候,就增加 cwnd,效果就是:每个RTT(往返时间)数据吞吐量翻倍,增长非常快。
什么时候结束 慢启动?
- 达到阈值:
ssthresh慢启动阈值 - 网络出现丢包(拥塞信号)
- 如果TCP检测到丢包(例如没有接收到ACK,或者重复接收到ACK),就认为网络拥塞了。
- 会执行 乘法减小算法
ssthresh = cwnd / 2cwnd 重置为 1 MSS,即重新开始慢启动(或进入快速恢复)
TCP的慢启动是一种预防性策略,它试探网络的承载能力,逐步放大传输规模,是TCP拥塞控制的一部分。
慢启动的整个过程就好比往一根未知深度的水管里倒水,一开始会谨慎的倒入一小杯(1 MSS),然后发现水可以流过去,那么就再倒2杯(2 MSS),还没堵?那么就倒入4杯、8杯…直到水开始堵塞了(丢包),此时我们发现水管最多只能承受10杯,于是在此之后就少量倒入,不再翻倍增长。
2.1.4 抢占带宽能力弱
- TCP的增长依赖ACK驱动和拥塞控制算法,这是一个“慢慢增加,快速减小”的过程。
- 一旦发生丢包或拥塞,TCP会将cwnd快速降为1(乘法减小),然后又进入慢启动重新适应。
- 如果新连接加入网络,老连接可能处于拥塞避免阶段,新连接处于慢启动初期,增长迅猛,反而可能抢走老连接的带宽。
TCP不主动抢夺带宽,只是试探性的使用可用带宽,并在出现问题时立刻退让。
2.1.5 即挑大也挑小
这句话的意思是:
- TCP既能处理大数据流(例如下载文件),也能传输小数据包(例如telnet输入的一个字符)。
- 原因在于TCP是面向字节流,底层会将小数据合并成段发送,也能将大数据分片发送。
技术支持:
- Nagle算法:会将小的数据暂存合并,直到等到确认或者足够大的时候发送,减少小包开销。
- 分段机制:大数据会被按照MSS分段处理。
TCP是一种通用型传输协议,它既能在延迟敏感型的应用中传输小数据,也能在带宽需求高的场景中承载大流量传输。
2.1.6 面向数据流
TCP把数据当作连续的无边界的字节流来处理,数据在发送方缓冲区流入,按照序号发送,接收方按照序列号拼接为原始流。
与UDP协议的数据报(一发一收)不同,TCP没有数据的天然边界。
举个例子:如果我们使用TCP发送三次:
send("hello");
send("world");
send("!!!");接收端可能一次性接收到“helloworld!!!”,也可能收到“hello” + "world!!!",我们无法预知边界,边界由应用层自己定义和解析。
TCP是为了可靠持续的数据传输而设计的,它把数据当成一个流管道,不关心你发送的是一段话还是一张图。
2.1.7 自带流量控制
什么是流量控制?
- 流量控制用于 保护接收端不被发送端压垮,保证发送方根据接收方处理能力进行发送。
- TCP使用滑动窗口机制实现流量控制。
工作方式
- 每个ACK包中会携带一个
窗口大小(window Size),告诉发送方“我还能接收多少字节”。 - 发送方根据这个窗口限制,控制未被确认的数据大小,不会超出接收方的能力。
2.2 UDP协议特点
2.2.1 不可靠传输
UDP协议不提供任何保证数据是否到达、是否按照顺序到达、是否被正确接收,但是UDP协议可以通过应用层面来保障可靠性,这里说的不可靠传输只是站在传输层来看的。
具体表现为:
- 没有序列号机制:不知道顺序是否错乱。
- 没有确认应答机制(ACK):不知道对方是否收到;
- 没有重传机制:丢了就丢了,不会自动补充发送。
- 没有连接状态:没有握手、没有断开,只是单纯的发送数据包。
本质理解:UDP协议牺牲了“可靠性”,换来了“低延迟、低资源消耗”,它把是否重传、是否纠错的责任交给了应用程序,例如:实时语音通话丢1、2个字不重发其实也可以听得懂的,但是如果重发了反而会卡顿。
2.2.2 非面向连接
什么是非连接型?
- 使用UDP协议不需要“建立连接”。
- 直接通过目标IP和端口,把数据报发送出去。
- 不维护通信状态,也不会“关闭连接”。
这样设计带来的好处:
- 快速、高效,无需三次握手,零延迟发送;
- 多播/广播更方便(TCP不支持广播);
- 非常适合服务器并发高的场景(例如DNS服务器)。
本质:UDP就是“开口就说话”,TCP是“打招呼再说话,聊完之后再告别”。
2.2.3 没有分片能力
可以通过应用层来负责对大的数据包进行切片。如果应用层不进行切片,那么就由IP层(网络层)来进行切片。
- UDP报文最大长度是65507字节(这是理论上最大,但是实际常常限制在512或者1472字节)。
- 超过这个长度,IP层会进行分片,但是UDP协议本身不管重组和丢片。
- 任意一个分片丢失,整个UDP报文都会被丢弃。
存在的问题/隐患:
- IP层分片极度脆弱。
- 没有分片重传机制。
- MTU(最大传输单元)稍微不一致就容易丢。
UDP协议的报文尽量保持报文短小,尤其是跨网络传输的时候。
2.2.4 抢占带宽能力强
只要应用层产生数据,就会直接发送出去。同时也是因为没有确认机制,没有像TCP那样的超时重传机制、慢启动这些特点。
为什么说UDP抢占带宽强?
- UDP没有 慢启动、拥塞避免、窗口控制 等这些TCP具备的机制;
- 一旦开始发送数据,UDP协议就会持续稳定的推送数据,不受对方限制;
- 如果应用层高频发送数据,UDP就可以长时间稳定占满链路。
在实际应用场景中:
- 在线游戏、直播平台、IPTV广播等依赖 UDP 的“野蛮带宽”能力。
- 在TCP存在的网络中,UDP可能会“把它挤掉”。
UDP是 野蛮生长,能不顾网络现状直接抢占带宽,这也是为什么在大型网络中谨慎使用它的原因。
2.2.5 面向数据报
应用的每一次操作,就会对应的产生一个报文,不像TCP那样,可以同时将多次产生的数据,通过数据流的方式传输。
2.2.6 即不挑大也不挑小
含义解释
UDP相比TCP,不关心你发送数据的大小,既不偏好大流量(例如FTP),也不怕小数据(例如游戏心跳包)。
- 发送10字节和发送1000字节没有本质差别;
- 应用层把一整块数据塞进来,UDP就一整个发送出去。
- 不涉及合并、小包延迟(比如TCP的Nagle算法)问题。
这样设计的优势
- 适合“短促、频繁的小包通信”。
- 也可以用在“流畅、不间断的大包推流”。
UDP是“你给我啥我就发送啥,不管大小”,TCP是“让我优化一下我再发送”。
0x03 协议报文
3.1 TCP协议报文
TCP报文头部最少一共20字节,最多60字节。

-
Source Port:源端口号,16位表示发送方(客户端)的端口号。
用于识别主机上的特定应用程序。
范围:0~65535,通常大于1024的端口为动态端口。
-
Destination Port:目的端口号,16位表示接收方(服务端)的端口号
确定目标主机上哪个应用服务要处理此报文。
-
Sequence Number:序列号,32位表示当前发送的数据的第一个字节在整个字节流中的位置编号。
TCP是基于字节流的,发送的数据每个字节都有序号。
建立连接时,客户端和服务器各自随机选择初始序列号(ISN)。
作用:
- 保证有序传输。
- 支持丢包重传。
- 防止旧连接报文干扰。
序列号管理是TCP实现可靠性核心机制之一。
-
Acknowledgement Number:确认号,32位表示期望接收的下一个字节的序号。
也就是说,接收方已经成功收到了
ack_number - 1的所有数据。例如:如果确认号 = 101,则表示我已经收到100,等待的是序号从101开始的数据。
此字段在flag 中ACK位置位为1时才有效,是实现TCP可靠性的基础之一。
-
Data Offset:首部长度,4位指定TCP报文头的长度,以32位(4字节)为单位。
最小值为5(即20字节,20字节时没有Options);
如果有TCP选项字段(例如MSS、窗口扩大因子),首部长度的值会变大。
这里是用来分辨报文头和数据部分的边界。
-
Reserved:保留位,6位目前未使用,预留给未来协议扩展使用的。
必须全为0.
-
Flags:控制标识位,6位或者9位这些flag位用来控制报文的行为,常见的标识位如下:
标志位 名称 作用说明 URG 紧急 表示紧急数据,配合 Urgent Pointer 使用 ACK 确认 表示确认号字段有效 PSH 推送 要求立即推送数据给应用程序,不缓冲 RST 重置 强制断开连接,通常用于异常终止连接 SYN 同步 建立连接时使用,发起握手过程 FIN 终止 表示数据发送完成,发起连接终止过程 例如:
SYN + ACK:表示对建立连接的响应。FIN+ACK:表示对断开连接的确认。 -
Window Size:窗口大小,16位告诉对方我还能接收多少字节的数据。
与
cwnd拥塞窗口一起控制TCP的流量控制与拥塞控制。实现滑动窗口协议的核心机制。
大窗口表示接收方处理能力强,小窗口则让发送方放慢速度。
-
Checksum:校验和,16位用于验证整个TCP报文(头部+数据)是否在传输过程中被篡改或损坏。
检查错误(如位翻转、数据破坏等)。
-
Urgent Pointer:紧急指针,16位指出紧急数据的结尾位置;
与URG标志位一起使用。
很少在现实网络中使用(telnet等老协议中可能出现)。
作用:优先处理某些重要命令,比如远程登录中的中断信号。
-
Options:可选字段,可变长。提供额外的功能扩展。
常见的选项包括:
- MSS:最大报文段长度
- 窗口扩大因子
- 时间戳
- SACK:选择性确认
Options 会影响
Data Offset字段的值。 -
Data:数据载荷,可变长这是实际传输的应用层数据
长度 = IP总长度 - TCP报头长度
可达数KB(通常受到MSS和MTU的限制)。
关于 sequence Number 和 Acknowledgment Number 的补充说明
-
序列号 Sequence Number
表示:发送方即将发送的数据字节的编号
TCP是字节流协议,每一个字节都会编号(不是每个包)
在建立连接(三次握手)时,客户端和服务端都会随机生成一个初始序列号 ISN (Initial Sequence Number)
例如:如果客户端发送的数据是
HELLO(一共5个字节),假设初始序列号为1000, 那么:数据内容 字节偏移 对应序列号 H 0 1000 E 1 1001 L 2 1002 L 3 1003 O 4 1004 报文段中只填 第一个字节的序列号,即这个包的
Sequence Number = 1000 -
确认号 Acknowledgment Number
表示:接收方期望下次收到的字节序号
本质是:“你发到哪了,我收到了,我现在等着从这个字节开始的新的数据。”
如果客户端已经成功发送了
HELLO(1000~1004),那么服务器会:- 回复:ACK报文段,确认号
ack_num=1005 - 表示:“我已经收到了
1000~1004,现在你从1005开始发送吧“。
- 回复:ACK报文段,确认号
-
在三次握手过程中seq num 和 ack num的变化过程
步骤 谁发送的 报文内容 seq ack 说明 1 客户端 SYN x - 告诉服务器我的起始序列号是x 2 服务器 SYN+ACK y x+1 告诉客户端我的起始序列是y,并确认你发送的x 3 客户端 ACK x+1 y+1 双方确认无误,建立了连接 -
在传输数据时的动态变化过程
假设场景如下:
- 客户端的
ISN = 1000 - 服务端的
ISN = 3000
客户端发送:
报文类型 seq num 数据长度 实际字节范围 服务端回复 ACK num SYN 1000 0 无 SYN+ACK 1001 Data 1 1001 5 字节 1001~1005 Ack 1006 Data 2 1006 3字节 1006~1008 Ack 1009 服务端确认过程:
- 每次都在 Ack 中告知客户端:”我收到了,下次请从这个字节开始发送“
- 这就是 滑动窗口确认 的体现。
- 客户端的
3.2 UDP协议报文
UDP协议的报文头非常简单,只有8个字节,包含4个字段,这种简洁的结构正是UDP协议高性能、低延迟的关键原因之一。

-
源端口
Source Port,16位含义:
- 表示发送方使用的端口号
- 是操作系统为当前发送进程分配的一个临时端口。
- 如果发送方不需要接收回应包,该字段可为0(可选字段)
作用和机制:
- 在收到回应时,目标主机会将这个端口作为目标来发送回应报文。
- 用于标识连接到具体进程(类似于TCP端口的作用)
-
目标端口
Destination Port,16位含义:
- 表示接收方进程监听的端口号
- 决定UDP数据应该交给哪个进程处理。
- 一般为服务进程绑定的固定端口
-
总长度
Length,16位含义:
- 指的是整个UDP报文的总长度(单位:字节)
- 包括UDP头部(8字节)+ 数据部分
- 最小值 = 8,仅仅只有头部,没有数据
- 最大值 = 65535(IP包最大值),但实际不超过MTU(通常 小于 1500字节)
工作机制:
- 接收方根据此字段知道数据包有多长,从IP层获得包之后进行解析。
- 如果实际长度小于
Length,说明包存在损坏,直接丢弃。
-
校验和
Checksum,16位含义:
- 用于检测 UDP 报文头部和数据在传输过程中是否发生错误。
- 覆盖范围:
- UDP头部 + 数据部分
- 伪首部:源IP、目标IP、协议号、UDP长度等(由IP层提供)
特点:
- UDP校验和是可选字段(但是在IPv6是必须的)
- 如果值为0,表示未启用校验(在IPv4中是允许的)
- 如果计算结果出错,接收端将直接丢弃该报文,不重传。
校验机制:接收方对收到的数据再次计算校验和,与包中的校验和字段进行对比:
- 如果一致,则说明数据为损坏。
- 如果不一致,则丢弃,且不重传。
-
数据部分
Data,可变长度含义:
- 实际传输的有效数据。
- 通常是来自上层协议的数据,比如DNS查询报文、音频帧、视频帧、游戏状态等。
特点:
- 没有分段或顺序机制,完全依赖于应用层解释。
- 最大长度一般不超过1472字节(受限于 MTU - IP + UDP头部)
- 如果超过,则需要应用层自定义“分片重组逻辑”。