Windows下 C++网络延时检测

清泛原创

一般需要连接服务器后端的软件都有服务器节点网络延迟的检测,帮助选择低延时、负载较低的服务器节点。例如:



那么这个功能是如何实现的呢?直接上代码吧,亲测可用:
Ping.h
#ifndef CPING_H
#define CPING_H

#include <windows.h>
#include <conio.h>
#include <winnt.h>

#define PING_TIMES 2 //ping 4 次

typedef struct _IPINFO
{
	unsigned char Ttl;				// Time To Live
	unsigned char Tos;				// Type Of Service
	unsigned char IPFlags;			// IP flags
	unsigned char OptSize;			// Size of options data
	unsigned char FAR *Options;		// Options data buffer
}IPINFO;

typedef IPINFO* PIPINFO;

typedef struct _ICMPECHO
{
	unsigned long Source;			// Source address
	unsigned long Status;			// IP status
	unsigned long RTTime;			// Round trip time in milliseconds
	unsigned long DataSize;			// Reply data size
	unsigned long Reserved;			// Unknown
	void FAR *pData;				// Reply data buffer
	IPINFO	ipInfo;					// Reply options
}ICMPECHO;

typedef ICMPECHO* PICMPECHO;

class CPing
{
public:
	CPing();
	~CPing();
	int Ping(char* strHost);
private:
	// ICMP.DLL Export Function Pointers
	HANDLE (WINAPI *pIcmpCreateFile)(VOID);
	BOOL (WINAPI *pIcmpCloseHandle)(HANDLE);
	DWORD (WINAPI *pIcmpSendEcho) (HANDLE, DWORD, LPVOID, WORD, PIPINFO, LPVOID, DWORD, DWORD);
	HANDLE hndlIcmp; // LoadLibrary() handle to ICMP.DLL
	BOOL bValid; // if it doesn't construct properly, it won't be valid
};

#endif

Ping.cpp
#include "Ping.h"

CPing::CPing()
{
	bValid = FALSE;

	// Dynamically load the ICMP.DLL
	hndlIcmp = LoadLibrary("ICMP.DLL");
	if (hndlIcmp == NULL)
	{
		/*cprintf("Error: Could not load ICMP.DLL\n");*/
		return;
	}
	// Retrieve ICMP function pointers
	pIcmpCreateFile  = (HANDLE (WINAPI *)(void))GetProcAddress((HMODULE)hndlIcmp, "IcmpCreateFile");
	pIcmpCloseHandle = (BOOL (WINAPI *)(HANDLE))GetProcAddress((HMODULE)hndlIcmp, "IcmpCloseHandle");
	pIcmpSendEcho = (DWORD (WINAPI *)(HANDLE, DWORD, LPVOID, WORD, PIPINFO, LPVOID, DWORD, DWORD))GetProcAddress((HMODULE)hndlIcmp, "IcmpSendEcho");
	// Check all the function pointers
	if (pIcmpCreateFile == NULL	|| pIcmpCloseHandle == NULL	|| pIcmpSendEcho == NULL)
	{
		//AfxMessageBox("Error: Error loading ICMP.DLL");
		FreeLibrary((HMODULE)hndlIcmp);
		return;
	}

	bValid = TRUE;
}

CPing::~CPing()
{
	FreeLibrary((HMODULE)hndlIcmp);
}

int CPing::Ping(char* strHost)
{
	u_char FAR data[4] = { 0L };	//
	//unsigned long Status = 0L;	//
	int PingTimes = 0;				//
	int Received  = -2;				//
	unsigned long Minimum = 1000000;// 最小值设置为超时值
	unsigned long Maximum = 0;		// 最大值设置为0
	unsigned long Time = 0;			// microsecond
	unsigned long Sum = 0;			//
	char hostIpBuf[64] = { 0L };	//
	struct in_addr iaDest;			// Internet address structure
    LPHOSTENT pHost = NULL;			// Pointer to host entry structure
	DWORD *dwAddress = NULL;		// IP Address
	IPINFO ipInfo;					// IP Options structure
	ICMPECHO icmpEcho = {0};				// ICMP Echo reply buffer
	HANDLE hndlFile = NULL;			// Handle for IcmpCreateFile()	
	
	LARGE_INTEGER litmp;
	QueryPerformanceFrequency(&litmp);
    LONGLONG dfFreq = litmp.QuadPart; // 获得计数器的时钟频率 
    
    if(!bValid)
	{
		return -2;
	}
	
	memset(data, '\xAA', 4); //data
	memset(hostIpBuf, 0, 64);

	// Lookup destination Use inet_addr() to determine if we're dealing with a name or an address
    iaDest.s_addr = inet_addr(strHost);
 //   if (iaDest.s_addr == INADDR_NONE)
	{
        pHost = gethostbyname(strHost);
	}
 //   else
	{
//        pHost = gethostbyaddr((const char *)&iaDest, sizeof(struct in_addr), AF_INET);
	}
	if (pHost == NULL)
	{
		return -1; // 非法的或不存在的主机
	}

	// Copy the IP address
	dwAddress = (DWORD *)(*pHost->h_addr_list);
	//cprintf("Pinging %s with %d bytes of data:\n", strHost, sizeof(icmpEcho)/* + sizeof(data)*/);
	
	// Get an ICMP echo request handle, 打开ping服务      
	hndlFile = pIcmpCreateFile();
		
    LONGLONG QPartB, QPartE;
	/*unsigned long tickB, tickE;*/
	while(PingTimes < PING_TIMES)
	{
		Sleep(10);

		PingTimes++;

		// Set some reasonable default values
		ipInfo.Ttl = 50;
		ipInfo.Tos = 0;
		ipInfo.IPFlags = 0;
		ipInfo.OptSize = 0;
		ipInfo.Options = NULL;	
		icmpEcho.Status = 0;
		
		//SYSTEMTIME timeBg;
		//GetSystemTime(&timeBg);
		/*tickB = GetTickCount();*/
		QueryPerformanceCounter(&litmp);
		QPartB = litmp.QuadPart; // 获得初始值

		// Reqest an ICMP echo
		pIcmpSendEcho(
			hndlFile,		// Handle from IcmpCreateFile()
			*dwAddress,		// Destination IP address
			NULL/*data*/,			// Pointer to buffer to send
			0/*sizeof(data)*/,	// Size of buffer in bytes
			&ipInfo,		// Request options
			&icmpEcho,		// Reply buffer
			sizeof(icmpEcho)/* + sizeof(data)*/,
			1000);			// Time to wait in milliseconds

		//SYSTEMTIME timeEd;
		//GetSystemTime(&timeEd);
		/*tickE = GetTickCount();*/
		QueryPerformanceCounter(&litmp);
		QPartE = litmp.QuadPart; // 获得终止值

		// Print the results
		iaDest.s_addr = icmpEcho.Source;
		//Status += icmpEcho.Status;
		if (icmpEcho.Status)
		{
			Received=-1;
		}
		else
		{			

			Time = (unsigned long)((QPartE - QPartB)*1000000/dfFreq);
			if(Received>=0)
				Received=(Received+Time/1000)/2;
			else
			Received=Time/1000;
		}
		Sleep(10);
	}
	pIcmpCloseHandle(hndlFile);

	return Received;
}

调用方法:
CPing ping;
int bResult = ping.Ping(ip); //result即为延迟毫秒数ms,负数表示ping不通

 


当然,以上的方法只是模拟dos的ping命令,只针对ip是否可ping通、延时多少。如果还要检测服务器指定端口是否正常服务,这就类似telnet命令了。
解决方案参考:《mfc telnet 端口,代码实现、不调用telnet.exe》。

Windows C++ 网络延时

分享到:
评论加载中,请稍后...
创APP如搭积木 - 创意无限,梦想即时!
回到顶部