一个简单的ping代码(转载的)

#include  < stdio.h >
#include 
< signal.h >
#include 
< arpa / inet.h >
#include 
< sys / types.h >
#include 
< sys / socket.h >
#include 
< unistd.h >
#include 
< netinet / in .h >
#include 
< netinet / ip.h >
#include 
< netinet / ip_icmp.h >
#include 
< netdb.h >
#include 
< setjmp.h >
#include 
< errno.h >

#define  PACKET_SIZE     4096
#define  MAX_WAIT_TIME   5
#define  MAX_NO_PACKETS  3

char  sendpacket[PACKET_SIZE];
char  recvpacket[PACKET_SIZE];
int  sockfd,datalen = 56 ;
int  nsend = 0 ,nreceived = 0 ;
struct  sockaddr_in dest_addr;
pid_t pid;
struct  sockaddr_in from;
struct  timeval tvrecv;

void  statistics( int  signo);
unsigned 
short  cal_chksum(unsigned  short   * addr, int  len);
int  pack( int  pack_no);
void  send_packet( void );
void  recv_packet( void );
int  unpack( char   * buf, int  len);
void  tv_sub( struct  timeval  * out , struct  timeval  * in );

/*
 *ping 是向目的地址发送一个icmp回显请求报文,然后等待回显应答报文
 *并在超时打印超时信息,本代码没有实现超时检测
 *icmp回显请求报文或回显应答报文格式
 *|   类型(0/8) |  代码(0)   |               校验和             |
 *|          标志符                 |               序号                 |
 *|                                选项数据                                |
 *其中标志符是本进程号,用于区别收到的其他报文,这样可以允许多个ping程序同时运行
 *序号是从0开始,每次都加1
 *有时第一个往返时间可能比后面的长,因为第一次可能要查找ARP
 
*/



void  statistics( int  signo)
{       printf(" --------------------PING statistics------------------- ");
        printf(
"%d packets transmitted, %d received , %%%d lost ",nsend,nreceived,
                        (nsend
-nreceived)/nsend*100);
        close(sockfd);
        exit(
1);
}

/*校验和算法*/
unsigned 
short  cal_chksum(unsigned  short   * addr, int  len)
{       int nleft=len;
        
int sum=0;
        unsigned 
short *w=addr;
        unsigned 
short answer=0;

        
/*把ICMP报头二进制数据以2字节为单位累加起来*/
        
while(nleft>1)
        
{       sum+=*w++;
                nleft
-=2;
        }

         
/*若ICMP报头为奇数个字节,会剩下最后一字节。
          *把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加
*/

        
if( nleft==1)
        
{       *(unsigned char *)(&answer)=*(unsigned char *)w;
                sum
+=answer;
        }

        sum
=(sum>>16)+(sum&0xffff);
        sum
+=(sum>>16);
        answer
=~sum;
        
return answer;
}

/*设置ICMP报头*/
int  pack( int  pack_no)
{       int i,packsize;
        
struct icmp *icmp;
        
struct timeval *tval;

        icmp
=(struct icmp*)sendpacket;
        icmp
->icmp_type=ICMP_ECHO;
        icmp
->icmp_code=0;
        icmp
->icmp_cksum=0;
        icmp
->icmp_seq=pack_no;
        icmp
->icmp_id=pid;
        packsize
=8+datalen;
        tval
= (struct timeval *)icmp->icmp_data;
        gettimeofday(tval,NULL);    
/*记录发送时间*/
        icmp
->icmp_cksum=cal_chksum( cicmp,packsize); /*校验算法*/
        
return packsize;
}


/*发送三个ICMP报文*/
void  send_packet()
{       int packetsize;
        
while( nsend<MAX_NO_PACKETS)
        
{       nsend++;
                packetsize
=pack(nsend); /*设置ICMP报头*/
                
if( sendto(sockfd,sendpacket,packetsize,0,
                          (
struct sockaddr *)&dest_addr,sizeof(dest_addr) )<0  )
                
{       perror("sendto error");
                        
continue;
                }

                sleep(
1); /*每隔一秒发送一个ICMP报文*/
        }

}


/*接收所有ICMP报文*/
void  recv_packet()
{       int n,fromlen;
        
extern int errno;

        signal(SIGALRM,statistics);
        fromlen
=sizeof(from);
        
while( nreceived<nsend)
        
{
                alarm(MAX_WAIT_TIME);
                
if( (n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,
                                (
struct sockaddr *)&from,&fromlen)) <0)
                
{       if(errno==EINTR)continue;
                        perror(
"recvfrom error");
                        
continue;
                }

                gettimeofday(
&tvrecv,NULL);  /*记录接收时间*/
                
if(unpack(recvpacket,n)==-1)continue;
                nreceived
++;
        }

}


// 打印ip报文的信息
void  printIP( char   * packet,  int  len)
{
    
struct sockaddr_in addr_src,addr_dst;
    
struct ip *pip = (struct ip*)packet;
    printf(
"ip head len:%d ",pip->ip_hl<<2);
    printf(
"ip len:%d ",ntohs(pip->ip_len));
    printf(
"ip pro id:%d ",pip->ip_p);
    printf(
"ip ttl:%d ",pip->ip_ttl);
    printf(
"ip offset:%d ",ntohs(pip->ip_off)&IP_OFFMASK);

    memset(
&addr_src,0,sizeof(struct sockaddr_in));
    memset(
&addr_dst,0,sizeof(struct sockaddr_in));
    memcpy(
&addr_src,&pip->ip_src,sizeof(struct sockaddr_in));
    memcpy(
&addr_dst,&pip->ip_dst,sizeof(struct sockaddr_in));
    printf(
"src ip:%x ",addr_src.sin_addr);
    printf(
"dst ip:%x ",addr_dst.sin_addr);
}


/*剥去ICMP报头*/
int  unpack( char   * buf, int  len)
{       int i,iphdrlen;
        
struct ip *ip;
        
struct icmp *icmp;
        
struct timeval *tvsend;
        
double rtt;

        ip
=(struct ip *)buf;
        iphdrlen
=ip->ip_hl<<2;    /*求ip报头长度,即ip报头的长度标志乘4*/
        icmp
=(struct icmp *)(buf+iphdrlen);  /*越过ip报头,指向ICMP报头*/
        len
-=iphdrlen;            /*ICMP报头及ICMP数据报的总长度*/
        
if( len<8)                /*小于ICMP报头长度则不合理*/
        
{       printf("ICMP packets's length is less than 8 ");
                
return -1;
        }

        
/*确保所接收的是我所发的的ICMP的回应*/
        
if( (icmp->icmp_type==ICMP_ECHOREPLY) && (icmp->icmp_id==pid) )
        
{       tvsend=(struct timeval *)icmp->icmp_data;
                tv_sub(
&tvrecv,tvsend);  /*接收和发送的时间差*/
                rtt
=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;  /*以毫秒为单位计算rtt*/
                
/*显示相关信息*/
                printf(
"%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms ",
                        len,
                        inet_ntoa(from.sin_addr),
                        icmp
->icmp_seq,
                        ip
->ip_ttl,
                        rtt);
        }

        
else    return -1;
}


main(
int  argc, char   * argv[])
{       struct hostent *host;
        
struct protoent *protocol;
        unsigned 
long inaddr=0l;
        
int waittime=MAX_WAIT_TIME;
        
int size=50*1024;
  
        
if(argc<2)
        
{       printf("usage:%s hostname/IP address ",argv[0]);
                exit(
1);
        }

        
    printf(
"step1 ");
    
        
if( (protocol=getprotobyname("icmp") )==NULL)
        
{       perror("getprotobyname");
                exit(
1);
        }

      
        printf(
"step2 "); 
        
    
/*生成使用ICMP的原始套接字,这种套接字只有root才能生成*/
        
if( (sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto) )<0)
        
{       perror("socket error");
                exit(
1);
        }

        
    
/* 回收root权限,设置当前用户权限*/
        setuid(getuid());
        
    
/*扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的
          的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答
*/

        setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,
&size,sizeof(size) );
        bzero(
&dest_addr,sizeof(dest_addr));
        dest_addr.sin_family
=AF_INET;

        
/*判断是主机名还是ip地址*/
        
if( inaddr=inet_addr(argv[1])==INADDR_NONE)
        
{
        
if((host=gethostbyname(argv[1]) )==NULL) /*是主机名*/
                
{       perror("gethostbyname error");
                        exit(
1);
                }

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

        
else    /*是ip地址*/
    
{
        dest_addr.sin_addr.s_addr 
= inaddr;
                
//memcpy( (char *)&dest_addr,(char *)&inaddr,host->h_length);
        }

        
/*获取main的进程id,用于设置ICMP的标志符*/
        pid
=getpid();
        printf(
"PING %s(%s): %d bytes data in ICMP packets. ",argv[1],
                        inet_ntoa(dest_addr.sin_addr),datalen);
        send_packet();  
/*发送所有ICMP报文*/
        recv_packet();  
/*接收所有ICMP报文*/
        statistics(SIGALRM); 
/*进行统计*/

        
return 0;

}

/*两个timeval结构相减*/
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;
}

/*------------- The End -----------*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值