?? pingsimulator.cpp
字號:
#pragma comment(lib, "Ws2_32.lib") // 加載Ws2_32.lib
#include "packetheader.h" // 自定義的頭文件
#include <iostream>
#include <iomanip>
#include <cstring>
#include <Winsock2.h>
#include <ws2tcpip.h>
using namespace std;
// *************************** begin 常量定義 ************************************
const unsigned char ICMP_ECHO_REQUEST = 8; // Ping請求報文的類型字段為8
const unsigned char ICMP_ECHO_REPLY = 0; // Ping響應(yīng)報文的類型字段為0
const int DEFAULT_ICMP_DATA_SIZE = 32; // Ping請求報文數(shù)據(jù)段的默認大小
const int MAX_ICMP_PACKET_SIZE = 1024; // Ping請求報文的最大值
// *************************** end 常量定義 **************************************
// *************************** begin 變量定義 ************************************
// 命令提供的選項,以及選項的值
bool hasParam_t = false; // 選項"-t",表示一直ping,直到輸入Ctrl-C強行終止
bool hasParam_a = false; // 選項"-a",表示將目標(biāo)主機的IP地址解析成主機名
bool hasParam_n_count = false; // 選項"-n count",表示Ping的次數(shù)
long totalPingRequests = 4; // 對每臺主機ping的次數(shù),默認為4
bool hasParam_l_size = false; // 選項"-l size",設(shè)定發(fā)送緩沖區(qū)的大小
long sendBufferSize = 32; // 發(fā)送緩沖區(qū)的大小
bool hasParam_i_ttl = false; // 選項"-i TTL",設(shè)定TTL的值
unsigned char ttl = 255; // 生存時間
bool hasParam_v_tos = false; // 選項"-v TOS",設(shè)定TOS的值
unsigned char tos = 0; // 服務(wù)類型
bool hasParam_w_timeout = false; // 選項"-w timeout",設(shè)定接收的超時時間(單位:ms)
long timeout = 5000; // 接收Ping響應(yīng)報文的超時時間,默認為5秒
bool hasParam_1 = false; // 選項"-1",表示僅有一臺目標(biāo)主機
bool hasParam_2 = false; // 選項"-2",表示有多臺目標(biāo)主機
bool hasDestHost = false; // 如果獲得了目標(biāo)主機的有效IP,則為true;否則為false
bool onlyOneDestHost = true; // 如果為true,則表示僅有一臺目標(biāo)主機,否則有多臺
unsigned long destIP; // 目標(biāo)主機的IP地址
char destHostName[256] = {0}; // 目的主機名
// 原始套接字
SOCKET rawSocket;
// 控制及狀態(tài)變量
int numberOfHostsToPing = 0; // 等待被Ping的主機的數(shù)目
long pingRequestsToSend = 0; // 對于指定的目標(biāo)主機,需要發(fā)送的Ping請求報文的數(shù)目
unsigned short sequenceNumber = 0; // Ping請求報文的序號
// *************************** end 變量定義 **************************************
// *************************** begin 函數(shù)聲明 ************************************
bool parseCmdlineParams(int argc, char* argv[]); // 解析命令行參數(shù)
void showUsage(); // 顯示程序的用法以及選項等信息
bool checkParams(); // 檢測命令行參數(shù)是否存在沖突
void setSocketOptions(); // 設(shè)置socket選項,如TTL、TOS、timeout等
void ping(); // 對目標(biāo)主機進行Ping操作
void fillIcmpPacket(char* pIcmpPacket, int size); // 填充Ping請求報文
unsigned short getIcmpChecksum(unsigned short* pData, int size); // 計算ICMP報文的校驗和字段
void showDestHostInfo(); // 顯示目標(biāo)主機的相關(guān)信息,如IP地址、主機名等
void ipToString(unsigned long ip, char* ipstr); // 將unsigned long型的IP地址轉(zhuǎn)換成點分十進制形式的字符串
void showStatisticsInfo(const StatisticsRecord& statisticsRecord); // 顯示Ping的統(tǒng)計信息
bool parseReceivedPacket(const char* pRecvBuffer, PingRecord& pingRecord); // 對收到的數(shù)據(jù)包進行解析
void showReply(const PingRecord& pingRecord); // 顯示接收到的Ping響應(yīng)信息
void setStatisticsRecord(StatisticsRecord& statisticsRecord, const PingRecord& pingRecord); // 更新統(tǒng)計信息
BOOL WINAPI ctrlHandler(DWORD dwCtrlType); // 自定義函數(shù),用來處理快捷鍵 Ctrl-C 和 Ctrl-Break
// *************************** end 函數(shù)聲明 **************************************
// *******************************************************************************
// 主函數(shù)
// 功 能:程序的入口函數(shù)
// 參 數(shù):1. argc : 命令行參數(shù)的個數(shù)
// 2. argv : 命令行參數(shù)
// 返回值:當(dāng)程序正常結(jié)束時,返回0;否則返回其他值
// *******************************************************************************
int main(int argc, char* argv[])
{
// 更改控制臺的標(biāo)題
char* consoleTitle = "PingSimulator - 使用ICMP協(xié)議實現(xiàn)簡單的Ping程序 - (學(xué)號:2120080354 姓名:劉洪濤)";
SetConsoleTitle(consoleTitle);
// 更改控制臺的背景色為黑色,前景色為淡綠色
system("color 0A");
// 初始化Windows Socket dll
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
cout << "\n初始化Windows Socket dll失敗.\n";
system("color"); // 恢復(fù)控制臺的默認背景色和前景色
return -1;
}
// 首先對命令行參數(shù)進行解析
if (!parseCmdlineParams(argc, argv))
{
// 如果解析命令行參數(shù)出錯,則退出
system("color"); // 恢復(fù)控制臺的默認背景色和前景色
return -1;
}
// 檢測參數(shù)是否有沖突的情況
if (!checkParams())
{
// 如果參數(shù)存在沖突,則報錯并退出
system("color"); // 恢復(fù)控制臺的默認背景色和前景色
return -1;
}
// 創(chuàng)建原始套接字
rawSocket = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (INVALID_SOCKET == rawSocket)
{
cout << "\n創(chuàng)建原始套接字失敗.\n";
system("color"); // 恢復(fù)控制臺的默認背景色和前景色
return -1;
}
// 根據(jù)命令行參數(shù)對socket選項進行設(shè)置
setSocketOptions();
// 添加一個函數(shù)ctrlHandler,用來響應(yīng)Ctrl-C和Ctrl-Break消息
SetConsoleCtrlHandler(ctrlHandler, true);
// 進行Ping操作
ping();
// 將ctrlHandler函數(shù)從處理列表中移除
SetConsoleCtrlHandler(ctrlHandler, false);
// 恢復(fù)控制臺的默認背景色和前景色
system("color");
// 注銷Windows Socket dll
return WSACleanup();
}
// ********************************************************************************
// 解析命令行參數(shù)
// 功 能:對命令行參數(shù)進行解析,從中提取出目標(biāo)主機的IP地址以及其他選項等信息
// 參 數(shù):1. argc : 命令行參數(shù)的個數(shù)
// 2. argv : 命令行參數(shù)
// 返回值:如果解析過程中發(fā)生錯誤,則返回false;否則返回true
// ********************************************************************************
bool parseCmdlineParams(int argc, char* argv[])
{
if (1 == argc) // 如果沒有附加參數(shù),則輸出命令的使用方法
{
showUsage();
return false;
}
else // 如果有附加參數(shù),則對其進行解析
{
for (int i = 1; i < argc; i++)
{
if (0 == strcmp(argv[i], "-t")) // 處理"-t"選項
{
hasParam_t = true;
continue;
}
else if (0 == strcmp(argv[i], "-a")) // 處理"-a"選項
{
hasParam_a = true;
continue;
}
else if (0 == strcmp(argv[i], "-n")) // 處理"-n count"選項
{
hasParam_n_count = true;
if (i + 1 < argc) // 如果選項字段沒有結(jié)束
{
// 獲取count字段
totalPingRequests = atol(argv[++i]);
if (totalPingRequests > 0)
{
// 已經(jīng)成功獲取count字段,轉(zhuǎn)去處理下一個參數(shù)
continue;
}
else // count字段無效
{
cout << "\n錯誤:\n";
cout << " 選項\"-n count\"中的count字段無效,請輸入PingSimulator.exe查看詳細用法.\n";
return false;
}
}
else
{
cout << "\n錯誤:\n";
cout << " 未在選項\"-n count\"中找到count字段,請輸入PingSimulator.exe查看詳細用法.\n";
return false;
}
}
else if (0 == strcmp(argv[i], "-l")) // 處理選項"-l size"
{
hasParam_l_size = true;
if (i + 1 < argc)
{
// 獲取size字段
sendBufferSize = atoi(argv[++i]);
if (sendBufferSize >= 32 && sendBufferSize <= 65500)
{
// 已正確獲取size字段,轉(zhuǎn)去處理下一個選項
continue;
}
else
{
cout << "\n錯誤:\n";
cout << " 選項\"-l size\"中的size字段無效,請輸入PingSimulator.exe查看詳細用法.\n";
return false;
}
}
else
{
cout << "\n錯誤:\n";
cout << " 未在選項\"-l size\"中找到size字段,請輸入PingSimulator.exe查看詳細用法.\n";
return false;
}
}
else if (0 == strcmp(argv[i], "-i")) // 處理選項"-i TTL"
{
hasParam_i_ttl = true;
if (i + 1 < argc)
{
// 獲取TTL字段
ttl = atoi(argv[++i]);
if (ttl >= 1 && ttl <= 255)
{
// 已正確獲取TTL字段,轉(zhuǎn)去處理下一個選項
continue;
}
else
{
cout << "\n錯誤:\n";
cout << " 選項\"-i TTL\"中的TTL字段無效,請輸入PingSimulator.exe查看詳細用法.\n";
return false;
}
}
else
{
cout << "\n錯誤:\n";
cout << " 未在選項\"-i TTL\"中找到TTL字段,請輸入PingSimulator.exe查看詳細用法.\n";
return false;
}
}
else if (0 == strcmp(argv[i], "-v")) // 處理選項"-v TOS"
{
hasParam_v_tos = true;
if (i + 1 < argc)
{
// 獲取TOS字段
tos = atoi(argv[++i]);
continue;
}
else
{
cout << "\n錯誤:\n";
cout << " 未在選項\"-v TOS\"中找到TOS字段,請輸入PingSimulator.exe查看詳細用法.\n";
return false;
}
}
else if (0 == strcmp(argv[i], "-w")) // 處理選項"-w timeout"
{
hasParam_w_timeout = true;
if (i + 1 < argc)
{
// 獲取timeout字段
timeout = atoi(argv[++i]);
if (timeout >= 1)
{
// 成功獲取到timeout字段,轉(zhuǎn)去處理下一個選項
continue;
}
else
{
cout << "\n錯誤:\n";
cout << " 選項\"-w timeout\"中的timeout字段無效,請輸入PingSimulator.exe查看詳細用法.\n";
return false;
}
}
else
{
cout << "\n錯誤:\n";
cout << " 未在選項\"-w timeout\"中找到timeout字段,請輸入PingSimulator.exe查看詳細用法.\n";
return false;
}
}
else if (0 == strcmp(argv[i], "-1")) // 處理選項"-1"
{
hasParam_1 = true;
continue;
}
else if (0 == strcmp(argv[i], "-2")) // 處理選項"-2"
{
hasParam_2 = true;
continue;
}
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -