计算机网络通信之TCP/UDP

下面我用嵌入式网络开发视角,把这几个东西讲清楚:

text 复制代码
TCP 客户端 / TCP 服务端
UDP 客户端 / UDP 服务端
UDP 广播 Broadcast
UDP 组播 Multicast

它们本质都在这套网络协议栈里:

text 复制代码
┌──────────────────────────────┐
│ 应用层:HTTP / MQTT / 自定义协议 │
├──────────────────────────────┤
│ 传输层:TCP / UDP              │
├──────────────────────────────┤
│ 网络层:IP                     │
├──────────────────────────────┤
│ 链路层:Ethernet / Wi-Fi        │
├──────────────────────────────┤
│ 物理层:网线 / 无线电           │
└──────────────────────────────┘

1. 先理解 IP、端口、Socket

网络通信最核心的是:

text 复制代码
IP 地址 + 端口号

比如:

text 复制代码
192.168.1.100:8080

其中:

text 复制代码
192.168.1.100  是设备地址
8080           是应用程序端口

可以类比成:

text 复制代码
IP 地址 = 一栋楼的地址
端口号 = 这栋楼里的某个房间号
Socket = 你打开的一条通信通道

例如:

text 复制代码
电脑 IP:192.168.1.10
ESP32 IP:192.168.1.20
服务器端口:8080

ESP32 要连接电脑的 TCP Server,就是连接:

text 复制代码
192.168.1.10:8080

2. TCP 和 UDP 最大区别

对比项 TCP UDP
是否连接 有连接 无连接
可靠性 可靠,丢了会重传 不可靠,丢了就丢了
顺序 保证顺序 不保证顺序
数据形式 字节流 数据报
速度 较稳,开销较大 快,开销小
是否有粘包 没有 TCP 那种粘包
典型应用 HTTP、MQTT、OTA、文件传输 视频、语音、广播发现、实时控制
API connect/send/recv sendto/recvfrom

一句话:

text 复制代码
TCP 像打电话:先接通,再连续说话,可靠。
UDP 像发快递/扔纸条:直接发,快,但不保证一定到。

3. TCP 服务端通信流程

TCP 是面向连接的。

TCP Server 的典型流程是:

c 复制代码
socket()
bind()
listen()
accept()
recv()
send()
close()

画成流程图:

text 复制代码
TCP Server

1. socket()
   创建一个 TCP 套接字

2. bind()
   绑定本机 IP 和端口

3. listen()
   开始监听端口,等待客户端连接

4. accept()
   阻塞等待客户端连接进来

5. recv()
   接收客户端数据

6. send()
   回复客户端数据

7. close()
   关闭连接

3.1 socket() 是干啥的?

c 复制代码
int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

意思是创建一个 TCP socket。

参数含义:

text 复制代码
AF_INET       使用 IPv4
SOCK_STREAM   使用 TCP 流式套接字
IPPROTO_IP    协议自动选择,一般就是 TCP

可以理解为:

text 复制代码
我要开一个网络通信接口,准备用 TCP 通信。

3.2 bind() 是干啥的?

c 复制代码
bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr));

bind() 是把 socket 和本机端口绑定起来。

例如:

text 复制代码
本机 IP:0.0.0.0
端口:8080

其中 0.0.0.0 表示:

text 复制代码
监听本机所有网卡上的 8080 端口

比如你的电脑有:

text 复制代码
Wi-Fi IP:192.168.1.10
有线网卡 IP:192.168.2.10

绑定 0.0.0.0:8080,就是两个网卡都能接收连接。


3.3 listen() 是干啥的?

c 复制代码
listen(listen_sock, 5);

listen() 表示:

text 复制代码
我这个端口现在开始接客了。

第二个参数 5 是 backlog,表示连接等待队列长度。

简单理解:

text 复制代码
如果多个客户端同时连接,系统可以先排队几个。

3.4 accept() 是干啥的?

c 复制代码
int client_sock = accept(listen_sock,
                         (struct sockaddr *)&client_addr,
                         &addr_len);

accept() 会阻塞等待客户端连接。

注意一个关键点:

text 复制代码
listen_sock 只负责监听
client_sock 才负责和客户端真正通信

也就是说:

text 复制代码
listen_sock = 门口保安,只负责等人进来
client_sock = 具体接待窗口,负责和某个客户端聊天

如果有 3 个客户端连接服务器,服务器通常会得到 3 个不同的 client_sock

text 复制代码
listen_sock
 ├── client_sock_1  和客户端1通信
 ├── client_sock_2  和客户端2通信
 └── client_sock_3  和客户端3通信

3.5 TCP Server 完整通信图

text 复制代码
TCP Server                          TCP Client

socket()
bind()
listen()

accept()   <────── connect() ─────── socket()

recv()     <────── send()
send()     ──────> recv()

close()    <────── close()

4. TCP 客户端通信流程

TCP Client 的流程比 Server 简单:

c 复制代码
socket()
connect()
send()
recv()
close()

流程图:

text 复制代码
TCP Client

1. socket()
   创建 TCP socket

2. connect()
   连接服务器 IP + 端口

3. send()
   发送数据

4. recv()
   接收服务器回复

5. close()
   关闭连接

4.1 connect() 底层发生了什么?

TCP 的 connect() 不是简单发个数据,它会触发 TCP 三次握手。

text 复制代码
Client                            Server

SYN        ─────────────────────>
           <────────────────────  SYN + ACK
ACK        ─────────────────────>

连接建立成功

解释一下:

text 复制代码
第一次:Client 说:我要连接你。
第二次:Server 说:我收到了,我也准备好了。
第三次:Client 说:好的,我们开始通信。

这就是 TCP 可靠连接的开始。


4.2 TCP 建立连接后怎么发数据?

连接建立后,两边都可以:

c 复制代码
send()
recv()

这时候 TCP 是全双工通信

也就是:

text 复制代码
客户端可以发给服务端
服务端也可以发给客户端
双方可以同时收发

例如:

text 复制代码
Client                         Server

send("hello") ───────────────>
             <─────────────── recv("hello")

recv("ok")   <─────────────── send("ok")

5. TCP 断开连接流程

TCP 断开连接通常是四次挥手。

text 复制代码
Client                            Server

FIN        ─────────────────────>
           <────────────────────  ACK

           <────────────────────  FIN
ACK        ─────────────────────>

简单理解:

text 复制代码
Client:我不发了。
Server:好的,我知道了。
Server:我也不发了。
Client:好的,连接关闭。

为什么不是一次就断?

因为 TCP 是全双工的:

text 复制代码
A 不想发了,不代表 B 也不想发了。

6. TCP 的重要特点:可靠字节流

TCP 不是一包一包给你看的,而是连续字节流

比如客户端这样发:

c 复制代码
send(sock, "hello", 5, 0);
send(sock, "world", 5, 0);

服务端可能这样收到:

text 复制代码
一次 recv 收到:
helloworld

也可能这样收到:

text 复制代码
第一次 recv:hel
第二次 recv:loworld

也可能:

text 复制代码
第一次 recv:hello
第二次 recv:world

这就是你之前问过的:

text 复制代码
TCP 粘包 / 拆包

6.1 TCP 为什么会粘包?

因为 TCP 不关心你应用层的"一条消息"边界。

TCP 只保证:

text 复制代码
数据可靠、有序地到达

但它不保证:

text 复制代码
你 send 几次,对方 recv 就收到几次

所以 TCP 应用层必须自己设计协议格式。

常见方式:

text 复制代码
方案1:固定长度
方案2:特殊结束符,例如 \r\n
方案3:帧头 + 长度 + 数据
方案4:帧头 + 长度 + 数据 + CRC

最常见的是:

text 复制代码
┌──────┬──────┬──────────┬──────┐
│ 帧头 │ 长度 │ 数据内容 │ CRC  │
└──────┴──────┴──────────┴──────┘

例如:

text 复制代码
AA 55 00 05 68 65 6C 6C 6F CRC

7. UDP 服务端通信流程

UDP 是无连接的。

UDP Server 流程:

c 复制代码
socket()
bind()
recvfrom()
sendto()
close()

画成图:

text 复制代码
UDP Server

1. socket()
   创建 UDP socket

2. bind()
   绑定本机端口

3. recvfrom()
   等待接收任意客户端发来的 UDP 数据

4. sendto()
   回复指定客户端

5. close()
   关闭 socket

7.1 UDP Server 为什么也要 bind?

因为服务端需要告诉系统:

text 复制代码
我要接收发到本机 8080 端口的数据。

例如:

text 复制代码
ESP32 UDP Server 绑定 8080 端口

那么别人发到:

text 复制代码
ESP32_IP:8080

的数据,ESP32 才能收到。


7.2 recvfrom() 和 TCP recv() 区别

UDP 用:

c 复制代码
recvfrom(sock, rx_buffer, sizeof(rx_buffer), 0,
         (struct sockaddr *)&source_addr, &socklen);

因为 UDP 没有连接,所以接收数据时要同时知道:

text 复制代码
是谁发来的?
IP 是多少?
端口是多少?

所以 recvfrom() 不光拿数据,也拿对方地址。


7.3 UDP Server 通信图

text 复制代码
UDP Server                         UDP Client

socket()
bind()

recvfrom() <────── sendto()
sendto()   ──────> recvfrom()

close()             close()

注意:

text 复制代码
没有 listen()
没有 accept()
没有 connect() 必须步骤

8. UDP 客户端通信流程

UDP Client 流程:

c 复制代码
socket()
sendto()
recvfrom()
close()

也可以不调用 bind()

为什么?

因为客户端发送时,系统会自动分配一个临时端口。

例如:

text 复制代码
ESP32 UDP Client
本地临时端口:52341
目标服务器:192.168.1.10:8080

发送出去的数据大概是:

text 复制代码
源 IP: ESP32_IP
源端口: 52341
目标 IP: 192.168.1.10
目标端口: 8080

服务端回复时,就回到:

text 复制代码
ESP32_IP:52341

9. UDP 的重要特点:数据报

UDP 是一包一包的。

比如客户端:

c 复制代码
sendto(sock, "hello", 5, 0, ...);
sendto(sock, "world", 5, 0, ...);

服务端通常会:

text 复制代码
第一次 recvfrom:hello
第二次 recvfrom:world

UDP 保留消息边界,所以没有 TCP 那种粘包问题。

但是 UDP 有其他问题:

text 复制代码
可能丢包
可能乱序
可能重复
包太大可能被 IP 分片
分片丢一个,整包就没了

所以 UDP 常用于:

text 复制代码
实时性比可靠性更重要的场景

例如:

text 复制代码
语音
视频
局域网设备发现
传感器快速上报
广播
组播
游戏同步

10. TCP Server / Client 和 UDP Server / Client 的核心区别

对比 TCP Server TCP Client UDP Server UDP Client
是否连接 等待连接 主动连接 不连接 不连接
关键函数 bind/listen/accept connect bind/recvfrom sendto
通信对象 accept 后固定对端 固定服务端 谁发来都能收 发给指定 IP/端口
可靠性 可靠 可靠 不可靠 不可靠
数据边界 字节流,无边界 字节流,无边界 数据报,有边界 数据报,有边界
是否适合大数据 适合 适合 不太适合 不太适合

11. UDP 广播 Broadcast

广播是 UDP 里很常用的功能。

它的作用是:

text 复制代码
一台设备发一包,局域网内很多设备都能收到。

广播常用于:

text 复制代码
局域网设备发现
自动寻找服务器
设备配网发现
遥控器寻找被控设备

11.1 广播地址是什么?

常见广播地址有两种:

1)受限广播地址

text 复制代码
255.255.255.255

表示:

text 复制代码
发给当前局域网内所有设备

通常不会被路由器转发。


2)定向广播地址

假设局域网是:

text 复制代码
IP:192.168.1.x
子网掩码:255.255.255.0

那么广播地址是:

text 复制代码
192.168.1.255

意思是:

text 复制代码
发给 192.168.1.0/24 这个网段里的所有设备

11.2 广播通信流程

假设:

text 复制代码
ESP32 想找局域网里的服务器

流程如下:

text 复制代码
ESP32 UDP Broadcast Client             局域网内所有设备

socket()
setsockopt(SO_BROADCAST)
sendto(192.168.1.255:9999, "who is server?")
                                  ├── 设备A收到
                                  ├── 设备B收到
                                  ├── 服务器收到
                                  └── 其他设备收到

服务器回复:
recvfrom()
sendto(ESP32_IP:临时端口, "I am server")

图示:

text 复制代码
ESP32
  │
  │ UDP 广播:who is server?
  ▼
192.168.1.255:9999
  │
  ├── 设备 A 收到
  ├── 设备 B 收到
  ├── 服务器收到并回复
  └── 手机/电脑可能也收到

11.3 广播发送端流程

UDP 广播发送端一般是:

c 复制代码
socket()
setsockopt(SO_BROADCAST)
sendto(broadcast_ip, port, data)
close()

关键是:

c 复制代码
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));

如果不开 SO_BROADCAST,很多系统不允许你发广播包。


11.4 广播接收端流程

广播接收端就是普通 UDP Server:

c 复制代码
socket()
bind(本地端口)
recvfrom()

只要绑定了对应端口,就能收到发往广播地址的 UDP 数据。

例如所有设备都监听:

text 复制代码
9999 端口

发送端发:

text 复制代码
192.168.1.255:9999

这些设备就都可能收到。


11.5 广播的特点

特点 说明
一发多收 局域网内多个设备可收到
不需要知道对方 IP 适合设备发现
只能局域网 一般不会跨路由器
占用网络资源 所有设备都要处理广播包
安全性一般 局域网内别人也可能收到
基于 UDP 不可靠,可能丢包

12. UDP 组播 Multicast

组播比广播更高级一点。

广播是:

text 复制代码
发给局域网所有设备

组播是:

text 复制代码
只发给加入某个组的设备

可以类比:

text 复制代码
广播 = 小区大喇叭,所有人都听到
组播 = 加入某个微信群的人才收到

12.1 组播地址范围

IPv4 组播地址范围是:

text 复制代码
224.0.0.0 ~ 239.255.255.255

常见例子:

text 复制代码
224.0.0.1      本地网络所有主机
224.0.0.251    mDNS
239.x.x.x      常用于私有组播

自己做项目时,常用私有组播地址:

text 复制代码
239.255.0.1
239.255.255.250

例如 SSDP/UPnP 常用:

text 复制代码
239.255.255.250:1900

12.2 组播通信流程

假设有几个设备:

text 复制代码
设备 A:加入组播组 239.1.1.1:5000
设备 B:加入组播组 239.1.1.1:5000
设备 C:没有加入
发送端:向 239.1.1.1:5000 发 UDP 数据

结果:

text 复制代码
设备 A 收到
设备 B 收到
设备 C 收不到

图示:

text 复制代码
发送端
  │
  │ UDP 组播数据
  ▼
239.1.1.1:5000
  │
  ├── 设备 A:已加入组播组 → 收到
  ├── 设备 B:已加入组播组 → 收到
  └── 设备 C:未加入组播组 → 不收

12.3 组播接收端流程

组播接收端比普通 UDP Server 多一步:

text 复制代码
加入组播组

流程:

c 复制代码
socket()
bind(本地端口)
setsockopt(IP_ADD_MEMBERSHIP)
recvfrom()

关键是 IP_ADD_MEMBERSHIP

c 复制代码
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("239.1.1.1");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);

setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

意思是:

text 复制代码
我要加入 239.1.1.1 这个组播组。

12.4 组播发送端流程

组播发送端和 UDP Client 差不多:

c 复制代码
socket()
sendto(239.1.1.1:5000, data)
close()

可选配置:

text 复制代码
设置 TTL
设置发送网卡
是否允许自己收到自己发出的组播

12.5 TTL 是什么?

TTL 是 Time To Live。

在组播里,TTL 决定组播包能传多远。

常见设置:

text 复制代码
TTL = 1   只在本地局域网
TTL > 1   可能跨路由器,前提是路由器支持组播转发

嵌入式局域网项目一般设置:

text 复制代码
TTL = 1

12.6 组播的特点

特点 说明
一发多收 多个组成员能收到
比广播更精准 只有加入组的设备接收
基于 UDP 不保证可靠
需要加入组 接收端需要 IP_ADD_MEMBERSHIP
常用于发现/媒体 mDNS、SSDP、视频流、局域网发现
跨网段复杂 需要路由器支持组播

13. 广播和组播区别

对比 广播 Broadcast 组播 Multicast
发送目标 局域网所有设备 某个组播组成员
IP 地址 255.255.255.255192.168.1.255 224.0.0.0 ~ 239.255.255.255
接收端是否要加入组 不需要 需要
网络负担 较大 较小
精准程度
是否跨路由 一般不跨 可以,但需要支持
常见用途 简单设备发现 mDNS、SSDP、视频、多设备同步

一句话:

text 复制代码
广播:所有人都收到。
组播:加入群的人才收到。

14. TCP、UDP、广播、组播放一起对比

类型 是否连接 一对一/一对多 是否可靠 典型用途
TCP Client/Server 有连接 一对一 可靠 HTTP、MQTT、OTA、文件
UDP Client/Server 无连接 一对一 不可靠 实时数据、小包通信
UDP 广播 无连接 一对多,所有设备 不可靠 局域网设备发现
UDP 组播 无连接 一对多,组成员 不可靠 mDNS、SSDP、视频流

15. 用实际例子理解

例子 1:ESP32 连接云服务器

适合 TCP。

text 复制代码
ESP32                         云服务器

连接 Wi-Fi
socket()
connect(server_ip:1883) ────>
send(MQTT数据) ─────────────>
recv(MQTT响应) <────────────

这种场景需要可靠通信:

text 复制代码
MQTT
HTTP
HTTPS
OTA
天气请求
云端控制

所以一般用 TCP。


例子 2:ESP32 局域网找电脑服务器

适合 UDP 广播。

text 复制代码
ESP32 不知道电脑 IP,只知道大家在同一个 Wi-Fi 下。

ESP32 发广播:
who is my server?

电脑 UDP Server 收到:
我是服务器,我的 IP 是 192.168.1.10

ESP32 收到回复后,再连接:
192.168.1.10:8080

流程:

text 复制代码
ESP32                   局域网                  电脑 Server

UDP Broadcast ────────> 所有设备
"who is server?"                               收到

ESP32 <─────────────────────────────────────── UDP 单播回复
                      "server ip is 192.168.1.10"

ESP32 ───────────────────────────────────────> TCP connect

很多智能设备发现都是这个思路。


例子 3:多个设备同时接收控制命令

可以用 UDP 组播。

比如你有 10 个灯,每个灯都加入:

text 复制代码
239.1.1.1:5000

控制器发:

text 复制代码
turn_on

所有加入组的灯都收到。

text 复制代码
控制器
  │
  │ sendto("turn_on", 239.1.1.1:5000)
  ▼
组播组
  ├── 灯1 收到
  ├── 灯2 收到
  ├── 灯3 收到
  └── 灯10 收到

16. TCP 多客户端服务端模型

TCP Server 如果要支持多个客户端,不能只 accept() 一次。

常见模型有几种。


16.1 一个客户端一个任务/线程

text 复制代码
主任务:
socket()
bind()
listen()
while(1) {
    client_sock = accept()
    创建新任务处理 client_sock
}

结构:

text 复制代码
TCP Server
 ├── listen socket
 ├── client task 1
 ├── client task 2
 ├── client task 3
 └── client task N

优点:

text 复制代码
逻辑简单
每个客户端独立处理

缺点:

text 复制代码
客户端多了占 RAM
嵌入式设备不适合无限创建任务

16.2 select/poll 模型

一个任务同时管理多个 socket。

text 复制代码
while(1) {
    select()
    如果 listen_sock 可读:accept 新连接
    如果 client_sock_1 可读:recv
    如果 client_sock_2 可读:recv
}

优点:

text 复制代码
省任务
适合多个连接

缺点:

text 复制代码
代码复杂
状态机多

ESP32/LwIP 也可以用 select()


17. UDP 为什么没有 accept?

因为 UDP 没有连接。

TCP 的连接关系是:

text 复制代码
客户端 A 和服务端建立连接
客户端 B 和服务端建立连接
服务端 accept 出不同 client_sock

UDP 是:

text 复制代码
谁给我这个端口发数据,我就收。

所以 UDP Server 只需要:

text 复制代码
一个 socket 绑定一个端口

然后通过 recvfrom() 区分是谁发来的。

text 复制代码
UDP Server 9999端口

recvfrom() 收到 A 的数据,source_addr = A
recvfrom() 收到 B 的数据,source_addr = B
recvfrom() 收到 C 的数据,source_addr = C

18. UDP 的 connect 是什么?

UDP 也可以调用 connect(),但它和 TCP 不一样。

UDP 的 connect() 不会三次握手。

它只是告诉系统:

text 复制代码
这个 UDP socket 默认只和某个 IP:Port 通信。

调用 UDP connect() 后,可以用:

c 复制代码
send()
recv()

不用每次 sendto() 填目标地址。

但是注意:

text 复制代码
UDP connect 不代表真的建立连接
对方不在线也可能 send 成功
底层仍然是不可靠 UDP

19. 单播、广播、组播

网络发送目标有三类:

text 复制代码
单播 Unicast
广播 Broadcast
组播 Multicast

19.1 单播 Unicast

一对一。

text 复制代码
192.168.1.20  ─────>  192.168.1.10

TCP 和普通 UDP 都是单播。


19.2 广播 Broadcast

一对所有。

text 复制代码
192.168.1.20  ─────>  192.168.1.255
                       ├── 192.168.1.10
                       ├── 192.168.1.11
                       ├── 192.168.1.12
                       └── ...

19.3 组播 Multicast

一对一组。

text 复制代码
发送到 239.1.1.1
    ├── 加入组的设备 A 收到
    ├── 加入组的设备 B 收到
    └── 没加入组的设备 C 不收

20. 嵌入式开发常见选择

TCP 适合

text 复制代码
HTTP 请求天气
HTTPS OTA 升级
MQTT 长连接
文件传输
云端通信
需要可靠传输的数据

例如:

text 复制代码
ESP32 获取天气:
ESP32 TCP Client → HTTP Server

UDP 单播适合

text 复制代码
低延迟状态上报
局域网遥控
简单传感器数据
允许偶尔丢包
自己实现 ACK/重传

UDP 广播适合

text 复制代码
局域网发现设备
不知道目标 IP
设备第一次配网后找服务器
遥控器找小车
APP 找局域网设备

UDP 组播适合

text 复制代码
多个设备同时接收同一消息
局域网服务发现
mDNS
SSDP
多设备同步
视频/音频局域网分发

21. 一个完整项目里的组合用法

实际产品中经常不是只用一种,而是组合使用。

例如一个 ESP32 设备:

text 复制代码
1. 上电连接 Wi-Fi
2. UDP 广播:寻找局域网服务器
3. 收到服务器 UDP 回复,得到服务器 IP
4. TCP 连接服务器
5. 后续用 TCP/MQTT 长连接通信

流程图:

text 复制代码
ESP32                         局域网服务器

Wi-Fi 连接成功

UDP 广播:
"who is server?" ────────────>

              <────────────── UDP 回复:
                              "I am 192.168.1.10"

TCP connect 192.168.1.10:8080 ─────>

TCP send/recv 正式通信

这个设计很常见。

因为:

text 复制代码
UDP 广播负责发现
TCP 负责可靠通信

22. 对 ESP32 / LwIP 来说的注意点

22.1 TCP 注意点

text 复制代码
1. recv() 可能一次收不完整
2. send() 不一定一次把所有数据写完
3. 需要处理断开重连
4. 需要心跳保活
5. 不要在一个任务里永久阻塞影响其他功能
6. 多客户端要考虑任务数量和 RAM
7. TCP 是字节流,必须设计应用层协议

22.2 UDP 注意点

text 复制代码
1. UDP 可能丢包
2. 包不要太大,避免 IP 分片
3. 广播需要 SO_BROADCAST
4. 组播接收需要加入组
5. Wi-Fi 路由器可能限制广播/组播
6. UDP 没有连接状态,需要自己判断对方是否在线

22.3 广播注意点

text 复制代码
1. 广播通常只在局域网有效
2. 有些路由器/AP 会过滤广播
3. 手机热点下广播行为可能不稳定
4. 广播太频繁会占网络资源
5. 广播接收端必须 bind 对应端口

22.4 组播注意点

text 复制代码
1. 接收端必须 IP_ADD_MEMBERSHIP
2. 路由器/AP 可能对组播做限速或过滤
3. Wi-Fi 下组播速率可能比较低
4. 跨网段组播需要路由器支持
5. 局域网项目 TTL 一般设为 1

23. 最终总结

你可以这样记:

text 复制代码
TCP Server:
    绑定端口,监听,accept 客户端连接,然后 recv/send。

TCP Client:
    知道服务器 IP 和端口,connect 过去,然后 send/recv。

UDP Server:
    绑定端口,不需要连接,谁发来就 recvfrom 谁。

UDP Client:
    不需要连接,直接 sendto 目标 IP 和端口。

UDP 广播:
    发给整个局域网,适合"我不知道目标 IP,先找一下"。

UDP 组播:
    发给加入某个组的设备,适合"一发多收,但不想所有人都收"。

再压缩成一句话:

text 复制代码
TCP:可靠连接通信。
UDP:快速无连接通信。
广播:UDP 发给局域网所有设备。
组播:UDP 发给指定组里的设备。

从嵌入式产品开发角度:

text 复制代码
要可靠、大数据、云通信:用 TCP。
要简单、低延迟、小数据:用 UDP。
要发现局域网设备:用 UDP 广播。
要多个设备同时接收:用 UDP 组播。



好,这次直接用具体 IP + 端口 + 数据流向来讲。

假设现在有 3 台设备在同一个 Wi-Fi 局域网:

text 复制代码
路由器网关:192.168.1.1

电脑 Server:192.168.1.10
ESP32 A:    192.168.1.20
ESP32 B:    192.168.1.21
手机:       192.168.1.30

子网掩码:255.255.255.0
广播地址:192.168.1.255

1. TCP 客户端 / 服务端:具体 IP 端口流程

1.1 场景

电脑作为 TCP Server:

text 复制代码
电脑 IP:192.168.1.10
监听端口:8080

ESP32 作为 TCP Client:

text 复制代码
ESP32 IP:192.168.1.20
本地临时端口:系统自动分配,例如 52341

通信目标是:

text 复制代码
ESP32 连接电脑的 192.168.1.10:8080

2. TCP Server 端具体做了什么?

电脑程序做:

c 复制代码
listen_sock = socket(AF_INET, SOCK_STREAM, 0);

bind(listen_sock, 0.0.0.0:8080);

listen(listen_sock, 5);

client_sock = accept(listen_sock);

recv(client_sock, ...);

send(client_sock, ...);

这里最关键的是:

text 复制代码
0.0.0.0:8080

它表示:

text 复制代码
电脑所有网卡的 8080 端口都监听

如果电脑 Wi-Fi IP 是:

text 复制代码
192.168.1.10

那么 ESP32 实际连接的是:

text 复制代码
192.168.1.10:8080

3. TCP Client 端具体做了什么?

ESP32 做:

c 复制代码
sock = socket(AF_INET, SOCK_STREAM, 0);

connect(sock, 192.168.1.10:8080);

send(sock, "hello", 5, 0);

recv(sock, ...);

ESP32 一般不用自己指定本地端口,系统会自动分配一个临时端口,比如:

text 复制代码
52341

于是 TCP 连接建立后,真正的通信四元组是:

text 复制代码
源 IP:   192.168.1.20
源端口: 52341

目标 IP: 192.168.1.10
目标端口:8080

可以写成:

text 复制代码
192.168.1.20:52341  --->  192.168.1.10:8080

4. TCP 三次握手的具体 IP 端口

TCP connect() 时,底层会三次握手。

text 复制代码
ESP32 Client                              电脑 Server

192.168.1.20:52341  ── SYN ───────────>  192.168.1.10:8080

192.168.1.20:52341  <─ SYN+ACK ───────  192.168.1.10:8080

192.168.1.20:52341  ── ACK ───────────>  192.168.1.10:8080

握手成功后,连接建立。

之后双方就可以收发数据。


5. TCP 数据发送的具体流向

ESP32 发送:

c 复制代码
send(sock, "hello server", 12, 0);

实际网络包大概是:

text 复制代码
源 IP:   192.168.1.20
源端口: 52341

目标 IP: 192.168.1.10
目标端口:8080

数据:hello server

方向:

text 复制代码
192.168.1.20:52341  --->  192.168.1.10:8080

电脑 Server 收到后回复:

c 复制代码
send(client_sock, "hello esp32", 11, 0);

实际网络包是:

text 复制代码
源 IP:   192.168.1.10
源端口: 8080

目标 IP: 192.168.1.20
目标端口:52341

数据:hello esp32

方向:

text 复制代码
192.168.1.10:8080  --->  192.168.1.20:52341

注意这里:服务端回复的目标端口不是 8080,而是 ESP32 的临时端口 52341。


6. TCP 多客户端具体例子

假设有两个 ESP32 同时连接电脑 Server。

text 复制代码
电脑 Server:
192.168.1.10:8080

ESP32 A:

text 复制代码
192.168.1.20:52341  --->  192.168.1.10:8080

ESP32 B:

text 复制代码
192.168.1.21:52342  --->  192.168.1.10:8080

电脑 Server 虽然只监听一个端口:

text 复制代码
8080

但是它能区分两个客户端,因为 TCP 连接靠四元组区分:

text 复制代码
连接 1:
192.168.1.20:52341 <--> 192.168.1.10:8080

连接 2:
192.168.1.21:52342 <--> 192.168.1.10:8080

所以 TCP Server 不是靠"一个端口只能连一个设备",而是靠:

text 复制代码
源 IP + 源端口 + 目标 IP + 目标端口

来区分不同连接。


7. TCP 里 listen_sock 和 client_sock 的具体区别

电脑 Server 代码里通常有两个 socket:

text 复制代码
listen_sock
client_sock

比如:

text 复制代码
listen_sock 绑定:0.0.0.0:8080

它只负责:

text 复制代码
等待客户端连接

当 ESP32 A 连接进来后:

text 复制代码
accept() 返回 client_sock_1

这个 client_sock_1 对应:

text 复制代码
192.168.1.20:52341 <--> 192.168.1.10:8080

当 ESP32 B 连接进来后:

text 复制代码
accept() 返回 client_sock_2

这个 client_sock_2 对应:

text 复制代码
192.168.1.21:52342 <--> 192.168.1.10:8080

可以这样理解:

text 复制代码
listen_sock = 门口接待台,只负责等新连接

client_sock_1 = 和 ESP32 A 通信的专用通道
client_sock_2 = 和 ESP32 B 通信的专用通道

8. TCP 总流程完整图

text 复制代码
电脑 Server:192.168.1.10:8080
ESP32 Client:192.168.1.20:52341

电脑 Server                                     ESP32 Client

socket()
bind(0.0.0.0:8080)
listen()

accept()  <──────────── connect(192.168.1.10:8080)
             三次握手:
             192.168.1.20:52341 -> 192.168.1.10:8080

recv()    <──────────── send("hello")
send("ok") ───────────> recv()

close()   <──────────── close()

9. UDP 客户端 / 服务端:具体 IP 端口流程

UDP 和 TCP 最大区别:

text 复制代码
TCP:先 connect,建立连接,再 send/recv
UDP:不建立连接,直接 sendto/recvfrom

9.1 UDP Server 例子

电脑作为 UDP Server:

text 复制代码
电脑 IP:192.168.1.10
监听端口:9000

电脑代码做:

c 复制代码
sock = socket(AF_INET, SOCK_DGRAM, 0);

bind(sock, 0.0.0.0:9000);

recvfrom(sock, ...);

sendto(sock, ...);

绑定:

text 复制代码
0.0.0.0:9000

表示:

text 复制代码
接收所有发到电脑 9000 端口的 UDP 数据

9.2 UDP Client 例子

ESP32 作为 UDP Client:

text 复制代码
ESP32 IP:192.168.1.20
本地临时端口:系统自动分配,例如 53001

ESP32 发送:

c 复制代码
sendto(sock, "hello udp", 9, 0, 192.168.1.10:9000);

实际数据包:

text 复制代码
源 IP:   192.168.1.20
源端口: 53001

目标 IP: 192.168.1.10
目标端口:9000

数据:hello udp

方向:

text 复制代码
192.168.1.20:53001  --->  192.168.1.10:9000

9.3 UDP Server 如何回复?

电脑 Server 用 recvfrom() 收数据时,不只是收到数据,还会收到对方地址:

text 复制代码
对方 IP:192.168.1.20
对方端口:53001

所以电脑回复时:

c 复制代码
sendto(sock, "udp ok", 6, 0, 192.168.1.20:53001);

实际数据包:

text 复制代码
源 IP:   192.168.1.10
源端口: 9000

目标 IP: 192.168.1.20
目标端口:53001

数据:udp ok

方向:

text 复制代码
192.168.1.10:9000  --->  192.168.1.20:53001

10. UDP 和 TCP 端口行为的关键区别

TCP Server:

text 复制代码
192.168.1.10:8080

客户端连接进来后,形成一条"连接"。

UDP Server:

text 复制代码
192.168.1.10:9000

没有连接,谁给这个端口发数据,Server 就能收到。

比如:

text 复制代码
ESP32 A:192.168.1.20:53001 ---> 192.168.1.10:9000
ESP32 B:192.168.1.21:53002 ---> 192.168.1.10:9000
手机:   192.168.1.30:53003 ---> 192.168.1.10:9000

电脑 UDP Server 用同一个 socket 收:

text 复制代码
recvfrom() 收到 A 的数据,source = 192.168.1.20:53001
recvfrom() 收到 B 的数据,source = 192.168.1.21:53002
recvfrom() 收到手机数据,source = 192.168.1.30:53003

UDP Server 不需要 accept(),因为 UDP 没有连接。


11. UDP 完整通信图

text 复制代码
电脑 UDP Server:192.168.1.10:9000
ESP32 UDP Client:192.168.1.20:53001

电脑 Server                                 ESP32 Client

socket()
bind(0.0.0.0:9000)

recvfrom() <──────────── sendto(192.168.1.10:9000)
            数据方向:
            192.168.1.20:53001 -> 192.168.1.10:9000

sendto(192.168.1.20:53001) ───────────> recvfrom()
            数据方向:
            192.168.1.10:9000 -> 192.168.1.20:53001

12. UDP Client 是否必须 bind?

不一定。

情况 1:不 bind

ESP32 直接:

c 复制代码
socket()
sendto(192.168.1.10:9000)

系统自动分配本地端口,比如:

text 复制代码
53001

这适合普通 UDP Client。


情况 2:主动 bind 本地端口

例如你希望 ESP32 固定用本地端口:

text 复制代码
192.168.1.20:7000

那 ESP32 可以:

c 复制代码
bind(sock, 0.0.0.0:7000);
sendto(sock, ..., 192.168.1.10:9000);

此时发出的 UDP 包是:

text 复制代码
192.168.1.20:7000  --->  192.168.1.10:9000

这样服务端回复时,也会回到:

text 复制代码
192.168.1.20:7000

什么时候需要 Client bind?

text 复制代码
1. 你希望本地端口固定
2. 对方需要固定向你这个端口发数据
3. 做广播/组播接收
4. 做某些固定协议,例如 mDNS、SSDP

13. UDP 广播:具体 IP 端口流程

广播用于:

text 复制代码
我不知道服务器 IP,但是我知道它可能在这个局域网里。

比如 ESP32 不知道电脑 Server 是:

text 复制代码
192.168.1.10

它只知道大家都在:

text 复制代码
192.168.1.x

那就可以发广播。


13.1 广播地址

当前网络:

text 复制代码
ESP32 IP:192.168.1.20
子网掩码:255.255.255.0

广播地址就是:

text 复制代码
192.168.1.255

也可以用:

text 复制代码
255.255.255.255

更常见推荐:

text 复制代码
192.168.1.255

13.2 广播发现服务器例子

约定:

text 复制代码
所有服务器都监听 UDP 9999 端口

电脑 Server:

text 复制代码
192.168.1.10:9999

ESP32 发送广播:

text 复制代码
目标 IP:192.168.1.255
目标端口:9999
数据:who is server?

ESP32 本地端口假设是:

text 复制代码
53001

实际 UDP 包:

text 复制代码
源 IP:   192.168.1.20
源端口: 53001

目标 IP: 192.168.1.255
目标端口:9999

数据:who is server?

方向:

text 复制代码
192.168.1.20:53001  --->  192.168.1.255:9999

局域网内所有监听 9999 端口的设备都有机会收到。

比如:

text 复制代码
192.168.1.10:9999 收到
192.168.1.21:9999 收到
192.168.1.30:9999 收到

但是只有真正的服务器回复:

text 复制代码
源 IP:   192.168.1.10
源端口: 9999

目标 IP: 192.168.1.20
目标端口:53001

数据:I am server, ip=192.168.1.10

方向:

text 复制代码
192.168.1.10:9999  --->  192.168.1.20:53001

14. UDP 广播完整流程图

text 复制代码
ESP32:192.168.1.20:53001
电脑 Server:192.168.1.10:9999
广播地址:192.168.1.255:9999

ESP32                                           局域网设备

sendto(192.168.1.255:9999, "who is server?")
  │
  │ 192.168.1.20:53001 -> 192.168.1.255:9999
  ▼

电脑 192.168.1.10:9999 收到
手机 192.168.1.30:9999 可能收到
ESP32 B 192.168.1.21:9999 可能收到

电脑回复:
192.168.1.10:9999 -> 192.168.1.20:53001
"I am server"

15. 广播发送端为什么要 SO_BROADCAST?

发送广播前通常要设置:

c 复制代码
int broadcast = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));

因为系统默认不允许随便往广播地址发包。

发送端流程:

c 复制代码
sock = socket(AF_INET, SOCK_DGRAM, 0);

setsockopt(sock, SOL_SOCKET, SO_BROADCAST, ...);

sendto(sock, "who is server?", ..., 192.168.1.255:9999);

接收端就是普通 UDP Server:

c 复制代码
sock = socket(AF_INET, SOCK_DGRAM, 0);

bind(sock, 0.0.0.0:9999);

recvfrom(sock, ...);

16. 广播里的端口非常重要

广播不是说"发给所有程序"。

它是:

text 复制代码
发给局域网所有设备的某个端口

例如:

text 复制代码
192.168.1.255:9999

表示:

text 复制代码
局域网所有设备上,监听 9999 端口的程序可以收到

如果某台设备没有程序绑定 9999 端口,它就算收到网络包,也会被系统丢掉。

所以广播必须约定端口:

text 复制代码
发现服务器端口:9999

17. UDP 组播:具体 IP 端口流程

组播和广播不同。

广播是发给:

text 复制代码
192.168.1.255

所有局域网设备都可能收到。

组播是发给:

text 复制代码
239.1.1.1

只有加入这个组播组的设备才收。


17.1 组播例子

约定组播地址:

text 复制代码
组播 IP:239.1.1.1
组播端口:5000

设备 A:

text 复制代码
192.168.1.20
加入组播组 239.1.1.1:5000

设备 B:

text 复制代码
192.168.1.21
加入组播组 239.1.1.1:5000

手机:

text 复制代码
192.168.1.30
没有加入组播组

电脑发送组播:

text 复制代码
源 IP:   192.168.1.10
源端口: 54000

目标 IP: 239.1.1.1
目标端口:5000

数据:turn on led

方向:

text 复制代码
192.168.1.10:54000  --->  239.1.1.1:5000

结果:

text 复制代码
ESP32 A 收到
ESP32 B 收到
手机没加入组,收不到

18. 组播接收端具体流程

ESP32 A 想收到:

text 复制代码
239.1.1.1:5000

需要做:

c 复制代码
sock = socket(AF_INET, SOCK_DGRAM, 0);

bind(sock, 0.0.0.0:5000);

setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, ...);

recvfrom(sock, ...);

注意顺序:

text 复制代码
1. 创建 UDP socket
2. bind 到本地端口 5000
3. 加入组播组 239.1.1.1
4. recvfrom 等待数据

为什么要 bind 到 5000?

因为发送端发的是:

text 复制代码
239.1.1.1:5000

接收端必须监听这个端口。

为什么还要加入组播组?

因为目标 IP 不是你的单播 IP,而是组播 IP:

text 复制代码
239.1.1.1

系统需要知道:

text 复制代码
我要接收发给 239.1.1.1 的数据

19. 组播发送端具体流程

电脑发送端:

c 复制代码
sock = socket(AF_INET, SOCK_DGRAM, 0);

sendto(sock, "turn on led", ..., 239.1.1.1:5000);

它不一定要加入组播组。

发送组播和发送普通 UDP 很像,只是目标 IP 变成组播 IP:

text 复制代码
普通 UDP:
192.168.1.10:54000 -> 192.168.1.20:5000

组播 UDP:
192.168.1.10:54000 -> 239.1.1.1:5000

20. 组播完整流程图

text 复制代码
组播组:239.1.1.1:5000

ESP32 A:192.168.1.20,加入 239.1.1.1:5000
ESP32 B:192.168.1.21,加入 239.1.1.1:5000
手机:   192.168.1.30,没有加入
电脑:   192.168.1.10,发送者

电脑发送:
192.168.1.10:54000 -> 239.1.1.1:5000
数据:"turn on led"

结果:
ESP32 A 收到
ESP32 B 收到
手机不收

图:

text 复制代码
电脑 192.168.1.10
      │
      │ UDP Multicast
      │ 192.168.1.10:54000 -> 239.1.1.1:5000
      ▼

组播组 239.1.1.1:5000
      ├── ESP32 A 192.168.1.20 已加入 → 收到
      ├── ESP32 B 192.168.1.21 已加入 → 收到
      └── 手机 192.168.1.30 未加入 → 不收

21. 单播、广播、组播用具体地址对比

21.1 UDP 单播

text 复制代码
192.168.1.20:53001  --->  192.168.1.10:9000

意思:

text 复制代码
ESP32 发给电脑一个设备

21.2 UDP 广播

text 复制代码
192.168.1.20:53001  --->  192.168.1.255:9999

意思:

text 复制代码
ESP32 发给 192.168.1.x 局域网所有设备的 9999 端口

21.3 UDP 组播

text 复制代码
192.168.1.10:54000  --->  239.1.1.1:5000

意思:

text 复制代码
电脑发给加入 239.1.1.1 组播组的设备

22. 四种模式放一起看

模式 源地址 目标地址 说明
TCP Client 连接 Server 192.168.1.20:52341 192.168.1.10:8080 ESP32 连接电脑 TCP 服务
UDP 单播 192.168.1.20:53001 192.168.1.10:9000 ESP32 给电脑发一包
UDP 广播 192.168.1.20:53001 192.168.1.255:9999 ESP32 发给整个局域网
UDP 组播 192.168.1.10:54000 239.1.1.1:5000 电脑发给组播组成员

23. 实际项目例子:ESP32 找服务器,然后 TCP 通信

这是非常常见的组合。

23.1 已知条件

电脑 Server:

text 复制代码
IP:192.168.1.10
UDP 发现端口:9999
TCP 正式通信端口:8080

ESP32:

text 复制代码
IP:192.168.1.20

23.2 第一步:ESP32 广播找服务器

ESP32 发 UDP 广播:

text 复制代码
源:192.168.1.20:53001
目标:192.168.1.255:9999
数据:DISCOVER_SERVER

电脑收到后回复:

text 复制代码
源:192.168.1.10:9999
目标:192.168.1.20:53001
数据:SERVER_IP=192.168.1.10,TCP_PORT=8080

23.3 第二步:ESP32 建立 TCP 连接

ESP32 拿到电脑 IP 和端口后:

text 复制代码
connect 192.168.1.10:8080

实际 TCP 连接:

text 复制代码
192.168.1.20:52341  <-->  192.168.1.10:8080

23.4 第三步:正式传数据

ESP32 发送传感器数据:

text 复制代码
源:192.168.1.20:52341
目标:192.168.1.10:8080
数据:{"temp":26.5,"hum":60}

电脑回复:

text 复制代码
源:192.168.1.10:8080
目标:192.168.1.20:52341
数据:OK

完整流程:

text 复制代码
ESP32                                  电脑 Server

UDP 广播:
192.168.1.20:53001
    -> 192.168.1.255:9999
"DISCOVER_SERVER"

                                      收到广播

UDP 单播回复:
192.168.1.10:9999
    -> 192.168.1.20:53001
"SERVER_IP=192.168.1.10,TCP_PORT=8080"

TCP 连接:
192.168.1.20:52341
    -> 192.168.1.10:8080

TCP 数据:
192.168.1.20:52341
    -> 192.168.1.10:8080
"{temp:26.5,hum:60}"

TCP 回复:
192.168.1.10:8080
    -> 192.168.1.20:52341
"OK"

24. 实际项目例子:多个 ESP32 接收控制命令

假设你有 3 个 ESP32 灯:

text 复制代码
ESP32 A:192.168.1.20
ESP32 B:192.168.1.21
ESP32 C:192.168.1.22

它们都加入组播:

text 复制代码
239.1.1.1:5000

电脑控制器:

text 复制代码
192.168.1.10

电脑发组播:

text 复制代码
源:192.168.1.10:54000
目标:239.1.1.1:5000
数据:LED_ON

结果:

text 复制代码
ESP32 A 收到 LED_ON
ESP32 B 收到 LED_ON
ESP32 C 收到 LED_ON

这比你一个个单播简单:

text 复制代码
单播方式:
192.168.1.10 -> 192.168.1.20
192.168.1.10 -> 192.168.1.21
192.168.1.10 -> 192.168.1.22

组播方式:
192.168.1.10 -> 239.1.1.1

25. 很容易搞混的几个点

点 1:Server 的端口固定,Client 的端口通常随机

TCP 例子:

text 复制代码
Server 固定:
192.168.1.10:8080

Client 随机:
192.168.1.20:52341

UDP 例子:

text 复制代码
Server 固定:
192.168.1.10:9000

Client 随机:
192.168.1.20:53001

Client 本地端口通常由系统自动分配。


点 2:服务端回复时,目标端口是客户端的源端口

ESP32 发:

text 复制代码
192.168.1.20:53001 -> 192.168.1.10:9000

电脑回复:

text 复制代码
192.168.1.10:9000 -> 192.168.1.20:53001

不是回复到 ESP32 的 9000,除非 ESP32 自己也 bind 到 9000。


点 3:UDP 广播不是发给某个 IP

广播目标是:

text 复制代码
192.168.1.255:9999

不是:

text 复制代码
192.168.1.10:9999

所以它的意义是:

text 复制代码
这个网段里谁监听 9999,谁就可能收到

点 4:UDP 组播不是广播

广播:

text 复制代码
192.168.1.255:9999

所有设备都可能收到。

组播:

text 复制代码
239.1.1.1:5000

只有加入 239.1.1.1 这个组的设备收到。


点 5:TCP 必须有 Server 先 listen

TCP Client 连接:

text 复制代码
192.168.1.10:8080

如果电脑上没有程序监听 8080,ESP32 会连接失败。

常见错误:

text 复制代码
Connection refused
Connection timeout

点 6:UDP sendto 成功不代表对方收到了

UDP 发送:

c 复制代码
sendto(...)

返回成功,只代表:

text 复制代码
你的数据交给本机协议栈了

不代表:

text 复制代码
对方一定收到了

UDP 没有 TCP 那种确认机制。


26. 最后用一句话总结

text 复制代码
TCP:
192.168.1.20:52341 连接 192.168.1.10:8080
建立可靠连接后,双方 send/recv。

UDP 单播:
192.168.1.20:53001 直接 sendto 到 192.168.1.10:9000
不建立连接,对方用 recvfrom 收。

UDP 广播:
192.168.1.20:53001 sendto 到 192.168.1.255:9999
整个 192.168.1.x 局域网里监听 9999 的设备都可能收到。

UDP 组播:
192.168.1.10:54000 sendto 到 239.1.1.1:5000
只有加入 239.1.1.1:5000 组播组的设备收到。

最实用的项目组合是:

text 复制代码
UDP 广播/组播:负责发现设备
TCP:负责后续可靠通信
UDP 单播:负责低延迟小数据
相关推荐
春蕾夏荷_7282977257 小时前
1、c++ acl udp服务器客户端简单实例-客户器端(2)
服务器·c++·udp
stars-he7 小时前
基于 Design Compiler 的 UDP Payload 追加控制模块综合与门级后仿真
笔记·fpga开发·udp
时空自由民.7 小时前
蓝牙GAP/GATT协议和计算机网络TCP/UDP通信对比
tcp/ip·计算机网络·udp
WIZnet8 小时前
W55MH32 RTThread+TCP通信测试
网络·网络协议·tcp/ip
HABuo10 小时前
【linux(四)】套接字编程--基于UDP协议的客户端服务端
linux·服务器·c++·网络协议·ubuntu·udp·centos
艾莉丝努力练剑10 小时前
【Linux网络】Linux 网络编程入门:UDP Socket 编程(下)
linux·运维·服务器·网络·计算机网络·安全·udp
liann11919 小时前
3.2_红队攻击框架--MITRE ATT&CK‌
python·网络协议·安全·网络安全·系统安全·信息与通信
zjun100120 小时前
TCP专栏-1.TCP协议概念说明
网络·网络协议·tcp/ip
仍然.1 天前
网络编程(二)---TCP字节流套接字编程
网络·网络协议·tcp/ip