方法:如何解决用MFC实现的ping功能中把目标主机不可到达的当成ping通的问题

转载请注明来源:http://www.cnblogs.com/xuesongshu/

  网上查到的资料能实现ping功能,但是都有一个问题,它只检测是否存在错误,而不检测ICMP数据包是哪个机器回复的,这样造成一种错误的情况:当PC与路由器连通时,如果路由器回复该主机不可达,那么程序一样回应PING通了。目前网络上搜索不到相关正确的资料,我把我的方法分享给网友们。

运行截图:

本段程序代码是我做的一个软件的其中的一个功能。该方法是一个线程的主体。

UINT DoPingHost(LPVOID lParam)
{
	WSADATA wdPing;
	SOCKET skPing;
	DWORD dwIpDest;
	LARGE_INTEGER liBegin,liEnd,liClockFrequency;
	double dSpan=0;
	struct sockaddr_in destAddr,fromAddr;
	int nTimeOut=3000,nPingCount=4,nBread=0,nFromLen=sizeof(fromAddr),nPingPort=0,nPingFailCount=0,nSliderPos=0;
	char* cIcmpData=new char[10];
	char cLoalName[100],cRecvBuffer[100];
	IcmpHeader* icmpData=(IcmpHeader*)cIcmpData;
	CLanCopyDlg* cd=(CLanCopyDlg*)lParam;
	CString szMsg,szTmp;
	BOOL bCanBrowse=FALSE;
	::QueryPerformanceFrequency(&liClockFrequency);
	memset(cIcmpData,0,sizeof(IcmpHeader));
	cd->GetDlgItem(IDC_BUTTON_MACHINE)->EnableWindow(FALSE);
	cd->GetDlgItem(IDC_BUTTON_MACHINE)->SetWindowText("请稍等");
	if (::WSAStartup(MAKEWORD(2,1),&wdPing))
	{
		::MessageBox(cd->m_hWnd,TEXT("网络初化异常,Socket创建失败!"),"异常",MB_OK|MB_ICONERROR);
		return 0;
	}
	skPing=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
	setsockopt(skPing,SOL_SOCKET,SO_RCVTIMEO,(char*)&nTimeOut,sizeof(nTimeOut));
	((CIPAddressCtrl*)(cd->GetDlgItem(IDC_IPADDRESS_DEST)))->GetAddress(dwIpDest);
	dwIpDest=MAKEIPADDRESS(FOURTH_IPADDRESS(dwIpDest),THIRD_IPADDRESS(dwIpDest),SECOND_IPADDRESS(dwIpDest),FIRST_IPADDRESS(dwIpDest));
	destAddr.sin_addr.S_un.S_addr=dwIpDest;
	destAddr.sin_family=AF_INET;
	srand(time(NULL));
	nPingPort=rand()%1024+1024;
	destAddr.sin_port=nPingPort;
	icmpData->i_type=8;
	icmpData->i_code=0;
	icmpData->i_id=(u_short)::GetCurrentProcessId();
	icmpData->i_seq=0;
	gethostname(cLoalName,100);
	nSliderPos=0;
	cd->m_sliderCopyFile.SetPos(nSliderPos);
	for (int i=0;i<nPingCount;i++)
	{
		::QueryPerformanceCounter(&liBegin);
		icmpData->i_cksum=0;
		icmpData->i_cksum=in_cksum((u_short*)cIcmpData,8);
		sendto(skPing,cIcmpData,8,0,(struct sockaddr*)&destAddr,sizeof(destAddr));
		nBread=recvfrom(skPing,cRecvBuffer,100,0,(struct sockaddr*)&fromAddr,&nFromLen);
		szTmp="";
		if (nBread==SOCKET_ERROR||fromAddr.sin_addr.S_un.S_addr!=destAddr.sin_addr.S_un.S_addr)
		{
			szTmp.Format("第%2d次尝试ping主机%s失败,错误码:%ld\r\n",i+1,inet_ntoa(destAddr.sin_addr),WSAGetLastError());
			szMsg.Insert(0,szTmp);
			nPingFailCount++;
		} 
		else
		{
			szTmp.Format("第%2d次尝试ping主机%s成功,端口为:%ld\r\n",i+1,inet_ntoa(destAddr.sin_addr),nPingPort);
			szMsg.Insert(0,szTmp);
		}
		::QueryPerformanceCounter(&liEnd);
		dSpan+=(double)(liEnd.QuadPart-liBegin.QuadPart)/(double)liClockFrequency.QuadPart;
		nSliderPos+=100/nPingCount;
		cd->m_sliderCopyFile.SetPos(nSliderPos);
	}
	closesocket(skPing);
	WSACleanup();
	dSpan/=nPingCount;
	if (nPingFailCount)
	{
		cd->m_brPingStatus=::CreateSolidBrush(RGB(0xFF,0,0));
		cd->GetDlgItem(IDC_STATIC_PING_STATUS)->SetWindowText("严重");
		szTmp.Format("警告:远程主机响应超时%2d次。\r\n",nPingFailCount);
		szMsg.Insert(0,szTmp);
		if(nPingFailCount<nPingCount)
		{
			bCanBrowse=TRUE;
		}
	}
	else if(dSpan>0.40)
	{
		cd->m_brPingStatus=::CreateSolidBrush(RGB(0xFF,0xFF,0));
		cd->GetDlgItem(IDC_STATIC_PING_STATUS)->SetWindowText("一般");
		szTmp.Format("平均响应时间是:%4.2f秒。\r\n",dSpan);
		szMsg.Insert(0,szTmp);
		bCanBrowse=TRUE;
	}
	else
	{
		cd->m_brPingStatus=::CreateSolidBrush(RGB(0,0xFF,0));
		cd->GetDlgItem(IDC_STATIC_PING_STATUS)->SetWindowText("良好");
		szTmp.Format("平均响应时间是:%4.2f秒。\r\n",dSpan);
		szMsg.Insert(0,szTmp);
		bCanBrowse=TRUE;
	}
	
	cd->GetDlgItem(IDC_STATIC_PING_STATUS)->Invalidate();
	::MessageBox(cd->m_hWnd,szMsg,"测试结果",MB_OK|MB_ICONINFORMATION);
	cd->GetDlgItem(IDC_BUTTON_MACHINE)->EnableWindow(TRUE);
	cd->GetDlgItem(IDC_BUTTON_MACHINE)->SetWindowText("测试");
	cd->GetDlgItem(IDC_BUTTON_BROWSE_DIR)->EnableWindow(bCanBrowse);
	return 0;
}

  顺便分享一下IcmpHeader和in_cksum。

下面是IcmpHeader:

#include "StdAfx.h"


typedef struct ip_option_information
{
    u_char Ttl;		/* Time To Live (used for traceroute) */
    u_char Tos; 	/* Type Of Service (usually 0) */
    u_char Flags; 	/* IP header flags (usually 0) */
    u_char OptionsSize; /* Size of options data (usually 0, max 40) */
    u_char FAR *OptionsData;   /* Options data buffer */
} IPINFO, *PIPINFO, FAR *LPIPINFO;


typedef struct icmp_echo_reply
{
    u_long Address; 	/* source address *.
    u_long Status;	/* IP status value (see below) */
    u_long RTTime;	/* Round Trip Time in milliseconds */
    u_short DataSize; 	/* reply data size */
    u_short Reserved; 	/* */
    void FAR *Data; 	/* reply data buffer */
    struct ip_option_information Options; /* reply options */
} ICMPECHO, *PICMPECHO, FAR *LPICMPECHO;


DWORD WINAPI IcmpSendEcho(
    HANDLE IcmpHandle, 	/* handle returned from IcmpCreateFile() */
    u_long DestAddress, /* destination IP address (in network order) */
    LPVOID RequestData, /* pointer to buffer to send */
    WORD RequestSize,	/* length of data in buffer */
    LPIPINFO RequestOptns,  /* see Note 2 */
    LPVOID ReplyBuffer, /* see Note 1 */
    DWORD ReplySize, 	/* length of reply (must allow at least 1 reply) */
    DWORD Timeout 	/* time in milliseconds to wait for reply */
);

typedef struct _ihdr
{
	BYTE i_type;
	BYTE i_code; /* type sub code */
	USHORT i_cksum;
	USHORT i_id;
	USHORT i_seq;
	/* This is not the std header, but we reserve space for time */
	ULONG timestamp;
} IcmpHeader;

#define IP_STATUS_BASE 11000
#define IP_SUCCESS 0
#define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1)
#define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2)
#define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3)
#define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4)
#define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5)
#define IP_NO_RESOURCES (IP_STATUS_BASE + 6)
#define IP_BAD_OPTION (IP_STATUS_BASE + 7)
#define IP_HW_ERROR (IP_STATUS_BASE + 8)
#define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9)
#define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
#define IP_BAD_REQ (IP_STATUS_BASE + 11)
#define IP_BAD_ROUTE (IP_STATUS_BASE + 12)
#define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13)
#define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14)
#define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15)
#define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16)
#define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17)
#define IP_BAD_DESTINATION (IP_STATUS_BASE + 18)
#define IP_ADDR_DELETED (IP_STATUS_BASE + 19)
#define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20)
#define IP_MTU_CHANGE (IP_STATUS_BASE + 21)
#define IP_UNLOAD (IP_STATUS_BASE + 22)
#define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50)
#define MAX_IP_STATUS IP_GENERAL_FAILURE
#define IP_PENDING (IP_STATUS_BASE + 255)

  in_cksum:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <WinSock.h>

unsigned short in_cksum(unsigned short *addr,int len)
{
        register int sum = 0;
        u_short answer = 0;
        register u_short *w = addr;
        register int nleft = len;

        while (nleft > 1)  {
                sum += *w++;
                nleft -= 2;
        }


        if (nleft == 1) {
                *(u_char *)(&answer) = *(u_char *)w ;
                sum += answer;
        }


        sum = (sum >> 16) + (sum & 0xffff);     
        sum += (sum >> 16);                   
        answer = ~sum;                         
        return(answer);
}

  

posted @ 2013-02-22 00:20  岬淢箫声  阅读(1003)  评论(0编辑  收藏  举报