//----------------------头文件------------------------
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include<conio.h>
#pragma comment(lib, "ws2_32.lib") //提供对以下网络相关API的支持
#define REPLY 0 //ICMP 回送回应报文
#define REQUEST 8 //ICMP 回送请求报文
#define DATASIZE 32 // 请求数据报的大小
#include <iostream>
using namespace std;
//----------------------数据结构定义------------------------
//定义IP首部的数据结构
typedef struct IPHeader
{
unsigned char VerLenth; //定义首部长度和版本,高4位为首部长度,低4位为版本
unsigned char Tos; //服务类型/区分服务
unsigned short TotalLenth; //总长度
unsigned short ID; //标识号
unsigned short Frag_Flags; //片偏移量
unsigned char TTL; //生存时间
unsigned char Protocol; //协议
unsigned short Checksum; //首部校验和
struct in_addr SrcIP; //源 IP 地址
struct in_addr DestIP; //目的地址
}IPHDR, *PIPHDR;
//定义 ICMP 首部格式
typedef struct ICMPHeader
{
unsigned char Type; //类型
unsigned char Code; //代码
unsigned short Checksum; //首部校验和
unsigned short ID; //标识
unsigned short Seq; //序列号
char Data; //数据
}ICMPHDR, *PICMPHDR;
//定义 ICMP 回应请求
typedef struct ECHOREQUEST
{
ICMPHDR icmpHdr;
DWORD dwTime;
char cData[DATASIZE];
}ECHOREQUEST, *PECHOREQUEST;
//定义 ICMP 回应答复
typedef struct ECHOREPLY
{
IPHDR ipHdr;
ECHOREQUEST echoRequest;
char cFiller[256];
}ECHOREPLY,*PECHOREPLY;
//-----------------------------------------------------
//计算校验和
unsigned short CheckSum(unsigned short *buffer, int len)
{
register int nleft = len;
register unsigned short *w = buffer;
register unsigned short answer;
register int sum = 0;
//使用32的位累加器,进行16位的计算
while ( nleft > 1 )
{
sum += *w++;
nleft -= 2;
}
if ( nleft == 1 )
{
unsigned short u = 0;
*(unsigned char *)(&u) = *(unsigned char*)w;
sum += u;
}
//将反馈的 16 位从高位移到低位
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}
//发送回应请求函数
int SendRequest(SOCKET s, struct sockaddr_in *lpstToAddr)
{
static ECHOREQUEST echoReq;
static int nId = 1;
static int nSeq = 1;
int nRet;
//填充回应请求消息
echoReq.icmpHdr.Type = REQUEST;
echoReq.icmpHdr.Code = 0;
echoReq.icmpHdr.Checksum = 0;
echoReq.icmpHdr.ID = nId++;
echoReq.icmpHdr.Seq = nSeq++;
//填充要发送的数据
for (nRet = 0; nRet < DATASIZE; nRet++)
{
echoReq.cData[nRet] = '1' + nRet;
}
//存储发送的时间
echoReq.dwTime = GetTickCount();
//计算回应请求的校验和
echoReq.icmpHdr.Checksum = CheckSum((unsigned short*)&echoReq, sizeof(ECHOREQUEST));
//发送回应请求
nRet = sendto(s,(LPSTR)&echoReq,sizeof(ECHOREQUEST),0,(struct sockaddr*)lpstToAddr,sizeof(SOCKADDR_IN));
if (nRet == SOCKET_ERROR)
{
printf("发送失败\t error:%d\n", WSAGetLastError());
}
return (nRet);
}
//接收应答回复并进行解析
DWORD RecvReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, unsigned 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)
{
printf("接收失败\t error:%d\n",WSAGetLastError());
}
//记录返回的 TTL
*pTTL = echoReply.ipHdr.TTL;
//返回应答时间
return(echoReply.echoRequest.dwTime);
}
//等待回应答复 ,使用select模型
int WaitForReply(SOCKET s)
{
struct timeval timeout;
fd_set readfds;
readfds.fd_count = 1;
readfds.fd_array[0] = s;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
return(select(1, &readfds, NULL, NULL, &timeout));
}
//PING
void Ping(char *pstrHost,bool logic)
{
char c;
SOCKET rawSocket;
LPHOSTENT lpHost;
struct sockaddr_in destIP;
struct sockaddr_in srcIP;
DWORD dwTimeSent;
DWORD dwElapsed;
unsigned char cTTL;
int nLoop,k=4;
int nRet,minimum=100000,maximum=0,average=0;
int sent=4,reveived=0,lost=0;
//创建原始套接字 ,ICMP 类型
//原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包)。另外,必须在管理员权限下才能使用原始套接字。
rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
// 第二个注释函数 socket
if (rawSocket == SOCKET_ERROR)
{
printf("原始套接字创建失败\t error:%d\n", WSAGetLastError());
return;
}
//检测目标主机
lpHost = gethostbyname(pstrHost);
if (lpHost==NULL)
{
printf("没有找到目标主机:%s\n", pstrHost);
return;
}
//设置目标机地址
destIP.sin_addr.s_addr = *((u_long FAR*)(lpHost->h_addr)); // 设置目标 IP
destIP.sin_family = AF_INET; //地址规格
destIP.sin_port = 0;
//提示开始进行 PING
printf("\n正在Ping %s [%s] 具有%d字节的数据......\n",pstrHost,inet_ntoa(destIP.sin_addr),DATASIZE);
//发起多次 PING 测试
for (nLoop=0; nLoop<k; nLoop++){
if (logic) k=k+1;
//发送 ICMP 回应请求
SendRequest(rawSocket, &destIP);
//等待回复的数据
nRet = WaitForReply(rawSocket);
if(nRet == SOCKET_ERROR)
{
printf("select() error:%d\n", WSAGetLastError());
break;
}
if (!nRet)
{
lost++;
printf("\n请求超时!");
continue;
}
//接收回复
dwTimeSent = RecvReply(rawSocket, &srcIP, &cTTL);
reveived++;
//计算花费的时间
dwElapsed = GetTickCount() - dwTimeSent;
if(dwElapsed>maximum) maximum=dwElapsed;
if(dwElapsed<minimum) minimum=dwElapsed;
average+=dwElapsed;
printf("\n来自%s的回复-->\t字节:%d\t时间:%ldms\tTTL:%d",
inet_ntoa(srcIP.sin_addr),DATASIZE,dwElapsed,cTTL);
if(_kbhit()) // Use _getch to throw key away.
{
if ((c=_getch())==0x2) //crrl -b
break;
} else
Sleep(1000);
}
printf("\n\n");
printf("%s的Ping统计信息如下:\n",inet_ntoa(srcIP.sin_addr));
printf("数据包: 已发送=%d\t已接收=%d\t丢失=%d\n", sent,reveived,lost,(float)(lost*1.0/sent)*100);
if(lost==0)
{
printf("往返时间如下:\n");
printf("最短时间=%dms\t最长时间=%dms\t平均时间=%dms\n",minimum,maximum,average/sent);
}
printf("****************************************************");
nRet = closesocket(rawSocket);
if (nRet == SOCKET_ERROR)
{
printf("closesocket() error:%d\n", WSAGetLastError());
}
}
//主程序
void main()
{
printf("*********************WelcometothePingTest*********************\n");
printf("*************************Time:2018.11.15*************************\n");
while(1)
{
WSADATA wsd;// 检测输入的参数
//初始化 Winsock
if(WSAStartup(MAKEWORD(1, 1), &wsd) != 0){// 第一个函数说明 WSAStartup()
printf(" 加载 Winsock 失败 !\n");
}
char opt1[100];
char *ptr=opt1;
bool log=false;
printf("\n\n请输入ip地址/域名:");
cin.getline(opt1,100,'\n');//ping 的地址 字符串
if(strstr(opt1, "-t")!=NULL)
{
log=true;
strncpy(ptr,opt1+0,strlen(opt1)-3);// 把原字符串的最后三位截取
ptr[strlen(opt1)-2]=0;
//printf("%s", ptr);
}
//开始 PING
Ping(ptr,log);
//程序释放 Winsock 资源
WSACleanup();
}
}