Ping是什么
很多人都知道Ping这个程序,在你决定要看看某台机器是不是能连通的时候会使用Ping这个命令,比如像下面这样:
ping 192.168.1.1
Ping这个名字源于声纳定位,是由Mike Muuss编写的,其目的是为了测试另一台主机是否可达,它会发送ICMP回显请求报文给目的主机,并期望得到ICMP回显应答报文。
一般来说,如果我们不能PING通某台主机,就不能Telnet到那台主机,但随着防火墙等的出现,很多时候PING不通某台主机很有可能是被对方机拦截了PING,而Telnet依然可以到达那台主机,所以PING不PING得通已经不再是机器是否可达的唯一标准了。
熟悉ICMP的读者一定知道,ICMP回显请求报文和ICMP回显应答报文是所有ICMP报文中比较常见的2种而已,下图列出了所有的ICMP报文类型:
ICMP回显报文和ICMP应答报文的格式如下:
ICMP报文是在IP报文之下的,所以在ICMP报文之上还有一个IP报文头。
以下是我编写的PING程序,写得比较简陋:
#include <stdio.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.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 64
#define MAX_WAIT_TIME 5
#define MAX_NO_PACKETS 3
unsigned char sendpacket[PACKET_SIZE];
unsigned 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();
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);
void statistics()
{
printf("\n=============PING==========\n");
printf("%d packages transmitted, %d received, %d lost\n",nsend,nreceived,nsend-nreceived);
close(sockfd);
}
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;
}
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=htons(pack_no);
icmp->icmp_id=htons(pid);//pid
packsize=8+datalen;
tval = (struct timeval *)icmp->icmp_data;
gettimeofday(tval,NULL);
icmp->icmp_cksum = cal_chksum((unsigned short*)icmp,packsize);
return packsize;
}
void send_packet()
{ int packetsize;
while(nsend<MAX_NO_PACKETS)
{ nsend++;
packetsize = pack(nsend);
if(sendto(sockfd/**socket description*/,
sendpacket/**address*/,
packetsize/**length*/,0,
(struct sockaddr*)&dest_addr,sizeof(dest_addr)) <0)
{
perror("sendto error");
continue;
}
sleep(1);
}
}
void recv_packet()
{ int n,fromlen;
extern int errno;
//signal(SIGALRM,statistics);
fromlen = sizeof(from);
while(nreceived<nsend)
{
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++;
}
}
void recv_packet_origin()
{ 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++;
}
}
int unpack(char *buf,int len)
{
int i,iphdrlen;
struct ip *ip;
struct icmp *icmp;
struct timeval *tvsend;
double rtt;
ip = (struct ip*)buf;//header is ip
iphdrlen = ip->ip_hl<<2;
icmp = (struct icmp*)(buf + iphdrlen);
len-= iphdrlen;
if(len < 8){
printf("ICMP packets\'s length is less then 8\n");
return -1;
}
//if((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id==pid))
if(icmp->icmp_type == ICMP_ECHOREPLY)
{ tvsend=(struct timeval*)icmp->icmp_data;
tv_sub(&tvrecv,tvsend);
rtt=tvrecv.tv_sec*1000 + tvrecv.tv_usec/1000;
struct ih_idseq *seq_part = (struct ih_idseq*) &(icmp->icmp_hun);
uint16_t seq = ntohs(seq_part->icd_seq);
printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n",
len,
inet_ntoa(from.sin_addr),
seq,
ip->ip_ttl,
rtt);
}
else return -1;
}
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;
}
int 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: hostname/IP address \n"); exit(1);
}
if((protocol=getprotobyname("icmp")) == NULL){
perror("getprotobyname error"); exit(1);
}
if((sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto))<0){
perror("socket error"); exit(1);
}
setuid(getuid());
setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size));
bzero(&dest_addr,sizeof(dest_addr));
dest_addr.sin_family=AF_INET;
host = gethostbyname(argv[1]);
memcpy((char*)&dest_addr.sin_addr,host->h_addr,host->h_length);
pid=getpid();
send_packet();
recv_packet();
statistics();
return 0;
}
更多参考信息,请看地址:http://docs.52im.net/extend/docs/book/tcpip/vol1/7/