第一篇博客,参考大神代码,整理思路写的。
#include "sys/socket.h"
#include "sys/types.h"
#include "stdio.h"
#include "stdlib.h"
#include "netdb.h"
#include "sys/time.h"
#include "time.h"
#include "unistd.h"
#include "arpa/inet.h"
#include "netinet/in.h"
#include "signal.h"
#include "string.h"
#define BUF_SIZE 1024*1024
#define TIMEOUT 2
#define INF 1<<30
struct IPHEADER{
unsigned char ver_hlen;
unsigned char tos;
unsigned short dlen;
unsigned short id;
unsigned short flag_index;
unsigned char ttl;
unsigned char proto;
unsigned short checksum;
unsigned int src;
unsigned int dest;
unsigned int add[0];
};
struct ICMPHEADER{
unsigned char type;
unsigned char code;
unsigned short checksum;
unsigned short id;
unsigned short seq;
};
//var
timeval start,end;
int sock_raw;
timeval st,rt,timeout;
char sendbuf[BUF_SIZE],recvbuf[BUF_SIZE];
int ptime=4;
hostent* ht;
sockaddr_in dest;
const int SOCK_DEFAULT_BUFSIZE=32*1024;
int cnt=0,pn=0;
//
int init(int argc,char*argv[]){
if(argc<2){
printf("no enough arg!\n");
return -1;
}else if(argc==3){
if(*(argv[2]+1)=='r')ptime=INF;
else {
ptime=atoi(argv[2]+1);
if(ptime==0){
printf("ptime invalid!\n");
return -1;
}
}
}
if((ht=gethostbyname(argv[1]))==NULL){
printf("hostname invalid!\n");
return -1;
}
memset(&dest,0,sizeof(dest));
dest.sin_family=AF_INET;
memcpy(&dest.sin_addr,ht->h_addr_list[0],sizeof(dest.sin_addr));
if((sock_raw=socket(PF_INET,SOCK_RAW,IPPROTO_ICMP))<0){
printf("socket error!\n");
close(sock_raw);
return -1;
}
if(setsockopt(sock_raw,SOL_SOCKET,SO_SNDBUF,&SOCK_DEFAULT_BUFSIZE,sizeof(SOCK_DEFAULT_BUFSIZE))<0){
printf("setsockopt error!\n");
close(sock_raw);
return -1;
}
if(setsockopt(sock_raw,SOL_SOCKET,SO_RCVBUF,&SOCK_DEFAULT_BUFSIZE,sizeof(SOCK_DEFAULT_BUFSIZE))<0){
printf("setsockopt error!\n");
close(sock_raw);
return -1;
}
return 0;
}
unsigned short calchecksum(unsigned short*buf,int size){
int i;
unsigned int sum=0;
for(i=0;i<size;i++)sum+=buf[i];
while(sum&0xffff0000){
sum=(sum>>16)+(sum&0x0000ffff);
}
return ~sum;
}
int unpack(int seq){
memset(recvbuf,0,sizeof(recvbuf));
if(recvfrom(sock_raw,recvbuf,sizeof(recvbuf),0,NULL,NULL)<0){
printf("connect shutdown!\n");
return -2;
}
gettimeofday(&rt,NULL);
IPHEADER*ip=(IPHEADER*)recvbuf;
ICMPHEADER*icmp=(ICMPHEADER*)(recvbuf+(((ip->ver_hlen)&0x0f)<<2));
if(calchecksum((unsigned short*)ip,(((ip->ver_hlen)&0x0f)<<2)/2)!=0){
printf("ipheader checksum error\n");
return 0;
}
if(calchecksum((unsigned short*)icmp,(ntohs(ip->dlen)-(((ip->ver_hlen)&0x0f)<<2))/2)!=0){
printf("icmpheader checksum error\n");
return 0;
}
if(icmp->id==getuid()&&icmp->seq==seq&&icmp->type==0){
printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%.1dms\n",ntohs(ip->dlen)
,inet_ntoa((in_addr)(dest.sin_addr))
,icmp->seq,ip->ttl,(rt.tv_sec-st.tv_sec)*1000+(rt.tv_usec-st.tv_usec)/1000
);
return 0;
}
return -1;
}
void packet(int seq){
memset(sendbuf,0,sizeof(sendbuf));
ICMPHEADER *icmp=(ICMPHEADER*)sendbuf;
icmp->type=8;
icmp->code=0;
icmp->seq=seq;
icmp->id=getuid();
gettimeofday(&st,NULL);
memcpy(sendbuf+sizeof(ICMPHEADER),&st,sizeof(st));
icmp->checksum=calchecksum((unsigned short*)sendbuf,(sizeof(ICMPHEADER)+sizeof(timeval))/2);
}
void ping(){
for(pn=1;pn<=ptime;pn++){
packet(pn);
if(sendto(sock_raw,sendbuf,sizeof(IPHEADER)+sizeof(ICMPHEADER)+sizeof(timeval),0,(sockaddr*)&dest,sizeof(dest))<0){
printf("connect shutdown!\n");
return;
}
while(1){
timeout.tv_sec=2;
timeout.tv_usec=0;
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(sock_raw,&fdset);
int res=select(sock_raw+1,&fdset,NULL,NULL,&timeout);
if(res<0){
printf("connect shutdown!\n");
return;
}else if(res==0){
printf("0 byte from %s: request timeout!\n",inet_ntoa((in_addr)(dest.sin_addr)));
break;
}else if(FD_ISSET(sock_raw,&fdset)){
int f=unpack(pn);
if(f==-1){
continue;
}else if(f==-2){
return;
}else {
cnt++;
break;
}
}
}
sleep(1);
}
}
void deal_f(int arg){
gettimeofday(&end,NULL);
if(pn==1){
printf("0 packets transmitted, 0 recieved, NAN lossed, time=%dms\n",(end.tv_sec-start.tv_sec)*1000+(end.tv_usec-start.tv_usec)/1000);
}
else printf("%d packets transmitted, %d recieved, %%%.0f lossed, time=%dms\n",pn-1,cnt,(pn-1-cnt)*100.0/(pn-1),(end.tv_sec-start.tv_sec)*1000+(end.tv_usec-start.tv_usec)/1000);
close(sock_raw);
exit(0);
}
int main(int argc,char*argv[]){
gettimeofday(&start,NULL);
//test
//argc=2;
//argv[1]="www.baidu.com";
//endtest
if(init(argc,argv)<0)return -1;
signal(SIGINT,deal_f);
printf("PING %s (%s) %d bytes of data:\n",ht->h_name,inet_ntoa((in_addr)(dest.sin_addr)),64);
ping();
deal_f(0);
return 0;
}
代码介绍:完成了ping。(存在问题,本地到网络的转换,但是我因为(懒)时间原因,未作修改,不影响大体功能)
知识点:
ip报文头与icmp报文头,需要网络标准。最好是单字节对齐
select(maxfd,fdset,NULL,NULL,timeout)的使用,关键:初始化fdset,初始化timeout。返回值0:超时;返回值-1:error;返回值大于0:有可读信息,如FD_SET()加入多个描述符,使用FD_ISSET()判断。