首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

使用 ICMP DLL 實現 Ping 功能:CSocket 衍生類封裝

     編輯:VC 教學

前言

Windows 無內建 ping API(直到 Winsock 2.0 仍無),必須直接呼叫 ICMP.dll (ICMPAPI.H/LIB) 發送 ICMP Echo Request 並接收 Echo Reply。本文介紹一個封裝 CSocket 的 CPing 類,提供主機名/IP 解析、Ping 發送、回應時間測量功能。

所需檔案

從 Microsoft SDK / Platform SDK 獲取並加入專案:

  • ICMPAPI.H、ICMPAPI.CPP、ICMP.LIB
  • IPEXPORT.H、IPEXPORT.CPP(IP 標頭定義)

類別介面


class CPing : public CSocket {
public:
    // 解析 IP 字串 -> ULONG (網路位元組序)
    unsigned long ResolveIP(CString strIP);

    // 解析主機名 -> ULONG (經 DNS/WINS)
    unsigned long ResolveName(CString strHostName);

    // ULONG IP -> 點分十進制字串
    CString GetIP(unsigned long ulIP);

    // 核心:Ping 指定 IP,回傳往返時間 (ms),失敗回傳 -1
    DWORD PingHost(unsigned long ulIP, int iPingTimeout = 2000);
};

核心實現:PingHost


DWORD CPing::PingHost(unsigned long ulIP, int iPingTimeout) {
    HANDLE hIcmp = IcmpCreateFile();
    if (hIcmp == INVALID_HANDLE_VALUE) return (DWORD)-1;

    // 準備發送緩衝區 (任意資料,如 "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
    char sendBuf[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    IP_OPTION_INFORMATION ipOpt = {0};
    ipOpt.Ttl = 128; // Time To Live

    // 接收緩衝區:ICMP_ECHO_REPLY + 回應資料
    DWORD replySize = sizeof(ICMP_ECHO_REPLY) + sizeof(sendBuf);
    LPVOID replyBuf = malloc(replySize);

    DWORD dwRet = IcmpSendEcho(
        hIcmp,
        ulIP,                 // 目標 IP (網路序)
        sendBuf, sizeof(sendBuf), // 發送資料
        &ipOpt,               // IP 選項
        replyBuf, replySize,  // 接收緩衝區
        iPingTimeout          // 逾時 (ms)
    );

    DWORD rtt = (DWORD)-1;
    if (dwRet > 0) {
        PICMP_ECHO_REPLY pReply = (PICMP_ECHO_REPLY)replyBuf;
        if (pReply->Status == IP_SUCCESS) {
            rtt = pReply->RoundTripTime; // 毫秒
        }
    }

    free(replyBuf);
    IcmpCloseHandle(hIcmp);
    return rtt;
}

輔助函數實現


unsigned long CPing::ResolveIP(CString strIP) {
    return inet_addr(strIP); // 點分十進制 -> ULONG (網路序)
}

unsigned long CPing::ResolveName(CString strHostName) {
    struct hostent *pHost = gethostbyname(strHostName);
    if (!pHost) return INADDR_NONE;
    return *(unsigned long*)pHost->h_addr_list[0]; // 已是網路序
}

CString CPing::GetIP(unsigned long ulIP) {
    struct in_addr addr; addr.S_un.S_addr = ulIP;
    return CString(inet_ntoa(addr)); // 網路序 -> 點分十進制
}

使用範例


CPing ping;
CString host = _T("www.google.com");
DWORD ip = ping.ResolveName(host);
if (ip != INADDR_NONE) {
    CString ipStr = ping.GetIP(ip);
    DWORD rtt = ping.PingHost(ip, 3000);
    if (rtt != (DWORD)-1)
        AfxMessageBox(_T("Ping ") + ipStr + _T(" 成功,RTT = ") + IntToStr(rtt) + _T(" ms"));
    else
        AfxMessageBox(_T("Ping 失敗或逾時"));
} else {
    AfxMessageBox(_T("無法解析主機名"));
}

注意事項

  • IcmpCreateFile 需管理員權限(Vista+ UAC),普通用戶可能失敗。解決:Manifest 要求 requireAdministrator 或使用 IcmpSendEcho2Ex (非阻塞) 搭配原始 Socket 實現(複雜度高)。
  • ICMP 回應可能被防火牆/路由器阻擋,**Ping 失敗不代表主機離線**。
  • 大量並發 Ping 建議使用 IcmpSendEcho2 (非同步) + 完成埠/事件通知。
  • IPv6 需用 Icmp6CreateFile/Icmp6SendEcho,結構體不同。

總結

封裝 ICMP.dll 實現 Ping 是 Windows 網路程式設計的經典課題。本文提供的 CPing 類涵蓋解析、發送、接收、計時完整流程,可直接整合至專案。現代開發若需跨平台,建議改用 boost::asio (ICMP) 或系統 ping 命令解析輸出。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved