ping 命令linux,linux 中的ping 命令的实现

编写.c 原文件

vi ping.c

编译 gcc -o ping ping.c

./ping IP 运行即可

编译内容如下:

/*ping.c*/

#include #include #include #include #include #include #include #include #include #include #include /*bzero*/

#include #include /*保存已经发送包的状态值*/

typedef struct pingm_pakcet{

struct timeval tv_begin;    /*发送的时间*/

struct timeval tv_end;        /*接收到的时间*/

short seq;                    /*序列号*/

int flag;        /*1,表示已经发送但没有接收到回应包0,表示接收到回应包*/

}pingm_pakcet;

static pingm_pakcet pingpacket[128];

static pingm_pakcet *icmp_findpacket(int seq);

static unsigned short icmp_cksum(unsigned char *data,  int len);

static struct timeval icmp_tvsub(struct timeval end,struct timeval begin);

static void icmp_statistics(void);

static void icmp_pack(struct icmp *icmph, int seq, struct timeval *tv, int length );

static int icmp_unpack(char *buf,int len);

static void *icmp_recv(void *argv);

static void *icmp_send(void *argv);

static void icmp_sigint(int signo);

static void icmp_usage();

#define K 1024

#define BUFFERSIZE 72                    /*发送缓冲区大小*/

static char send_buff[BUFFERSIZE];

static char recv_buff[2*K];    /*为防止接收溢出,接收缓冲区设置大一些*/

static struct sockaddr_in dest;        /*目的地址*/

static int rawsock = 0;                    /*发送和接收线程需要的socket描述符*/

static pid_t pid=0;                        /*进程PID*/

static int alive = 0;                    /*是否接收到退出信号*/

static short packet_send = 0;            /*已经发送的数据包有多少*/

static short packet_recv = 0;            /*已经接收的数据包有多少*/

static char dest_str[80];                /*目的主机字符串*/

static struct timeval tv_begin, tv_end,tv_interval;

/*本程序开始发送、结束和时间间隔*/

static void icmp_usage()

{

/*ping加IP地址或者域名*/

printf("ping aaa.bbb.ccc.ddd\n");

}

/*主程序*/

int main(int argc, char *argv[])

{

struct hostent * host = NULL;

struct protoent *protocol = NULL;

char protoname[]= "icmp";

unsigned long inaddr = 1;

int size = 128*K;

/*参数是否数量正确*/

if(argc < 2)

{

icmp_usage();

return -1;

}

/*获取协议类型ICMP*/

protocol = getprotobyname(protoname);

if (protocol == NULL)

{

perror("getprotobyname()");

return -1;

}

/*复制目的地址字符串*/

memcpy(dest_str,  argv[1], strlen(argv[1])+1);

memset(pingpacket, 0, sizeof(pingm_pakcet) * 128);

/*socket初始化*/

rawsock = socket(AF_INET, SOCK_RAW,  protocol->p_proto);

if(rawsock < 0)

{

perror("socket");

return -1;

}

/*为了与其他进程的ping程序区别,加入pid*/

pid = getuid();

/*增大接收缓冲区,防止接收的包被覆盖*/

setsockopt(rawsock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));

bzero(&dest, sizeof(dest));

/*获取目的地址的IP地址*/

dest.sin_family = AF_INET;

/*输入的目的地址为字符串IP地址*/

inaddr = inet_addr(argv[1]);

if(inaddr == INADDR_NONE)

{

/*输入的是DNS地址*/

host = gethostbyname(argv[1]);

if(host == NULL)

{

perror("gethostbyname");

return -1;

}

/*将地址复制到dest中*/

memcpy((char *)&dest.sin_addr, host->h_addr, host->h_length);

}

else        /*为IP地址字符串*/

{

memcpy((char*)&dest.sin_addr, &inaddr, sizeof(inaddr));

}

/*打印提示*/

inaddr = dest.sin_addr.s_addr;

printf("PING %s (%ld.%ld.%ld.%ld) 56(84) bytes of data.\n",

dest_str,

(inaddr&0x000000FF)>>0,

(inaddr&0x0000FF00)>>8,

(inaddr&0x00FF0000)>>16,

(inaddr&0xFF000000)>>24);

/*截取信号SIGINT,将icmp_sigint挂接上*/

signal(SIGINT, icmp_sigint);

alive = 1;                        /*初始化为可运行*/

pthread_t send_id, recv_id;        /*建立两个线程,用于发送和接收*/

int err = 0;

err = pthread_create(&send_id, NULL, icmp_send, NULL);        /*发送*/

if(err < 0)

{

return -1;

}

err = pthread_create(&recv_id, NULL, icmp_recv, NULL);        /*接收*/

if(err < 0)

{

return -1;

}

/*等待线程结束*/

pthread_join(send_id, NULL);

pthread_join(recv_id, NULL);

/*清理并打印统计结果*/

close(rawsock);

icmp_statistics();

return 0;

}

/*CRC16校验和计算icmp_cksum

参数:

data:数据

len:数据长度

返回值:

计算结果,short类型

*/

static unsigned short icmp_cksum(unsigned char *data,  int len)

{

int sum=0;                            /*计算结果*/

int odd = len & 0x01;                    /*是否为奇数*/

/*将数据按照2字节为单位累加起来*/

while( len & 0xfffe)  {

sum += *(unsigned short*)data;

data += 2;

len -=2;

}

/*判断是否为奇数个数据,若ICMP报头为奇数个字节,会剩下最后一字节*/

if( odd) {

unsigned short tmp = ((*data)<<8)&0xff00;

sum += tmp;

}

sum = (sum >>16) + (sum & 0xffff);    /*高低位相加*/

sum += (sum >>16) ;                    /*将溢出位加入*/

return ~sum;                             /*返回取反值*/

}

/*设置ICMP报头*/

static void icmp_pack(struct icmp *icmph, int seq, struct timeval *tv, int length )

{

unsigned char i = 0;

/*设置报头*/

icmph->icmp_type = ICMP_ECHO;    /*ICMP回显请求*/

icmph->icmp_code = 0;            /*code值为0*/

icmph->icmp_cksum = 0;      /*先将cksum值填写0,便于之后的cksum计算*/

icmph->icmp_seq = seq;            /*本报的序列号*/

icmph->icmp_id = pid &0xffff;    /*填写PID*/

for(i = 0; i< length; i++)

icmph->icmp_data[i] = i;

/*计算校验和*/

icmph->icmp_cksum = icmp_cksum((unsigned char*)icmph, length);

}

/*解压接收到的包,并打印信息*/

static int icmp_unpack(char *buf,int len)

{

int iphdrlen;

struct ip *ip = NULL;

struct icmp *icmp = NULL;

int rtt;

ip=(struct ip *)buf;                     /*IP头部*/

iphdrlen=ip->ip_hl*4;                    /*IP头部长度*/

icmp=(struct icmp *)(buf+iphdrlen);        /*ICMP段的地址*/

len-=iphdrlen;

/*判断长度是否为ICMP包*/

if( len<8)

{

printf("ICMP packets\'s length is less than 8\n");

return -1;

}

/*ICMP类型为ICMP_ECHOREPLY并且为本进程的PID*/

if( (icmp->icmp_type==ICMP_ECHOREPLY) && (icmp->icmp_id== pid) )

{

struct timeval tv_internel,tv_recv,tv_send;

/*在发送表格中查找已经发送的包,按照seq*/

pingm_pakcet* packet = icmp_findpacket(icmp->icmp_seq);

if(packet == NULL)

return -1;

packet->flag = 0;    /*取消标志*/

tv_send = packet->tv_begin;            /*获取本包的发送时间*/

gettimeofday(&tv_recv, NULL);        /*读取此时间,计算时间差*/

tv_internel = icmp_tvsub(tv_recv,tv_send);

rtt = tv_internel.tv_sec*1000+tv_internel.tv_usec/1000;

/*打印结果,包含

*  ICMP段长度

*  源IP地址

*  包的序列号

*  TTL

*  时间差

*/

printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%d ms\n",

len,

inet_ntoa(ip->ip_src),

icmp->icmp_seq,

ip->ip_ttl,

rtt);

packet_recv ++;                        /*接收包数量加1*/

}

else

{

return -1;

}

return 0;

}

/*计算时间差time_sub

参数:

end,接收到的时间

begin,开始发送的时间

返回值:

使用的时间

*/

static struct timeval icmp_tvsub(struct timeval end,struct timeval begin)

{

struct timeval tv;

/*计算差值*/

tv.tv_sec = end.tv_sec - begin.tv_sec;

tv.tv_usec = end.tv_usec - begin.tv_usec;

/*如果接收时间的usec值小于发送时的usec值,从usec域借位*/

if(tv.tv_usec < 0)

{

tv.tv_sec --;

tv.tv_usec += 1000000;

}

return tv;

}

/*发送ICMP回显请求包*/

static void* icmp_send(void *argv)

{

/*保存程序开始发送数据的时间*/

gettimeofday(&tv_begin, NULL);

while(alive)

{

int size = 0;

struct timeval tv;

gettimeofday(&tv, NULL);            /*当前包的发送时间*/

/*在发送包状态数组中找一个空闲位置*/

pingm_pakcet *packet = icmp_findpacket(-1);

if(packet)

{

packet->seq = packet_send;        /*设置seq*/

packet->flag = 1;                /*已经使用*/

gettimeofday( &packet->tv_begin, NULL);    /*发送时间*/

}

icmp_pack((struct icmp *)send_buff, packet_send, &tv, 64 );

/*打包数据*/

size = sendto (rawsock,  send_buff, 64,  0,        /*发送给目的地址*/

(struct sockaddr *)&dest, sizeof(dest) );

if(size <0)

{

perror("sendto error");

continue;

}

packet_send++;                    /*计数增加*/

/*每隔1s,发送一个ICMP回显请求包*/

sleep(1);

}

}

/*接收ping目的主机的回复*/

static void *icmp_recv(void *argv)

{

/*轮询等待时间*/

struct timeval tv;

tv.tv_usec = 200;

tv.tv_sec = 0;

fd_set  readfd;

/*当没有信号发出一直接收数据*/

while(alive)

{

int ret = 0;

FD_ZERO(&readfd);

FD_SET(rawsock, &readfd);

ret = select(rawsock+1,&readfd, NULL, NULL, &tv);

switch(ret)

{

case -1:

/*错误发生*/

break;

case 0:

/*超时*/

break;

default:

{

/*接收数据*/

int size = recv(rawsock, recv_buff,sizeof(recv_buff),

0);

if(errno == EINTR)

{

perror("recvfrom error");

continue;

}

/*解包,并设置相关变量*/

ret = icmp_unpack(recv_buff, size);

if(ret == -1)

{

continue;

}

}

break;

}

}

}

/*查找一个合适的包位置

*当seq为-1时,表示查找空包

*其他值表示查找seq对应的包*/

static pingm_pakcet *icmp_findpacket(int seq)

{

int i=0;

pingm_pakcet *found = NULL;

/*查找包的位置*/

if(seq == -1)/*查找空包的位置*/

{

for(i = 0;i<128;i++)

{

if(pingpacket[i].flag == 0)

{

found = &pingpacket[i];

break;

}

}

}

else if(seq >= 0)/*查找对应seq的包*/

{

for(i = 0;i<128;i++)

{

if(pingpacket[i].seq == seq)

{

found = &pingpacket[i];

break;

}

}

}

return found;

}

/*打印全部ICMP发送接收统计结果*/

static void icmp_statistics(void)

{

long time = (tv_interval.tv_sec * 1000 )+ (tv_interval.tv_usec/1000);

printf("--- %s ping statistics ---\n",dest_str);    /*目的IP地址*/

printf("%d packets transmitted, %d received, %d%% packet loss, time %ldms\n",

packet_send,                                    /*发送*/

packet_recv,                                      /*接收*/

(packet_send-packet_recv)*100/packet_send,     /*丢失百分比*/

time);                                             /*时间*/

}

/*终端信号处理函数SIGINT*/

static void icmp_sigint(int signo)

{

alive = 0;                            /*告诉接收和发送线程结束程序*/

gettimeofday(&tv_end, NULL);        /*读取程序结束时间*/

tv_interval = icmp_tvsub(tv_end, tv_begin);  /*计算一下总共所用时间*/

return;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值