📄 pingsimulator.cpp
字号:
// 类型字段设置成Ping Request
pIcmpHeader->type = ICMP_ECHO_REQUEST;
// 代码字段设置为0
pIcmpHeader->code = 0;
// 校验和暂时置为0
pIcmpHeader->checksum = 0;
// id字段为当前进程的id
pIcmpHeader->id = htons((unsigned short) GetCurrentProcessId());
// 填充序号字段
pIcmpHeader->sequenceNumber = htons(sequenceNumber);
// 填充数据段
memset(pIcmpPacket + sizeof (ICMPHeader), 'E', DEFAULT_ICMP_DATA_SIZE);
// 计算校验和
pIcmpHeader->checksum = getIcmpChecksum((unsigned short*) pIcmpPacket, size);
}
// ************************************************************************************
// 计算校验和
// 功 能:计算ICMP报文的校验和字段
// 参 数:1. pData : 存放ICMP报文的缓冲区
// 2. size : 缓冲区的大小
// 返回值:整个ICMP报文的校验和
// ************************************************************************************
unsigned short getIcmpChecksum(unsigned short* pData, int size)
{
// 首先将校验和置为0
unsigned long checksum = 0;
// 以2字节为单位反复累加
while (size > 1)
{
checksum += * (pData++);
size -= sizeof (unsigned short);
}
// 如果总字节数为奇数,则加上最后一个字节
if (size > 0)
{
checksum += * (unsigned char*) pData;
}
// 将高16位(进位)与低16位累加
checksum = (checksum >> 16) + (checksum & 0xFFFF);
// 将上一步中可能产生的高16位进位再次与低16位累加
checksum += (checksum >> 16);
// 返回16位的网际校验和
return (unsigned short) (~checksum);
}
// *****************************************************************************
// 显示目标主机的信息
// 功 能:显示待Ping主机的IP地址、主机名等信息
// 参 数:无
// 返回值:void
// *****************************************************************************
void showDestHostInfo()
{
// 如果指定了"-a"选项,则获取主机名
if (hasParam_a && 0 == strlen(destHostName))
{
// 根据IP地址获取主机
hostent* pDestHost = gethostbyaddr((char*) &destIP, sizeof (destIP), AF_INET);
// 获取主机名
if (pDestHost != NULL && pDestHost->h_name != NULL)
{
strcpy(destHostName, pDestHost->h_name);
}
}
// 将IP地址转换成字符串
char ipstr[16] = {0};
ipToString(htonl(destIP), ipstr);
// 输出主机名和IP地址等信息
cout << "\n--------------------------------------------------------------------------------";
if (strlen(destHostName) != 0)
{
cout << "Pinging " << destHostName << " [" << ipstr << "] with " << sendBufferSize << " bytes of data :\n\n";
memset(destHostName, 0, sizeof (destHostName));
}
else
{
cout << "Pinging " << ipstr << " with " << sendBufferSize << " bytes of data :\n\n";
}
}
// *******************************************************************************
// 将ip地址转换成点分十进制的形式
// 功 能:将unsigned long型的IP地址转换成点分十进制形式的字符串
// 参 数:1. ip : 待转换的IP地址
// 2. ipstr : 字符缓冲区,用来保存转换后的字符串
// 返回值:void
// *******************************************************************************
void ipToString(unsigned long ip, char* ipstr)
{
sprintf(ipstr, "%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);
}
// *******************************************************************************
// 显示Ping的统计信息
// 功 能:显示对某一台主机的Ping操作的统计信息
// 参 数:statisticsRecord : 待显示的统计信息,包括发包数、收包数、往返时间等
// 返回值:void
// *******************************************************************************
void showStatisticsInfo(const StatisticsRecord& statisticsRecord)
{
// 将目标主机的IP地址转换成点分十进制的形式
char ipstr[16] = {0};
ipToString(ntohl(destIP), ipstr);
// 获得总发包数、总收包数
unsigned long sent = statisticsRecord.totalRequests;
unsigned long received = statisticsRecord.totalResponses;
unsigned long lost = sent - received;
// 输出收发包的统计信息
cout << "\nPing statistics for " << ipstr << " :\n";
cout << " Packets : Sent = " << sent << ", Received = " << received
<< ", Lost = " << lost << " (" << (unsigned long) (((double) lost / sent) * 100) << "% loss).\n";
// 输出时间统计信息
if (received > 0)
{
cout << "Approximate round trip times in milli-seconds :\n";
cout << " Minimum = " << statisticsRecord.minTime << "ms, Maximum = "
<< statisticsRecord.maxTime << "ms, Average = "
<< (statisticsRecord.totalTime / received) << "ms.\n";
}
cout << "--------------------------------------------------------------------------------\n";
}
// **************************************************************************************
// 对接收到的数据包进行解析
// 功 能:解析接收到的数据包,判断是否为正确的Ping响应报文,如果是,则提取相关字段的值
// 参 数:1. pRecvBuffer : 接收缓冲区
// 2. pingRecord : 保存有待Ping主机的IP、Ping请求报文的id、序号,以及ttl等参数
// 返回值:如果接收到的是正确的Ping响应报文,则返回true;否则返回false
// **************************************************************************************
bool parseReceivedPacket(const char* pRecvBuffer, PingRecord& pingRecord)
{
IPHeader* pIPHeader = (IPHeader*) pRecvBuffer;
// 如果不是IPv4报文,或者上层协议不是ICMP,则返回false
if ((pIPHeader->versionAndHeaderLength >> 4) != 4 || (pIPHeader->protocol != 1))
{
return false;
}
// 如果不是正在Ping的主机的IP地址,则返回false
if (pIPHeader->srcIP != pingRecord.ipToPing)
{
return false;
}
// 计算IP报头的长度
int ipHeaderLength = (pIPHeader->versionAndHeaderLength & 0x0F) * 4;
// 得到ICMP报头
ICMPHeader* pIcmpHeader = (ICMPHeader*) (pRecvBuffer + ipHeaderLength);
// 如果不是ICMP回显响应报文,则返回false
if (pIcmpHeader->type != ICMP_ECHO_REPLY)
{
return false;
}
// 如果id和序号不正确,则返回false
if (pIcmpHeader->id != pingRecord.id || pIcmpHeader->sequenceNumber != pingRecord.sequenceNumber)
{
return false;
}
// 记录往返时间,保存TTL
pingRecord.roundTripTime = GetTickCount() - pingRecord.roundTripTime;
pingRecord.ttl = pIPHeader->timeToLive;
// 接收到了正确的Ping响应报文,返回true
return true;
}
// **************************************************************************************
// 显示响应信息
// 功 能:当收到正确的Ping响应报文时,调用该函数来显示响应信息,如往返时间、ttl等
// 参 数:pingRecord : 保存有往返时间、ttl等参数的结构体变量
// 返回值:void
// **************************************************************************************
void showReply(const PingRecord& pingRecord)
{
// 将ip地址转换成点分十进制的形式
char ipstr[16] = {0};
ipToString(ntohl(pingRecord.ipToPing), ipstr);
// 输出相关信息
cout << "Reply from " << ipstr << " : bytes=" << sendBufferSize << " ";
if (pingRecord.roundTripTime < 1)
{
cout << "time<1ms ";
}
else
{
cout << "time=" << pingRecord.roundTripTime << "ms ";
}
cout << "TTL=" << (int) pingRecord.ttl << endl;
}
// **************************************************************************************
// 更新Ping的统计信息
// 功 能:当收到正确的Ping响应报文时,更新统计信息,如收包数、往返时间等
// 参 数:1. statisticsRecord : 保存统计信息的结构体变量
// 2. pingRecord : 保存有往返时间、ttl等值的结构体变量
// 返回值:void
// **************************************************************************************
void setStatisticsRecord(StatisticsRecord& statisticsRecord, const PingRecord& pingRecord)
{
// 将接收到的Ping响应报文的数目加1
statisticsRecord.totalResponses++;
// 更新总的往返时间
statisticsRecord.totalTime += pingRecord.roundTripTime;
// 更新最大往返时间
if (statisticsRecord.maxTime < 0)
{
statisticsRecord.maxTime = pingRecord.roundTripTime;
}
else
{
statisticsRecord.maxTime = ((pingRecord.roundTripTime > statisticsRecord.maxTime) ? pingRecord.roundTripTime : statisticsRecord.maxTime);
}
// 更新最小往返时间
if (statisticsRecord.minTime < 0)
{
statisticsRecord.minTime = pingRecord.roundTripTime;
}
else
{
statisticsRecord.minTime = ((pingRecord.roundTripTime < statisticsRecord.minTime) ? pingRecord.roundTripTime : statisticsRecord.minTime);
}
}
// *************************************************************************************
// Ctrl-C和Ctrl-Break的消息处理函数
// 功 能:自定义Ctrl-C和Ctrl-Break的处理方法
// 参 数:dwCtrlType : 快捷键的类型
// 返回值:如果只想自己处理,而不由系统加以干涉,则返回TRUE;否则返回FALSE
// *************************************************************************************
BOOL WINAPI ctrlHandler(DWORD dwCtrlType)
{
// 如果是Ctrl-C,则终止对当前主机的Ping;如果有多台目标主机,则继续Ping下一台
if (CTRL_C_EVENT == dwCtrlType)
{
pingRequestsToSend = 0;
return TRUE;
}
// 如果是Ctrl-Break,则终止对所有主机的Ping操作
if (CTRL_BREAK_EVENT == dwCtrlType)
{
pingRequestsToSend = 0;
numberOfHostsToPing = 0;
return TRUE;
}
// 如果不是Ctrl-C或者Ctrl-Break,则交由系统来处理
return FALSE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -