知识点

整个流程遵循 “初始化→解析目标→创建 ICMP 句柄→循环发送请求→处理响应→清理资源” 的逻辑,核心依赖 Windows 的 ICMP API(IcmpCreateFile、IcmpSendEcho等)和 Winsock 库,实现了与系统 ping 命令类似的功能:向目标主机发送 ICMP 请求并显示响应结果。

各个函数说明:

  1. WSAStartup(MAKEWORD(2, 2), &wsaData)初始化 Windows 套接字库
  2. IcmpCreateFile()创建 ICMP 操作句柄,用于后续发送 ICMP 请求。
  3. IcmpSendEcho()发送 ICMP 回显请求(即 ping 包)。
  4. IcmpCloseHandle(hIcmpFile)关闭 ICMP 句柄,释放资源。
  5. LocalFree(ReplyBuffer)释放响应缓冲区的内存。

关键的头文件:

#include <iphlpapi.h>
#include <icmpapi.h>

关键的库:

#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")

过程

代码如下。

pro文件:

QT -= gui

CONFIG += c++11 console
CONFIG -= app_bundle

INCLUDEPATH += C:/Program Files (x86)/Windows Kits/8.1/Include/um


# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

main.cpp:

#include <iostream>
#include <winsock2.h>
#include <iphlpapi.h>
#include <icmpapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")

// ICMP响应缓冲区大小
#define REPLY_BUFSIZE 1024

// 发送ping请求
void Ping(const char* host, int count = 4, int timeout = 1000)
{
    HANDLE hIcmpFile;
    unsigned long ipaddr = INADDR_NONE;
    DWORD dwRetVal = 0;
    char SendData[32] = "Data Buffer";
    LPVOID ReplyBuffer = (VOID*)LocalAlloc(LPTR, REPLY_BUFSIZE);
    DWORD ReplySize = REPLY_BUFSIZE;

    // 解析主机名或IP地址
    ipaddr = inet_addr(host);

    // 创建ICMP句柄
    hIcmpFile = IcmpCreateFile();
    if (hIcmpFile == INVALID_HANDLE_VALUE)
    {
        std::cerr << "IcmpCreateFile 失败: " << GetLastError() << std::endl;
        return;
    }

    std::cout << "正在 Ping " << host << " [" << inet_ntoa(*(in_addr*)&ipaddr) << "] 具有 32 字节的数据:" << std::endl;

    // 发送多个ping请求
    for (int i = 0; i < count; i++)
    {
        // 发送ICMP echo请求
        dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData),
                               NULL, ReplyBuffer, ReplySize, timeout);

        if (dwRetVal != 0)
        {
            PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
            struct in_addr ReplyAddr;
            ReplyAddr.S_un.S_addr = pEchoReply->Address;

            // 显示响应信息
            if (pEchoReply->Status == IP_SUCCESS)
            {
                std::cout << pEchoReply->DataSize << " 字节,来自 "
                          << inet_ntoa(ReplyAddr) << ": "
                          << "时间=" << pEchoReply->RoundTripTime << "ms" << std::endl;
            }
            else
            {
                std::cout << "请求超时。" << std::endl;
            }
        }
        else
        {
            std::cerr << "IcmpSendEcho 失败: " << GetLastError() << std::endl;
            break;
        }

        // 等待1秒再发送下一个请求
        Sleep(1000);
    }

    // 清理资源
    IcmpCloseHandle(hIcmpFile);
    LocalFree(ReplyBuffer);
}

int main(int argc, char* argv[])
{
    WSADATA wsaData;
    int iResult;

    // 初始化Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0)
    {
        std::cerr << "WSAStartup 失败: " << iResult << std::endl;
        return 1;
    }

    // 默认ping目标
    const char* target = "8.8.8.8";

    // 发送ping请求
    Ping(target, 4, 1000);

    // 清理Winsock
    WSACleanup();
    return 0;
}

程序运行截图如下:

更多推荐