ping程序的C语言源代码

目录

C语言源代码 ping.c

Makefile

编译并运行


 C语言源代码 ping.c

/*
 *                      P I N G . C
 *
 * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
 * measure round-trip-delays and packet loss across network paths.
 *
 * Author -
 *      Mike Muuss
 *      U. S. Army Ballistic Research Laboratory
 *      December, 1983
 * Modified at Uc Berkeley
 *
 * Changed argument to inet_ntoa() to be struct in_addr instead of u_long
 * DFM BRL 1992
 *
 * Status -
 *      Public Domain.  Distribution Unlimited.
 *
 * Bugs -
 *      More statistics could always be gathered.
 *      This program has to run SUID to ROOT to access the ICMP socket.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <netdb.h>

#define MAXWAIT         10      /* max time to wait for response, sec. */
#define MAXPACKET       4096    /* max packet size */
#define VERBOSE         1       /* verbose flag */
#define QUIET           2       /* quiet flag */
#define FLOOD           4       /* floodping flag */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN  64
#endif

unsigned char packet[MAXPACKET];
int           i, pingflags, options;
extern int    errno;

int s;                  /* Socket file descriptor */
struct hostent *hp;     /* Pointer to host info */
struct timezone tz;     /* leftover */

struct sockaddr whereto;/* Who to ping */
int datalen;            /* How much data */

char usage[] = "Usage:  ping [-dfqrv] host [packetsize [count [preload]]]\n";

char *hostname;
char hnamebuf[MAXHOSTNAMELEN];

int npackets;
int preload = 0;                /* number of packets to "preload" */
int ntransmitted = 0;           /* sequence # for outbound packets = #sent */
int ident;

int nreceived = 0;              /* # of packets we got back */
int timing = 0;
int tmin = 999999999;
int tmax = 0;
int tsum = 0;                   /* sum of all times, for doing average */
void finish(), catcher(), pinger();
void pr_pack(char *buf,int cc,struct sockaddr_in *from);

/*
 *                      M A I N
 */
int main(int argc, char *argv[])
{
        struct sockaddr_in from;
        char **av = argv;
        struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
        int on = 1;
        struct protoent *proto;

        argc--, av++;
        while (argc > 0 && *av[0] == '-')
        {
                while (*++av[0]) switch (*av[0])
                {
                        case 'd':
                                options |= SO_DEBUG;
                                break;
                        case 'r':
                                options |= SO_DONTROUTE;
                                break;
                        case 'v':
                                pingflags |= VERBOSE;
                                break;
                        case 'q':
                                pingflags |= QUIET;
                                break;
                        case 'f':
                                pingflags |= FLOOD;
                                break;
                }
                argc--, av++;
        }

        if(argc < 1 || argc > 4)
        {
                printf("%s", usage);
                exit(1);
        }

        bzero((char *)&whereto, sizeof(struct sockaddr));
        to->sin_family = AF_INET;
        to->sin_addr.s_addr = inet_addr(av[0]);
        if(to->sin_addr.s_addr != (unsigned)-1)
        {
                strcpy(hnamebuf, av[0]);
                hostname = hnamebuf;
        } else {
                hp = gethostbyname(av[0]);
                if (hp)
                {
                        to->sin_family = hp->h_addrtype;
                        bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
                        hostname = hp->h_name;
                } else {
                        printf("%s: unknown host %s\n", argv[0], av[0]);
                        exit(1);
                }
        }

        if( argc >= 2 )
                datalen = atoi( av[1] );
        else
                datalen = 64-8;

        if (datalen > MAXPACKET)
        {
                fprintf(stderr, "ping: packet size too large\n");
                exit(1);
        }

        if (datalen >= sizeof(struct timeval))  /* can we time 'em? */
                timing = 1;

        if (argc >= 3)
                npackets = atoi(av[2]);

        if (argc == 4)
                preload = atoi(av[3]);

        ident = getpid() & 0xFFFF;

        if ((proto = getprotobyname("icmp")) == NULL)
        {
                fprintf(stderr, "icmp: unknown protocol\n");
                exit(10);
        }

        if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0)
        {
                perror("ping: socket");
                exit(5);
        }

        if (options & SO_DEBUG)
        {
                if(pingflags & VERBOSE)
                        printf("...debug on\n");
                setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on));
        }

        if (options & SO_DONTROUTE)
        {
                if(pingflags & VERBOSE)
                        printf("...no routing\n");
                setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on));
        }

        if(to->sin_family == AF_INET)
        {
                printf("PING %s (%s): %d data bytes\n", hostname,
                  inet_ntoa(to->sin_addr), datalen);    /* DFM */
        } else {
                printf("PING %s: %d data bytes\n", hostname, datalen );
        }
        setlinebuf(stdout);

        signal(SIGINT, finish);
        signal(SIGALRM, catcher);

        /* fire off them quickies */
        for(i=0; i < preload; i++)
                pinger();

        if(!(pingflags & FLOOD))
                catcher();      /* start things going */

        for (;;)
        {
                int len = sizeof (packet);
                int fromlen = sizeof (from);
                int cc;
                struct timeval timeout;
                int fdmask = 1 << s;

                timeout.tv_sec = 0;
                timeout.tv_usec = 10000;

                if(pingflags & FLOOD)
                {
                        pinger();
                        if( select(32, (fd_set *)&fdmask, 0, 0, &timeout) == 0)
                                continue;
                }

                if ( (cc=recvfrom(s, packet, len, 0, (struct sockaddr *)&from, &fromlen)) < 0)
                {
                        if( errno == EINTR )
                                continue;
                        perror("ping: recvfrom");
                        continue;
                }

                pr_pack( packet, cc, &from );
                if (npackets && nreceived >= npackets)
                        finish();
        }
        /*NOTREACHED*/
}

/*
 *                      I N _ C K S U M
 *
 * Checksum routine for Internet Protocol family headers (C Version)
 *
 */
unsigned short in_cksum(struct icmp *addr,int len)
{
        register int nleft = len;
        register unsigned short *w = (unsigned short *)addr;
        register unsigned short answer;
        register int sum = 0;

        /*
         *  Our algorithm is simple, using a 32 bit accumulator (sum),
         *  we add sequential 16 bit words to it, and at the end, fold
         *  back all the carry bits from the top 16 bits into the lower
         *  16 bits.
         */
        while( nleft > 1 )
        {
                sum += *w++;
                nleft -= 2;
        }

        /* mop up an odd byte, if necessary */
        if( nleft == 1 )
        {
                u_short u = 0;

                *(u_char *)(&u) = *(u_char *)w ;
                sum += u;
        }

        /*
         * add back carry outs from top 16 bits to low 16 bits
         */
        sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
        sum += (sum >> 16);                     /* add carry */
        answer = ~sum;                          /* truncate to 16 bits */
        return (answer);
}


/*
 *                      T V S U B
 * 
 * Subtract 2 timeval structs:  out = out - in.
 * 
 * Out is assumed to be >= in.
 */
void tvsub(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;
}


/*
 *                      C A T C H E R
 * 
 * This routine causes another PING to be transmitted, and then
 * schedules another SIGALRM for 1 second from now.
 * 
 * Bug -
 *      Our sense of time will slowly skew (ie, packets will not be launched
 *      exactly at 1-second intervals).  This does not affect the quality
 *      of the delay and loss statistics.
 */
void catcher(void)
{
        int waittime;

        pinger();
        if (npackets == 0 || ntransmitted < npackets)
                alarm(1);
        else{
                if (nreceived)
                {
                        waittime = 2 * tmax / 1000;
                        if (waittime == 0)
                                waittime = 1;
                } else
                        waittime = MAXWAIT;
                signal(SIGALRM, finish);
                alarm(waittime);
        }
}

/*
 *                      P I N G E R
 * 
 * Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
 * will be added on by the kernel.  The ID field is our UNIX process ID,
 * and the sequence number is an ascending integer.  The first 8 bytes
 * of the data portion are used to hold a UNIX "timeval" struct in VAX
 * byte-order, to compute the round-trip time.
 */
void pinger(void)
{
        static u_char outpack[MAXPACKET];
        register struct icmp *icp = (struct icmp *) outpack;
        int i, cc;
        register struct timeval *tp = (struct timeval *) &outpack[8];
        register u_char *datap = &outpack[8+sizeof(struct timeval)];

        icp->icmp_type = ICMP_ECHO;
        icp->icmp_code = 0;
        icp->icmp_cksum = 0;
        icp->icmp_seq = ntransmitted++;
        icp->icmp_id = ident;           /* ID */

        cc = datalen+8;                 /* skips ICMP portion */

        if (timing)
        {
                gettimeofday( tp, &tz );
        }

        for( i=8; i<datalen; i++)       /* skip 8 for time */
                *datap++ = i;

        /* Compute ICMP checksum here */
        icp->icmp_cksum = in_cksum( icp, cc );

        /* cc = sendto(s, msg, len, flags, to, tolen) */
        i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );
        if( i < 0 || i != cc )
        {
                if( i<0 )
                        perror("sendto");
                printf("ping: wrote %s %d chars, ret=%d\n", hostname, cc, i );
                fflush(stdout);
        }

        if(pingflags == FLOOD)
        {
                putchar('.');
                fflush(stdout);
        }
}

/*
 *                      P R _ T Y P E
 *
 * Convert an ICMP "type" field to a printable string.
 */
char * pr_type( register int t)
{
        static char *ttab[] = {
                "Echo Reply",
                "ICMP 1",
                "ICMP 2",
                "Dest Unreachable",
                "Source Quench",
                "Redirect",
                "ICMP 6",
                "ICMP 7",
                "Echo",
                "ICMP 9",
                "ICMP 10",
                "Time Exceeded",
                "Parameter Problem",
                "Timestamp",
                "Timestamp Reply",
                "Info Request",
                "Info Reply"
        };

        if( t < 0 || t > 16 )
                return("OUT-OF-RANGE");

        return(ttab[t]);
}

/*
 *                      P R _ P A C K
 *
 * Print out the packet, if it came from us.  This logic is necessary
 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
 * which arrive ('tis only fair).  This permits multiple copies of this
 * program to be run without having intermingled output (or statistics!).
 */
void pr_pack(char *buf,int cc,struct sockaddr_in *from)
{
        struct ip *ip;
        register struct icmp *icp;
        register long *lp = (long *) packet;
        register int i;
        struct timeval tv;
        struct timeval *tp;
        int hlen, triptime;

        from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
        gettimeofday( &tv, &tz );

        ip = (struct ip *) buf;
        hlen = ip->ip_hl << 2;
        if (cc < hlen + ICMP_MINLEN)
        {
                if (pingflags & VERBOSE)
                        printf("packet too short (%d bytes) from %s\n", cc,
                                inet_ntoa(from->sin_addr)); /* DFM */
                return;
        }

        cc -= hlen;
        icp = (struct icmp *)(buf + hlen);
        if( (!(pingflags & QUIET)) && icp->icmp_type != ICMP_ECHOREPLY )
        {
                printf("%d bytes from %s: icmp_type=%d (%s) icmp_code=%d\n",
                  cc, inet_ntoa(from->sin_addr),
                  icp->icmp_type, pr_type(icp->icmp_type), icp->icmp_code);/*DFM*/
                if (pingflags & VERBOSE)
                {
                        for( i=0; i<12; i++)
                                printf("x%2.2lx: x%8.8lx\n", i*sizeof(long),
                                  *lp++);
                }
                return;
        }

        if( icp->icmp_id != ident )
                return;                 /* 'Twas not our ECHO */

        if (timing)
        {
                tp = (struct timeval *)&icp->icmp_data[0];
                tvsub( &tv, tp );
                triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
                tsum += triptime;
                if( triptime < tmin )
                        tmin = triptime;
                if( triptime > tmax )
                        tmax = triptime;
        }

        if(!(pingflags & QUIET))
        {
                if(pingflags != FLOOD)
                {
                        printf("%d bytes from %s: icmp_seq=%d", cc,
                          inet_ntoa(from->sin_addr),
                          icp->icmp_seq );      /* DFM */
                        if (timing)
                                printf(" time=%d ms\n", triptime );
                        else
                                putchar('\n');
                } else {
                        putchar('\b');
                        fflush(stdout);
                }
        }
        nreceived++;
}


/*
 *                      F I N I S H
 *
 * Print out statistics, and give up.
 * Heavily buffered STDIO is used here, so that all the statistics
 * will be written with 1 sys-write call.  This is nice when more
 * than one copy of the program is running on a terminal;  it prevents
 * the statistics output from becomming intermingled.
 */
void finish(void)
{
        putchar('\n');
        fflush(stdout);
        printf("\n----%s PING Statistics----\n", hostname);
        printf("%d packets transmitted, ", ntransmitted);
        printf("%d packets received, ", nreceived);
        if (ntransmitted)
                if( nreceived > ntransmitted)
                        printf("-- somebody's printing up packets!");
                else
                        printf("%d%% packet loss",
                          (int) (((ntransmitted-nreceived)*100) /
                          ntransmitted));
        printf("\n");
        if (nreceived && timing)
            printf("round-trip (ms)  min/avg/max = %d/%d/%d\n",
                tmin,
                tsum / nreceived,
                tmax );
        fflush(stdout);
        exit(0);
}

Makefile

ping:
	gcc ping.c -o ping
clean:
	rm -rf ping

编译并运行

$ make
gcc ping.c -o ping
$ ls
Makefile  ping  ping.c
$ sudo ./ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 1.0.0.127: icmp_type=8 (Echo) icmp_code=0
64 bytes from 1.0.0.127: icmp_seq=0 time=0 ms
64 bytes from 1.0.0.127: icmp_type=8 (Echo) icmp_code=0
64 bytes from 1.0.0.127: icmp_seq=1 time=0 ms
64 bytes from 1.0.0.127: icmp_type=8 (Echo) icmp_code=0
64 bytes from 1.0.0.127: icmp_seq=2 time=0 ms
64 bytes from 1.0.0.127: icmp_type=8 (Echo) icmp_code=0
64 bytes from 1.0.0.127: icmp_seq=3 time=0 ms
^C

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的 C 语言 Ping 程序,可以用于在 Windows 系统下测试网络连接: ``` #include <stdio.h> #include <winsock2.h> #include <windows.h> #include <iphlpapi.h> #include <icmpapi.h> #pragma comment(lib, "iphlpapi.lib") #pragma comment(lib, "ws2_32.lib") int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s <host>\n", argv[0]); return 1; } const char* host = argv[1]; if (strlen(host) > MAX_PATH) { printf("Host name is too long!\n"); return 1; } WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup failed!\n"); return 1; } HANDLE icmpHandle = IcmpCreateFile(); if (icmpHandle == INVALID_HANDLE_VALUE) { printf("IcmpCreateFile failed!\n"); WSACleanup(); return 1; } char sendBuf[32] = { 0 }; DWORD dataSize = sizeof(sendBuf); DWORD ipAddr = inet_addr(host); if (ipAddr == INADDR_NONE) { struct hostent* hostEnt = gethostbyname(host); if (hostEnt == NULL || hostEnt->h_addrtype != AF_INET) { printf("Unknown host %s!\n", host); IcmpCloseHandle(icmpHandle); WSACleanup(); return 1; } ipAddr = *(DWORD*)hostEnt->h_addr_list[0]; } printf("Pinging %s [%s] with %d bytes of data:\n", host, inet_ntoa(*(in_addr*)&ipAddr), dataSize); for (int i = 0; i < 4; i++) { if (IcmpSendEcho(icmpHandle, ipAddr, sendBuf, dataSize, NULL, NULL, 1000) == 0) { printf("Request timed out.\n"); } else { printf("Reply from %s: %d bytes in %ldms TTL=%d\n", inet_ntoa(*(in_addr*)&ipAddr), dataSize, ((PIP_ECHO_REPLY)sendBuf)->RoundTripTime, ((PIP_ECHO_REPLY)sendBuf)->Options.Ttl); } Sleep(1000); } IcmpCloseHandle(icmpHandle); WSACleanup(); return 0; } ``` 这个程序使用了 WinSock 和 ICMP 协议,可以在 Windows 系统下运行。它会发送四个 ICMP 回显请求(即 Ping 请求),并等待每个请求的回复。如果在一定时间内没有收到回复,就认为该请求超时。如果收到回复,则打印回复报文的信息,包括源 IP 地址、数据长度、往返时间(即时延)和 TTL(生存时间)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值