ping程序的实现

本文将详细探讨ping程序的工作机制,从发送ICMP回显请求到接收并解析响应的过程,揭示网络连通性检测的基本思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ping 程序的实现

#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#define PACKET_SIZE 4096
const int MAX_TIME_WAIT = 10;
const int maxpackage = 8;
struct sockaddr_in addr;
struct sockaddr_in from;
struct hostent *host;
int sendnum = 0, recvnum = 0;
char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
int datalen = 56;
struct timeval recvtime;

void subtime(struct timeval *in, struct timeval *out) ;

double sumtime;
int socketfd;

unsigned short check_sum(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, int pid) {
    struct icmp *icmp;
    int packsize;
    icmp = (struct icmp *) sendpacket;
    icmp->icmp_type = ICMP_ECHO;
    icmp->icmp_code = 0;
    icmp->icmp_cksum = 0;
    icmp->icmp_seq = pack_no;                                               /**标识icmp报文的编号**/
    icmp->icmp_id = pid;                                                    /**标识icmp报文的发出者是进程pid**/
    packsize = datalen + 8;
    gettimeofday((struct timeval *) icmp->icmp_data, NULL);                    /**获取当前时间存入icmp->icmp_data中**/
    icmp->icmp_cksum = check_sum((unsigned short *) icmp, packsize);

    return packsize;
}

/**
    TYPE    CODE    Description
    0   0   Echo Reply——回显应答(Ping应答)
    3   0   Network Unreachable——网络不可达
    3   1   Host Unreachable——主机不可达
    3   2   Protocol Unreachable——协议不可达
    3   3   Port Unreachable——端口不可达
    3   4   Fragmentation needed but no frag. bit set——需要进行分片但设置不分片比特
    3   5   Source routing failed——源站选路失败
    3   6   Destination network unknown——目的网络未知
    3   7   Destination host unknown——目的主机未知
    3   8   Source host isolated (obsolete)——源主机被隔离(作废不用)
    3   9   Destination network administratively prohibited——目的网络被强制禁止
    3   10  Destination host administratively prohibited——目的主机被强制禁止
    3   11  Network unreachable for TOS——由于服务类型TOS,网络不可达
    3   12  Host unreachable for TOS——由于服务类型TOS,主机不可达
    3   13  Communication administratively prohibited by filtering——由于过滤,通信被强制禁止
    3   14  Host precedence violation——主机越权
    3   15  Precedence cutoff in effect——优先中止生效
    4   0   Source quench——源端被关闭(基本流控制)
    5   0   Redirect for network——对网络重定向
    5   1   Redirect for host——对主机重定向
    5   2   Redirect for TOS and network——对服务类型和网络重定向
    5   3   Redirect for TOS and host——对服务类型和主机重定向
    8   0   Echo request——回显请求(Ping请求)
    9   0   Router advertisement——路由器通告
    10  0   Route solicitation——路由器请求
    11  0   TTL equals 0 during transit——传输期间生存时间为0
    11  1   TTL equals 0 during reassembly——在数据报组装期间生存时间为0
    12  0   IP header bad (catchall error)——坏的IP首部(包括各种差错)
    12  1   Required options missing——缺少必需的选项
    13  0   Timestamp request (obsolete)——时间戳请求(作废不用)
    14      Timestamp reply (obsolete)——时间戳应答(作废不用)
    15  0   Information request (obsolete)——信息请求(作废不用)
    16  0   Information reply (obsolete)——信息应答(作废不用)
    17  0   Address mask request——地址掩码请求
    18  0   Address mask reply——地址掩码应答
     **/


void statistics(int signo) {
    printf("--- %s ping statistics ---\n", inet_ntoa(from.sin_addr));
    printf("%d packets transmitted, %d received, %.2f packet loss, time %.2fms\n", sendnum, recvnum, (sendnum - recvnum) * 1.0 / sendnum * 100, sumtime);
    close(socketfd);
    return;
}


void send_packet(int pid) {
    while (sendnum < maxpackage) {
        sendnum++;
        int packetsize = pack(sendnum, pid);
        if (sendto(socketfd, sendpacket, (size_t)packetsize, 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
            perror("sendto ERROR\n");
            return;
        }
    }
}

int unpack(char *buf, int len, int pid) {
    struct ip *ip;
    struct icmp *icmp;
    ip = (struct ip *) buf;//得到ip报文
    int iplen = ip->ip_hl << 2;
    icmp = (struct icmp *) (buf + iplen);
    len -= iplen;
    if (len < 8) {
        printf("ICMP packet's length is less than 8\n");
        return -1;
    }
    if ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid)) {
        gettimeofday((struct timeval *) icmp->icmp_data, &recvtime);
        double rtt = recvtime.tv_sec * 1000 + recvtime.tv_usec / 1000;
        sumtime += rtt;
        printf("%d bytes from %s: icmp_seq=%d ttl = %d time=%.3f ms\n", len, inet_ntoa(from.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt);
    } else return -1;

}

void recv_packet(int pid) {
    signal(SIGALRM, statistics);
    socklen_t fromlen;
    fromlen = sizeof(from);
    while (recvnum < maxpackage) {
        alarm(MAX_TIME_WAIT);
        int n;
        if ((n = recvfrom(socketfd, recvpacket, (size_t)sizeof(recvpacket), 0, (struct sockaddr *) &from, &fromlen)) < 0) {
            if (errno == EINTR) continue;
            perror("recvfrom error\n");
            continue;
        }
        gettimeofday(&recvtime, NULL);/**获得接收到包的时间,发送包的时间存在icmp报文中了**/
        if (unpack(recvpacket, n, pid) == -1) continue;
        recvnum++;
    }
}


void subtime(struct timeval *in, struct timeval *out) {
    if (out->tv_usec - in->tv_usec < 0) {
        out->tv_sec--;
        out->tv_usec = out->tv_usec - in->tv_usec + 1000000;
    }
    out->tv_sec = out->tv_sec - in->tv_sec;
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Invalid IP Address\n");
        return -1;
    }
    struct protoent *protoent1;
    if ((protoent1 = getprotobyname("icmp")) == NULL) {
        printf("protocol error\n");
        return -1;
    }
    socketfd = socket(AF_INET, SOCK_RAW, protoent1->p_proto);
    if (socketfd < 0) {
        perror("socket error\n");
        return -1;
    }
    int size = 50 * 1024;
    setuid(getuid());
    setsockopt(socketfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));//对套接字进行设置
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    long inaddr = inet_addr(argv[1]);/*此函数将一个点分十进制的IP地址转化成一个长整型的数字,如果不是点分十进制,返回INADDR_NONE*/
    if (inaddr == INADDR_NONE) {
        if ((host = gethostbyname(argv[1])) == NULL) {
            perror("host error\n");
            return -1;
        }
        memcpy((char *) &addr.sin_addr, host->h_addr, (size_t)host->h_length);
    } else {
        memcpy((char *) &addr.sin_addr, (char *) &addr, (size_t)host->h_length);
    }
    int pid = getpid();
    printf("Ping %s(%s): %d bytes data in ICMP packets\n", argv[1], inet_ntoa(addr.sin_addr), datalen);
    send_packet(pid);
    recv_packet(pid);
    statistics(SIGALRM);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值