linux下简单的ping程序

        简单的整理了一个以前用的ping程序, 个人觉得对初学者学习网络协议还是有一定帮助的,最好是利用抓包工具查看下具体的数据报文。

/*
 * ping.cpp
 *
 *  Created on: Sep 10, 2014
 *      Author: xtank.nie@gmail.com
 */

#include <sys/time.h>
#include <netinet/ip_icmp.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>

#define SEND_PACKET_NUM (1)
int calculate_checksum(unsigned short *packet, int length)
{
    int num_left = length;
    int sum = 0;
    unsigned short *w = packet;
    unsigned short cksum = 0;

    while (num_left > 1)
    {
       sum += *w++;
       num_left -= 2;
    }

    if (num_left == 1)
    {
        /* 如果是奇数个字节,要在后面补上一个字节的0 */
        *((unsigned char *)&cksum) = *((unsigned char *)w);
        sum += cksum;
    }

    sum = (sum>>16) + (sum & 0xffff);
    sum += (sum >> 16);
    cksum= ~sum;

    return cksum;
}

int crate_icmp_packet(char *packet_buf, uint32_t packet_no)
{
    struct icmp *icmp_st;
    int data_size = 56;
    int packet_size;

    //head
    icmp_st = (struct icmp *)packet_buf;
    icmp_st->icmp_type = ICMP_ECHO; //请求回送
    icmp_st->icmp_code = 0;
    icmp_st->icmp_cksum = 0;
    icmp_st->icmp_seq = packet_no;
    icmp_st->icmp_id = getpid();

    //data
    gettimeofday((struct timeval *)icmp_st->icmp_data, NULL);

    packet_size = 8 + data_size;
    icmp_st->icmp_cksum = calculate_checksum((unsigned short *)packet_buf, packet_size);
    return packet_size;
}

int send_packet(int socket_fd, struct sockaddr *addr, char *packet_buf, int packet_no)
{
    int packet_size;

    packet_size = crate_icmp_packet(packet_buf, packet_no);
    if ((packet_size = sendto(socket_fd, packet_buf, packet_size, 0, addr, sizeof(struct sockaddr_in))) == -1)
    {
        perror("sendto:");
        return (-1); /* error */
    }
    //printf("send pack size=%d\n", packet_size);
    return 0; /* success */
}

void calculate_interval(struct timeval *recv_time, struct timeval *send_time)
{
    if ((recv_time->tv_usec -= send_time->tv_usec) < 0)
    {
        recv_time->tv_sec -= 1;
        recv_time->tv_usec += 1000000;
    }
    recv_time->tv_sec -= send_time->tv_sec;
}

int unpack(struct sockaddr_in *from, char *packet_buf, int packet_size,
           struct timeval *recv_time, double *rtt ,unsigned int *ttl)
{
    struct ip *ip_st;
    struct icmp *icmp_st;
    int ipheader;

    ip_st = (struct ip *)packet_buf;
    ipheader = (ip_st->ip_hl << 2);
    icmp_st = (struct icmp *)(packet_buf + ipheader);

    packet_size -= ipheader;
    if (packet_size < 8)
    {
    	printf( "recvfrom packet_size < 8!\n");
        return (-1); /* error */
    }

    if ((icmp_st->icmp_type == ICMP_ECHOREPLY) && (icmp_st->icmp_id == getpid()))
    {
        calculate_interval(recv_time, (struct timeval *)icmp_st->icmp_data);//收到的时间与发送的时间
        *rtt = (recv_time->tv_sec * 1000) + (recv_time->tv_usec / 1000);
        *ttl = ip_st->ip_ttl;
        printf("%d byte from %s: icmp_seq = %u, ttl = %d, rtt = %.3fms\n",
               packet_size, inet_ntoa(from->sin_addr), icmp_st->icmp_seq, ip_st->ip_ttl, *rtt);
	    return 0;
    }
    else
    {
        printf( "unpack error! icmp_type is %d, icmp_id is %d\r\n", (icmp_st->icmp_type == ICMP_ECHOREPLY), (icmp_st->icmp_id == getpid()));
        return -1;
    }
}


int recv_packet(int socket_fd, char *packet_buf, int *length, double *rtt, unsigned int *ttl)
{
    int nbyte;
    struct sockaddr_in from_addr;
    struct timeval recv_time;
    socklen_t size = sizeof(struct sockaddr_in);

    if ((nbyte = recvfrom(socket_fd, packet_buf, *length, 0, (struct sockaddr *)&from_addr, &size)) == -1)
    {
    	printf( "recvfrom return -1!\n" );
        return (-1);
    }
    else
    {
        ;//printf("recv data len=%d\n", nbyte);
    }

    *length = nbyte;
    gettimeofday(&recv_time, NULL);
    if (unpack(&from_addr, packet_buf, nbyte, &recv_time, rtt, ttl) != 0)
    {
        return (-1);
    }
    return 0;
}


int ping(const char *addr)
{
    unsigned long inaddr = 0;
    struct sockaddr_in dest_addr;
    struct hostent *host;
    int socket_ping = -1;
    int packet_size = 2048;
    char send_pac[2048] = {0};
    char recv_pac[2048] = {0};
    double iTimespend;
    unsigned int iTTL, send_packet_cnt = 0, recv_packet_cnt = 0;;

    memset(&dest_addr, 0x0, sizeof(struct sockaddr_in));
    dest_addr.sin_family = AF_INET;
    if ((inaddr = inet_addr(addr)) == INADDR_NONE)
    {
        if ((host = gethostbyname(addr)) == NULL)
        {
            fprintf(stderr, "gethostbyname error.\n");
            return -1;
        }
        memcpy((char *)&(dest_addr.sin_addr), host->h_addr, host->h_length);
    }
    else
    {
       dest_addr.sin_addr.s_addr = inaddr;
    }

    if((socket_ping = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
    {
        perror("socket:");
        return -1;
    }

    printf("Ping %s(%s): %d bytes data in ICMP packets.\n", addr, inet_ntoa(dest_addr.sin_addr), 56);
    fd_set read_set;
    struct timeval timeout;
    int i = 1, ret;

    for (i = 1; i <=SEND_PACKET_NUM; i++)
    {
        if (send_packet(socket_ping, (struct sockaddr *)&dest_addr, send_pac, i) == -1)
        {
            printf("send_packet error\n");
            close(socket_ping);
            return -1;
        }
        else
        {
            ++send_packet_cnt;
        }

        FD_ZERO(&read_set);
        FD_SET(socket_ping, &read_set);
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;

        ret = select(socket_ping+1, &read_set, NULL, NULL, &timeout);
        if (ret > 0)
        {
            if (recv_packet(socket_ping, recv_pac, &packet_size, &iTimespend, &iTTL) == -1)
            {
                printf("recv_packet error\n");
            }
            else
            {
                ++recv_packet_cnt;
            }
        }
        else if (ret == 0)
        {
              printf("Request time out.\n");
              continue;
        }
        else
        {
            perror("select error.");
            continue;
        }
    }
    close(socket_ping);

    //statistics
    printf("---- (%s) ping statistics ----\n", addr);
    printf("%d packets transmitted, %d received, %d%% packet loss\n",
            send_packet_cnt, recv_packet_cnt, (int)(((send_packet_cnt-recv_packet_cnt) * 100)/send_packet_cnt));

    return 0;
}

//quick test
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s [address]\n", argv[0]);
        return 0;
    }
    return ping(argv[1]);
 }

结合抓包工具就比较容易理解:

执行:sudo ./ping 114.114.114.114


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值