程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> 用VC++ 6.0制作網絡自動測試程序

用VC++ 6.0制作網絡自動測試程序

編輯:vc教程

本報2000年11月27日第46期的應用編程中,登載了“用VC++6.0制作網絡測試程序”一文。該文介紹的網絡測試方法是通過調用Windows目錄下的PING.EXE命令來實現的。程序執行時,須用鼠標點中拓撲圖某網絡節點熱區,並通過所運行PING.EXE命令的DOS窗口來觀察測試結果。由於沒有自動測試功能,所以使用不夠方便。

由MICROSOFT公司開發的WINDOWS SOCKETS提供了WINDOWS環境下網絡通訊的編程接口。在VC++6.0中,可以通過調用Windows SOCKETS函數,采用原始套接字(RAW SOCKETS)類型和互連網控制消息協議(ICMP),來編制一個能實現PING功能的函數。通過定時調用該函數,就可實現網絡的自動測試。若再加上語音報警功能,就是一個很實用的網絡測試程序。本文擬介紹實現此功能的程序的制作方法。

為便於說明起見,我們還是按“用VC++6.0制作網絡測試程序”一文介紹的方法,先做一個簡單的網絡測試程序(Netest)。注意,在制作Netest工程的STEP 4 OF 6時 ,要鉤選Windows SOCKETS選項。否則,在下面要編譯AUTOP.CPP文件時,將會出錯。在Netest工程編譯成功後,再做以下幾項工作:

一、增加AUTOP. CPP到工程文件中

在工程à添加工程à文件à將AUTOP.CPP添加到當前工程文件中(AUTOP.CPP的源碼見下面第三節的內容所示,應事先將其COPY到當前工程的目錄中)。

二、在工程中增加自動測試的有關菜單和函數

(1)在“用VC++6.0制作網絡測試程序”一文介紹的Netest工程中,其Readinfo()函數的最後一條語句GlobalFree(hHost)釋放了裝載有初始化信息的內存,當隨後調用PING.EXE時,問題不是很大。但若調用WINSOCK函數,內存中的初始化信息會被沖掉。所以須將該語句移到程序結束處再執行。可如下增加OnDestroy()函數:

在ClassWizard中,對應Class name=CNetestView, Object IDs= CNetestVIEw,Message =WM_DESTROY,點擊Add FunctionàOnDestroyàEdit Code,增加相應代碼如下:
void CNtestVIEw::OnDestroy() 
{
CFormVIEw::OnDestroy();
GlobalFree(hHost); //從Readinfo()移到此
for (int i=0; i<nodeNum; i++)
DeleteObject(lpHost[i].hrgn);
}
(2)增加自動測試菜單Autotest:在資源工作區,選中MENUàIDR_MAINFRAMEà點中空的菜單條à屬性à令ID=ID_AUTOTEST,標題=Autotest。
在ClassWizard中,對應Class name=CNetestVIEw,Object IDs=ID_AUTOTEST,Message =COMMAND,點擊Add FunctionàOnAutotest àEdit Code,增加相應代碼如下:
void CNtestVIEw::OnAutotest() 
{
SetTimer(1, 30000,NULL); //each 30s interupt 1 time. 
Tc=20; 
CWnd* pParent=GetParent();
CMenu * pMenu=pParent->GetMenu();
pMenu->EnableMenuItem(ID_AUTOPING,MF_BYCOMMAND| MF_DISABLED | MF_GRAYED); 
pMenu->EnableMenuItem(ID_STOPAUTO,MF_ENABLED); 
AfxGetMainWnd()->SendMessage (WM_TIMER,0, 0L); 
}

其中SetTimer()將定時器設為每30秒中斷一次。由Tc計算中斷次數。余下幾句條語句令Autotest菜單變灰,以免多次重入。最後一條語句使得鼠標點擊Autotest菜單後,即轉到OnTimer()函數開始自動測試。

(3)增加定時測試代碼。在ClassWizard中,對應Class name=CNetestVIEw的Message, 選中WM_TIMERàAdd FunctionàEdit Code,在OnTimer() 函數中增加如下代碼:
  
void CNetestVIEw::OnTimer(UINT nIDEvent) 
{
if(Tc++<20) return;
KillTimer(1); 
Tc=0; 
BOOL bOK=TRUE; 
InvalidateRect(NULL);
for(int ipT=0;ipT

if(Autotest(lpHost[ipT].nodeIP,3)==FALSE) { 
CString strerr;
strerr.Format("err%d.wav",ipT+1);
sndPlaySound(strerr, SND_LOOP |SND_ASYNC ); 
HDC hdc= CreateDC("DISPLAY",0,0,0); 
SelectObject(hdc,lpHost[ipT].hrgn); 
InvertRgn(hdc,lpHost[ipT].hrgn);
DeleteDC(hdc); 
bOK=FALSE;
}
else if(bOK) sndPlaySound("Bird0.wav", SND_ASYNC); 

SetTimer(1, 30000,NULL); 
CFormVIEw::OnTimer(nIDEvent);
}

本程序設計為每10分鐘對網絡作一次自動測試。所以第一條語句須檢查定時器中斷是否已夠20次(30秒*20=10分鐘)。若夠的話,就關斷定時器,循環調用Autotest()對所有網絡節點進行測試。若Autotest()的返回值為FALSE,說明該節點有問題,隨即調用對應聲波文件發出不停的報警聲。在報警的同時,程序繼續往下運行,作余下網絡節點的測試。若所有節點均正常,則調用BIRD0.WAV聲波文件發出動聽的鳥鳴聲。全部節點測試完後,才用SetTimer()再次啟動定時器。

報警聲波文件的制作,可在Windows的附件à娛樂à錄音機,通過麥克風錄下報警語音。按照在INFO.INI文件中各節點的順序,將語音文件分別存為ERR1.WAV,ERR2.WAV……。這樣,網絡節點的報警聲就能和出錯節點正確對應。

測試時,若有2個以上網絡節點有問題,前一個出錯節點只會報警一次,最後一個出錯節點則會發出循環報警聲。為了便於用戶觀察出錯情況,用InvertRgn()來反相顯示出錯節點的區域。InvalidateRect(NULL)函數用來使屏幕刷新,以便下一次測試的觀察。

三、AUTOP.CPP的源碼及說明

AUTO.CPP的源碼如下:

//#include 頭文件略
. . . . . . . . . . .
typedef struct _ihdr {
BYTE i_type, i_code;
u_short i_cksum,i_id, i_seq; 
}IcmpHeader; 
struct sockaddr_in saDestAddr; 
CString Messtr,Tmpstr;
  
u_short checksum(u_short *buffer, int size) {
. . . . . . .(略)

  
BOOL Autotest(char far * szDestHost,int Ktest) 
{
WSADATA wsaData;
SOCKET sockRaw;
struct sockaddr_in dest,from;
char icmp_data[10], recvbuf[100]; 
unsigned int addr=0;
int fromlen = sizeof(from); 
int timeout = 1000; //ms
  

WSAStartup(MAKEWord(2,1),&wsaData) ;
sockRaw = socket (AF_INET,SOCK_RAW,IPPROTO_ICMP); 
setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,
(char*)&timeout,sizeof(timeout) );
memset(&dest,0,sizeof(dest)); 
dest.sin_family = AF_INET;
dest.sin_addr.s_addr= inet_addr(szDestHost); 
memset(icmp_data,0,sizeof(icmp_data)); //clear icmp_data.
((IcmpHeader*)icmp_data)->i_type = 8; //ICMP_ECHO
((IcmpHeader*)icmp_data)->i_code = 0;
((IcmpHeader*)icmp_data)->i_id = (u_short)GetCurrentProcessId();
((IcmpHeader*)icmp_data)->i_seq = 0;
  
for(int k=0; k

((IcmpHeader*)icmp_data)->i_cksum = 0; 
((IcmpHeader*)icmp_data)->i_seq ++;
((IcmpHeader*)icmp_data)->i_cksum=checksum((u_short*)icmp_data,8); 
sendto(sockRaw,icmp_data,8,0,(struct sockaddr*)&dest,sizeof(dest)); 
int bread=recvfrom(sockRaw,recvbuf,1024,0,(struct sockaddr*)&from,
&fromlen);
if (bread == SOCKET_ERROR){
if(k==Ktest-1) goto ERR1 ; 
else continue; //try again(3 times)


return TRUE; //no erros.
ERR1:
closesocket (sockRaw); 
sockRaw= INVALID_SOCKET; 
WSACleanup();
return FALSE; 
}

為簡潔起見,源碼中大部分的出錯處理語句都刪去了。整個函數執行的過程如下:

(1)用WSAStartup()函數初始化WINSOCK DLL。

(2)用socket()函數建立一個原始套接字sockRaw。

(3)用setsockopt()函數來設置套接字的選擇項。這裡用SO_RCVTIMEO參數來設置接收超時。超時值由timeout=1000設定超時最長時間為1秒鐘。

(4)給被測節點的dest和Icmpheader結構變量賦值。其中szDestHost放被測節點的IP地址(點間隔格式),inet_addr()函數將一個點間隔的地址轉換成4字節的地址;Icmpheader結構的i_type=8表示發送一個請求響應數據包(ICMP_ECHO),i_id為數據包的標識,i_cksum為數據包的校驗和,i_seq用來為發送包計數。

(5)用sendto()函數向被測節點發送信息,用recvfrom()函數接收被測節點的應答信息。若在規定時間內(1秒)收不到應答信息,再連測2次。若3次的發送均收不到應答信息,可認為網絡故障。Ktest的值決定對被測節點反復測試的次數。(當收到應答信息時,還認應檢查應答信息內容正確後,才認為網絡正常。本例略去這些檢查似乎也無妨)。

(6)測試完一個節點後,用closesocket()函數關閉套接字,用WSAcCleanup()函數釋放為應用程序分配的資源。

至此,就可以編譯和執行程序,體會個中樂趣。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved