转载请注明来源: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);
}