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

C語言實現 Ping 功能:原始 ICMP 封包構造與收發

     編輯:關於C語言

核心原理

Ping 基於 ICMP 協議(RFC 792):發送 Echo Request (Type 8),接收 Echo Reply (Type 0)。需手動構造 IP 頭 + ICMP 頭 + 資料,計算校驗和,使用原始套接字(SOCK_RAW, IPPROTO_ICMP)發送接收。

關鍵資料結構

// IP 頭 (位元域對應 RFC 791)
typedef struct {
    unsigned char h_len:4, version:4;
    unsigned char tos;
    unsigned short total_len;
    unsigned short ident;
    unsigned short frag_and_flags;
    unsigned char ttl;
    unsigned char proto;
    unsigned short checksum;
    unsigned int sourceIP, destIP;
} XIpHeader;

// ICMP 頭
typedef struct {
    unsigned char i_type;      // 8=Request, 0=Reply
    unsigned char i_code;      // 0
    unsigned short i_cksum;    // 校驗和
    unsigned short i_id;       // 進程 ID
    unsigned short i_seq;      // 序號
    unsigned long i_timestamp; // 發送時間戳
} XIcmpHeader;

校驗和算法(網際標準)

unsigned short in_cksum(unsigned short *addr, int len) {
    int sum = 0, nleft = len;
    while (nleft > 1) { sum += *addr++; nleft -= 2; }
    if (nleft == 1) { unsigned short t=0; *(unsigned char*)&t = *(unsigned char*)addr; sum += t; }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    return ~sum;
}

發送流程

  1. 填充 ICMP 資料(可選,如時間戳、模式字元)。
  2. 填充 ICMP 頭:Type=8, Code=0, ID=getpid(), Seq=遞增, Timestamp=當前時間,計算校驗和覆蓋頭+資料。
  3. sendto(raw_sock, buf, icmp_len, 0, (sockaddr*)&dest, sizeof(dest))。

接收與解析

  1. recvfrom(raw_sock, buf, sizeof(buf), 0, ...)。
  2. 解析 IP 頭:取 h_len 計算頭長度(h_len * 4),定位 ICMP 頭。
  3. 驗證 ICMP Type=0, ID 匹配, Seq 匹配。
  4. 計算 RTT = 當前時間 - ICMP 時間戳。
  5. 顯示:來源 IP、TTL、序號、RTT。

跨平台處理

#ifdef WIN32
#include <winsock.h>
#pragma comment(lib, "Wsock32.lib")
#define GET_PID GetCurrentProcessId()
#define GET_TICK GetTickCount()
#else
#include <sys/socket.h>, <netinet/ip_icmp.h>, <unistd.h>
#define GET_PID getpid()
#define GET_TICK (time(NULL)*1000)  // 秒級,精度較低
#endif

完整程式碼結構

原文提供單一 xping.cpp(約 300 行),包含上述所有函數與 main() 迴圈發送 3 次 Echo Request。編譯:

# Windows (VC/GCC)
cl xping.cpp wsock32.lib
# 或 g++ xping.cpp -lwsock32

# Linux
gcc xping.cpp -o xping  # 需 root 權限建立 raw socket

關鍵注意事項

  • Root/Administrator 權限:原始套接字需特權。
  • 校驗和必須正確:否則核心會丟棄封包。
  • IP 頭通常由核心自動填充:IP_HDRINCL 選項控制是否用戶態提供 IP 頭。本範例僅構造 ICMP,依賴核心補 IP 頭。
  • 防火牆可能封鎖 ICMP:測試時確保允許 Echo Request/Reply。

學習建議

  1. 先閱讀 RFC 792 (ICMP) 與 RFC 791 (IP),理解封包格式。
  2. 使用 tcpdump -i any icmp 或 Wireshark 抓包對照自造封包。
  3. 嘗試擴展:支援 IPv6 (ICMPv6)、添加 -c 次數、-s 封包大小、-t TTL 參數。

手寫 Ping 是深入理解網路協議棧、原始套接字與位元級封包操作的極佳實戰專案。

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