?? 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 + -