本文提供一個完整的 ICMP Ping 實現,包含校驗和計算、數據封裝、發送接收、統計顯示等核心功能,基於 Linux 系統調用開發。
#define PACKET_SIZE 4096
#define MAX_WAIT_TIME 5
#define MAX_NO_PACKETS 3
char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
int sockfd, datalen = 56;
int nsend = 0, nreceived = 0;
struct sockaddr_in dest_addr;
pid_t pid;
struct sockaddr_in from;
struct timeval tvrecv;
unsigned short cal_chksum(unsigned short* addr, int len) {
int nleft = len;
int sum = 0;
unsigned short* w = addr;
unsigned short answer = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(unsigned char*)(&answer) = *(unsigned char*)w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
int pack(int pack_no) {
struct icmp* icmpp = (struct icmp*)sendpacket;
icmpp->icmp_type = ICMP_ECHO;
icmpp->icmp_code = 0;
icmpp->icmp_cksum = 0;
icmpp->icmp_seq = pack_no;
icmpp->icmp_id = pid;
int packsize = 8 + datalen;
struct timeval* tval = (struct timeval*)icmpp->icmp_data;
gettimeofday(tval, NULL); // 記錄發送時間
icmpp->icmp_cksum = cal_chksum((unsigned short*)icmpp, packsize);
return packsize;
}
void send_packet() {
while (nsend < MAX_NO_PACKETS) {
nsend++;
int packetsize = pack(nsend);
if (sendto(sockfd, sendpacket, packetsize, 0,
(struct sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) {
perror("sendto error");
continue;
}
sleep(1);
}
}
void recv_packet() {
int n, fromlen;
// 接收循環解析 ICMP 回應...
}
void statistics(int signo) {
printf("\n-----------------PING statistics----------------\n");
printf("%d packet transmitted,%d received,%%%d lost\n",
nsend, nreceived, (nsend-nreceived)/nsend*100);
close(sockfd);
exit(1);
}
完整代碼可直接編譯運行,實現標準 Ping 功能。