📄 pingsimulator.cpp
字号:
else // 如果都不是以上选项,则认为是目的主机的IP地址或者主机名
{
destIP = inet_addr(argv[i]);
if (destIP != INADDR_NONE)
{
hasDestHost = true;
onlyOneDestHost = true;
memset(destHostName, 0, sizeof (destHostName));
continue;
}
else if (0 == strcmp(argv[i] + strlen(argv[i]) - 2, ".*"))
{
// 用数字1替换末尾的*号,并重新尝试转换
char* str = new char[strlen(argv[i]) + 1];
strcpy(str, argv[i]);
str[strlen(str) - 1] = '1';
// 再次尝试进行转换
destIP = inet_addr(str);
if (destIP != INADDR_NONE)
{
hasDestHost = true;
onlyOneDestHost = false;
memset(destHostName, 0, sizeof (destHostName));
continue;
}
}
// 如果上面的转换都没有成功,则认为用户输入的是目标主机的主机名
hostent* pDestHost = gethostbyname(argv[i]);
if (pDestHost != NULL && pDestHost->h_addr_list != NULL)
{
destIP = (*(in_addr*) pDestHost->h_addr_list[0]).S_un.S_addr;
hasDestHost = true;
onlyOneDestHost = true;
strcpy(destHostName, argv[i]);
continue;
}
else
{
cout << "\n错误:\n";
cout << " 参数\"" << argv[i] << "\"不是有效的IP地址或者主机名.\n";
return false;
}
}
} // for循环结束
return true;
}
}
// ********************************************************************************
// 显示命令的使用方法
// 功 能:输出程序的用法、选项、以及提示信息
// 参 数:无
// 返回值:void
// ********************************************************************************
void showUsage()
{
// 输出使用方法
cout << "用法 :\n";
cout << " PingSimulator.exe [-t] [-a] [-n count] [-l size] [i TTL] [-v TOS]\n";
cout << " [-w timeout] [-1] [-2] target_name\n";
// 输出选项的详细介绍
cout << "选项:\n";
cout.setf(ios::left);
cout << " " << setw(16) << "-t" << "Ping 指定的主机,直到按[Ctrl-C]键强行终止.\n";
cout << " " << setw(16) << "-a" << "将目的主机的IP地址解析为主机名.\n";
cout << " " << setw(16) << "-n count" << "发送count个Ping请求报文.\n";
cout << " " << setw(16) << "-l size" << "设定发送缓冲区的大小为size字节.\n";
cout << " " << setw(16) << "-i TTL" << "设定生存时间为TTL指定的数值.\n";
cout << " " << setw(16) << "-v TOS" << "设定服务类型为TOS指定的数值.\n";
cout << " " << setw(16) << "-w timeout" << "设定每一个Ping响应报文的超时时间为timeout毫秒.\n";
cout << " " << setw(16) << "-1" << "仅对指定的一台主机进行Ping操作.\n";
cout << " " << setw(16) << "-2" << "对指定的多台主机进行Ping操作.\n\n";
cout << " " << setw(16) << "target_name" << "目的主机的IP地址或者主机名.\n";
cout << " " << setw(16) << " " << "如果指定了参数\"-1\",则target_name表示唯一的目的主机.\n";
cout << " " << setw(16) << " " << "如果指定了参数\"-2\",则target_name表示多个目的主机.\n";
cout << " " << setw(16) << " " << "如果没有指定这两个参数,则根据target_name的形式自动进行判断.\n";
// 显示提示
cout << "提示:\n";
cout << " 在Ping的过程当中,您可以使用 [Ctrl-C] 键终止对某一台主机的Ping操作\n";
cout << " 也可是使用 [Ctrl-Break] 键来终止对全部主机的Ping操作\n\n";
// 暂停一下
system("pause");
}
// *********************************************************************************
// 检测参数是否存在冲突的情况
// 功 能:检测是否有不能共存的参数,如果有,则输出错误信息
// 参 数:无
// 返回值:如果参数不存在冲突,则返回true;否则返回false
// *********************************************************************************
bool checkParams()
{
// 选项"-t"与"-n count"不能共存
if (hasParam_t && hasParam_n_count)
{
cout << "\n错误:\n";
cout << " 选项\"-t\"表示不停的Ping,而\"-n count\"指定Ping的次数,二者不能同时使用.\n";
return false;
}
// 选项"-1"与"-2"不能共存
if (hasParam_1 && hasParam_2)
{
cout << "\n错误:\n";
cout << " 选项\"-1\"表示仅有一台目标主机,选项\"-2\"表示有多台目标主机,二者不能同时使用.\n";
return false;
}
// 检测是否已经获得了目标主机的IP地址,如果没有,则报错
if (!hasDestHost)
{
cout << "\n错误:\n";
cout << " 必须指定目标主机的IP地址或者主机名.\n";
return false;
}
// 检测IP地址的类型与参数是否不匹配
if (hasDestHost && onlyOneDestHost && hasParam_2)
{
cout << "\n错误:\n";
cout << " 选项\"-2\"表示有多台目标主机,与目的IP的类型不匹配.\n";
return false;
}
// 检测IP地址的类型与参数是否不匹配
if (hasDestHost && !onlyOneDestHost && hasParam_1)
{
cout << "\n错误:\n";
cout << " 选项\"-1\"表示仅有一台目标主机,与目的IP的类型不匹配.\n";
return false;
}
// 如果没有发生以上冲突,则认为参数都是有效的
return true;
}
// ************************************************************************************
// 设置socket选项
// 功 能:对创建的原始套接字的选项进行设定或更改
// 参 数:无
// 返回值:void
// ************************************************************************************
void setSocketOptions()
{
// 设置发送缓冲区的大小
setsockopt(rawSocket, SOL_SOCKET, SO_SNDBUF, (char*) &sendBufferSize, sizeof (sendBufferSize));
// 设置ttl
setsockopt(rawSocket, IPPROTO_IP, IP_TTL, (char*) &ttl, sizeof (ttl));
// 设置tos
setsockopt(rawSocket, IPPROTO_IP, IP_TOS, (char*) tos, sizeof (tos));
// 设置超时时间
setsockopt(rawSocket, SOL_SOCKET, SO_SNDTIMEO, (char*) &timeout, sizeof (timeout));
setsockopt(rawSocket, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof (timeout));
}
// ***********************************************************************************
// 对目标主机进行Ping操作
// 功 能:对一台或多台目标主机进行Ping操作
// 参 数:无
// 返回值:void
// ***********************************************************************************
void ping()
{
// 获取目标主机的数目
numberOfHostsToPing = (onlyOneDestHost ? 1 : 255);
// 循环处理每一个目标主机
while (numberOfHostsToPing > 0)
{
// 重置要发送的Ping请求报文的数目
pingRequestsToSend = totalPingRequests;
// 初始化统计信息
StatisticsRecord statisticsRecord;
memset(&statisticsRecord, 0, sizeof (statisticsRecord));
statisticsRecord.maxTime = -1; // 负数表示无效值,即没有目前还没有收到Ping响应报文
statisticsRecord.minTime = -1; // 负数表示无效值,即没有目前还没有收到Ping响应报文
// 显示目标主机的主机名或者IP地址等信息
showDestHostInfo();
// 填充目标主机的socket地址
sockaddr_in destSocketAddress;
memset(&destSocketAddress, 0, sizeof (destSocketAddress));
destSocketAddress.sin_family = AF_INET;
destSocketAddress.sin_addr.S_un.S_addr = destIP;
// Ping当前的目标主机
while (pingRequestsToSend > 0) // 当指定数目的Ping请求未发送完时,继续进行Ping操作
{
// 构造Ping请求报文
char icmpSendBuffer[sizeof (ICMPHeader) + DEFAULT_ICMP_DATA_SIZE];
fillIcmpPacket(icmpSendBuffer, sizeof (ICMPHeader) + DEFAULT_ICMP_DATA_SIZE);
// 记录当前时间以及报文的序号
PingRecord pingRecord;
pingRecord.ipToPing = destIP;
pingRecord.id = htons((unsigned short) GetCurrentProcessId());
pingRecord.sequenceNumber = htons(sequenceNumber++);
pingRecord.roundTripTime = GetTickCount();
// 发送Ping请求报文
if (SOCKET_ERROR == sendto(rawSocket, icmpSendBuffer, sizeof (icmpSendBuffer), 0, (sockaddr*) &destSocketAddress, sizeof (destSocketAddress)))
{
cout << "发送Ping请求报文失败.\n";
return;
}
else // 发送成功,将已发送的Ping请求报文数目加1
{
statisticsRecord.totalRequests++;
}
// 准备接收Ping响应报文
sockaddr_in from; // 发送响应报文的socket地址
int fromLength = sizeof (from); // socket地址的长度
int recvDataLength = 0; // 接收到的数据的长度
char recvIcmpBuffer[MAX_ICMP_PACKET_SIZE]; // 接收缓冲区
// 循环接收Ping响应报文,直到获得所需的Ping响应,或者超时
while (true)
{
recvDataLength = recvfrom(rawSocket, recvIcmpBuffer, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*) &from, & fromLength);
if (recvDataLength != SOCKET_ERROR) // 接收到数据
{
// 对接收到的数据报进行解析,如果是正确的Ping响应报文则显示之,并更新统计信息
if (parseReceivedPacket(recvIcmpBuffer, pingRecord))
{
showReply(pingRecord); // 显示响应信息
setStatisticsRecord(statisticsRecord, pingRecord); // 更新统计信息
Sleep(1000); // 暂停1秒后,发送下一个Ping请求
break;
}
else // 如果接收到了错误的包,则重新接收
{
continue;
}
}
else if (WSAETIMEDOUT == WSAGetLastError()) // 如果超时,则输出超时信息,并发送下一个Ping请求
{
cout << "Request timed out.\n";
break;
}
else // 接收出错
{
cout << "接收Ping响应报文时发生错误.\n";
return;
}
}
// 如果没有启用"-t"选项,则将需要发送的Ping请求报文的数目减1
if (!hasParam_t)
{
pingRequestsToSend--;
}
}
// 输出统计信息
showStatisticsInfo(statisticsRecord);
// 将未处理的目标主机的数目减1
numberOfHostsToPing--;
// 将目标主机设为当前所Ping主机的下一台
destIP = (htonl)((ntohl)(destIP) + 1);
}
}
// ***********************************************************************************
// 填充ICMP报文
// 功 能:填充ICMP报文的各个字段
// 参 数:1. pIcmpPacket : 发送缓冲区,用来存放待发送的Ping请求报文
// 2. size : 发送缓冲区的大小
// 返回值:void
// ***********************************************************************************
void fillIcmpPacket(char* pIcmpPacket, int size)
{
ICMPHeader* pIcmpHeader = (ICMPHeader*) pIcmpPacket;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -