UDP 与 TCP 协议详解

文章目录

  • 一、UDP协议
  • 二、TCP协议
    • [2.1 TCP协议端格式](#2.1 TCP协议端格式)
    • [2.2 TCP确认应答](#2.2 TCP确认应答)
    • [2.3 TCP超时重传](#2.3 TCP超时重传)
    • [2.4 TCP连接管理](#2.4 TCP连接管理)
      • [2.4.1 三次握手](#2.4.1 三次握手)
      • [2.4.2 四次挥手](#2.4.2 四次挥手)
    • [2.5 TCP滑动窗口](#2.5 TCP滑动窗口)
    • [2.6 流量控制](#2.6 流量控制)
      • [2.6.1 窗口探测](#2.6.1 窗口探测)
    • [2.7 拥塞控制](#2.7 拥塞控制)
    • [2.8 延迟应答](#2.8 延迟应答)
    • [2.9 捎带应答](#2.9 捎带应答)
    • [2.10 面向字节流](#2.10 面向字节流)
    • [2.11 粘包问题](#2.11 粘包问题)
    • [2.12 异常情况](#2.12 异常情况)
    • [2.13 总结](#2.13 总结)

一、UDP协议

无连接,不可靠传输,面向数据报,全双工

UDP协议端格式

源端口号(16位)发送端应用程序使用的端口号。用于接收端知道从哪里回送数据。若不需要回复,可设为0。

目的端口号(16位)接收端应用程序的端口号。UDP根据此字段将数据交给正确的进程。

UDP长度(16位):整个UDP数据报(首部 + 数据)的字节数。最小值为8字节(仅首部,无数据),最大为65535字节。

UDP检验和(16位):用于检测UDP首部、数据和伪首部在传输中是否出错。可选,若不用则填0。


二、TCP协议

有连接,可靠传输,面向字节流,全双工。

2.1 TCP协议端格式

下面按照序号顺序,逐一解析每个字段的含义和作用:

序号 字段名 长度(位) 说明
1 源端口号 16 发送端应用程序的端口号,用于标识本地通信进程。
2 目的端口号 16 接收端应用程序的端口号,与源端口一起唯一确定一条连接的两个端点。
3 32位序号 32 该报文段中第一个数据字节的序列号,用于保证数据的有序到达和可靠性。
4 32位确认序号 32 期望收到对方下一个报文段的首字节序号,仅当 ACK 标志位为 1 时有效。
5 4位首部长度 4 表示 TCP 头部的长度(以 32 位字为单位),最小为 5(即 20 字节),最大为 15(60 字节)。
6 保留位 6 保留给未来使用,必须置为 0。
7 URG 1 紧急指针标志位,为 1 时表示紧急指针字段有效。
8 ACK 1 确认标志位,为 1 时确认序号字段有效。
9 PSH 1 推送标志位,为 1 时表示接收方应尽快将数据交给应用层。
10 RST 1 复位标志位,为 1 时表示连接出现严重错误,必须释放连接并重新建立。
11 SYN 1 同步标志位,用于建立连接时的同步序号(三次握手)。
12 FIN 1 结束标志位,为 1 时表示发送方没有数据要传输了,用于关闭连接。
13 16位窗口大小 16 用于流量控制,表示本端接收窗口的剩余容量(以字节为单位),告知对方可以发送的数据量。
14 16位检验和 16 覆盖 TCP 头部、数据和伪首部的校验和,用于检测传输过程中的错误。
15 16位紧急指针 16 仅当 URG=1 时有效,指出紧急数据在报文段中的结束位置(相对于序号字段的值)。
16 选项 可变 可选字段,如 MSS、窗口缩放因子、时间戳等,长度可变且需为 32 位字的整数倍。
17 数据 可变 上层应用数据,不是 TCP 头部的一部分,但通常紧跟在头部之后。

总结 :整个 TCP 头部固定部分为 20 字节(不含选项),选项最多可扩展至 40 字节。这些字段共同实现了 TCP 的可靠传输、流量控制、拥塞控制和连接管理等功能。


2.2 TCP确认应答

TCP 数据传输过程中的可靠传输机制 ( 数据传输 + 确认应答(保证可靠性))

TCP将每个字节的数据都进行了编号,就是序列号。

每个字节对应一个序列号,但传输时用一个段的首字节序号代表整个段

2.3 TCP超时重传

主机A向主机B发送数据,可能因为网络堵塞,数据无法到达主机B。

如果主机A在特定的时间内没有接受到主机B的发来的确认应答,也有可能是因为ACK丢失了。

此时,主机B就会收到很多重复的数据,那么TCP就需要识别出来哪些包是重复的包,并把重复的包丢弃。

这时候,就可以利用前面的序列号,很容易做到去重的效果。

那么超时时间如何确定呢?

  • 最理想的情况下,找到一个最小的时间,保证"确认应答一定能在这个时间内返回"。
  • 但是这个时间的长短,随着网络环境的不同,是有差异的
  • 如果超时时间设的太长,会影响整体的重传效率
  • 如果超时时间设的太短,有可能会频繁发送重复的包
  • 如果超时时间设的太短,有可能会频繁发送重复的包

TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态的计算 这个的最大超时时间。

比如:在Windows中,超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。

如果重发一次之后,仍然得不到应答,等待2* 500后再进行重传。

如果仍得不到应答,等待4* 500ms,依次类推,以指数形式递增。

累计到一定的重传次数,TCP认为网络或对端主机出现异常,强制关闭连接。

确认应答和超时重传这两个机制,保证了TCP的可靠传输


2.4 TCP连接管理

在正常情况下,TCP要进行三次握手建立连接和四次挥手进行断开连接。

2.4.1 三次握手

为什么要建立三次握手?

1.确保网络链路通畅。

2.验证双方通信的发送过程和接收过程是否正常。

三次握手过程:

2.4.2 四次挥手


注意:通常情况下,中间两步不能合并。因为TCP是全双工通信,主机A发送FIN只代表A不再发送数据,但服务器可能还有数据要发送给A。

  • 服务器收到FIN后,会先回复ACK确认收到断开请求,但此时服务器仍然可以向主机A发送剩余的数据。
  • 等服务器把所有数据发送完毕后,才会发送FIN来关闭自己的发送通道。
  • 这两个动作之间有时间差,无法直接合并成ACK+FIN。

特殊情况下可以合并: 可以通过延时应答的方式来实现。TCP延时应答机制,允许主机在收到报文后,不立即回复ACK,就可以把ACK和这些报文一起发送,减少一次单独的ACK交互。在四次挥手场景中,服务器收到主机的FIN后,没有立即回复ACK,而是等待服务器把剩下的数据发送完毕后,再把ACK(确认FIN)和FIN(关闭自己的发送通道)合并成一个ACK+FIN报文发送给主机A。

这样原本的四次挥手就变成了三次交互。


2.5 TCP滑动窗口

刚刚上面的确认应答策略,对每一个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送下一个数据段,这样做的一个比较大的缺点就是:在数据返回时间较长的时候,性能较差。

既然这样一发一应答的方式性能较差,那我们可以采取,一次发多条数据,这样就可以大大提升性能(其实就是在多个等待的时间重叠在一起了)。

  • 窗口大小是无需等待确认应答,可以发送数据的最大值,上图的窗口大小就是4000个字节(四个段)。
  • 发送前四个段的时候,不需要等待任何的ACK,直接发送
  • 收到第一个ACK后,窗口向后滑动,继续发送第五个段的数据,依次类推。
  • 操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲缓冲区来记录当前还有哪些数据没有应答,只有确认应答过的数据,才能从这个缓冲区删掉。
  • 窗口越大,则网络的吞吐率就越高。

那么如果丢包了,如何进行重传?

这里分两种情况进行讨论。
情况1 :数据包已经到达了,ACK丢了

ACK 丢失,但后续还有数据 后续数据的 ACK 会顺带确认前面的数据(无额外动作)。但如果ACK 丢失,且这是最后一段数据 发送方超时 → 重传数据 → 接收方重发 ACK。

情况2 :数据包直接丢了

例如上图,数据包1001-2000丢失后,主机B每次都返回ACK=1001。当发送方收到3个重复的ACK(即总共3次相同的ACK=1001,不算原始的那个)时,立即触发快速重传,重传1001-2000。重传成功后,接收方收到缺失数据,后续ACK正常更新,继续传输后续数据包。

这种机制被称为"高速重发控制"(也叫"快重传")。


2.6 流量控制

接收端处理速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等一系列连锁反应。

因此TCP支持根据接收端的处理能力,来决定发送端的发送速度,这个机制就叫做流量控制。

  • 接收端将自己可以接收的缓冲区大小放入TCP首部中的"窗口大小"字段,通过ACK端通知发送端。
  • 窗口大小字段越大,说明网络的吞吐量越高。
  • 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的通知的值给发送端。
  • 发送端接收到这个窗口后,就会减满自己的发送速度。
  • 如果接收端的缓冲区满了,就会将窗口设置为0,这时,发送方不再发送数据,但是需要定期发送一个窗口的探测数据段,使接收端把窗口大小告诉给发送端。

2.6.1 窗口探测

窗口探测是 TCP 防止"零窗口死锁"的重要保活机制,确保即使控制消息丢失,通信仍能恢复

例如上图:

  • 主机A向主机B发送数据包1-1000,主机B返回ACK期望下一个数据是1001,并告知主机A窗口大小为3000。

  • 主机A继续发送1001-2000,主机B确认应答并告知期望下一个是2001,窗口大小为2000,重复上述过程。

  • 直到主机B返回ACK,期望下一个是4001,窗口大小为0,此时主机A收到这个指令,就停止发送数据。

  • 主机A等待主机B发送窗口更新通知(例如"现在可用 2000 字节"),此时如果这个

    这个通知丢失了,如果没有窗口探测就会出现死锁。

  • 为了避免死锁,发送方会主动发送一个"窗口探测包",这个包通常只包含 1 字节的新数据(或仅携带 ACK,不带数据),主要目的是触发接收方重新通告当前窗口大小。

  • 图中A 在超时后未收到窗口更新,就发出一个窗口探测包(带"下一个是 4001"的期待)。

  • B 收到探测包后,回复窗口更新通知(例如告诉 A 可以从 4001 开始发送)

  • 窗口更新通知也可能丢失,如果丢失了,A 仍然不知道可以发送数据,就会时不时再次发送窗口探测包(周期性地),直到收到非零窗口的确认。


接收方如何告知窗口大小?

通过 TCP 首部中 16 位的窗口字段(Window Size),2^16=65536字节。

65535 字节的限制如何突破

使用 窗口扩大因子(Window Scaling) 选项(TCP 选项,40 字节中一部分),

实际窗口大小 = 窗口字段值 << 扩大因子(左移 M 位,即乘以 2^M)。

窗口扩大的协商时机

只在 TCP 三次握手 的 SYN 包中协商。

双方各自通告自己的扩大因子(M),实际通信时按较小的一方(或各向对方发送的窗口用自己的因子,但常见实现是对称使用)。


2.7 拥塞控制

虽然TCP有了滑动窗口,能够高效可靠的发送大量数据,但是如果在刚开始阶段就发送大量数据,仍可能出现问题。

因为网络上有很多计算机,可能当前网络状态就已经比较拥堵,在不清楚当前网络的情况下,贸然发送大量数据,是有可能引起雪上加霜了。

TCP引入慢启动机制,先发少量的数据,探探路,摸清当前网课的拥堵状态,再决定按照多大的速度传输数据。

  • 此处引入一个概念程为 拥塞窗口

  • 发送开始的时候,定义拥塞窗口大小为1;

  • 每次收到一个ACK应答,拥塞窗口加1;

  • 每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口;

像上面这样的拥塞窗口增长速度,是指数级别的。"慢启动"只是指初使时慢,但是增长速度非常快。

  • 为了不增长的那么快,因此不能使拥塞窗口单纯的加倍。

  • 此处引入一个叫做慢启动的阈值

  • 当拥塞窗口超过这个阈值的时候,不再 按照指数方式 增长,而是按照线性方式增长

  • 当TCP开始启动的时候,慢启动阈值等于窗口最大值

  • 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1;

少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们就认为网络拥塞

当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发送拥堵,吞吐量会立刻下降

拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但又要避免网络造成太大压力的折中方案。


2.8 延迟应答

它是为了提高网络传输效率而设计的

如果接收数据的主机立刻返回ACK应答,这个时候返回的窗口可能比较小

  • 假设接收端缓冲区为1M。一次收到了500K的数据;如果立刻应答,返回的窗口就是500K;

  • 但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了;

  • 在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来;

  • 如果接收端稍微等一会再应答,比如等待200ms再应答,那么这个时候返回的窗口大小就是1M;

  • 一定要记得,窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。


那么所有的包都可以延迟应答么?

肯定也不是,有限制。

  • 数量限制:每隔N个包就应答一次;

  • 时间限制:超过最大延迟时间就应答一次;

具体的数量和超时时间,依据操作系统不同也有差异;一般N取2,超时时间取200ms。

如下图:每收到两个数据包才回复一个 ACK


2.9 捎带应答

在接收方需要回复应用层数据时,把原本要单独发送的 ACK 附加在这个数据包中一起发送,而不是发送一个只有 ACK 的包。

例如:

客户端发 "How are you"

服务器不仅要回复 ACK 确认收到,还要回复应用层数据 "Fine, thank you"

不优化:先发 ACK(60 字节),再发 "Fine, thank you"(数据包)。 → 两个包

捎带应答:只发一个包,里面包含 TCP 首部 + "Fine, thank you" + ACK 标志 → 一个包搞定。


2.10 面向字节流

创建一个TCP的socket,同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区

  • 调用write时,数据会先写入发送缓冲区中;

  • 如果发送的字节数太长 ,会被拆分成多个TCP的数据包发出;

  • 如果发送的字节数太短 ,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去;

  • 接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区;

  • 然后应用程序可以调用read从接收缓冲区拿数据;

  • 另一方面,TCP的一个连接,既有发送缓冲区,也有接收缓冲区,那么对于这一个连接,既可以读数据,也可以写数据。这个概念叫做 全双工

由于缓冲区的存在,TCP程序的读和写不需要一一匹配
例如:

  • 写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write,每次写一个字节;
  • 读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read 100个字节,也可以一次read一个字节,重复100次。

2.11 粘包问题

首先要明确,粘包问题中的"包",是指的应用层的数据包。

  • 在TCP的协议头中,没有如同UDP一样的"报文长度"这样的字段,但是有一个序号这样的字段。

  • 站在传输层的角度,TCP是一个一个报文过来的。按照序号排好序放在缓冲区中。

  • 站在应用层的角度,看到的只是一串连续的字节数据。

  • 那么应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分,是一个完整的应用层数据包。

那么如何避免粘包问题呢?

归根结底就是一句话,明确两个包之间的边界

  • 对于定长的包,保证每次都按固定大小读取即可。

  • 对于变长的包,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置;还可以在包和包之间使用明确的分隔符(应用层协议,是程序猿自己来定的,只要保证分隔符不和正文冲突即可)。

思考:对于UDP协议来说,是否也存在"粘包问题"呢?

UDP 没有粘包问题,因为它是基于数据报 的传输,每个 UDP 报文独立交付,应用程序收到的是完整、独立的应用层数据包,边界由协议自身保证。


2.12 异常情况

1. 进程终止(如程序崩溃或被 kill)

  • 现象:进程终止时,操作系统会自动关闭该进程打开的所有文件描述符,包括 socket。
  • TCP 行为:关闭 socket 会正常触发 TCP 四次挥手,发送 FIN 包给对端。
  • 结果 :对端能感知到连接关闭,与正常调用 close() 几乎一样。

2. 机器重启(正常重启)

  • 现象:操作系统在关机过程中会终止所有进程并关闭所有 socket。
  • TCP 行为:同样会发送 FIN,完成正常释放。
  • 注意:如果是突然断电,则走下面的"掉电"场景。

3. 机器掉电 / 网线断开

这是最严重的异常,没有机会发送 FIN

  • 对端视角:对端(接收端)认为连接仍然存在,因为没收到 FIN。
  • 情况A:接收端尝试发送数据
    发送数据时,会收到 RST 包(因为对方主机已不可达或端口不存在),或者发送超时,从而发现连接已断。
  • 情况B:双方都没有数据发送
    TCP 本身提供 保活定时器(Keep-Alive)(默认通常关闭或 2 小时以上)。如果开启,会定时发送探测包,若多次无响应则关闭连接。

4. 应用层心跳机制

除了 TCP 自带的保活,很多应用层协议会实现自己的心跳

  • HTTP 长连接 :通过 Keep-Alive 头或定期发送请求检测对方是否存活。
  • QQ / 微信:断线后自动尝试重连,并定时发送心跳包维持会话。

异常场景 TCP 行为 对方是否感知
进程终止 发送 FIN ✅ 能感知(正常关闭)
机器重启 发送 FIN ✅ 能感知
掉电/断网 不发 FIN ❌ 对方以为连接还在;需要写入超时或保活探测来发现

关键点 :掉电/断网时,没有 FIN,依赖写入超时TCP Keep-Alive应用层心跳来检测死连接。


2.13 总结

TCP 核心设计思想:TCP 的复杂性来源于既要可靠,又要高效

TCP 通过校验和、序号保证可靠性;通过滑动窗口、快速重传、延迟应答、拥塞控制提升性能;并用多种定时器处理超时、保活和连接关闭等辅助任务。

相关推荐
路由侠内网穿透.3 小时前
本地部署静态网站托管平台 Netlify 并实现外部访问(Windows 版本)
网络·网络协议
Patrick_Wilson3 小时前
CLI 工具突然变慢了?别急着怀疑网络,按这四步排查
网络协议·性能优化·命令行
长谷深风1114 小时前
HTTP请求全过程解析【个人八股】
网络·网络协议·http·多线程下载·tcp 连接·请求报文、响应报文·网络请求流程
xhbh6664 小时前
MC端口映射完全教程:路由器虚拟服务器配置+防火墙放行+内网穿透备用方案
运维·服务器·网络·网络协议·tcp/ip·智能路由器·流量端口转发
Ether IC Verifier4 小时前
TCP拥塞控制详解
网络·网络协议·tcp/ip·计算机网络·dpu
切糕师学AI4 小时前
计算机网络层次结构详解:从OSI七层模型到TCP/IP四层模型
网络·tcp/ip·计算机网络
Linux运维技术栈4 小时前
一次暴力枚举攻击的防御实践:从 IP 封禁到 WAF,再到 Nginx+Lua 业务层防御
tcp/ip·nginx·安全·lua·云服务器
Ether IC Verifier6 小时前
TCP三次握手与四次挥手详解
网络·网络协议·tcp/ip·计算机网络
pengyi87101514 小时前
独享IP池自动化维护方案,智能检测自动延长使用寿命
网络协议·tcp/ip·自动化