/************************************************************************* > File Name: Ping.c > Author: ICKelin > Mail: [email protected] > Created Time: 2014年12月17日 星期三 04时53分50秒 ************************************************************************/ #include <stdio.h> #include <netdb.h> #include <errno.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/time.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <cmath> #include <vector> #include <numeric> #include <algorithm> const size_t MXLEN = 1500; const size_t DATALEN = 56; const size_t REQLEN = ICMP_MINLEN + DATALEN; const size_t NAMELEN = 50; char dstIP[NAMELEN], dstName[NAMELEN]; //目标IP,目标主机名 char sendBuf[REQLEN], recvBuf[MXLEN]; //发送缓存,接收缓冲 sockaddr_in dstAddr; //目标地址 uint32_t nrSend, nrRecv; //发送包数量,接收包数量 std::vector<double> rtts; //记录每次的rtt timeval startTime, endTime; //开始时间,结束时间 //计算校验和 uint16_t calChecksum(uint16_t *buf, size_t len) { uint32_t sum = 0; while (len > 1) sum += *buf++, len -= 2; if (len == 1) sum += (*(uint8_t *)buf) << 4 ; sum = (sum >> 16) + (sum & 0xFFFF); sum += sum >> 16; return (uint16_t)~sum; } //输出统计信息 void calStat(int signo) { //计时结束,计算花费的时间 gettimeofday(&endTime, NULL); int elapse = 1000 * (endTime.tv_sec - startTime.tv_sec) + (endTime.tv_usec - startTime.tv_usec) / 1000; printf("\n--- %s ping statistics ---\n" "%u packets transmitted, %u received, %.lf%% packet loss, time %dms\n", dstName, nrSend, nrRecv, 1 - (double)nrRecv / nrSend, elapse); //统计rtt信息 std::sort(rtts.begin(), rtts.end()); double mn = *rtts.begin(), mx = *rtts.rbegin(); double avg = accumulate(rtts.begin(), rtts.end(), 0.0) / rtts.size(); double mdev = 0; for (std::vector<double>::iterator p = rtts.begin(); p != rtts.end(); p++) mdev += (*p - avg) * (*p - avg); mdev = pow(mdev / rtts.size(), 0.5); printf("rtt min/avg/max/mdev = %.3lf/%.3lf/%.3lf/%.3lf ms\n", mn, avg, mx, mdev); exit(0); } //发送ICMP包 void sendRequest(int sockfd) { //包计数 static uint32_t no = 1; //填充ICMP包内容 icmp *icmp = (struct icmp *)sendBuf; icmp->icmp_type = ICMP_ECHO; icmp->icmp_code = 0; icmp->icmp_cksum = 0; icmp->icmp_seq = no++; icmp->icmp_id = getpid(); timeval *tv = (timeval *)icmp->icmp_data; gettimeofday(tv, NULL); icmp->icmp_cksum = calChecksum((uint16_t *)icmp, REQLEN); //发送 int n = sendto(sockfd, sendBuf, REQLEN, 0, (sockaddr *)&dstAddr, sizeof(dstAddr)); if (n <= 0) { if (errno == EWOULDBLOCK) printf("Send timed out.\n"); else perror("sendto error"); } else nrSend++; } //接收ICMP包 void recvReply(int sockfd) { socklen_t addrLen = sizeof(dstAddr); while (true) //接收ICMP应答,无限循环直到成功接收或超时出错。 { int n = recvfrom(sockfd, recvBuf, sizeof(recvBuf), 0, (sockaddr *)&dstAddr, &addrLen); if (n <= 0) { if (errno == EWOULDBLOCK) printf("Receive timed out.\n"); else perror("recvfrom error"); return; } ip *ip = (struct ip *)recvBuf; int iphdrLen = ip->ip_hl << 2; if ((n -= iphdrLen) < 8) { printf("ICMP packets too short.\n"); return; } icmp *icmp = (struct icmp *)(recvBuf + iphdrLen); if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == getpid()) //确实是之前发送包的应答 { nrRecv++; timeval *tvSend = (timeval *)icmp->icmp_data, tvRecv; gettimeofday(&tvRecv, NULL); double rtt = (tvRecv.tv_sec - tvSend->tv_sec) * 1000 + (tvRecv.tv_usec - tvSend->tv_usec) / 1000.0; //时间差 rtts.push_back(rtt); printf("%d byte from %s: icmp_req=%u, ttl=%d, time=%.1lf ms\n", n, dstIP, icmp->icmp_seq, ip->ip_ttl, rtt); return; } } } //无限ping void ping(int sockfd) { //扩大缓冲区 int size = 1024 * 50; setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); //设置超时值 timeval tv = {5, 0}; setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); //无限ping只能靠ctrl-c结束,设置该信号的响应 signal(SIGINT, calStat); nrSend = nrRecv = 0; while (1) //无限发送ICMP请求和接收应答 { sendRequest(sockfd); recvReply(sockfd); sleep(1); //1秒1次 } } int main(int argc, char **argv) { if (argc != 2) { printf("Usage: Ping IP/host.\n"); return 0; } gettimeofday(&startTime, NULL); //计时开始 int sockfd; if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { perror("Creating socket error"); exit(1); } memset(&dstAddr, 0, sizeof(dstAddr)); dstAddr.sin_family = AF_INET; if (inet_pton(AF_INET, argv[1], &dstAddr.sin_addr) <= 0) { //输入的是主机名 hostent *host; if ((host = gethostbyname(argv[1])) == NULL) { perror("Invalid parameter"); exit(1); } memcpy((char *)&dstAddr.sin_addr, host->h_addr, sizeof(dstAddr.sin_addr)); inet_ntop(AF_INET, (void *)&dstAddr.sin_addr, dstIP, sizeof(dstIP)); strcpy(dstName, host->h_name); } else { //输入的是IP地址 memcpy(dstIP, argv[1], sizeof(argv[1])); memcpy(dstName, argv[1], sizeof(argv[1])); } printf("PING %s (%s) %d(%d) bytes of data.\n", dstName, dstIP, DATALEN, REQLEN + 20); ping(sockfd); return 0; }
参考链接:http://noalgo.info/810.html
时间: 2024-10-24 20:01:40