第1章:Socket编程基础
各位同学好,我是老张。今天咱们聊聊Socket编程。说实话,我在鸿蒙系统上折腾网络协议栈也有几年了,踩过的坑不少,积累的经验也还行。这一章,我会把Socket最核心的东西讲清楚。
1.1 什么是Socket?
Socket,中文叫「套接字」。说白了,它就是网络通信的「门」。你想啊,两台设备要通信,总得有个入口吧?Socket就是这个入口。
我习惯这么理解:Socket = IP地址 + 端口号。IP地址找到设备,端口号找到设备上的具体应用。两者一组合,通信的双方就确定了。
核心要点:Socket是应用层与传输层之间的抽象接口。它屏蔽了底层协议的复杂性,让开发者可以像读写文件一样进行网络通信。
你可能会问:「为什么不用更高级的接口?」嗯,这个问题我当年也想过。后来发现,Socket虽然原始,但胜在灵活。你想控制超时、缓冲区、协议选项?Socket全给你。高级接口封装得再好,底层还是Socket。
1.2 鸿蒙Socket API概览
鸿蒙的Socket API,其实和POSIX标准很接近。如果你写过Linux下的Socket程序,上手会非常快。但有几个鸿蒙特有的点,我得提醒你注意。
先看一张表,把常用的API列出来:
| 函数 | 作用 | 鸿蒙注意事项 |
|---|---|---|
socket() |
创建Socket | 支持AF_INET、AF_INET6 |
bind() |
绑定地址和端口 | 端口号建议大于1024 |
listen() |
监听连接(TCP) | backlog参数别设太大 |
connect() |
发起连接(TCP) | 超时处理要自己加 |
accept() |
接受连接(TCP) | 返回新的Socket fd |
send() / recv() |
发送/接收数据 | 注意返回值处理 |
sendto() / recvfrom() |
UDP发送/接收 | 需要指定目标地址 |
close() |
关闭Socket | 记得释放资源 |
我的经验:鸿蒙的socket()函数在创建失败时,返回的是-1,不是NULL。很多新手在这里翻车,检查错误时用成了if (sock == NULL),结果死活查不出问题。
1.3 TCP Socket通信流程
TCP是面向连接的协议。说白了,就是通信前先「握手」,建立一条可靠的通道。我做过一个物联网项目,设备端上报数据用的就是TCP,因为数据不能丢。
TCP通信的流程,我总结为「三步走」:
- 服务端:创建Socket → 绑定地址 → 监听 → 接受连接 → 收发数据 → 关闭
- 客户端:创建Socket → 连接服务端 → 收发数据 → 关闭
- 注意:服务端accept()返回的Socket和监听Socket不是同一个
来看一个简单的TCP服务端示例:
// TCP服务端 - 鸿蒙环境
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main() {
int server_fd, client_fd;
struct sockaddr_in addr;
char buffer[1024] = {0};
// 1. 创建Socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
// 错误处理
return -1;
}
// 2. 绑定地址
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(8080);
if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
close(server_fd);
return -1;
}
// 3. 监听
if (listen(server_fd, 5) < 0) {
close(server_fd);
return -1;
}
// 4. 接受连接
client_fd = accept(server_fd, NULL, NULL);
// 5. 接收数据
recv(client_fd, buffer, sizeof(buffer), 0);
// 6. 关闭
close(client_fd);
close(server_fd);
return 0;
}
我曾经踩过的坑:有一次,我在服务端忘了调用bind(),直接listen()了。结果listen()返回成功,但客户端死活连不上。查了半天才发现,系统自动分配了一个随机端口,根本不是我想用的8080。记住:服务端必须bind(),客户端可以不用。
1.4 UDP Socket通信流程
UDP就简单多了。它不需要连接,直接发数据就行。我做过一个视频流传输的项目,用的就是UDP。为什么?因为视频丢几帧没关系,但延迟大了可不行。
UDP的流程:
- 服务端:创建Socket → 绑定地址 → 收发数据 → 关闭
- 客户端:创建Socket → 收发数据 → 关闭
- 关键区别:没有listen()和accept(),直接用sendto()和recvfrom()
来看UDP服务端的代码:
// UDP服务端 - 鸿蒙环境
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main() {
int sock_fd;
struct sockaddr_in addr, client_addr;
char buffer[1024] = {0};
socklen_t addr_len = sizeof(client_addr);
// 1. 创建Socket
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0) {
return -1;
}
// 2. 绑定地址
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(9090);
bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
// 3. 接收数据(不需要accept)
recvfrom(sock_fd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&client_addr, &addr_len);
// 4. 发送数据回客户端
sendto(sock_fd, buffer, strlen(buffer), 0,
(struct sockaddr*)&client_addr, addr_len);
// 5. 关闭
close(sock_fd);
return 0;
}
我的建议:UDP虽然简单,但有个大问题——数据可能丢,也可能乱序。如果你做的是文件传输、消息推送这类要求可靠性的场景,老老实实用TCP。但如果是实时音视频、游戏同步,UDP是更好的选择。
1.5 TCP vs UDP:怎么选?
很多新手会纠结这个问题。我直接给你一个判断标准:
- 选TCP:数据必须完整、顺序必须正确。比如文件下载、网页浏览、数据库操作。
- 选UDP:实时性优先、可以容忍少量丢包。比如视频通话、在线游戏、DNS查询。
你想想看,如果视频通话用TCP,一旦丢包就重传,画面卡成PPT,谁受得了?反过来,银行转账用UDP,丢了一笔钱,那还得了?
一句话总结:TCP保可靠,UDP保速度。没有绝对的好坏,只有合不合适的场景。
好了,这一章的内容就到这里。Socket编程是网络通信的基石,后面的章节我们会深入鸿蒙特有的网络特性。下一章,咱们聊聊鸿蒙的分布式网络通信,那才是真正有意思的东西。
公众号:蓝海资料掘金营,微信deep3321