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;
}
sendto(raw_sock, buf, icmp_len, 0, (sockaddr*)&dest, sizeof(dest))。recvfrom(raw_sock, buf, sizeof(buf), 0, ...)。h_len 計算頭長度(h_len * 4),定位 ICMP 頭。#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
IP_HDRINCL 選項控制是否用戶態提供 IP 頭。本範例僅構造 ICMP,依賴核心補 IP 頭。tcpdump -i any icmp 或 Wireshark 抓包對照自造封包。-c 次數、-s 封包大小、-t TTL 參數。手寫 Ping 是深入理解網路協議棧、原始套接字與位元級封包操作的極佳實戰專案。