一、重新认识端口号
在TCP/I 协议中,可以用 "源 IP","源端口号","目的 IP","目的端口号","协议号" 这样一个五元组来标识一个通信 (可以通过 netstat -n 查看);
1.1 端口号范围划分
0‑1023:知名端口号,HTTP,FTP,SSH 等这些广为使用的应用层协议,他们的端口号都是固定的。
1024‑65535:操作系统动态分配的端口号。客户端程序的端口号,就是由操作系统从这个范围分配的。但是也有一些服务会选择1024‑65535的端口号进行绑定:
| 端口 | 协议 | 服务 | 用途说明 |
|---|---|---|---|
| 1521 | TCP | Oracle | Oracle 数据库默认端口 |
| 2049 | TCP/UDP | NFS | Linux 网络文件共享服务 |
| 2181 | TCP | Zookeeper | 分布式协调组件 |
| 2701 | TCP | Steam 游戏服务 | |
| 3000 | TCP | Node/Grafana | 前端开发服务器、监控面板 |
| 3306 | TCP | MySQL/MariaDB | 最常用关系型数据库 |
| 5000 | TCP | Flask | Python Web 开发默认端口 |
| 5432 | TCP | PostgreSQL | 开源关系数据库 |
| 5672 | TCP | RabbitMQ | 消息队列 AMQP 协议端口 |
| 6379 | TCP | Redis | 内存缓存数据库 |
| 8000 | TCP | Django/FastAPI | Python 后端常用端口 |
| 8080 | TCP | HTTP 备用 | Tomcat、代理、Web 开发端口 |
| 8443 | TCP | HTTPS 备用 | Jenkins、K8s Dashboard |
| 9000 | TCP | MinIO | 对象存储服务 |
| 9042 | TCP | Cassandra | 数据库 CQL 访问端口 |
| 9092 | TCP | Kafka | 消息队列服务端口 |
| 9200 | TCP | Elasticsearch | 搜索引擎 REST 接口 |
| 11211 | TCP/UDP | Memcached | 内存缓存 |
| 15672 | TCP | RabbitMQ 管理页面 | Web 管理 UI |
| 27017 | TCP/UDP | MongoDB | NoSQL 文档数据库 |
1.2 认识知名端口号 (Well‑Know Port Number)
有些服务器是非常常用的,为了使用方便,人们约定一些常用的服务器,都是用以下这些固定的端口号:
1.ssh 服务器,使用 22 端口
2.ftp 服务器,使用 21 端口
3.telnet 服务器,使用 23 端口
4.http 服务器,使用 80 端口
5.https 服务器,使用 443
我们可以通过执行下面的命令,来看到这些知名端口号
bash
cat /etc/services

一个进程可以绑定多个端口号,但是一个端口号不能被多个进程绑定!
二、UDP协议
2.1 UDP协议的格式
UDP协议规定,UDP首部固定8 字节,4 个字段,每个字段 16 位。
16位源端口号是发送方进程的端口,目的端口号是接收方进程的端口。16位UDP长保存的是整个 UDP 数据报总长度(首部 + 数据),最小为 8(只有头部,无数据);最大为16 位,即65535 字节。
16位UDP检验和保存的是校验UDP首部+数据,用于检测传输是否出错。如果出错则直接丢弃报文。
数据(有效载荷)是向上层应用交付的数据,长度可变。

UDP协议是如何实现报头和有效载荷分离的呢?
UDP采用的是固定长度的报头,在固定长度的报头中,存在一个16位UDP长度,用于标识整个报文的长度。有了报文的长度和报头长度,就可以知道有效载荷的长度,也就能实现报头与有效载荷的分离了。
UDP中的有效载荷是如何分用的呢?
UDP 的有效载荷依靠端口号完成分用。客户端发送UDP报文时,UDP首部携带16位目的端口号,标识服务器上目标进程的端口。当报文到达传输层后,操作系统读取该目的端口号,根据端口号把 UDP报文中的有效载荷,交付给服务器上对应的应用进程,以此实现有效载荷的向上交付。
UDP叫做面向数据报协议,是面向数据报的,每一个 UDP 报文都有明确的长度。当接收方拿到 UDP 报文时,拿到的就是一个完整独立的数据报,报文与报文之间相互隔离,因此UDP 不会发生粘包问题。

UDP 报头本质上可以理解为逻辑上的结构体。操作系统大多使用 C 语言实现,协议头部在内存中是以结构体的形式组织。接收方拿到二进制报文之后,通过指针按照协议格式解析,就能直接提取出源端口、目的端口、长度、检验和等不同字段的内容。
与之对应,封装的过程本质上可以类比为结构体变量的拷贝。有效载荷每向下穿过一层网络协议栈,都会在数据前端追加当前层级对应的协议报头,而每一层报头都是结构化的数据,整个拼装流程,逻辑上近似于对结构体数据进行复制填充。
在操作系统内部,同一时刻会存在大量待处理、待转发的网络报文,操作系统需要统一调度、维护这些报文,因此要先通过数据结构描述报文信息,再将所有报文有序组织管理**(先描述,再组织)** 。Linux内核中专门使用sk_buff结构体 来承载、管理每一个网络报文。这套报文管理逻辑,和操作系统管理进程的设计思路高度相似:操作系统依靠 task_struct 结构体描述单个进程,再通过链表组织全部进程;同理依靠 sk_buff 描述单个报文,再用链表批量管理所有网络数据包。
而报文本身,本质上由两部分构成 :一是用于记录报文元信息、描述报文属性的sk_buff结构体变量 ,二是该结构体配套、在内核内存中专门开辟的缓冲区,缓冲区中存放完整的协议头部与传输数据。

报文贯穿整个协议栈,其封装与解包操作的核心实现方式,其实仅靠移动缓冲区指针即可完成。封装时向前移动指针,预留空间写入当前层协议报头;包时向后移动指针,跳过当前层头部直接取上层载荷。
每个套接字对应的sock结构中维护着接收队列与发送队列。当传输层UDP完成报文解封装,需要将数据交付给上层应用时,会剥离UDP报头,把承载应用数据的sk_buff挂载到对应套接字的接收队列,以此向上交付给应用层;而应用向下发送数据时,则会把封装好头部的sk_buff放入套接字发送队列,向下递交给下层协议栈。
2.2 UDP协议的特点
UDP协议是无连接、不可靠、面向数据报的:
无连接:知道对端的 IP 和端口号就直接进行传输,不需要建立连接;
不可靠:没有确认机制,没有重传机制;如果因为网络故障该段无法发到对方,UDP 协议层也不会给应用层返回任何错误信息;
面向数据报:不能够灵活的控制读写数据的次数和数量;
无连接、不可靠
前文提到过,当接收端校验UDP检验和时一旦发现数据出错,会直接丢弃该报文。又因为UDP属于无连接协议,不存在应答、重传机制,报文丢失后接收方不会向发送方反馈任何丢失通知,发送方也不会主动等待确认、不会重新发送丢失的数据,这也是UDP被定义为不可靠传输协议的核心原因。
面向数据报
应用层交给 UDP 多长的报文,UDP 原样发送,既不会拆分,也不会合并;
当使用UDP协议传输100 个字节的数据:如果发送端调用一次sendto,发送 100 个字节,那么接收端也必须调用对应的一次recvfrom,接收 100 个字节;而不能循环调用 10 次recvfrom,每次接收 10 个字节。正由于UDP面向数据报的特点,发送端发一次数据,接收端就得接收一次数据,所以UDP协议就不会存在粘包问题。
那既然UDP协议是不可靠的,为什么我们还要有这个协议呢?
所谓的不可靠,不是说数据传输不过去,而是指的是数据传不过去时,而是当出现丢包、错包时,协议本身不会感知、不会处理,不会保障数据一定送达。UDP不可靠,不代表它不可用。其次,可靠传输需要一系列复杂机制支撑,要做更多的工作也就更多。所以为什么TCP是可靠的,因为它面向连接,同时额外实现了确认应答、重传、流量控制、拥塞控制等大量保障逻辑来确保它是可靠的。UDP相于TCP来说工作更少,所以也更简单。当我们在一些对丢包数据不敏感的环境下,我们就可以使用UDP进行数据的分发。
所以我们不要把UDP的不可靠当作缺点,而是要当作其特点。在对实现逻辑要求简单、追求高实时性、并发用户规模大,或是局域网环境这类场景下,UDP会被更多采用。
2.3 UDP的缓冲区
UDP没有真正意义上的发送缓冲区。调用sendto会将数据直接交给内核,由内核将数据传给网络层协议进行后续的传输动作。
UDP具有接收缓冲区。但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致;如果缓冲区满了,再到达的UDP数据就会被丢弃。
UDP socket既能读,也能写,所以UDP是全双工的。
2.4 使用UDP时的注意事项
我们注意到,UDP 协议首部中有一个 16 位的长度字段。也就是说一个 UDP 报文能够传输的最大长度是 64K(包含 UDP 首部)。
然而 64K 在当今的互联网环境下,是一个非常小的数字。
如果我们需要传输的数据超过 64K,就需要在应用层手动的分包,多次发送,并在接收端手动拼装。
2.5 基于UDP的应用层协议
1.DNS(域名解析协议) 默认使用 UDP 53 端口。域名查询报文短小,不需要建立连接;只有响应报文很大的时候才会降级使用 TCP。
2.DHCP(动态主机配置协议) UDP:客户端端口 68,服务端端口 67。主机刚启动还没有 IP,无法建立 TCP 连接,所以用 UDP 广播获取 IP 地址。
3.TFTP(简单文件传输协议) UDP 69 端口。用于局域网简单传小文件,比如 PXE 网卡启动,实现简单,性能优先,自己在应用层做简单可靠性。
4.SNMP(简单网络管理协议) UDP 161/162 端口。网管设备监控,频繁轻量查询网络设备状态。
5.NTP(网络时间协议) UDP 123 端口。客户端向时间服务器同步系统时间,报文简短,追求低延迟。
6.RTP(实时传输协议) 用于直播、视频会议、VoIP 语音通话。跑在 UDP 之上,允许少量丢包,保证实时性;配套 RTCP 做控制。
7.QUIC(HTTP/3 底层) 基于 UDP 实现,在用户态实现类似 TCP 的可靠传输、拥塞控制;解决 TCP 队头阻塞,0‑RTT 握手。HTTP/3 = HTTP over QUIC over UDP。
8.BOOTP(引导程序协议) UDP:客户端端口 68,服务端端口 67,是DHCP的前身。无盘设备开机无IP地址,依靠UDP广播获取网络配置与引导文件路径,结构简单、无地址租期。
9.NFS(网络文件系统) 早期NFSv2仅支持UDP,NFSv3兼容UDP/TCP,新版NFSv4及以上仅支持TCP。多用于Linux局域网文件挂载共享,UDP版本主打低延迟局域网传输。
10.游戏自定义协议 很多网游、竞技游戏,自定义 UDP 报文,优先保证实时,允许少量丢包。