网络基础一次理清:IP、端口、套接字与 TCP/UDP
1. 先放一张总览:数据怎么走
应用发出的字节不会直接变成网线里的电信号。协议栈会一层层加头、再一层层拆头。日常开发里最常碰到的是 TCP/IP 四层模型(和 OSI 七层对照时,你只要记住「应用 / 传输 / 网络 / 链路」这四层就够用了)。
graph TB
A[应用层<br/>HTTP / DNS / SSH] --> B[传输层<br/>TCP / UDP + 端口]
B --> C[网络层<br/>IP 地址 + 路由]
C --> D[链路/物理层<br/>MAC、帧、网卡]
| 层 | 核心问题 | 你天天见到的标识 |
|---|---|---|
| 应用层 | 业务语义怎么编码 | URL、HTTP 方法、DNS 查询 |
| 传输层 | 进程之间怎么交付 | 端口号、TCP 连接 / UDP 数据报 |
| 网络层 | 包怎么跨网到达主机 | IP 地址 |
| 链路层 | 同一网段怎么到下一跳 | MAC 地址 |
一句话记忆: IP 负责把包送到「哪台机器」,端口负责送到「机器上的哪个进程」,TCP/UDP 规定「交付方式靠不靠谱、要不要先握手」,Socket 则是操作系统给应用程序的那把「把手」。

2. IP:给主机在网络里贴门牌
2.1 IP 做什么
IP(Internet Protocol)工作在网络层,主要职责:
- 寻址:每个网卡/接口配置 IP,标识主机(更准确说是网络接口)在网际中的位置
- 路由转发:中间路由器根据目的 IP 查表,决定下一跳
- 分片与重组(IPv4 常见):过大报文可按路径 MTU 切开,对端再拼回来
- 尽力而为(best-effort):IP 不保证 一定送达、不丢包、不乱序;可靠传输要靠上层(通常是 TCP)补
常见版本:
| 版本 | 地址形态 | 备注 |
|---|---|---|
| IPv4 | 32 位,点分十进制,如 192.168.1.10 |
仍是大多数内网默认 |
| IPv6 | 128 位,冒号十六进制 | 地址空间大,很多云环境双栈 |
私有地址段(IPv4,不会在公网路由)记这三段就够日常用:
10.0.0.0/8172.16.0.0/12192.168.0.0/16
127.0.0.1 是本机回环,流量不出网卡,适合本机自测。
2.2 IP alone 不够:为什么还要端口
一台机器上同时跑浏览器、SSH、数据库、聊天客户端。包到了 192.168.1.10 之后,内核还得知道交给 哪个进程。传输层用 16 位端口号(0–65535)做多路复用/分用。
可以粗暴地记:
主机定位 → IP
进程定位 → 端口
两端会话 → 往往还要看「五元组」(见下文)
3. 端口号:进程在传输层的工位号
3.1 端口范围(IANA)
端口是 16 位无符号整数,空间分成三段(工程上按这个记):
| 范围 | 名称 | 常见用法 |
|---|---|---|
| 0–1023 | 系统 / 知名端口(Well-known) | HTTP 80、HTTPS 443、SSH 22、DNS 53 |
| 1024–49151 | 注册端口(Registered) | 许多中间件、自定义服务登记在此 |
| 49152–65535 | 动态 / 私有端口(Ephemeral) | 客户端临时源端口,连出去时由 OS 分配 |
权威登记表见 IANA Service Name and Transport Protocol Port Number Registry。
注意两点,面试和排障都好用:
- TCP 的 80 和 UDP 的 80 是两套命名空间。同一数字端口,TCP 与 UDP 互不占用。
- Linux 上绑定 1024 以下端口通常需要特权(或
CAP_NET_BIND_SERVICE);开发机上自测服务多用 8000、8080、3000 等。
3.2 常见端口速查
| 端口 | 协议(常见) | 服务 |
|---|---|---|
| 22 | TCP | SSH |
| 53 | UDP/TCP | DNS |
| 80 | TCP | HTTP |
| 443 | TCP | HTTPS |
| 3306 | TCP | MySQL |
| 6379 | TCP | Redis |
ss -lntp / netstat -an 能看到本机谁在听哪个端口;防火墙放行的也是「协议 + 端口」,不是只写一个数字就完事。
4. 套接字 Socket:程序眼里的网络端点
4.1 定义别背八股,抓这句
套接字(Socket)是操作系统提供的、面向应用的通信抽象,把「协议族 + 类型 + 本地/对端地址」封装成一个可 read/write(或 send/recv)的对象。
网络编程里常说的「一个 TCP 连接两端各有一个 socket」,可以理解为:
套接字地址 ≈ (协议, 本地 IP, 本地端口) 以及连接建立后的对端信息
更完整地描述一条 TCP 连接,用 五元组:
(协议, 源 IP, 源端口, 目的 IP, 目的端口)
五元组唯一确定一条传输层会话。NAT、负载均衡、连接跟踪表,本质上都在跟五元组打交道。

4.2 Socket 不是协议
容易混的三点:
| 说法 | 对不对 | 说明 |
|---|---|---|
| Socket = TCP | 错 | Socket 是 API/抽象;底下可以是 TCP、UDP,甚至 Unix domain |
| Socket = IP + 端口 | 半对 | 地址部分常这么记;完整还要协议类型、已连接状态等 |
| 先有 TCP/IP,后有 Socket 编程接口 | 对 | Berkeley Socket 成为事实上的跨平台网络 API 风格 |
应用层协议(HTTP、WebSocket 等)跑在传输层之上;浏览器访问网页时,下面通常是 TCP socket(或基于 UDP 的 QUIC,那是另一条故事线)。
4.3 TCP 与 UDP 在 socket 类型上的差别
SOCK_STREAM → 字节流,对应 TCP:面向连接、有序、可靠(在协议设计目标上)
SOCK_DGRAM → 数据报,对应 UDP:无连接、按消息边界、尽力送达
Python 里创建方式直接暴露了这个选择:
import socket
# TCP
tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# UDP
udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
AF_INET 表示 IPv4;IPv6 用 AF_INET6。路径、权限、阻塞/非阻塞、超时,都是挂在这个 fd/句柄上的选项,和「协议长什么样」是两层问题。
5. TCP:面向连接的可靠字节流
5.1 设计目标
TCP(Transmission Control Protocol)面向 字节流:
- 发送方
send多次,接收方可能一次recv读完,也可能多次才拼齐——没有应用层消息边界 - 通过序号、确认、重传、流量控制、拥塞控制,尽量在不可靠的 IP 之上提供可靠、有序的交付
适合:网页、文件传输、SSH、数据库连接——错一个字节都不行 的场景。
5.2 三次握手(建立连接)
客户端 服务端
| |
| -------- SYN seq=x --------> | 1. 我要连,起始序号 x
| |
| <---- SYN+ACK seq=y ack=x+1- | 2. 好,我的序号 y,确认你的 x
| |
| -------- ACK ack=y+1 ------> | 3. 确认你的 y,连接建立
| |
要点:
- 双方各自通告初始序号,后续数据靠序号对齐
- 第三次握手可以捎带数据(多数栈支持),但教学上仍画成三步
- 失败常见原因:SYN 被墙、对端没 listen、backlog 满、路由不通

5.3 四次挥手(断开)与 TIME_WAIT
断开比建立啰嗦,因为 TCP 全双工,每个方向都要单独半关闭:
A B
| ---- FIN ----> | A 说:我这侧没数据了
| <---- ACK ---- | B 确认
| <---- FIN ---- | B 也发完了
| ---- ACK ----> | A 确认,进入 TIME_WAIT
TIME_WAIT 存在是为了:
- 保证最后的 ACK 丢失时还能重传语义正确收尾
- 让旧连接延迟报文在超时后从网络中消失,避免被新连接误收
高并发短连接会堆很多 TIME_WAIT,这是协议行为,不是单纯「没关干净」。优化方向通常是连接复用(HTTP keep-alive、连接池),而不是一上来关 TIME_WAIT。
5.4 TCP 还顺带解决什么
| 机制 | 作用 |
|---|---|
| 序号 + ACK | 检测丢包、乱序,触发重传 |
| 滑动窗口 | 流量控制,避免打爆对端接收缓冲 |
| 拥塞控制 | 体谅网络,慢启动、拥塞避免等 |
| 校验和 | 端到端基本完整性检查 |
这些细节够写几篇专项文。入门阶段只要建立直觉:TCP 贵在状态机和缓冲,换来的是应用少操心丢包重传。
6. UDP:无连接的数据报
6.1 它故意做得很少
UDP(User Datagram Protocol)几乎只做:
- 源/目的端口
- 长度
- 可选校验和
- 把数据交给 IP 发出去
没有 握手、没有确认、没有重传、没有拥塞控制(应用或上层协议如 QUIC 可自己做)。
因此:
- 延迟通常更低、实现更简单
- 可能丢包、重复、乱序
- 保留消息边界:一次
recvfrom对应一次sendto的数据报(在未被截断的前提下)
6.2 典型场景
| 场景 | 为什么常用 UDP |
|---|---|
| DNS 查询 | 一问一答,丢了客户端重试即可 |
| 实时音视频、游戏状态 | 晚到的包不如直接丢,保实时 |
| 内网广播/组播发现 | 无连接模型更自然 |
| QUIC / HTTP/3 | 在 UDP 上自建可靠与多路复用 |
不要把 UDP 理解成「不可靠所以垃圾」——它是把可靠性策略交给 更懂业务的人。直播里丢一帧可以糊过去;银行转账丢一个包就必须有人重传,那人往往是 TCP。
7. TCP vs UDP:对照表 + 选型

| 维度 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接(握手/挥手) | 无连接 |
| 可靠性 | 确认、重传、有序 | 尽力而为 |
| 交付模型 | 字节流(无消息边界) | 数据报(有边界) |
| 速度/开销 | 头部与状态开销更大 | 头部小,快路径多 |
| 拥塞控制 | 有 | 无(默认) |
| 典型应用 | HTTP/HTTPS、SSH、邮件、DB | DNS、RTC、游戏、部分隧道 |
| Socket 类型 | SOCK_STREAM |
SOCK_DGRAM |
选型口诀:
- 正确性优先、逻辑是长连接会话 → TCP
- 实时性优先、能容忍丢失或自己做重传 → UDP
- 已经在 UDP 上需要可靠多路复用时,看 QUIC 这类方案,而不是硬在业务里重造半套 TCP
8. 最小可运行示例(Python)
下面两段代码只为把概念钉死:地址是 (host, port),TCP 要 listen/accept 或 connect,UDP 直接 bind + sendto/recvfrom。
8.1 TCP 回显
# tcp_echo_server.py
import socket
HOST, PORT = "127.0.0.1", 9000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(8)
print(f"TCP listening on {HOST}:{PORT}")
conn, addr = s.accept()
with conn:
print("client:", addr)
data = conn.recv(1024)
conn.sendall(b"echo: " + data)
# tcp_echo_client.py
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(("127.0.0.1", 9000))
s.sendall(b"hello tcp")
print(s.recv(1024))
8.2 UDP 回显
# udp_echo_server.py
import socket
HOST, PORT = "127.0.0.1", 9001
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.bind((HOST, PORT))
print(f"UDP bound on {HOST}:{PORT}")
data, addr = s.recvfrom(1024)
s.sendto(b"echo: " + data, addr)
# udp_echo_client.py
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.sendto(b"hello udp", ("127.0.0.1", 9001))
data, _ = s.recvfrom(1024)
print(data)
对照着跑一遍,你会实际看到:
- TCP 必须先有人
listen,客户端connect失败会立刻报错 - UDP 客户端即使对端没开,
sendto也常常「看起来成功」(失败可能在后续 ICMP 或根本静默)
9. 把概念串成一条故事线
访问 https://example.com 时,简化链路是这样的:
- DNS(多为 UDP 53)把域名解析成 IP
- 浏览器(或系统)创建 TCP socket,对端一般是
IP:443 - 三次握手 建立 TCP
- 其上再跑 TLS,然后是 HTTP 请求/响应
- 内核用 本地 IP:临时端口 ↔ 服务器 IP:443 这条五元组区分连接
- 关闭标签页后,连接复用或四次挥手结束
你在应用代码里只摸到 socket 和读写;底下 IP 负责找主机,端口负责找进程,TCP 负责尽量可靠地搬字节。
10. 排障时怎么用这些知识
| 现象 | 优先怀疑 |
|---|---|
connect 超时 |
路由/防火墙/安全组丢 SYN,或对端没监听 |
Connection refused |
到达主机了,但该端口无进程 listen |
| 能 ping 通但业务不通 | ICMP 通不代表 TCP/UDP 端口通 |
| 偶发错乱的「半包粘包」 | TCP 字节流正常现象,要在应用层做定界(长度头/分隔符) |
| UDP「丢包」 | 可能是真丢、也可能是缓冲区满、或对端逻辑丢弃 |
命令工具(Linux):ping、traceroute/mtr、ss -lntp、curl -v、dig/nslookup、抓包用 tcpdump/Wireshark。Windows 上对应 Test-NetConnection、netstat、Wireshark 等。
11. 小结
- IP:主机(接口)级寻址与跨网转发,不保证可靠
- 端口:同一主机上区分应用进程;TCP/UDP 端口空间分离
- Socket:OS 给应用的通信句柄;TCP 用流套接字,UDP 用数据报套接字
- TCP:连接 + 可靠字节流,握手挥手、重传与拥塞控制换正确性
- UDP:无连接数据报,轻、快,可靠性交给上层
把这五块钉牢,再看 HTTP、gRPC、WebRTC、服务网格,都只是在这层楼上继续加规则。
参考与延伸阅读
- RFC 791 — Internet Protocol
- RFC 9293 — Transmission Control Protocol (TCP)
- RFC 768 — User Datagram Protocol
- IANA 端口号登记
- AWS:OSI 模型说明(含传输层 TCP/UDP)
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)