鸿蒙PC端C++开发实战:轻量级网络端口扫描工具
编译错误修复:核心解决缺失导致的timevalfd_set未定义问题,这是鸿蒙PC musl libc的典型坑点;网络适配:鸿蒙PC完全兼容POSIX套接字接口,但需注意非阻塞IO、超时设置的内核行为差异;性能优化:基于鸿蒙PC的CPU核心数动态调整线程数,避免资源浪费;稳定性:严格的资源释放、异常处理,适配鸿蒙PC的句柄限制和错误码特性。该案例可扩展为TCP客户端/服务端、UDP扫描、网络测速等
鸿蒙PC端C++开发实战:轻量级网络端口扫描工具
鸿蒙PC基于OpenHarmony内核构建,其Linux兼容层为C++网络编程提供了完整的POSIX套接字(Socket)支持。本文以“轻量级TCP端口扫描工具”为例,详解鸿蒙PC端C++网络编程的核心适配要点、编译调试流程,以及鸿蒙内核下网络接口的特有处理方式,同时修复编译阶段的头文件依赖问题,为网络类C++应用开发提供可直接复用的完整模板。
一、案例背景与核心特性
本案例实现的端口扫描工具具备以下能力:
- 扫描指定IP的指定端口范围,判断端口是否开放;
- 支持超时控制,适配鸿蒙PC的网络调度特性;
- 基于C++11封装Socket操作,面向对象设计;
- 兼容鸿蒙PC的ARM aarch64架构与musl libc库;
- 修复编译阶段
timeval/fd_set未定义等核心问题。
相比系统监控工具,本案例重点体现:
- 鸿蒙PC下的Socket网络编程适配;
- 非阻塞IO与超时处理的鸿蒙内核兼容;
- 多线程扫描的资源调度优化(适配鸿蒙PC的CPU核心调度策略)。
二、环境准备(复用基础配置)
2.1 编译器验证
沿用鸿蒙PC默认的Clang++编译器,验证命令:
clang++ --version
预期输出(鸿蒙PC标准版):
OHOS (BiSheng Mobile STD 203.2.0.B175-20250723142036) clang version 15.0.4 (9e3d9b8a15b2)
Target: aarch64-unknown-linux-ohos
Thread model: posix
InstalledDir: /data/app/BiSheng.org/BiSheng_1.0/llvm/bin
2.2 网络依赖确认
鸿蒙PC默认支持POSIX套接字,无需额外安装网络库,仅需确保核心头文件存在:
# 验证关键头文件可用性
ls /usr/include/sys/time.h /usr/include/sys/socket.h
三、实战开发:TCP端口扫描工具
3.1 核心代码(鸿蒙PC适配+编译错误修复版)
// 必须在所有头文件前定义POSIX宏,确保timeval/fd_set正确定义
#define _POSIX_C_SOURCE 200809L
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <mutex>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <cstring>
#include <chrono>
#include <climits>
// 核心修复:补充timeval/fd_set定义的头文件(鸿蒙PC必须显式包含)
#include <sys/time.h>
// 适配鸿蒙PC musl libc:定义未暴露的宏
#ifndef SO_RCVTIMEO
#define SO_RCVTIMEO 0x1006
#endif
#ifndef SO_SNDTIMEO
#define SO_SNDTIMEO 0x1005
#endif
#ifndef LINE_MAX
#define LINE_MAX 2048
#endif
// 扫描结果结构体
struct ScanResult {
int port;
bool is_open;
std::string reason;
};
// 端口扫描工具类(单例模式,适配鸿蒙多线程调度)
class PortScanner {
public:
static PortScanner& getInstance() {
static PortScanner instance;
return instance;
}
// 扫描指定IP的端口范围
std::vector<ScanResult> scan(const std::string& ip, int start_port, int end_port, int timeout_ms = 1000) {
std::vector<ScanResult> results;
std::mutex mtx;
std::vector<std::thread> threads;
// 鸿蒙PC CPU核心数适配:线程数不超过逻辑核心数
long cpu_cores = sysconf(_SC_NPROCESSORS_ONLN);
int max_threads = cpu_cores > 0 ? (int)cpu_cores : 4;
int ports_per_thread = (end_port - start_port + 1) / max_threads + 1;
// 分线程扫描
for (int i = 0; i < max_threads; ++i) {
int thread_start = start_port + i * ports_per_thread;
int thread_end = std::min(thread_start + ports_per_thread - 1, end_port);
if (thread_start > end_port) break;
threads.emplace_back([&, ip, thread_start, thread_end, timeout_ms]() {
for (int port = thread_start; port <= thread_end; ++port) {
ScanResult res = scanSinglePort(ip, port, timeout_ms);
std::lock_guard<std::mutex> lock(mtx);
results.push_back(res);
}
});
}
// 等待所有线程完成
for (auto& t : threads) {
if (t.joinable()) t.join();
}
return results;
}
private:
PortScanner() = default;
~PortScanner() = default;
PortScanner(const PortScanner&) = delete;
PortScanner& operator=(const PortScanner&) = delete;
// 扫描单个端口(核心逻辑,适配鸿蒙Socket特性)
ScanResult scanSinglePort(const std::string& ip, int port, int timeout_ms) {
ScanResult res;
res.port = port;
res.is_open = false;
// 创建TCP套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
res.reason = "Socket create failed: " + std::string(strerror(errno));
return res;
}
// 设置套接字为非阻塞模式(适配鸿蒙内核调度)
int flags = fcntl(sockfd, F_GETFL, 0);
if (flags < 0 || fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) {
close(sockfd);
res.reason = "Set non-block failed: " + std::string(strerror(errno));
return res;
}
// 设置超时(鸿蒙PC需同时设置读写超时)
struct timeval timeout;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
// 配置目标地址
struct sockaddr_in target_addr;
memset(&target_addr, 0, sizeof(target_addr));
target_addr.sin_family = AF_INET;
target_addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip.c_str(), &target_addr.sin_addr) <= 0) {
close(sockfd);
res.reason = "Invalid IP address";
return res;
}
// 连接端口(非阻塞模式适配鸿蒙内核)
int ret = connect(sockfd, (struct sockaddr*)&target_addr, sizeof(target_addr));
if (ret == 0) {
res.is_open = true;
res.reason = "Port open";
} else if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
// 鸿蒙PC非阻塞连接处理:使用select检测连接状态
fd_set write_fds;
FD_ZERO(&write_fds);
FD_SET(sockfd, &write_fds);
int select_ret = select(sockfd + 1, nullptr, &write_fds, nullptr, &timeout);
if (select_ret > 0) {
int err;
socklen_t err_len = sizeof(err);
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &err_len);
if (err == 0) {
res.is_open = true;
res.reason = "Port open";
} else {
res.reason = "Port closed: " + std::string(strerror(err));
}
} else if (select_ret == 0) {
res.reason = "Connection timeout";
} else {
res.reason = "Select failed: " + std::string(strerror(errno));
}
} else {
res.reason = "Connect failed: " + std::string(strerror(errno));
}
close(sockfd);
return res;
}
};
// 打印扫描结果(格式化输出,适配鸿蒙终端)
void printScanResults(const std::string& ip, const std::vector<ScanResult>& results) {
std::cout << "===== HarmonyOS PC Port Scan Result =====\n";
std::cout << "Target IP: " << ip << "\n";
std::cout << "=========================================\n";
std::cout << "Port\tStatus\tReason\n";
std::cout << "=========================================\n";
for (const auto& res : results) {
std::cout << res.port << "\t"
<< (res.is_open ? "OPEN" : "CLOSED") << "\t"
<< res.reason << "\n";
}
std::cout << "=========================================\n";
}
// 命令行参数解析
bool parseArgs(int argc, char* argv[], std::string& ip, int& start_port, int& end_port) {
if (argc != 4) {
std::cerr << "Usage: " << argv[0] << " <IP> <StartPort> <EndPort>\n";
std::cerr << "Example: " << argv[0] << " 127.0.0.1 1 1000\n";
return false;
}
ip = argv[1];
start_port = atoi(argv[2]);
end_port = atoi(argv[3]);
if (start_port < 1 || start_port > 65535 || end_port < 1 || end_port > 65535 || start_port > end_port) {
std::cerr << "Invalid port range (1-65535)\n";
return false;
}
return true;
}
int main(int argc, char* argv[]) {
try {
std::string target_ip;
int start_port, end_port;
// 解析命令行参数
if (!parseArgs(argc, argv, target_ip, start_port, end_port)) {
return 1;
}
// 开始扫描(记录耗时,适配鸿蒙性能分析)
auto start_time = std::chrono::high_resolution_clock::now();
PortScanner& scanner = PortScanner::getInstance();
std::vector<ScanResult> results = scanner.scan(target_ip, start_port, end_port, 1000);
auto end_time = std::chrono::high_resolution_clock::now();
// 打印结果
printScanResults(target_ip, results);
// 输出耗时
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "Scan completed in " << duration.count() << " ms\n";
} catch (const std::exception& e) {
std::cerr << "Fatal error: " << e.what() << std::endl;
return 1;
}
return 0;
}
3.2 关键修复与适配点(鸿蒙PC特有)
1. 编译错误核心修复
- 头文件补充:显式包含
<sys/time.h>,解决struct timeval/fd_set未定义问题; - POSIX宏位置:将
_POSIX_C_SOURCE=200809L定义在所有头文件之前,确保宏生效; - 宏定义补充:新增
SO_SNDTIMEO宏定义,避免套接字超时设置报错。
2. 网络编程适配
- 非阻塞模式强制开启:通过
fcntl设置套接字为非阻塞,适配鸿蒙内核的网络调度逻辑; - CPU核心数动态适配:根据鸿蒙PC的逻辑核心数调整线程数,避免线程过多导致调度压力;
- 超时设置双保险:同时设置
SO_SNDTIMEO/SO_RCVTIMEO,解决鸿蒙PC单超时设置无效问题。
3. 稳定性优化
- 资源严格释放:确保套接字文件描述符在所有分支中关闭,避免鸿蒙PC句柄泄漏;
- 异常分支全覆盖:补充非阻塞模式设置失败的错误处理,提升程序鲁棒性;
- 参数合法性校验:严格校验端口范围(1-65535),避免无效扫描。
四、编译:鸿蒙PC端Clang++编译命令
4.1 最终编译命令
# 修复后可直接编译的命令(无需额外调整)
clang++ -Wall -O2 -std=c++11 -o harmony_portscan so.cpp -lpthread -ldl -lm
说明:因代码中已提前定义
_POSIX_C_SOURCE=200809L,编译命令中可省略该宏,简化指令。
4.2 命令参数解析(鸿蒙PC重点)
| 参数 | 作用 |
|---|---|
clang++ |
C++编译器(鸿蒙PC推荐,替代g++,避免兼容性问题); |
-std=c++11 |
C++11标准是std::thread/std::chrono的最低要求; |
-Wall |
开启所有警告,提前发现鸿蒙PC特有的语法/接口问题; |
-O2 |
二级优化(平衡性能与编译速度,避免-O3的兼容性风险); |
-lpthread |
链接线程库(std::thread基于pthread实现,鸿蒙PC必须显式链接); |
-ldl -lm |
链接动态库和数学库(鸿蒙PC musl libc的基础依赖); |
4.3 编译常见问题终极解决
| 报错信息 | 原因 | 解决方法 |
|---|---|---|
struct timeval 不完整类型 |
缺失<sys/time.h> |
显式包含该头文件,且确保_POSIX_C_SOURCE宏在头文件前定义; |
fd_set 未知类型名 |
缺失<sys/time.h> |
同上; |
undefined reference to 'std::thread::join()' |
未链接pthread库 | 编译命令添加-lpthread; |
SO_SNDTIMEO 未定义 |
musl libc宏缺失 | 代码中手动定义#define SO_SNDTIMEO 0x1005; |
inet_pton 函数未定义 |
缺失<arpa/inet.h> |
包含该头文件; |
五、运行:鸿蒙PC端端口扫描工具
5.1 赋予执行权限
chmod +x harmony_portscan
5.2 运行示例(扫描本地端口)
./harmony_portscan 127.0.0.1 1 100
5.3 预期输出(鸿蒙PC)
HarmonyOS PC Port Scan Result =====
Target IP: 127.0.0.1
=========================================
Port Status Reason
=========================================
97 CLOSED Connect failed: Connection refused
55 CLOSED Connect failed: Connection refused
25 CLOSED Connect failed: Connection refused
13 CLOSED Connect failed: Connection refused
56 CLOSED Connect failed: Connection refused
37 CLOSED Connect failed: Connection refused
26 CLOSED Connect failed: Connection refused
79 CLOSED Connect failed: Connection refused
14 CLOSED Connect failed: Connection refused
98 CLOSED Connect failed: Connection refused
57 CLOSED Connect failed: Connection refused
27 CLOSED Connect failed: Connection refused
38 CLOSED Connect failed: Connection refused
15 CLOSED Connect failed: Connection refused
61 CLOSED Connect failed: Connection refused
80 CLOSED Connect failed: Connection refused
85 CLOSED Connect failed: Connection refused
99 CLOSED Connect failed: Connection refused
49 CLOSED Connect failed: Connection refused
39 CLOSED Connect failed: Connection refused
16 CLOSED Connect failed: Connection refused
58 CLOSED Connect failed: Connection refused
62 CLOSED Connect failed: Connection refused
81 CLOSED Connect failed: Connection refused
40 CLOSED Connect failed: Connection refused
17 CLOSED Connect failed: Connection refused
59 CLOSED Connect failed: Connection refused
82 CLOSED Connect failed: Connection refused
19 CLOSED Connect failed: Connection refused
100 CLOSED Connect failed: Connection refused
86 CLOSED Connect failed: Connection refused
18 CLOSED Connect failed: Connection refused
60 CLOSED Connect failed: Connection refused
83 CLOSED Connect failed: Connection refused
50 CLOSED Connect failed: Connection refused
41 CLOSED Connect failed: Connection refused
28 CLOSED Connect failed: Connection refused
87 CLOSED Connect failed: Connection refused
91 CLOSED Connect failed: Connection refused
84 CLOSED Connect failed: Connection refused
31 CLOSED Connect failed: Connection refused
42 CLOSED Connect failed: Connection refused
20 CLOSED Connect failed: Connection refused
67 CLOSED Connect failed: Connection refused
51 CLOSED Connect failed: Connection refused
92 CLOSED Connect failed: Connection refused
32 CLOSED Connect failed: Connection refused
29 CLOSED Connect failed: Connection refused
88 CLOSED Connect failed: Connection refused
21 CLOSED Connect failed: Connection refused
68 CLOSED Connect failed: Connection refused
63 CLOSED Connect failed: Connection refused
93 CLOSED Connect failed: Connection refused
33 CLOSED Connect failed: Connection refused
22 CLOSED Connect failed: Connection refused
30 CLOSED Connect failed: Connection refused
94 CLOSED Connect failed: Connection refused
34 CLOSED Connect failed: Connection refused
89 CLOSED Connect failed: Connection refused
52 CLOSED Connect failed: Connection refused
23 CLOSED Connect failed: Connection refused
69 CLOSED Connect failed: Connection refused
64 CLOSED Connect failed: Connection refused
35 CLOSED Connect failed: Connection refused
95 CLOSED Connect failed: Connection refused
24 CLOSED Connect failed: Connection refused
53 CLOSED Connect failed: Connection refused
7 CLOSED Connect failed: Connection refused
70 CLOSED Connect failed: Connection refused
1 CLOSED Connect failed: Connection refused
90 CLOSED Connect failed: Connection refused
65 CLOSED Connect failed: Connection refused
73 CLOSED Connect failed: Connection refused
36 CLOSED Connect failed: Connection refused
96 CLOSED Connect failed: Connection refused
43 CLOSED Connect failed: Connection refused
71 CLOSED Connect failed: Connection refused
54 CLOSED Connect failed: Connection refused
2 CLOSED Connect failed: Connection refused
44 CLOSED Connect failed: Connection refused
66 CLOSED Connect failed: Connection refused
8 CLOSED Connect failed: Connection refused
74 CLOSED Connect failed: Connection refused
3 CLOSED Connect failed: Connection refused
45 CLOSED Connect failed: Connection refused
72 CLOSED Connect failed: Connection refused
4 CLOSED Connect failed: Connection refused
75 CLOSED Connect failed: Connection refused
46 CLOSED Connect failed: Connection refused
5 CLOSED Connect failed: Connection refused
47 CLOSED Connect failed: Connection refused
9 CLOSED Connect failed: Connection refused
76 CLOSED Connect failed: Connection refused
6 CLOSED Connect failed: Connection refused
48 CLOSED Connect failed: Connection refused
77 CLOSED Connect failed: Connection refused
78 CLOSED Connect failed: Connection refused
10 CLOSED Connect failed: Connection refused
11 CLOSED Connect failed: Connection refused
12 CLOSED Connect failed: Connection refused
=========================================
Scan completed in 3 ms
5.4 鸿蒙PC网络权限说明
-
本地端口扫描无需特殊权限,普通用户即可执行;
-
扫描外部IP需确保鸿蒙PC的网络防火墙放行(配置路径:
/etc/iptables/rules.v4); -
扫描1-1024特权端口需加
sudo:sudo ./harmony_portscan 192.168.1.1 1 1024
六、进阶优化:鸿蒙PC网络程序调优
6.1 静态编译(分发无忧)
clang++ -Wall -O2 -std=c++11 -static -o harmony_portscan so.cpp -lpthread -ldl -lm
静态编译后的程序可直接在其他鸿蒙PC上运行,无需依赖系统库。
6.2 性能优化(鸿蒙内核适配)
-
线程优先级调整:提升扫描线程优先级,适配鸿蒙PC调度策略:
// 在线程函数中添加 struct sched_param param; param.sched_priority = 50; // 鸿蒙PC优先级范围1-99 pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); -
批量端口扫描:使用
poll替代select,适配鸿蒙PC大文件描述符场景; -
缓存优化:调整套接字缓冲区大小,提升扫描效率:
int buf_size = 8192; setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size));
6.3 调试网络问题(鸿蒙PC特有)
-
查看端口占用:
netstat -tulpn; -
抓包分析扫描过程(需安装:
ohpm install tcpdump):tcpdump -i any host 127.0.0.1 and port 22
七、案例总结
本案例通过TCP端口扫描工具,完整展示了鸿蒙PC端C++网络编程的核心要点:
- 编译错误修复:核心解决
<sys/time.h>缺失导致的timeval/fd_set未定义问题,这是鸿蒙PC musl libc的典型坑点; - 网络适配:鸿蒙PC完全兼容POSIX套接字接口,但需注意非阻塞IO、超时设置的内核行为差异;
- 性能优化:基于鸿蒙PC的CPU核心数动态调整线程数,避免资源浪费;
- 稳定性:严格的资源释放、异常处理,适配鸿蒙PC的句柄限制和错误码特性。
该案例可扩展为TCP客户端/服务端、UDP扫描、网络测速等工具,核心Socket适配逻辑完全复用,是鸿蒙PC端C++网络开发的标准参考模板。
更多推荐


所有评论(0)