【分享】一個集成tracert和ping的網絡監測工具


最近接到一個需求,需求背景是這樣的:目前Windows平台下本身都有tracert和ping的實現,而且可以直接在cmd下使用。

需求中有兩個要求:

1. Windows平台中的tracert執行速度太慢,一次tracert可能要花十幾分鍾。所以,需要一個快速的tracert實現。

2.將tracert和ping結合起來:tracert出來的節點,需要ping一下,看看當前節點的連通性。

 

接到這個需求后,開始的時候有點無從下手,只能先從研究tracert的原理出發,搞清楚原理后才能去提升速率。

tracert原理:

Tracert 命令用 IP 生存時間 (TTL) 字段和 ICMP 錯誤消息來確定從一個主機到網絡上其他主機的路由。

首先,tracert送出一個TTL是1的IP 數據包到目的地,當路徑上的第一個路由器收到這個數據包時,它將TTL減1。此時,TTL變為0,所以該路由器會將此數據包丟掉,並送回一個「ICMP time exceeded」消息(包括發IP包的源地址,IP包的所有內容及路由器的IP地址),tracert 收到這個消息后,便知道這個路由器存在於這個路徑上,接着tracert 再送出另一個TTL是2 的數據包,發現第2 個路由器,以此往復。。。。。

tracert 每次將送出的數據包的TTL 加1來發現另一個路由器,這個重復的動作一直持續到某個數據包 抵達目的地。當數據包到達目的地后,該主機則不會送回ICMP time exceeded消息,一旦到達目的地,由於tracert通過UDP數據包向不常見端口(30000以上)發送數據包,因此會收到「ICMP port unreachable」消息,故可判斷到達目的地。

tracert 有一個固定的時間等待響應(ICMP TTL到期消息)。如果這個時間過了,它將打印出一系列的*號表明:在這個路徑上,這個設備不能在給定的時間內發出ICMP TTL到期消息的響應。然后,Tracert給TTL記數器加1,繼續進行。

讀懂tracert的原理后,其實不難發現,TTL=1到TTL=30的請求其實關聯性不大,我們要加速實現tracert可以從這里入手。

看一下tracert結果的格式:可以發現,每一次tracert最多會有30個節點,每個節點包含3個時間數據,經查閱是三次請求的響應時間

 

網上查閱了一些資料,發現可以基於ICMP.dll實現tracert,網友給出了代碼:

  1 //TraceRoute3.cpp
  2 #include <stdio.h>
  3 #include <winsock2.h>
  4 #include <windows.h>
  5 #include <IPHlpApi.h>
  6 //增加靜態鏈接庫ws2_32.lib
  7 #pragma comment(lib,"ws2_32.lib")
  8 //聲明3個函數類型的指針
  9 typedef HANDLE (WINAPI *lpIcmpCreateFile)(VOID);
 10 typedef BOOL (WINAPI *lpIcmpCloseHandle)(HANDLE  IcmpHandle);
 11 typedef DWORD (WINAPI *lpIcmpSendEcho)(
 12     HANDLE                   IcmpHandle,
 13     IPAddr                   DestinationAddress,
 14     LPVOID                   RequestData,
 15     WORD                     RequestSize,
 16     PIP_OPTION_INFORMATION   RequestOptions,
 17     LPVOID                   ReplyBuffer,
 18     DWORD                    ReplySize,
 19     DWORD                    Timeout
 20     );
 21 int main(int argc,char* argv[]){
 22  if(argc!=2){
 23   printf("Usage: %s destIP/n",argv[0]);
 24   exit(-1);
 25  }
 26  WSADATA wsa;
 27  if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){
 28   printf("WSAStartup failed./n");
 29   exit(-1);
 30  }
 31  //轉換IP地址到整數
 32  unsigned long ip = inet_addr(argv[1]);
 33  if(ip==INADDR_NONE){
 34   //用戶可能輸入的是域名
 35   hostent* pHost = gethostbyname(argv[1]);
 36   //如果域名無法解析
 37   if(pHost==NULL){
 38    printf("Invalid IP or domain name: %s/n", argv[1]);
 39    exit(-1);
 40   }
 41   //取域名的第一個IP地址
 42   ip = *(unsigned long*)pHost->h_addr_list[0];
 43   printf("trace route to %s(%s)/n/n",argv[1],inet_ntoa(*(in_addr*)&ip));
 44  }else{
 45   printf("trace route to %s/n/n",argv[1]);
 46  }
 47  //載入ICMP.DLL動態庫
 48  HMODULE hIcmpDll = LoadLibrary("icmp.dll");
 49  if(hIcmpDll==NULL){
 50   printf("fail to load icmp.dll/n");
 51   exit(-1);
 52  }
 53  //定義3個函數指針
 54  lpIcmpCreateFile IcmpCreateFile;
 55  lpIcmpCloseHandle IcmpCloseHandle;
 56  lpIcmpSendEcho IcmpSendEcho;
 57  //從ICMP.DLL中獲取所需的函數入口地址
 58  IcmpCreateFile = (lpIcmpCreateFile)GetProcAddress(hIcmpDll,"IcmpCreateFile");
 59  IcmpCloseHandle = (lpIcmpCloseHandle)GetProcAddress(hIcmpDll,"IcmpCloseHandle");
 60  IcmpSendEcho = (lpIcmpSendEcho)GetProcAddress(hIcmpDll,"IcmpSendEcho");
 61  //打開ICMP句柄
 62  HANDLE hIcmp;
 63  if ((hIcmp = IcmpCreateFile()) == INVALID_HANDLE_VALUE){
 64   printf("/tUnable to open ICMP file./n");
 65   exit(-1);
 66  }
 67  //設置IP報頭的TTL值
 68  IP_OPTION_INFORMATION IpOption;
 69  ZeroMemory(&IpOption,sizeof(IP_OPTION_INFORMATION));
 70  IpOption.Ttl = 1;
 71  //設置要發送的數據
 72  char SendData[32];
 73  memset(SendData,'0',sizeof(SendData));
 74  //設置接收緩沖區
 75  char ReplyBuffer[sizeof(ICMP_ECHO_REPLY)+32];
 76  PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
 77  BOOL bLoop = TRUE;
 78  int iMaxHop = 30;
 79  while(bLoop && iMaxHop--){
 80   printf("%2d: ",IpOption.Ttl);
 81   //發送ICMP回顯請求
 82   if(IcmpSendEcho(hIcmp,(IPAddr)ip, SendData, sizeof(SendData), &IpOption, 
 83    ReplyBuffer, sizeof(ReplyBuffer), 3000)!=0){
 84     if(pEchoReply->RoundTripTime==0){
 85      printf("/t<1ms");
 86     }else{
 87      printf("/t%dms",pEchoReply->RoundTripTime);
 88     }
 89     printf("/t%s/n",inet_ntoa(*(in_addr*)&(pEchoReply->Address)));
 90     //判斷是否完成路由路徑探測
 91     if((unsigned long)pEchoReply->Address==ip){
 92      printf("/nTrace complete./n");
 93      bLoop = FALSE;
 94     }
 95   }else{
 96    printf("/t*/tRequest time out./n");
 97   }
 98   IpOption.Ttl++;
 99  }
100  IcmpCloseHandle(hIcmp);
101  FreeLibrary(hIcmpDll);
102  WSACleanup();
103  return 0;
104 }
View Code

運行后可以看到,這里實現了和windows的dos里面一模一樣的tracert,但距離我們想要的“快速”還有一定差距。

我們可以在這個基礎上做改進:使用多線程來實現最多30跳躍點的請求,每次請求分別執行三次,最后我們可以直接使用90個線程來執行程序,最終將結果

按固定位置填充到定義的數組中。

至於結合ping就比較簡單了,直接調用 windows下的ping命令。

最后,需要注意的是,我們使用c++做成一個exe供程序直接調用,這里需要接收外部參數:網址,是否執行ping命令,ping命令的-l參數,ping命令的-n參數

最終結果:

最終效果非常好,執行效率相比windows下的tracert快了非常多,而且非常方便。

注意:在某些系統上執行的時候會報錯,需要添加兩個dll文件:msvcp120d.dll和msvcr120d.dll到本機的windows/system32或者windows/SysWOW64下

 小工具分享給大家,下載地址

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM