Chinaunix首页 | 论坛 | 博客
  • 博客访问: 977242
  • 博文数量: 145
  • 博客积分: 1302
  • 博客等级: 中尉
  • 技术积分: 1778
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-07 16:00
文章分类

全部博文(145)

文章存档

2018年(1)

2016年(1)

2015年(6)

2014年(4)

2013年(59)

2012年(32)

2011年(36)

2009年(1)

2007年(2)

2006年(3)

分类: 网络与安全

2013-04-27 17:39:46

分为两个部分:一个为头文件ping.h,另一个为ping.cpp。

首先,看PING.h的头文件定义代码:



//在该头文件中定义IP和ICMP协议头的结构
#pragma pack(1)           //指定结构成员使用紧凑对齐
#define ICMP_ECHOREPLY 0
#define ICMP_ECHOREQ   8


//定义符合RFC791的IP协议头
typedef struct tagIPHDR
{
    u_char VIHL;            //Version and IHL(指明头部的长度)
    u_char TOS;             //服务类型-Tape Operating System 磁带操作系统([美]IBM公司软件)
    u_short TotLen;            //总长度
    u_short ID;                //标识 Identification
    u_short FlagOff;            //Flags and Fragment Offset
u_char TTL;             //生存期
u_char Protocol;        //协议
u_short Checksum;        //校验和
struct in_addr iaSrc;   //英特网源地址
struct in_addr isDst;   //英特网目的地址
}IPHDR,*PIPHDR;


//定义符合RFC792的ICMP协议头
typedef struct tagICMPHDR
{
u_short ID;            //标识
    u_char Type;          //类型
u_char Code;          //代码
u_short Checksum;      //校验和
    u_short Seq;           //序列 Sequence
    char Data;             //数据
}ICMPHDR,*PICMPHDR;


//请求回送的数据长度
#define REQ_DATASIZE 32


//ICMP回送请求的数据结构
typedef struct tagECHOREQUEST
{
ICMPHDR icmpHdr;      //ICMP协议头
DWORD dwTime;         //数据传输时间
char cData[REQ_DATASIZE];   //传输的数据
}ECHOREQUEST,*PECHOREQUEST;


//ICMP回送应答
typedef struct tagECHOREPLY
{
IPHDR         ipHdr;    //IP协议头
ECHOREQUEST   echoRequest;   //
char          cFiller[256];
}ECHOREPLY,*PECHOREPLY;
#pragma pack()

2.再看ping.cpp部分的源代码:

// SOCK_RAW.cpp : Defines the entry point for the console application.
// 在project->setting->Debug中program arguments输入IP地址   测试程序:127.0.0.1

#include
#include
#include
#include "PING.h"


//函数声明
void Ping(LPCSTR pstrHost);
void ReportError(LPCSTR pstrFrom);
int WaitForEchoReply(SOCKET s);
u_short in_cksum(u_short *addr,int len);


//ICMP回送请求和应答函数声明
int SendEchoRequest(SOCKET,LPSOCKADDR_IN);
DWORD RecvEchoReply(SOCKET,LPSOCKADDR_IN,u_char*);


//主程序
void main(int argc,char **argv)
{
WSADATA wsaData;
WORD wVersionRequested=MAKEWORD(1,1);   //Winsock1.1
int nRet;

//命令行参考检查
if(argc!=2)
{
   fprintf(stderr,"\nUsage:ping hostname\n");
   return;
}

//初始化Winsock
nRet=WSAStartup(wVersionRequested,&wsaData);
if(nRet)
{
   fprintf(stderr,"\nError initializing Winsock\n");
}

//Winsock版本检查
if(wsaData.wVersion!=wVersionRequested)
{
   fprintf(stderr,"\nWinsock version not supported\n");
   return;
}

//调用Ping函数
Ping(argv[1]);

//释放Winsock
WSACleanup();
}


///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
//Ping()函数
//调用SendEchoRequest()和RecvEchoReply()函数并输出结果
void Ping(LPCSTR pstrHost)
{
SOCKET rawSocket;
LPHOSTENT lpHost;
struct sockaddr_in saDest;
struct sockaddr_in saSrc;
DWORD dwTimeSent;
DWORD dwElapsed;
u_char cTTL;
int nLoop;
int nRet;

//创建一个原始套接口
rawSocket=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
    if(rawSocket==SOCKET_ERROR)
{
        ReportError("socket()");
        return;
}

    //根据主机名查询主机地址
// lpHost=gethostbyname(pstrHost);
saDest.sin_addr.s_addr = inet_addr(pstrHost);
     //根据主机地址查询主机名
lpHost=gethostbyaddr((char *)&saDest.sin_addr,4,AF_INET);
if(lpHost==NULL)
    {
   fprintf(stderr,"\nHost not found:%\n",pstrHost);
   return;
}


//设置目标套接口地址
saDest.sin_addr.s_addr=*((u_long FAR*)(lpHost->h_addr));
saDest.sin_family=AF_INET;
saDest.sin_port=0;


//输出Ping程序的提示信息例如进行本机Ping时,输出:Pinging localhost [127.0.0.1] with 32 bytes of data:
printf("\nPinging localhost srvhostname %s to desthostIp %s [$s] with %d bytes of data:\n",
        lpHost->h_name,
     inet_ntoa(saDest.sin_addr),
     REQ_DATASIZE);

//控制Ping执行的次数
for(nLoop=0;nLoop<4;nLoop++)
{
  
   //发送ICMP回送请求
   SendEchoRequest(rawSocket,&saDest);
  
   //使用select()等待接收回送的数据
   nRet=WaitForEchoReply(rawSocket);
   if(nRet==SOCKET_ERROR)
   {
    ReportError("select()");
    break;
   }
   if(!nRet)
   {
      printf("\nTimeOut");     //输出超时提示
    break;
   }
  
   //接收应答
   dwTimeSent=RecvEchoReply(rawSocket,&saSrc,&cTTL);

   //计算传输时间,并输出提示信息。如:Reply from:127.0.0.1:bytes=32time=0ms TTL=128
        dwElapsed=GetTickCount()-dwTimeSent;
   printf("\nReply from: %s: bytes=%d time=%ldms TTL=%d",
       inet_ntoa(saSrc.sin_addr),
     REQ_DATASIZE,
     dwElapsed,
     cTTL);
}

printf("\n");
nRet=closesocket(rawSocket);
if(nRet==SOCKET_ERROR)
   ReportError("closesocket()");
}


//SendEchoRequest()
//给目标站点发送回送请求
int SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr)
{
static ECHOREQUEST echoReq;
static nId=1;
static nSeq=1;
int nRet;

//填写回送请求信息
    echoReq.icmpHdr.Type          =ICMP_ECHOREQ;
echoReq.icmpHdr.Code          =0;
echoReq.icmpHdr.Checksum      =0;
echoReq.icmpHdr.ID            =nId++;
echoReq.icmpHdr.Seq           =nSeq++;

//填写要发送的数据
for(nRet=0;nRet    echoReq.cData[nRet]=' '+nRet;

//保存发送时间
echoReq.dwTime=GetTickCount();

//数据存入包中并计算校验和
echoReq.icmpHdr.Checksum=in_cksum((u_short*)&echoReq,sizeof(ECHOREQUEST));

//发送回送请求
nRet=sendto(s,
          (LPSTR)&echoReq,
     sizeof(ECHOREQUEST),
     0,
     (LPSOCKADDR)lpstToAddr,
     sizeof(SOCKADDR_IN));
if(nRet==SOCKET_ERROR)
        ReportError("sendto()");
return(nRet);

}
//RecvEchoReply()
//接收数据并对字段进行分析
DWORD RecvEchoReply(SOCKET s,LPSOCKADDR_IN lpsaFrom,u_char *pTTL)
{
ECHOREPLY echoReply;
int nRet;
int nAddrLen=sizeof(struct sockaddr_in);

//接收回送应答
   nRet=recvfrom(s,
               (LPSTR)&echoReply,
       sizeof(ECHOREPLY),
       0,
       (LPSOCKADDR)lpsaFrom,
       &nAddrLen);
  
//检查返回的值
   if(nRet==SOCKET_ERROR)
    ReportError("recvfrom()");

//返回发送时间的IP生存时间TTL
   *pTTL=echoReply.ipHdr.TTL;
   return(echoReply.echoRequest.dwTime);
}


//ReportError()
//输出发生的错误
void ReportError(LPCSTR pWhere)
{
fprintf(stderr,"n%s error: %d\n",
   WSAGetLastError());
}

//WaitForEchoReply()
//使用select()函数进行状态查询,以确定是否有数据等待读取
int WaitForEchoReply(SOCKET s)
{
struct timeval Timeout;
fd_set readfds;
readfds.fd_count=1;
    readfds.fd_array[0]=s;
Timeout.tv_sec=5;
Timeout.tv_usec=0;
return(select(1,&readfds,NULL,NULL,&Timeout));
}


//in_cksum()
//计算校验和
u_short in_cksum(u_short *addr,int len)
{
register int nleft=len;
register u_short *w=addr;
register u_short answer;
register int sum=0;

while(nleft>1)
{
   sum+=*w++;
   nleft-=2;
}

if(nleft==1)
{
   u_short u=0;
   *(u_char*)(&u)=*(u_char*)w;
   sum+=u;
}

sum=(sum>>16)+(sum & 0xffff);
sum+=(sum>>16);           //add carry 加进位
answer=~sum;              //truncate to 16 bits 截断到16位
return(answer);
}

3. 编译运行,ping 127.0.0.1 测试下显示localhost和路由 ,再用ping [yourIP] 看能否显示主机名和路由

阅读(2182) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~