ping程序-c语言实现

来源:互联网 发布:什么是半结构化数据 编辑:程序博客网 时间:2024/06/09 17:52

创建时间: 2017-08-17
最后修改时间: 2017-08-17

因个人水平有限,文章中存在不足,错误之处,还望指正

实验环境
Linux 2.6.32
gcc version 4.4.6 20120305 (Red Hat 4.4.6-4) (GCC)

引言

本ping程序并没有提供网络上公开可得的ping程序那样强大的功能(支持众多不同的选项),而是仅实现最基本的功能来了解我们所关注的问题:网络编程的概念和技巧。

ping程序简介

ping程序可以用来测试网络上的节点是否可达或网络连接速度。该程序发送一个ICMP回显请求给目的端,并等待其返回ICMP回显应答。

一般来说,如果不能ping到某主机,那么就不能Telnet到该主机。但这并不是绝对的,随着Internet安全意识的增强,出现了提供访问控制的路由器和防火墙,一台主机的可达性可能不只取决于IP层是否可达,还取决于使用何种协议以及端口。ping程序运行结果显示某台主机不可达,但我们却可以用Telnet远程登录该主机。

IP和ICMP协议简介

IP(Internet Protocol),网际协议,是TCP/IP协议族中最为核心的协议。所有TCP、UDP、ICMP及IGMP数据都以IP数据报格式传输。

它提供不可靠、无连接的数据报传送服务。

不可靠的意思是它不能保证IP数据报能成功地到达目的地。如果发生某种错误时,如某个路由器的缓冲区已满,IP有一个简单的错误处理方法:丢弃该数据报,然后发送ICMP消息给源端。任何要求的可靠性必须由上层服务来提供,如TCP。

无连接的意思是IP并不维护任何关于后续数据报信息。每个数据报的处理是相互独立的。这说明,IP数据报可以不按发送顺序接收。如果源端向目的端发送两个连续的数据报A、B,每个数据报都是独立进行路由选择,可能选择不同的线路,因此B可能在A之前先到达。

IP首部

0       4       8               16    19                        32 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Version|  IHL  |Type Of Service|    Total Length(in bytes)     |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|         Identification        |Flags|    Fragment Offset      |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Time To Live  |    Protocol   |       Header Checksum         |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                       Source Ipv4 Address                     |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                     Destination Ipv4 Address                  |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+~                          Options (if any)                     ~+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+~                             Data                              ~+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                            IPv4首部

ICMP(Internet Control Message Protocol),Internet控制消息协议,是TCP/IP协议族的一个子协议,属于网络层协议,用于传递差错报文以及其他需要注意的信息。

ICMP报文在IP数据报内部被传输的。如下:

|-------------------------   IP数据报    ------------------------|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|     IP首部      |                ICMP报文                      |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

ICMP报文有许多种类型,这里只列出我们所关注的报文格式:

0               8               16                              32 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|     Type      |      Code     |          Checksum             |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|           Identifier          |        Sequence Number        |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+~                          Option Data                          ~+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                   ICMP回显请求和回显应答报文首部Type                ICMP报文类型Code                代码Checksum            检验和Identifier          标识符,由源端设定,应答报文的Identifier与请求报文的保持一致Sequence Number     序列号,由源端设定,一般是递增的,应答报文的Sequence Number与请求报文的保持一致Option Data         数据,应答报文的Option Data与请求报文的保持一致

与IP和ICMP首部相关的结构

在/usr/include/目录下可找到这些头文件。

ip结构体在头文件 “netinet/ip.h”中定义如下:

struct ip{#if __BYTE_ORDER == __LITTLE_ENDIAN    unsigned int ip_hl:4;               /* header length */    unsigned int ip_v:4;                /* version */#endif#if __BYTE_ORDER == __BIG_ENDIAN    unsigned int ip_v:4;                /* version */    unsigned int ip_hl:4;               /* header length */#endif    u_int8_t ip_tos;                    /* type of service */    u_short ip_len;                     /* total length */    u_short ip_id;                      /* identification */    u_short ip_off;                     /* fragment offset field */#define IP_RF 0x8000                    /* reserved fragment flag */#define IP_DF 0x4000                    /* dont fragment flag */#define IP_MF 0x2000                    /* more fragments flag */#define IP_OFFMASK 0x1fff               /* mask for fragmenting bits */    u_int8_t ip_ttl;                    /* time to live */    u_int8_t ip_p;                      /* protocol */    u_short ip_sum;                     /* checksum */    struct in_addr ip_src, ip_dst;      /* source and dest address */};

icmp结构体在头文件”netinet/ip_icmp.h”中定义如下:

struct icmp{  u_int8_t  icmp_type;  /* type of message, see below */  u_int8_t  icmp_code;  /* type sub code */  u_int16_t icmp_cksum; /* ones complement checksum of struct */  union  {    u_char ih_pptr;             /* ICMP_PARAMPROB */    struct in_addr ih_gwaddr;   /* gateway address */    struct ih_idseq             /* echo datagram */    {      u_int16_t icd_id;      u_int16_t icd_seq;    } ih_idseq;    u_int32_t ih_void;    /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */    struct ih_pmtu    {      u_int16_t ipm_void;      u_int16_t ipm_nextmtu;    } ih_pmtu;    struct ih_rtradv    {      u_int8_t irt_num_addrs;      u_int8_t irt_wpa;      u_int16_t irt_lifetime;    } ih_rtradv;  } icmp_hun;#define icmp_pptr       icmp_hun.ih_pptr#define icmp_gwaddr     icmp_hun.ih_gwaddr#define icmp_id         icmp_hun.ih_idseq.icd_id#define icmp_seq        icmp_hun.ih_idseq.icd_seq#define icmp_void       icmp_hun.ih_void#define icmp_pmvoid     icmp_hun.ih_pmtu.ipm_void#define icmp_nextmtu    icmp_hun.ih_pmtu.ipm_nextmtu#define icmp_num_addrs  icmp_hun.ih_rtradv.irt_num_addrs#define icmp_wpa        icmp_hun.ih_rtradv.irt_wpa#define icmp_lifetime   icmp_hun.ih_rtradv.irt_lifetime  union  {    struct    {      u_int32_t its_otime;      u_int32_t its_rtime;      u_int32_t its_ttime;    } id_ts;    struct    {      struct ip idi_ip;      /* options and then 64 bits of data */    } id_ip;    struct icmp_ra_addr id_radv;    u_int32_t   id_mask;    u_int8_t    id_data[1];  } icmp_dun;#define icmp_otime      icmp_dun.id_ts.its_otime#define icmp_rtime      icmp_dun.id_ts.its_rtime#define icmp_ttime      icmp_dun.id_ts.its_ttime#define icmp_ip         icmp_dun.id_ip.idi_ip#define icmp_radv       icmp_dun.id_radv#define icmp_mask       icmp_dun.id_mask#define icmp_data       icmp_dun.id_data};

c语言实现

该实现主要划分成三大部分:主体结构、ICMP请求发送和ICMP应答处理。在主体结构部分中主要是处理用户参数并创建一个原始套接字;在ICMP请求发送部分中主要是构建一个ICMP回显请求报文并发送到目的端;在ICMP应答处理部分中主要是解析报文,计算并打印ICMP应答信息。

宏定义、全局变量定义以及函数声明:

#define EXIT_ERR        1           /* 异常退出状态码 */#define BUFSIZE         1500        /* 缓冲区大小 */    #define SEQSIZE         100         /* 已发送的icmp回显请求报文的seq数组大小 */#define ECHO_RATE       1           /* 报文发送速率,单位(s) */#define ICMP_HLEN       8           /* icmp报文头部长度 *//********** 全局变量 **********/int sockfd;                         /* 套接字变量 */pid_t pid;                          /* 进程ID */int nsend, nrecv;                   /* 发送报文数和接收报文数 */char sendbuf[BUFSIZE];int seqarr[SEQSIZE];                /* 已发送的icmp回显请求报文的seq数组 */struct sockaddr_in destaddr;        /* 目标主机信息 */struct hostent *desthostent;        int datalen = 56;/********** 函数声明 **********/int seq_init(int seqarr[], int len);int seq_add(int seq, int seqarr[], int len);int seq_del(int seq, int seqarr[], int len);uint16_t in_cksum(uint16_t *addr, int len);void icmp_echo(void);void proc_echoreply(void);void statistics(void);

主体结构:

1、处理用户输入的参数,并将其转换成对应的结构;
2、创建一个原始套接字,这需要超级用户特权。创建好后放弃超级用户特权;
3、修改套接字缓冲区大小;

int main(int argc, char *argv[]){        int size;        struct sigaction act;        in_addr_t saddr;        if (argc < 2) {                printf("usage: ping <host> \n");                exit(EXIT_ERR);        }        /* 初始化目标地址信息 */        if ((saddr = inet_addr(argv[1])) == INADDR_NONE) {                if ((desthostent = gethostbyname(argv[1])) == NULL) {                        printf("unknow host: %s \n", argv[1]);                        exit(EXIT_ERR);                } else {                        memmove(&saddr, desthostent->h_addr_list[0], desthostent->h_length);                }        }        bzero(&destaddr, sizeof(destaddr));        destaddr.sin_family = AF_INET;        destaddr.sin_addr.s_addr = saddr;;        /* 创建原始套接字后,放弃超级用户特权(创建原始套接字需要超级用户特权) */        if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {                perror("socket error");                exit(EXIT_ERR);        }        setuid(getuid());        /* 修改套接字的接收缓冲区大小 */        size = 60 * 1024;        setsockopt(sockfd, SOL_SOCKET,                 SO_RCVBUF, &size, sizeof(size));        printf("PING %s (%s) %d bytes of data. \n",                 desthostent==NULL? inet_ntoa(destaddr.sin_addr):desthostent->h_name,                 inet_ntoa(destaddr.sin_addr),                 datalen);/*        /* 设定闹钟 */        alarm(ECHO_RATE);        /* 处理回显应答 */        proc_echoreply(); */         return 0;}

ICMP回显请求发送:

1、设置一个闹钟,每1s调用一次icmp_echo函数发送一个回显请求报文;

int main(int argc, char *argv[]){        ...        /* 建立SIGALRM信号处理函数 */        sigemptyset(&act.sa_mask);        act.sa_handler = sig_alrm;        act.sa_flags = 0;        sigaction(SIGALRM, &act, NULL);        ...        alarm(ECHO_RATE);}/* *  SIGALRM信号处理函数 */void sig_alrm(int signo){        icmp_echo();        alarm(ECHO_RATE);        return ;}/* *  icmp报文发送函数 */void icmp_echo(void){        struct icmp *icmp;        size_t len;        /* 如果已发送报文序号数组已满,则向自身发送SIGINT信号 */        if (seq_add(nsend, seqarr, SEQSIZE) < 0) {                raise(SIGINT);                pause();                return ;        }        icmp = (struct icmp *) sendbuf;        icmp->icmp_type = ICMP_ECHO;        icmp->icmp_code = 0;        icmp->icmp_id = pid;        icmp->icmp_seq = nsend++;        memset(icmp->icmp_data, 0xa5, datalen);        gettimeofday((struct timeval *) icmp->icmp_data, NULL);        len = ICMP_HLEN + datalen;        icmp->icmp_cksum = 0;        icmp->icmp_cksum = in_cksum((u_short *) icmp, len);        if (sendto(sockfd, sendbuf, len, 0, (struct sockaddr *) &destaddr, sizeof(destaddr)) < 0) {                perror("sendto error");                exit(EXIT_ERR);        }}

ICMP报文处理:

1、读取套接字,并过滤掉不要的数据;
2、计算并打印数据;

/* *  处理接收到的icmp响应报文函数 */void proc_echoreply(void){        char recvbuf[BUFSIZE];        ssize_t n;        double rtt;        struct ip *ip;        struct icmp *icmp;        int iphlen, icmplen;        struct timeval *tvsend, *tvrecv;        struct timeval tv;        for (;;) {                if ((n = recv(sockfd, recvbuf, BUFSIZE, 0)) < 0) {                        if (errno == EINTR)                                continue;                        else {                                perror("recv error");                                exit(EXIT_ERR);                        }                }                /* 获取数据接收到的时间 */                gettimeofday(&tv, NULL);                tvrecv = &tv;                /* 解析报文 */                ip = (struct ip *) recvbuf;                iphlen = ip->ip_hl << 2;        /* 获取ip报文头部长度 */                if (ip->ip_p != IPPROTO_ICMP)                        continue;                icmp = (struct icmp *) (recvbuf + iphlen);                if ((icmplen = n - iphlen) < ICMP_HLEN)                        continue;                if (icmp->icmp_type != ICMP_ECHOREPLY)                        continue;                if (icmp->icmp_id != pid)                        continue;                if (icmplen < 16)                         continue;                /* 若报文的序号不存在已发送数组中,则不计数 */                if (seq_del(icmp->icmp_seq, seqarr, SEQSIZE) != -1) {                        nrecv++;                }                /* 计算rtt */                tvsend = (struct timeval *) icmp->icmp_data;                if ((tvrecv->tv_usec -= tvsend->tv_usec ) < 0) {                        --tvrecv->tv_sec;                        tvrecv->tv_usec += 1000000;                }                tvrecv->tv_sec -= tvsend->tv_sec;                rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;                printf("%d bytes from %s: icmp_seq=%d, ttl-%d, rtt=%.3f ms \n",                icmplen, inet_ntoa(ip->ip_src), icmp->icmp_seq, ip->ip_ttl, rtt);        }}

统计ICMP报文收发结果:

1、设置SIGINT信号处理函数,当收到SIGINT信号时调用statistic函数统计结果,并结束程序;

int main(int argc, char *argv[]){        ...        /* 建立SIGINT信号处理函数 */        sigemptyset(&act.sa_mask);        act.sa_flags = 0;        act.sa_handler = sig_int;        sigaction(SIGINT, &act, NULL);        ...}/* *  SIGINT信号处理函数 */void sig_int(int signo){        statistics();        exit(0);}/* *  统计报文发送和接收的结果 */void statistics(void){        int nloss;        nloss = nsend - nrecv;        printf("\n");        printf("--- %s ping statistics --- \n",                 inet_ntoa(destaddr.sin_addr));        printf("%d packets transmitted, %d received, %.1f%% packet loss \n",                nsend, nrecv,                 (double) nloss / nsend * 100);}

完整源码:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <strings.h>#include <unistd.h>#include <errno.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/ip.h>#include <netinet/ip_icmp.h>#include <netdb.h>#include <sys/signal.h>#define EXIT_ERR        1           /* 异常退出状态吗 */#define BUFSIZE         1500        /* 缓冲区大小 */    #define SEQSIZE         100         /* 已发送的icmp回显请求报文的seq数组大小 */#define ECHO_RATE       1           /* 报文发送速率,单位(s) */#define ICMP_HLEN       8           /* icmp报文头部长度 *//********** 全局变量 **********/int sockfd;                         /* 套接字变量 */pid_t pid;                          /* 进程ID */int nsend, nrecv;                   /* 发送报文数和接收报文数 */char sendbuf[BUFSIZE];int seqarr[SEQSIZE];                /* 已发送的icmp回显请求报文的seq数组 */struct sockaddr_in destaddr;        /* 目标主机信息 */struct hostent *desthostent;        int datalen = 56;/********** 函数声明 **********/int seq_init(int seqarr[], int len);int seq_add(int seq, int seqarr[], int len);int seq_del(int seq, int seqarr[], int len);uint16_t in_cksum(uint16_t *addr, int len);void icmp_echo(void);void proc_echoreply(void);void statistics(void);/* *  初始化数组 */int seq_init(int seqarr[], int len){        int i;        for (i=0; i<len; i++)                seqarr[i] = -1;        return 0;}/* *  添加一个元素到数组中,若成功,则返回该下标,否则返回-1 */int seq_add(int seq, int seqarr[], int len){        int i;        for (i=0; i<len; i++)                if (seqarr[i] == -1) {                        seqarr[i] = seq;                        break;                }        if (i >= len)                return -1;        return i;}/* *  删除数组中对应的元素,若成功,则返回该下标,否则返回-1 */int seq_del(int seq, int seqarr[], int len){        int i;        for (i=0; i<len; i++) {                if (seqarr[i] == seq) {                        seqarr[i] = -1;                        break;                }        }        if (i >= len)                return -1;        return i;}/* *  计算校验和 */uint16_t in_cksum(uint16_t *addr, int len){        int nleft = len;        uint32_t sum = 0;        uint16_t *w = addr;        uint16_t 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;}/* *  icmp报文发送函数 */void icmp_echo(void){        struct icmp *icmp;        size_t len;        /* 如果已发送报文序号数组已满,则向自身发送SIGINT信号 */        if (seq_add(nsend, seqarr, SEQSIZE) < 0) {                raise(SIGINT);                pause();                return ;        }        icmp = (struct icmp *) sendbuf;        icmp->icmp_type = ICMP_ECHO;        icmp->icmp_code = 0;        icmp->icmp_id = pid;        icmp->icmp_seq = nsend++;        memset(icmp->icmp_data, 0xa5, datalen);        gettimeofday((struct timeval *) icmp->icmp_data, NULL);        len = ICMP_HLEN + datalen;        icmp->icmp_cksum = 0;        icmp->icmp_cksum = in_cksum((u_short *) icmp, len);        if (sendto(sockfd, sendbuf, len, 0, (struct sockaddr *) &destaddr, sizeof(destaddr)) < 0) {                perror("sendto error");                exit(EXIT_ERR);        }}/* *  处理接收到的icmp响应报文函数 */void proc_echoreply(void){        char recvbuf[BUFSIZE];        ssize_t n;        double rtt;        struct ip *ip;        struct icmp *icmp;        int iphlen, icmplen;        struct timeval *tvsend, *tvrecv;        struct timeval tv;        for (;;) {                if ((n = recv(sockfd, recvbuf, BUFSIZE, 0)) < 0) {                        if (errno == EINTR)                                continue;                        else {                                perror("recv error");                                exit(EXIT_ERR);                        }                }                /* 获取数据接收到的时间 */                gettimeofday(&tv, NULL);                tvrecv = &tv;                /* 解析报文 */                ip = (struct ip *) recvbuf;                iphlen = ip->ip_hl << 2;        /* 获取ip报文头部长度 */                if (ip->ip_p != IPPROTO_ICMP)                        continue;                icmp = (struct icmp *) (recvbuf + iphlen);                if ((icmplen = n - iphlen) < 8)                        continue;                if (icmp->icmp_type != ICMP_ECHOREPLY)                        continue;                if (icmp->icmp_id != pid)                        continue;                if (icmplen < 16)                         continue;                /* 若报文的序号不存在已发送数组中,则不计数 */                if (seq_del(icmp->icmp_seq, seqarr, SEQSIZE) != -1) {                        nrecv++;                }                /* 计算rtt */                tvsend = (struct timeval *) icmp->icmp_data;                if ((tvrecv->tv_usec -= tvsend->tv_usec ) < 0) {                        --tvrecv->tv_sec;                        tvrecv->tv_usec += 1000000;                }                tvrecv->tv_sec -= tvsend->tv_sec;                rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;                printf("%d bytes from %s: icmp_seq=%d, ttl-%d, rtt=%.3f ms \n",                icmplen, inet_ntoa(ip->ip_src), icmp->icmp_seq, ip->ip_ttl, rtt);        }}/* *  统计报文发送和接收的结果 */void statistics(void){        int nloss;        nloss = nsend - nrecv;        printf("\n");        printf("--- %s ping statistics --- \n",                 inet_ntoa(destaddr.sin_addr));        printf("%d packets transmitted, %d received, %.1f%% packet loss \n",                nsend,                 nrecv,                 (double) nloss / nsend * 100);}/* *  SIGALRM信号处理函数 */void sig_alrm(int signo){        icmp_echo();        alarm(ECHO_RATE);        return ;}/* *  SIGINT信号处理函数 */void sig_int(int signo){        statistics();        exit(0);}int main(int argc, char *argv[]){        int size;        struct sigaction act;        in_addr_t saddr;        if (argc < 2) {                printf("usage: ping <ip> \n");                exit(EXIT_ERR);        }        /* 初始化目标地址信息 */        if ((saddr = inet_addr(argv[1])) == INADDR_NONE) {                if ((desthostent = gethostbyname(argv[1])) == NULL) {                        printf("unknow host: %s \n", argv[1]);                        exit(EXIT_ERR);                } else {                        memmove(&saddr, desthostent->h_addr_list[0], desthostent->h_length);                }        }        bzero(&destaddr, sizeof(destaddr));        destaddr.sin_family = AF_INET;        destaddr.sin_addr.s_addr = saddr;;        /* 创建原始套接字后,放弃超级用户特权(创建原始套接字需要超级用户特权) */        if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {                perror("socket error");                exit(EXIT_ERR);        }        setuid(getuid());        /* 修改套接字的接收缓冲区大小 */        size = 60 * 1024;        setsockopt(sockfd, SOL_SOCKET,                 SO_RCVBUF, &size, sizeof(size));        /* 建立SIGALRM,SIGINT信号处理函数 */        sigemptyset(&act.sa_mask);        act.sa_handler = sig_alrm;        act.sa_flags = 0;        sigaction(SIGALRM, &act, NULL);        act.sa_handler = sig_int;        sigaction(SIGINT, &act, NULL);        /* 初始化已发送报文序号数组 */        seq_init(seqarr, SEQSIZE);        printf("PING %s (%s) %d bytes of data. \n",                 desthostent==NULL? inet_ntoa(destaddr.sin_addr):desthostent->h_name,                 inet_ntoa(destaddr.sin_addr),                 datalen);        alarm(ECHO_RATE);        proc_echoreply();        return 0;}

运行结果

[root@localhost test]# ./a.out 192.168.15.4PING 192.168.15.4 (192.168.15.4) 56 bytes of data. 64 bytes from 192.168.15.4: icmp_seq=0, ttl-64, rtt=1.710 ms 64 bytes from 192.168.15.4: icmp_seq=1, ttl-64, rtt=0.425 ms 64 bytes from 192.168.15.4: icmp_seq=2, ttl-64, rtt=0.879 ms 64 bytes from 192.168.15.4: icmp_seq=3, ttl-64, rtt=0.703 ms ^C--- 192.168.15.4 ping statistics --- 4 packets transmitted, 4 received, 0.0% packet loss [root@localhost test]# arp -a           # ARP高速缓存? (192.168.15.4) at 00:0c:29:06:00:8c [ether] on eth0? (192.168.15.1) at 00:50:56:c0:00:08 [ether] on eth0[root@localhost test]# 

扩展知识

  • 第一个ICMP消息的rtt几乎总是比后面的时间要长
    在分析ping的输出时,发现第一条输出几乎总是比后面的输出的rtt时间要长,这是因为源端会先发送ARP请求来确定目的端的MAC地址(如果目的端MAC不存在于ARP高速缓存中),这个过程耗费了一定时间。之后发送到相同目的端时就不用再发送ARP请求了。

参考

《UNIX网络编程 卷1:套接字联网API》
《TCP/IP详解 卷1:协议》
Fireplusplus-用C语言实现Ping命令:http://blog.csdn.net/qq_33724710/article/details/51576444

原创粉丝点击