在网络编程的世界里,Socket(套接字) 是支持 TCP/IP 协议簇进行双向通信的基本操作单元。它就像是不同主机进程间通信的端点。在实际开发中,我们最常接触的是 TCP 和 UDP 两种协议,它们的性格截然不同:
1. TCP (传输控制协议):可靠的“电话”
- 面向连接:通信前必须先建立连接(三次握手),通信结束后断开连接(四次挥手)。
- 可靠传输:通过确认应答、超时重传、错误校验机制,保证数据不丢失、不重复、按序到达。
- 适用场景:文件传输、指令交互、数据上报等对完整性要求高的场景。
2. UDP (用户数据报协议):高效的“广播”
- 无连接:发送数据前不需要建立连接,资源消耗小,传输效率高。
- 不可靠:不保证数据一定送达,也不进行重传,数据包大小限制在 64KB 以内。
- 适用场景:音视频流媒体、即时通讯、广播通知、DNS 查询等对实时性要求高、允许少量丢包的场景。
核心区别对比表
| 特性 | UDP (无连接) | TCP (面向连接) |
|---|---|---|
| 连接方式 | 无需建立连接,随时发送 | 必须先建立连接才能通信 |
| 可靠性 | 不保证,可能丢包 | 可靠,确保数据完整有序 |
| 传输效率 | 高,头部开销小 | 相对较低,有复杂的控制机制 |
| 资源占用 | 少 | 多 |
| 通信模式 | 支持一对一、一对多(广播/多播) | 仅支持一对一(点对点) |
💻 实战开发:嵌入式鸿蒙下的 Socket 编程
基于嵌入式开发环境(OpenHarmony + LwIP),以下是 TCP/UDP 客户端与服务端的核心开发逻辑梳理。
1. TCP 编程实战
TCP 客户端 (Client) 流程:
- 创建套接字:
socket(AF_INET, SOCK_STREAM, 0) - 连接服务端:
connect()指定 IP 和端口。 - 循环发送/接收:使用
send()发送数据,recv()接收数据。
TCP 服务端 (Server) 流程:
- 创建套接字:
socket() - 绑定地址:
bind()绑定本地 IP 和端口。 - 监听连接:
listen()开始监听。 - 接受连接:
accept()阻塞等待客户端接入,生成新的通信套接字。 - 数据收发:通过
accept返回的client_fd进行recv/send。
2. UDP 编程实战
UDP 客户端/服务端 (通用):
- 创建套接字:
socket(AF_INET, SOCK_DGRAM, 0)(注意类型为 DGRAM)。 - 配置地址:填充
sockaddr_in结构体,指定目标 IP 和端口。 - 发送数据:使用
sendto()替代 send,需指定目标地址。 - 接收数据:使用
recvfrom()替代 recv,可获取发送方地址。
UDP 广播 (Broadcast) 特殊配置:
在发送广播前,必须设置套接字选项以开启广播权限:
int yes = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *) &yes, sizeof(yes));
- 广播地址:目标 IP 设置为
INADDR_BROADCAST(通常是 255.255.255.255)。
🛠️ 核心代码逻辑参考 (C语言/LwIP)
以下是提炼的通用逻辑伪代码,适用于嵌入式 IoT 开发:
TCP 客户端核心逻辑
// 1. 创建 socket
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 配置服务器地址
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("192.168.1.100"); // 服务端IP
server_addr.sin_port = htons(8080); // 端口
// 3. 建立连接
connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
// 4. 循环发送
while(1) {
send(sock_fd, "Hello", strlen("Hello"), 0);
usleep(1000000); // 1秒
}
UDP 广播核心逻辑
// 1. 创建 UDP socket
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
// 2. 启用广播选项
int broadcast_enable = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &broadcast_enable, sizeof(broadcast_enable));
// 3. 配置广播地址
struct sockaddr_in broadcast_addr;
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); // 广播地址
broadcast_addr.sin_port = htons(8080);
// 4. 循环广播
while(1) {
sendto(sock_fd, "Broadcast Msg", strlen("Broadcast Msg"), 0,
(struct sockaddr*)&broadcast_addr, sizeof(broadcast_addr));
usleep(1000000);
}
💡 总结与建议
-
选型建议:
- 如果你需要传输文件、传感器精确数据,或者需要确保每一条指令都必须被执行,请使用 TCP。
- 如果你在做视频推流、局域网设备发现(广播)、或者对实时性要求极高(如游戏数据同步),请使用 UDP。
- 调试技巧:
- 开发时务必确保设备与 PC 处于同一局域网(同一热点或路由器下)。
- 利用 PC 端的“网络调试助手”模拟服务端或客户端,配合串口打印进行双向调试。