UDP与TCP协议

我们先前学习了HTTP应用层协议,那么我们就现在就继续从应用层往下学习传输层协议。

前言:先搞懂「传输层」是干嘛的?

我们先从网络分层的最基础逻辑说起:

  • 网络层(IP 协议):只负责把数据从「一台主机」送到「另一台主机」(比如把你的电脑数据传到服务器),它不管数据到了主机后,该给哪个应用用。
  • 传输层(UDP/TCP):负责端到端的精准交付------ 数据到了主机后,找到对应的应用程序(比如浏览器、微信、游戏),实现应用之间的通信。

举个例子:你电脑同时开了浏览器 (访问网页)和微信 (发消息),网络层把数据传到电脑后,传输层靠端口号区分:给 80 端口的是网页数据,给微信端口的是聊天数据,绝不会搞混。

1:传输层核心基础:端口号

1:基本特性

  • 16 位数字,范围:0 ~ 65535
  • 一个进程可以绑定多个端口;一个端口只能被一个进程绑定 (否则会报Address already in use错误)

2:端口号分类

端口范围 类型 用途 典型例子
0 ~ 1023 知名端口 系统 / 通用服务固定占用 HTTP(80)、HTTPS(443)、SSH(22)、FTP(21)
1024 ~ 65535 动态端口 操作系统随机分配给客户端 浏览器、微信等客户端的临时端口

3:五元组

网络中用5 个信息 确定唯一的通信链路,用netstat -n命令可查看:源IP + 源端口 + 目的IP + 目的端口 + 协议号(TCP/UDP)

2:UDP协议:简单粗暴的寄信

UDP 全称用户数据报协议(User Datagram Protocol) ,它的设计理念是极简、快速,像现实中「寄平信」:不用提前打招呼,丢了不负责,寄多少收多少。

1. UDP 报文格式

UDP 头部只有8 字节,非常轻量,结构如下:

  • 源 / 目的端口:找到对应应用
  • UDP 总长度:最大64KB(首部 + 数据),超了需要应用层手动分包
  • 校验和:校验数据是否损坏,出错直接丢弃

2:UDP核心四大特点

1:无连接

知道对方的IP + 端口 就能直接发数据,不需要提前建立连接,发完就走,效率极高。

2:不可靠

没有「确认收到」「丢包重传」机制:

  • 数据丢了、乱序了,UDP 完全不管,也不会给应用层报错
  • 像寄信,邮局不保证送达、不保证顺序
3:面向数据报

发多少,收多少,不拆分、不合并

  • 应用层发 100 字节,UDP 就发 100 字节
  • 接收端必须一次 recvfrom 收完 100 字节,不能分 10 次每次收 10 字节
  • 数据边界清晰,不会出现「粘包」
4:全双工

一个 UDP socket 既能读数据,也能写数据,双向通信无阻碍。

3. UDP 缓冲区特性

  • 无发送缓冲区 :调用sendto直接把数据交给内核,立刻发往网络层
  • 有接收缓冲区:但不保证数据顺序,缓冲区满了,新数据直接丢弃

4. UDP 适用场景

追求速度、实时性,能容忍少量丢包的场景:

  • 域名解析:DNS
  • 动态 IP 分配:DHCP
  • 实时直播、视频通话、网络游戏
  • 简单文件传输:TFTP

3:TCP协议:严谨可靠的打电话

TCP 全称传输控制协议(Transmission Control Protocol) ,设计理念是可靠 + 高效,像现实中「打电话」:先接通、再通话、确认听到、最后挂断,绝不丢数据、不乱序。

TCP 为了实现可靠性,设计了一整套复杂机制,也是我们学习的重点。

1:TCP报文格式

TCP 头部最小 20 字节,最大 60 字节,核心字段如下:

关键标志位

  • SYN:请求建立连接
  • ACK:确认应答有效
  • FIN:请求关闭连接
  • RST:强制断开连接(异常)

2:TCP核心可靠机制

TCP 能做到不丢包、不乱序、不重复,全靠以下机制:

1:确认应答

TCP 给每个字节数据编序列号(SEQ) ,接收方收到数据后,回确认号(ACK)

  • 发送方发1~1000字节,SEQ=1
  • 接收方收到后,回 ACK=1001(意思:「我收到 1~1000,下次从 1001 发」)
2:超时重传

发送方发完数据,超过固定时间没收到 ACK ,就自动重发;靠序列号去重:接收方收到重复数据,直接丢弃。

3:连接管理:三次握手+四次挥手

TCP 是面向连接的协议,通信前必须建立连接,结束后必须断开连接。

① 三次握手:建立连接(客户端→服务端)

通俗类比

  1. 客户端:「我要和你通信,准备好了吗?」(发 SYN)

  2. 服务端:「我收到了,我也准备好了!」(回 SYN+ACK)

  3. 客户端:「收到,连接建立!」(回 ACK)

**为什么是三次?**防止「失效的连接请求」被服务端接收,导致建立无效连接浪费资源。

② 四次挥手:断开连接

TCP 是全双工(双方都能发数据),所以双方都要主动关闭:

  1. 客户端:「我发完数据了,要关闭发送通道」(发 FIN)

  2. 服务端:「收到,我知道了」(回 ACK)

  3. 服务端:「我也发完了,要关闭」(发 FIN)

  4. 客户端:「收到,我等 2MSL 再彻底关」(回 ACK)

3:关键状态

1:TIME_WAIT

主动关闭连接的一方,会进入TIME_WAIT,等待2MSL(报文最大生存时间):

  • 防止最后一个 ACK 丢失,服务端重发 FIN
  • 清理网络中残留的旧报文,避免干扰新连接
2:CLOSE_WAIT

服务端收到客户端的 FIN 后,进入CLOSE_WAIT,如果一直停留,说明代码没调用 close 关闭 socket,是程序 BUG

4:TCP性能优化机制

TCP在保证可靠的同时,还做了大量优化提升传输速度:

1:滑动窗口

不用「发一个包、等一个 ACK」,一次发多个包,窗口大小就是「无需等待 ACK 可发送的最大数据量」,大幅提升吞吐率。


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

2:快重传

发送方连续接收到3次相同的ACK,立即重传对应数据,不用等超时,减少等待时间。

如果遭传输过程中数据包丢了


当某一段报文段丢失之后, 发送端会一直收到 1001 这样的 ACK, 就像是在提醒
发送端 "我想要的是 1001" 一样;
如果发送端主机连续三次收到了同样一个 "1001" 这样的应答, 就会将对应的数
据 1001 - 2000 重新发送;
这个时候接收端收到了 1001 之后, 再次返回的 ACK 就是 7001 了(因为 2001 -
7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中;
这种机制被称为 "高速重发控制"(也叫 "快重传").

3:流量控制

接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这
个时候如果发送端继续发送, 就会造成丢包, 继而引起丢包重传等等一系列连锁反应.
因此 TCP 支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量
控制**(Flow Control)**;
接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段, 通过 ACK 端通知发送端;
窗口大小字段越大, 说明网络的吞吐量越高;
接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
发送端接受到这个窗口之后, 就会减慢自己的发送速度;
如果接收端缓冲区满了, 就会将窗口置为 0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端

接收端如何把窗口大小告诉发送端呢? 回忆我们的 TCP 首部中, 有一个 16 位窗口字段, 就是存放了窗口大小信息;
那么问题来了, 16 位数字最大表示 65535, 那么 TCP 窗口最大就是 65535 字节么?
实际上, TCP 首部 40 字节选项中还包含了一个窗口扩大因子 M, 实际窗口大小是 窗口字段的值左移 M 位;

4:拥塞控制

虽然 TCP 有了滑动窗口这个大杀器, 能够高效可靠的发送大量的数据. 但是如果在刚开始阶段就发送大量的数据, 仍然可能引发问题.
因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据, 是很有可能引起雪上加霜的.
TCP 引入 慢启动机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据;


此处引入一个概念称为拥塞窗口
发送开始的时候, 定义拥塞窗口大小为 1;
每次收到一个 ACK 应答, 拥塞窗口加 1;
每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口;


像上面这样的拥塞窗口增长速度, 是指数级别的. "慢启动" 只是指初使时慢, 但是增长速
度非常快.
为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍.
此处引入一个叫做慢启动的阈值
当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长

当 TCP 开始启动的时候, 慢启动阈值等于窗口最大值;
在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回 1;
少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;
当 TCP 通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
拥塞控制, 归根结底是 TCP 协议想尽可能快的把数据传输给对方, 但是又要避免给网络 造成太大压力的折中方案.

5:延迟应答

如果接收数据的主机立刻返回 ACK 应答, 这时候返回的窗口可能比较小。
假设接收端缓冲区为 1M. 一次收到了 500K 的数据; 如果立刻应答, 返回的窗口
就是 500K;
但实际上可能处理端处理的速度很快, 10ms 之内就把 500K 数据从缓冲区消费
掉了;
这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也
能处理过来;
如果接收端稍微等一会再应答, 比如等待 200ms 再应答, 那么这个时候返回的
窗口大小就是 1M;


一定要记得, 窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率;
那么所有的包都可以延迟应答么? 肯定也不是;
数量限制: 每隔 N 个包就应答一次;
时间限制: 超过最大延迟时间就应答一次;


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

6:捎带应答

在延迟应答的基础上, 我们发现, 很多情况下, 客户端服务器在应用层也是 "一发一收"的. 意味着客户端给服务器说了 "How are you", 服务器也会给客户端回一个 "Fine,thank you";
那么这个时候 ACK 就可以搭顺风车, 和服务器回应的 "Fine, thank you" 一起回给客户端

5:TCP面向字节流和粘包问题

1:面向字节流

TCP 有发送 / 接收缓冲区

  • 数据太长会拆分,太短会攒起来一起发
  • 应用层读写无需一一匹配(写 100 次 1 字节,读 1 次 100 字节也可以)
2:粘包问题

因为 TCP 没有数据边界,应用层会收到连续的字节流,分不清「一个完整的数据包从哪开始、到哪结束」,这就是粘包。

解决方法

  1. 定长包:固定每个包的大小
  2. 包头带长度:在数据包开头加「总长度」字段
  3. 分隔符:用特殊符号分隔数据包(如\r\n

6:TCP适用场景

追求可靠性,不能丢包的场景:

  • 网页访问:HTTP/HTTPS
  • 远程登录:SSH
  • 文件传输:FTP
  • 邮件发送:SMTP

4:UDP vs TCP 终极对比

对比维度 UDP TCP
连接特性 无连接,知道 IP + 端口直接发 面向连接,必须三次握手建立、四次挥手断开
可靠性 不可靠,无确认、无重传、丢包 / 乱序不管 可靠,有 ACK 确认、超时重传、去重、严格有序
头部长度 固定8 字节,极小开销 最小20 字节,最大 60 字节,开销更大
传输模式 面向数据报,一次收发完整匹配 面向字节流,数据像水流,无固定边界
数据边界 有边界,无粘包问题 无边界,必然粘包,需应用层手动解决
缓冲区 无发送缓冲区;有接收缓冲区(不保证顺序,满则丢) 有发送 + 接收缓冲区,自动排序、攒包 / 拆包
连接建立 无需建立,直接传输 必须三次握手,防止无效连接浪费资源
连接断开 直接断开,无挥手 必须四次挥手,全双工双向关闭
重传机制 无重传,丢包就丢 超时重传 + 快重传,保证数据必达
流量控制 无,发送方想发就发 有,接收方告知窗口大小,防止撑爆缓冲区
拥塞控制 无,网络堵了也照发 有,慢启动 + 拥塞避免,保护网络稳定
报文最大长度 限制64KB(含首部),超了需分包 无固定上限,由 MSS/MTU 自动分片
顺序保证 不保证,后发可能先到 严格保证顺序,按序列号重组
丢包处理 直接丢弃,不通知应用层 重传丢包,直到确认收到
全双工 支持,一个 Socket 双向通信 支持,全双工,双方可同时收发
错误处理 仅校验和,错了直接丢 校验 + 重传 + 排序,错误自动修复
编程复杂度 极低,API 简单,无需处理连接 / 重传 高,需处理连接、粘包、重传、状态机
系统资源 占用极低,无连接状态 占用高,需维护连接状态、缓冲区、窗口
传输效率 极高,延迟低,适合实时场景 较高,为可靠牺牲部分速度
核心设计理念 简单、快速、轻量化 可靠、有序、稳定、容错
典型应用协议 DNS、DHCP、TFTP、直播、游戏、视频通话 HTTP/HTTPS、FTP、SSH、SMTP、文件传输
适用场景 实时性优先,可容忍少量丢包 可靠性优先,绝对不能丢数据

5:总结

  • 传输层的核心:靠端口号实现应用间通信,五元组唯一标识连接。
  • UDP:极简、快速、无连接、不可靠,适合实时场景。
  • TCP:复杂、可靠、面向连接,靠三次握手 / 四次挥手、确认重传、滑动窗口等机制,保证数据安全。
  • 选择原则:要快选 UDP,要稳选 TCP。
相关推荐
2401_8734794011 小时前
如何从零搭建私有化IP查询平台?数据采集、清洗、建库到API发布全流程
服务器·网络·tcp/ip
学代码的真由酱11 小时前
HTTPS
网络协议·http·https
Vis-Lin13 小时前
BLE 协议栈:ATT 协议详解
网络·物联网·网络协议·iot·ble
TechWayfarer14 小时前
攻防对抗:利用IP段归属查询工具快速封禁攻击源——3步联动防火墙(附脚本)
python·网络协议·tcp/ip·安全
科技牛牛19 小时前
IP定位误差导致封号_深度解析
网络·网络协议·tcp/ip
思麟呀20 小时前
应用层协议HTTP
linux·服务器·网络·c++·网络协议·http
pengyi8710151 天前
共享IP使用基础注意事项,从源头降低关联风险
网络·网络协议·tcp/ip·安全·http
X7x51 天前
网工核心:直连 / 静态 / 动态路由全解,附华为 / 华三 / 思科配置 + 高级应用
运维·网络·网络协议·信息与通信
北京耐用通信1 天前
告别通讯掉线!耐达讯自动化Modbus转Profinet网关:工业现场的“定海神针”
服务器·人工智能·网络协议·自动化·信息与通信