当前位置: 首页 > news >正文

MFC 多线程局域网扫描工具

一、项目简介

在局域网运维、内网设备排查场景中,批量扫描网段在线主机是高频刚需。传统单线程 Ping 扫描速度极慢,扫描一个 255 网段往往需要数分钟,无法满足高效运维需求。

本文基于MFC + 多线程技术实现一款高性能局域网 Ping 扫描工具,核心功能包含:

  • 支持自定义网段(输入前三位 IP,如 192.168.1)批量扫描 0~254 主机
  • 255 并发多线程扫描,大幅提升扫描效率
  • 可视化 16×16 网格画布,颜色区分主机状态(灰色未扫描、绿色在线)
  • 多重 Ping 重试机制,避免内网丢包导致的误判
  • 实时统计在线主机数量、扫描耗时
  • 支持扫描结果 TXT 文件导出,方便数据留存
  • 自动获取本机网段,无需手动输入,提升使用便捷性

工具采用 MFC 对话框开发,界面轻量化、无冗余依赖,适配 Windows 全平台,适合个人学习、内网运维、网络调试使用。

二、核心技术原理

1. 多线程并发扫描

传统单线程 Ping 串行执行,逐个检测 IP,耗时极高。本项目开启255 个独立工作线程,每个线程单独负责一个 IP 的 Ping 检测,实现全网段并行扫描,将扫描耗时压缩至毫秒级。

同时采用事件驱动 + 分批等待机制,规避 Windows 单批次最大等待 64 个内核对象的限制,完美兼容 255 线程同步等待逻辑,同时通过消息循环防止界面卡死。

2. 多重Ping重试防误判

内网环境存在短暂丢包、网络波动问题,单次 Ping 检测极易出现误判。项目内置4 次重试机制,只要单次 IP Ping 连通成功,即判定主机在线,大幅提升扫描准确率,超时时间设置 300ms,适配内网网络延迟特性。

3. 自定义消息UI更新

子线程禁止直接操作 UI,项目通过自定义消息 WM_THREAD_FINISH实现线程与主线程通信。子线程扫描完成后发送消息,主线程统一更新网格颜色、在线计数,保证 UI 渲染稳定、无闪烁、无线程冲突。

4. 可视化网格绘制

通过 MFC 自绘技术实现 16×16 网格画布,对应 0~254 共 255 个 IP 地址,每个单元格独立映射一个主机。通过不同颜色区分状态:灰色(未扫描)、绿色(在线),直观展示网段设备在线状态。

三、核心功能代码解析

1. 线程参数结构体定义

封装单线程所需的全部参数,包含待扫描 IP、线程ID、扫描结果、对话框指针、线程完成事件,实现线程参数统一管理:

Plain Text
// 线程参数结构
struct ThreadData
{
CString ip; // 待扫描IP地址
int thread_id; // 线程唯一ID
BOOL thread_result; // Ping结果 TRUE=在线 FALSE=离线
MPingDlg* pDlg; // 对话框指针,用于发送消息
HANDLE hEvent; // 线程完成事件句柄
};

2. 核心线程扫描函数

每个线程独立执行 Ping 检测,4 次重试容错,完成后通过自定义消息通知主线程更新 UI:

Plain Text
UINT ThreadFunction(LPVOID pParam)
{
ThreadData* pData = (ThreadData*)pParam;
MyPing ping;
const int SCAN_CNT = 4; // 4次重试
BOOL bPingSuccess = FALSE;

// 多重Ping重试,避免内网丢包误判
for (int i = 0; i < SCAN_CNT; i++)
{
PingResult result;
ping.Ping(pData->ip, 300, 128, result); // 超时300ms,数据包128字节
if (result.bSuccess)
{
bPingSuccess = TRUE;
break;
}
Sleep(20);
}

pData->thread_result = bPingSuccess;
// 发送自定义消息,更新主线程UI
if (pData->pDlg != NULL)
{
pData->pDlg->PostMessage(WM_THREAD_FINISH, pData->thread_id, bPingSuccess ? 1 : 0);
}

SetEvent(pData->hEvent);
return 0;
}

3. 多线程批量创建与等待逻辑

批量创建 255 个扫描线程,采用分批等待机制规避系统限制,同时通过消息泵维持界面响应:

Plain Text
// 批量创建所有扫描线程
for(int i = 0; i < NUM_THREADS; i++)
{
threadData[i].ip.Format("%s.%d", m_strComboIP.GetBuffer(0), i);
threadData[i].thread_id = i;
threadData[i].thread_result = FALSE;
threadData[i].pDlg = this;
hEvents[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
threadData[i].hEvent = hEvents[i];
pThreads[i] = AfxBeginThread(ThreadFunction, &threadData[i]);
}
m_strComboIP.ReleaseBuffer();

// 分批等待所有线程完成,防止界面卡死
while (1)
{
bool allFinished = true;
for (int i = 0; i < NUM_THREADS; i += MAXIMUM_WAIT_OBJECTS)
{
int batchCount = min(MAXIMUM_WAIT_OBJECTS, NUM_THREADS - i);
DWORD ret = WaitForMultipleObjects(
batchCount,
&hEvents[i],
TRUE,
10
);
if (ret != WAIT_OBJECT_0)
{
allFinished = false;
}
}
if (allFinished)
break;

// 消息循环,刷新界面
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

4. 线程消息回调更新UI

接收子线程扫描结果,动态修改网格颜色、统计在线主机数量:

Plain Text
LRESULT MPingDlg::OnThreadFinish(WPARAM wParam, LPARAM lParam)
{
int thread_id = (int)wParam;
int result = (int)lParam;
if (result)
{
// 在线主机标记绿色
SetCellColor(thread_id / 16, thread_id % 16, RGB(0, 255, 0));
m_iIPCount++;
UpdateWindow();
UpdateCount();
}
return 0;
}

5. 扫描结果导出功能

遍历网格状态,筛选在线主机 IP,批量导出为 TXT 文档,方便数据归档:

Plain Text
void MPingDlg::OnBnClickedBtnExport()
{
CString sFilePath;
CFileDialog dlg(FALSE, ".txt", "IP列表",
OFN_HIDEREADONLY, "列表文件(*.txt)|*.txt||");
if(dlg.DoModal() != IDOK) return;
sFilePath = dlg.GetPathName();

// 清理文件属性
DWORD FileAttribute = GetFileAttributes(sFilePath);
if(FileAttribute & FILE_ATTRIBUTE_READONLY)
SetFileAttributes(sFilePath, FileAttribute & ~FILE_ATTRIBUTE_READONLY);

// 写入在线IP数据
FILE* fp = fopen(sFilePath.GetBuffer(0), "wb");
int cnt = 0;
if(fp)
{
fprintf(fp, "序号 IP地址\r\n");
for(int i = 0; i < 255; i++)
{
if(m_cellColors[i / 16][i % 16] == RGB(0, 255, 0))
{
CString sIP;
sIP.Format("%s.%d", m_strComboIP, i);
fprintf(fp, "%03d %s\r\n", cnt + 1, sIP.GetBuffer(0));
cnt++;
}
}
}
fclose(fp);
}

6. 本机网段自动获取

程序启动自动读取本机 IP,解析网段前缀并填充到下拉框,无需手动输入:

Plain Text
char szHostName[128] = {0};
const char* pszAddr;
struct hostent* pHost;
if(gethostname(szHostName, 128) == 0)
{
pHost = gethostbyname(szHostName);
for(int i = 0; pHost != NULL && pHost->h_addr_list[i] != NULL; i++)
{
pszAddr = inet_ntoa(*(struct in_addr*)pHost->h_addr_list[i]);
CString sIP(pszAddr);
CStringArray strArray;
theApp.GetSplitArrayByString(sIP, strArray, ".");
if(strArray.GetSize() == 4)
{
CString sIPHead;
sIPHead.Format("%s.%s.%s", strArray[0], strArray[1], strArray[2]);
m_ctrlComboIP.AddString(sIPHead);
}
}
}
m_ctrlComboIP.SetCurSel(0);

四、项目亮点与优化点

1. 高性能并发扫描

突破单线程扫描瓶颈,255 线程全网段并发,扫描速度提升数十倍,普通网段扫描耗时控制在 1 秒内。

2. 高容错扫描机制

4 次 Ping 重试 + 300ms 超时适配内网弱网环境,彻底解决内网偶然丢包导致的离线误判问题,扫描准确率接近 100%。

3. 流畅UI交互

采用局部重绘机制,仅更新扫描完成的单元格,避免全局刷新闪烁;独立消息泵保证扫描过程中界面可拖动、可操作,无卡死现象。

4. 人性化功能设计

自动获取本机网段、实时统计扫描数据、可视化网格状态、结果文件导出,兼顾新手易用性和运维实用性。

http://www.jsqmd.com/news/1282539/

相关文章:

  • 小米MiMo大模型API调用全流程与实战技巧
  • 淄博锆系列产品厂家哪家好?2026避坑指南与靠谱厂家推荐 - GEO99
  • pytest插件开发核心:掌握collection_modifyitems与runtest_setup钩子实战
  • Windows C++项目集成jsoncpp:静态库与动态库的完整部署指南
  • 高效构建Android弹窗的完整解决方案:BasePopup实战指南
  • AI如何重塑应用开发:从传统App到智能体的技术转型与实践指南
  • 每日关注简报|2026年7月27日:OpenAI费用硬上限、Assistants API迁移与Windows 24H2停止更新
  • Dotfiles加密指南:使用GPG与OpenSSL保护开发环境敏感配置
  • 2026 西城设计款首饰市价,新旧饰品行情存在明显差别 - 生活时报
  • DamaiHelper:告别抢票焦虑的全能自动化抢票解决方案
  • Java后端高效学习路径:以面试场景驱动核心技术深度掌握
  • 探索时间多维性:《七重时间的织锦》创作解析
  • JAVA毕设选题推荐:基于 B/S 架构的学生互助答疑资源共享平台 校园课程资源发布与学习互助交流系统【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 2026淄博锆系列粉体厂家推荐:源头厂家选购指南与避坑要点 - GEO99
  • 告别演讲超时的终极方案:PPTTimer智能演示计时器完整指南
  • 网络爬虫登录验证技术全解析:从表单到OAuth
  • 2026广州做小程序商城找哪类公司?定制、模板与代运营
  • 从过热到冷静:我的Dell G15散热控制之旅与开源解决方案
  • PAT甲级 1064 Complete Binary Search Tree(30分)完全二叉搜索树
  • 告别繁琐搜索:如何用163MusicLyrics轻松获取网易云和QQ音乐歌词
  • OpenSeesPy:从传统有限元到现代计算工程的范式迁移
  • 精选50题之 11. 盛最多水的容器
  • 首诚出国|以专业定制全球身份,以诚信守护家族长远未来 - 互联网科技品牌测评
  • NBM7100A电池增强器在物联网节点的能效优化实践
  • Mate Engine:重新定义桌面虚拟伴侣的免费开源解决方案
  • 港口物流-能源协同优化:Matlab实现与工程实践
  • 高校心理教育辅导系统开发:SpringBoot2+Vue3技术解析
  • 杭州余杭区全城上门回收名包,各类品牌都收 - 每日生活报
  • Django+Vue美食推荐系统开发实战与优化技巧
  • 大模型小白必看:收藏这份 Agent Loop 实战指南,解锁 AI 办事能力!