日期:2014-05-16  浏览次数:20839 次

关于ping程序的问题
我写了一个简单的ping程序,代码如下:
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

void sigalrm_handler(int); //通过对alarm信号响应发送icmp数据包
void send_icmp(void);
void recv_icmp(void);
void tvsub(struct timeval *,struct timeval *); //calculate the transmission time
unsigned short in_cksum(); //calculate the confirmation code
void pr_icmp(char *ptr,int len);
int sockfd;
int pid; //用于设置icmp的id
int datalen=56; //用于设置icmp数据包的打消
char hostname[128];
struct sockaddr_in dest;
char sendbuf[256];
int seq=0; //用于设置下一个icmp数据包的序列号

int main(int argc,char * argv[])
{
struct sigaction act;
struct hostent *he;
int n;
if(argc!=2)
{
fprintf(stderr,"usage:ping host\n");
exit(1);
}
act.sa_handler=sigalrm_handler;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
sigaction(SIGALRM,&act,NULL); //此函数用于检查或修改指定信号关联的处理动作
pid=getpid();
if(inet_aton(argv[1],&dest.sin_addr)==1) //若输入的是ip地址
{
strcpy(hostname,argv[1]);
}else{ //若输入的是域名
he=gethostbyname(argv[1]);
if(he!=NULL)
{
strcpy(hostname,he->h_name);
dest.sin_addr=*(struct in_addr*)he->h_addr_list[0];
}else{
fprintf(stderr,"host name error:%s %s\n",argv[1],hstrerror(h_errno));
exit(1);
}
}
printf("PING %s(%s):\n",hostname,inet_ntoa(dest.sin_addr));
sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); //SOCK_RAW为原始套接字
if(sockfd<0)
{
fprintf(stderr,"socket error:%s\n",strerror(errno));
exit(1);
}
setuid(getuid());
sigalrm_handler(SIGALRM);
recv_icmp();
}


void recv_icmp(void)
{
char recvbuf[256];
int len;
int n;
for(;;)
{
n=recvfrom(sockfd,recvbuf,sizeof(recvbuf),0,NULL,NULL);
if(n<0)
{
if(errno==EINTR) 
{
printf("recverro: EINTR\n");
continue;
}else{
printf("recvfrom error\n");
continue;
}
}
}
pr_icmp(recvbuf,n);
}

#define icmp_type type
#define icmp_code code
#define icmp_cksum checksum
#define icmp_id un.echo.id
#define icmp_seq un.echo.sequence

void pr_icmp(char *ptr,int len)
{
int hlen1,icmplen;
double rtt;
struct ip *ip;
struct icmphdr *icmp;
struct timeval *tvsend;
struct timeval tvrecv;
ip=(struct ip *)ptr; //是ip指向收到的数据包
hlen1=ip->ip_hl<<2; //ip_hl存储的是ip数据包首部的长度,是以4字节为单位的,将其右移两位得到ip数据包首部的长度
icmp=(struct icmphdr *)(ptr+hlen1); //与ip数据包首部相连的是icmp数据包
if((icmplen=len-hlen1)<8) //icmp应答数据包的首部长度为8字节
printf("icmplen(%d)<8\n",icmplen);
if(icmp->icmp_type==ICMP_ECHOREPLY) //判断该icmp数据包是否是应答数据包
{
printf("Received reply!\n"); //测试
if(icmp->icmp_id!=pid) return;
if(icmplen<16)
printf("icmplen(%d)<16",icmplen);
gettimeofday(&tvrecv,NULL);
tvsend=(struct timeval *)(icmp+1); //与icmp数据包首部相连的是该icmp发送的时间,由于数据包头部为8字节,icmp指针加一就移动到发送时间项
tvsub(&tvrecv,tvsend);
rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;
printf("%d bytes from %s(%s):seq=%u,ttl=%d,rtt=%.3f ms\n",icmplen,hostname,inet_ntoa(dest.sin_addr),icmp->icmp_seq,ip->ip_ttl,rtt);
}
}

void tvsub(struct timeval *