ping程序

发布时间:2017-7-1 11:39:27编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"ping程序 ",主要涉及到ping程序 方面的内容,对于ping程序 感兴趣的同学可以参考一下。

ping程序目的是为了测试另一台主机是否可达。该程序发送一份ICMP回显请求(icmp消息类型0x8,ICMP_ECHO)报文给主机,并等待返回ICMP回显应答(消息类型0x0,ICMP_ECHOREPLY)。

ping程序还能测出到这台主机的往返时间,以表明该主机离我们“多远”。

用tcpdump抓取一包ping 8.8.8.8的数据:

~$sudo tcpdump -i eth0 icmp -n -v -s0tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes17:41:11.487640 IP (tos 0x0, ttl 64, id 48592, offset 0, flags [DF], proto ICMP (1), length 84)    192.168.1.10 > 8.8.8.8: ICMP echo request, id 6914, seq 1, length 6417:41:11.980277 IP (tos 0x0, ttl 40, id 54159, offset 0, flags [none], proto ICMP (1), length 84)    8.8.8.8 > 192.168.1.10: ICMP echo reply, id 6914, seq 1, length 64

输出的第一行包括目的主机的 I P地址,尽管指定的是它的名字( www.baidu.com)。这说明名字已经经过解析器被转换成 I P地址了。我们将在第 1 4章介绍解析器和 D N S。现在,我们发现,如果敲入 p i n g命令,几秒钟过后会在第 1行打印出 I P地址, D N S就是利用这段时间来确定主机名所对应的 I P地址。

通常,第 1个往返时间值要比其他的大。这是由于目的端的硬件地址不在 A R P高速缓存中的缘故。在发送第一个回显请求之前要发送一个 A R P请求并接收A R P应答,这需要花费几毫秒的时间。

ICMP回显请求和回显应答报文如图:

unix系统在实现ping程序时把ICMP报文中的标识符字段置为发送进程的ID号。这样即使在同一台主机上同时运行多个ping程序,ping程序也可以识别返回的信息。

序列号从0开始,每发送一个新的回显请求就加1。ping程序打印出返回的每个分组的序列号,允许我们查看是否有分组丢失、失序或重复。IP是一种最好的数据报传递服务,因此这三个条件都有可能发生。

ping程序通过在ICMP报文数据中存放发送请求的时间值来计算往返时间。当应答返回时,用当前时间减去存放在ICMP报文中的时间值,即往返时间。

ping程序初版

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <sys/types.h>#include <stdbool.h>#include <sys/time.h>#include <sys/wait.h>#include <signal.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/ip.h>#include <netinet/ip_icmp.h>#include <netdb.h>#include <arpa/inet.h>#include <errno.h>#define PACKET_SIZE        4096#define MAX_WAIT_TIME    1#define ICMP_HEADSIZE    8struct timeval tvrecv;struct sockaddr_in dest_addr, recv_addr;int sockfd;pid_t pid;char packetsend[PACKET_SIZE];char packetrecv[PACKET_SIZE];int seq_no = 0;int err_ret = 0;unsigned short cal_chksum(unsigned short *addr, int len);int pack(int pkt_no, char *packet);int unpack(int cur_seq, char *buf, int len);int send_packet(int pkt_no, char *packet);int recv_packet(int pkt_no, char *packet);void tv_sub(struct timeval *out, struct timeval *in);void close_socket();void print_hex(const char *buf, int len);void print_hex(const char *buf, int len){    int i = 0;    printf("------------------------------\n");    for(i = 0; i < len;){        printf("0x%.2X ", (unsigned char)buf[i]);        i++;        if(i%4 == 0){            printf("\n");        }    }    printf("------------------------------\n");}void alarm_task(int signo){    printf("------------------ping-------%d-----------------------\n", seq_no);    if(send_packet(seq_no, packetsend)<0){        printf("[NetStatus] error : send_packet\n");        err_ret = -1;    } else {        if(recv_packet(seq_no, packetrecv)<0){            printf("[NetStatus] error : recv_packet\n");            err_ret = -1;        } else {            seq_no++;            alarm(MAX_WAIT_TIME);        }    }}int main(int argc, char **argv){    struct hostent *host = NULL;    struct protoent *protocol = NULL;#ifdef _USE_DNS    char hostname[32];    sprintf(hostname, "%s", "www.baidu.com");    bzero(&dest_addr, sizeof(dest_addr));    dest_addr.sin_family = AF_INET;    if(host=gethostbyname(hostname) == NULL){        printf("[NetStatus] error : can't get serverhost info!\n");            return -1;    }        bcopy((char *)host->h_addr, (char*)&dest_addr.sin_addr, host->h_length);#else    dest_addr.sin_addr.s_addr = inet_addr("8.8.8.8");#endif    if((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0){        printf("[NetStatus] error : socket\n");        return -1;    }    pid = getpid();    printf("pid=[%d]\n", pid);    signal(SIGALRM, alarm_task);    raise(SIGALRM);    while(1){        sleep(1);        if(err_ret == -1){            close_socket();            return -1;        }        }    close_socket();    return 0;}int send_packet(int pkt_no, char * packet){    int packetsize;    packetsize = pack(pkt_no, packet);    if(sendto(sockfd, packet, packetsize, 0, \        (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0){        printf("[NetStatus] error : sendto error\n");        return -1;    }    printf("send packet [%d]:\n", packetsize);    print_hex(packet, packetsize);    return 1;}int recv_packet(int pkt_no, char *packet){    int n, fromlen;    fd_set rfds;    struct timeval tv;    int ret = 0;        FD_ZERO(&rfds);    FD_SET(sockfd, &rfds);    fromlen = sizeof(recv_addr);    tv.tv_sec = 1;    tv.tv_usec = 0;    while(1){        ret = select(sockfd+1, &rfds, NULL, NULL, &tv);        if(ret > 0){            if(FD_ISSET(sockfd, &rfds)){                if((n = recvfrom(sockfd, packet, PACKET_SIZE, 0, \                    (struct sockaddr *)(&recv_addr), (socklen_t *)&fromlen)) < 0){                    if(errno == EINTR)                        continue;                    perror("recvform error");                    return -1;                }            }            gettimeofday(&tvrecv, NULL);            if(unpack(pkt_no, packet, n) < 0){                //continue;                return -1;            }            return 1;        } else if(ret == 0){            return 0;        } else {            return -1;        }    }}int pack(int pkt_no, char *packet){    int packsize;    struct icmp *icmp;    struct timeval *tv;    icmp = (struct icmp*)packet;    icmp->icmp_type = ICMP_ECHO;    icmp->icmp_code = 0;    icmp->icmp_cksum = 0;    icmp->icmp_seq = pkt_no;    icmp->icmp_id = pid;    packsize = ICMP_HEADSIZE + sizeof(struct timeval);    tv = (struct timeval*)icmp->icmp_data;    gettimeofday(tv, NULL);    icmp->icmp_cksum = cal_chksum((unsigned short*)icmp, packsize);    return packsize;}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 unpack(int cur_seq, char *buf, int len){    int iphdrlen;    struct ip *ip;    struct icmp *icmp;    struct timeval *ptv;    double rtt = 0.0;    ip = (struct ip *)buf;    iphdrlen=ip->ip_hl<<2;    icmp = (struct icmp*)(buf + iphdrlen);    len -= iphdrlen;    if(len < 8){        return -1;    }    ptv = (struct timeval *)icmp->icmp_data;    if((icmp->icmp_type == ICMP_ECHOREPLY) \        && (icmp->icmp_id == pid) \        && (icmp->icmp_seq == cur_seq)) {        printf("receive packet %d:\n", len);        print_hex((char *)icmp, len);        tv_sub(&tvrecv, ptv);        rtt = tvrecv.tv_sec * 1000 + tvrecv.tv_usec/1000.0;        printf("count %.3f ms\n", rtt);        return 0;    } else {        return -1;    }}void tv_sub(struct timeval *out, struct timeval *in){    if((out->tv_usec -= in->tv_usec) < 0){        --out->tv_sec;        out->tv_usec += 1000000;    }    out->tv_sec -= in->tv_sec;}void close_socket(){    close(sockfd);    sockfd = 0;}

运行:

~$sudo ./a.out pid=[8719]------------------ping-------0-----------------------send packet [16]:------------------------------0x08 0x00 0x97 0x3E 0x0F 0x22 0x00 0x00 0xE4 0xDE 0x4F 0x59 0x18 0x67 0x05 0x00 ------------------------------------------------ping-------1-----------------------send packet [16]:------------------------------0x08 0x00 0xEC 0x38 0x0F 0x22 0x01 0x00 0xE6 0xDE 0x4F 0x59 0xC0 0x6C 0x05 0x00 ------------------------------receive packet 16:------------------------------0x00 0x00 0xF4 0x38 0x0F 0x22 0x01 0x00 0xE6 0xDE 0x4F 0x59 0xC0 0x6C 0x05 0x00 ------------------------------count 285.443 ms------------------ping-------2-----------------------......

参考:

1. linux环境下C编程指南

2. TCP/IP详解卷一

3. linux下判断网络是否连接


上一篇:vue.js devtools安装
下一篇:Java抽象类与接口的区别

相关文章

关键词: ping程序

相关评论

本站评论功能暂时取消,后续此功能例行通知。

一、不得利用本站危害国家安全、泄露国家秘密,不得侵犯国家社会集体的和公民的合法权益,不得利用本站制作、复制和传播不法有害信息!

二、互相尊重,对自己的言论和行为负责。

好贷网好贷款