1. 程式人生 > >c++計算網路延遲毫秒數

c++計算網路延遲毫秒數

注:核心內容使用了http://www.cnblogs.com/goagent/p/4078940.html的實現,我只是做了下小封裝

開一個執行緒專門來刷ip的延時,可以使用在類似於聯機大廳計算到各IP的延遲。有一個需求需要計算N個ip的延遲,所以才知道了有ICMP這個東西,學習了。

簡易的執行緒封裝,支援基於message的同步

#ifndef _INC_MACROHELPER_
#define _INC_MACROHELPER_
//////////////////////////////////////////////////////////////////////////
#define READWRITE_PROPERTY(VAR, NAME, TYPE)		protected:TYPE VAR;READWRITE_INTERFACE(VAR, NAME, TYPE);
#define READWRITE_INTERFACE(VAR, NAME, TYPE)	READ_INTERFACE(VAR, NAME, TYPE)WRITE_INTERFACE(VAR, NAME, TYPE)
#define READ_INTERFACE(VAR, NAME, TYPE)			public:TYPE Get##NAME(){return VAR;}
#define WRITE_INTERFACE(VAR, NAME, TYPE)		public:void Set##NAME(TYPE _var){VAR = _var;}
//////////////////////////////////////////////////////////////////////////
#endif


#ifndef _INC_THREADRUNNER_
#define _INC_THREADRUNNER_
//////////////////////////////////////////////////////////////////////////
#include <Windows.h>
#include <process.h>
#include <exception>
#include "MacroHelper.h"
//////////////////////////////////////////////////////////////////////////
typedef unsigned char ThreadNumber;
//////////////////////////////////////////////////////////////////////////
#define RUNTHREAD_PREPARED_EVENT	"RunThread prepared event"
//////////////////////////////////////////////////////////////////////////
class ThreadRunner
{
public:
	enum THREAD_STATE
	{
		TS_STOP,
		TS_RUN,
		TS_PAUSE
	};

	enum THREAD_RESULT
	{
		TR_SETEVENTFAILED = 0xFFFF0000
	};

public:
	ThreadRunner()
	{
		m_eThreadState = TS_STOP;
		m_hPreparedEvt = NULL;
		m_hThread = NULL;
		m_uThreadID = 0;
		m_dwRunSleepTime = 1;

		m_uTerminate = m_uPause = 0;
		m_dwLastRunCostTime = 0;

		m_hPreparedEvt = CreateEvent(NULL,
			FALSE,//	Autoreset
			FALSE,
			RUNTHREAD_PREPARED_EVENT);

		if(m_hPreparedEvt == NULL)
		{
			throw std::exception("Can't create the thread event in [ThreadRunner]");
		}
	}
	virtual ~ThreadRunner()
	{
		Stop();

		CloseHandle(m_hPreparedEvt);

		while(1)
		{
			if(GetThreadState() == TS_STOP)
			{
				break;
			}
		}
	}

public:
	virtual bool Run()
	{
		if(GetThreadState() != TS_STOP)
		{
			return false;
		}

		m_hThread = (HANDLE)_beginthreadex(NULL,
			0,
			&ThreadRunner::ThreadProc,
			this,
			0,
			&m_uThreadID);

		if(NULL == m_hThread)
		{
			return false;
		}

		return true;
	}
	virtual unsigned int Thread_DoWork()
	{
		return 0;
	}
	virtual unsigned int Thread_Initialize()
	{
		return 0;
	}
	virtual unsigned int Thread_ProcessMessage(const MSG* _pMsg)
	{
		return 0;
	}

public:
	void Stop()
	{
		m_uTerminate = 1;
		//m_eThreadState = TS_STOP;
	}
	void Pause()
	{
		m_uPause = 1;
		//m_eThreadState = TS_PAUSE;
	}
	void Resume()
	{
		m_uPause = 0;
		//m_eThreadState = TS_RUN;
	}

	void PostRunnerMessage(const MSG* _pMsg)
	{
		if(GetThreadState() == TS_RUN)
		{
			PostThreadMessage(GetThreadID(), _pMsg->message, _pMsg->wParam, _pMsg->lParam);
		}
	}

public:
	//	setter and getter
	READWRITE_INTERFACE(m_hThread, ThreadHandle, HANDLE);
	READWRITE_INTERFACE(m_uThreadID, ThreadID, unsigned int);
	READWRITE_INTERFACE(m_dwRunSleepTime, RunSleepTime, DWORD);
	READ_INTERFACE(m_eThreadState, ThreadState, THREAD_STATE);
	READ_INTERFACE(m_hPreparedEvt, PreparedEvt, HANDLE);
	READ_INTERFACE(m_dwLastRunCostTime, LastRunCostTime, DWORD);

private:
	static unsigned int __stdcall ThreadProc(void* _pParam)
	{
		ThreadRunner* pThread = (ThreadRunner*)_pParam;
		unsigned int uRet = 0;

		//	Create the message loop
		MSG msg;
		PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
		ZeroMemory(&msg, sizeof(MSG));

		if(!SetEvent(pThread->m_hPreparedEvt))
		{
			uRet = TR_SETEVENTFAILED;
		}

		//	Initialize
		uRet = pThread->Thread_Initialize();
		DWORD dwCurTick = GetTickCount();

		//	a runloop
		while(0 == uRet)
		{
			//	Process thread message
			if(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				uRet = pThread->Thread_ProcessMessage(&msg);
			}
			if(0 != uRet)
			{
				break;
			}

			//	Stop or pause this thread
			if(pThread->m_uPause)
			{
				pThread->m_eThreadState = TS_PAUSE;
				SleepEx(pThread->GetRunSleepTime(), TRUE);
				continue;
			}
			if(pThread->m_uTerminate)
			{
				break;
			}

			//	Do thread work
			pThread->m_eThreadState = TS_RUN;
			dwCurTick = GetTickCount();

			uRet = pThread->Thread_DoWork();
			if(0 != uRet)
			{
				break;
			}

			pThread->m_dwLastRunCostTime = GetTickCount() - dwCurTick;

			//	Into sleep
			SleepEx(pThread->GetRunSleepTime(), TRUE);
		}

		pThread->m_eThreadState = TS_STOP;
		return uRet;
	}

protected:
	THREAD_STATE m_eThreadState;
	HANDLE m_hPreparedEvt;
	ThreadNumber m_uTerminate;
	ThreadNumber m_uPause;

	HANDLE m_hThread;
	unsigned int m_uThreadID;

	DWORD m_dwRunSleepTime;
	DWORD m_dwLastRunCostTime;
};
//////////////////////////////////////////////////////////////////////////
#endif


封裝了ICMP計算ping延遲的函式,返回值基於message,獲取對應的自定義message即可。該封裝執行緒安全。

#ifndef _INC_PING_THREAD_
#define _INC_PING_THREAD_
//////////////////////////////////////////////////////////////////////////
#include <WinSock2.h>
#include "ThreadRunner.h"
#include <list>
//////////////////////////////////////////////////////////////////////////
//	ping struct declaration
struct PingTask
{
	int nTaskID;
	char szDeskIP[20];
	ULONG ulInetIP;
};
typedef std::list<PingTask*> PingTaskList;

#define DEF_PACKET_SIZE 32
#define ECHO_REQUEST 8
#define ECHO_REPLY 0

#define DEF_PINGTIMEOUT	200

#pragma pack(push, 1)

struct IPHeader
{
	BYTE m_byVerHLen; //4位版本+4位首部長度
	BYTE m_byTOS; //服務型別
	USHORT m_usTotalLen; //總長度
	USHORT m_usID; //標識
	USHORT m_usFlagFragOffset; //3位標誌+13位片偏移
	BYTE m_byTTL; //TTL
	BYTE m_byProtocol; //協議
	USHORT m_usHChecksum; //首部檢驗和
	ULONG m_ulSrcIP; //源IP地址
	ULONG m_ulDestIP; //目的IP地址
};

struct ICMPHeader
{ 
	BYTE m_byType; //型別
	BYTE m_byCode; //程式碼
	USHORT m_usChecksum; //檢驗和 
	USHORT m_usID; //識別符號
	USHORT m_usSeq; //序號
	ULONG m_ulTimeStamp; //時間戳(非標準ICMP頭部)
};

#pragma pack(pop)

struct PingReply
{
	USHORT m_usSeq;
	DWORD m_dwRoundTripTime;
	DWORD m_dwBytes;
	DWORD m_dwTTL;
};
//////////////////////////////////////////////////////////////////////////
DWORD GetTickCountCalibrate();
//////////////////////////////////////////////////////////////////////////
class PingThread : public ThreadRunner
{
public:
	PingThread();
	virtual ~PingThread();

public:
	virtual unsigned int Thread_DoWork();

public:
	bool Init();
	void UnInit();

	int AddTask(const char* _pszIP);
	bool RemoveTask(int _nTaskID);
	void ClearTask();

	DWORD GetPingTimeout()
	{
		return m_dwPingTimeout;
	}
	void SetPingTimeout(DWORD _dwTimeout)
	{
		m_dwPingTimeout = _dwTimeout;
	}

	void SetReceiverMsgID(DWORD _dwMsgID)
	{
		m_dwReceiverMsgID = _dwMsgID;
	}
	void SetReceiverHwnd(HWND _hReceiver)
	{
		m_hReceiverHwnd = _hReceiver;
	}

protected:
	void LockTaskList()
	{
		EnterCriticalSection(&m_stCriticalSection);
	}
	void UnlockTaskList()
	{
		LeaveCriticalSection(&m_stCriticalSection);
	}
	bool DoPingWork(const PingTask* _pTask);
	USHORT CalCheckSum(USHORT *pBuffer, int nSize);

protected:
	bool m_bInitOk;
	PingTaskList m_xPingTaskList;
	CRITICAL_SECTION m_stCriticalSection;
	int m_nTaskIDSeed;
	int m_nPingICMPSeed;
	//	ICMP buffer
	char* m_szICMPData;
	//	socket relative
	SOCKET m_socketICMP;
	WSAEVENT m_event;
	USHORT m_usCurrentProcID;

	//	settings
	DWORD m_dwPingTimeout;

	//	receiver
	DWORD m_dwReceiverMsgID;
	HWND m_hReceiverHwnd;
};
//////////////////////////////////////////////////////////////////////////
#endif


#include "PingThread.h"
//////////////////////////////////////////////////////////////////////////
#pragma comment(lib, "Ws2_32.lib")
//////////////////////////////////////////////////////////////////////////
ULONG GetTickCountCalibrate()
{
	static ULONG s_ulFirstCallTick = 0;
	static LONGLONG s_ullFirstCallTickMS = 0;

	SYSTEMTIME systemtime;
	FILETIME filetime;
	GetLocalTime(&systemtime);    
	SystemTimeToFileTime(&systemtime, &filetime);
	LARGE_INTEGER liCurrentTime;
	liCurrentTime.HighPart = filetime.dwHighDateTime;
	liCurrentTime.LowPart = filetime.dwLowDateTime;
	LONGLONG llCurrentTimeMS = liCurrentTime.QuadPart / 10000;

	if (s_ulFirstCallTick == 0)
	{
		s_ulFirstCallTick = GetTickCount();
	}
	if (s_ullFirstCallTickMS == 0)
	{
		s_ullFirstCallTickMS = llCurrentTimeMS;
	}

	return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);
}
//////////////////////////////////////////////////////////////////////////
PingThread::PingThread()
{
	InitializeCriticalSection(&m_stCriticalSection);
	m_nTaskIDSeed = 0;
	m_nPingICMPSeed = 0;
	m_bInitOk = false;
	m_szICMPData = NULL;
	m_dwPingTimeout = DEF_PINGTIMEOUT;
	m_usCurrentProcID = 0;
	m_dwReceiverMsgID = 0;
	m_hReceiverHwnd = NULL;
}

PingThread::~PingThread()
{

}

//////////////////////////////////////////////////////////////////////////
bool PingThread::Init()
{
	WSADATA WSAData;
	WSAStartup(MAKEWORD(1, 1), &WSAData);
	m_event = WSACreateEvent();
	m_socketICMP = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);

	if (m_socketICMP != SOCKET_ERROR)
	{
		WSAEventSelect(m_socketICMP, m_event, FD_READ);
		m_szICMPData = new char[DEF_PACKET_SIZE + sizeof(ICMPHeader)];
		m_bInitOk = true;
	}
	else
	{
		UnInit();
	}

	return m_bInitOk;
}

void PingThread::UnInit()
{
	ClearTask();
	WSACleanup();
	delete[] m_szICMPData;
	m_szICMPData = NULL;
}

unsigned int PingThread::Thread_DoWork()
{
	LockTaskList();

	PingTaskList::iterator begIter = m_xPingTaskList.begin();
	PingTaskList::iterator endIter = m_xPingTaskList.end();

	for(begIter;
		begIter != endIter;
		++begIter)
	{
		PingTask* pTask = *begIter;
		DoPingWork(pTask);
	}


	UnlockTaskList();

	return 0;
}

int PingThread::AddTask(const char* _pszIP)
{
	unsigned long ulAddr = inet_addr(_pszIP);
	if(ulAddr == INADDR_NONE)
	{
		return 0;
	}

	LockTaskList();

	PingTask* pTask = new PingTask;
	strcpy(pTask->szDeskIP, _pszIP);
	pTask->ulInetIP = ulAddr;
	pTask->nTaskID = ++m_nTaskIDSeed;
	m_xPingTaskList.push_back(pTask);

	UnlockTaskList();

	return pTask->nTaskID;
}

bool PingThread::RemoveTask(int _nTaskID)
{
	LockTaskList();

	bool bRet = false;
	PingTaskList::iterator begIter = m_xPingTaskList.begin();
	PingTaskList::iterator endIter = m_xPingTaskList.end();

	for(begIter;
		begIter != endIter;
		)
	{
		PingTask* pTask = *begIter;

		if(0 == _nTaskID)
		{
			//	remove all
			delete pTask;
			pTask = NULL;
			begIter = m_xPingTaskList.erase(begIter);
		}
		else
		{
			if(pTask->nTaskID == _nTaskID)
			{
				delete pTask;
				pTask = NULL;
				m_xPingTaskList.erase(begIter);
				bRet = true;
				break;
			}
                        else{
				++begIter;
			}
		}
	}

	UnlockTaskList();

	if(0 == _nTaskID)
	{
		return true;
	}
	else
	{
		return bRet;
	}
}

void PingThread::ClearTask()
{
	RemoveTask(0);
}

bool PingThread::DoPingWork(const PingTask* _pTask)
{
	if(!m_bInitOk)
	{
		return false;
	}

	//配置SOCKET
	sockaddr_in sockaddrDest; 
	sockaddrDest.sin_family = AF_INET; 
	sockaddrDest.sin_addr.s_addr = _pTask->ulInetIP;
	int nSockaddrDestSize = sizeof(sockaddrDest);

	//構建ICMP包
	int nICMPDataSize = DEF_PACKET_SIZE + sizeof(ICMPHeader);
	ULONG ulSendTimestamp = GetTickCountCalibrate();
	USHORT usSeq = ++m_nPingICMPSeed;    
	memset(m_szICMPData, 0, nICMPDataSize);
	ICMPHeader *pICMPHeader = (ICMPHeader*)m_szICMPData;
	pICMPHeader->m_byType = ECHO_REQUEST; 
	pICMPHeader->m_byCode = 0; 
	pICMPHeader->m_usID = m_usCurrentProcID;    
	pICMPHeader->m_usSeq = usSeq;
	pICMPHeader->m_ulTimeStamp = ulSendTimestamp;
	pICMPHeader->m_usChecksum = CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);

	//傳送ICMP報文
	if (sendto(m_socketICMP, m_szICMPData, nICMPDataSize, 0, (struct sockaddr*)&sockaddrDest, nSockaddrDestSize) == SOCKET_ERROR)
	{
		return false;
	}

	char recvbuf[256] = {"\0"};
	while (TRUE)
	{
		//接收響應報文
		if (WSAWaitForMultipleEvents(1, &m_event, FALSE, m_dwPingTimeout, FALSE) != WSA_WAIT_TIMEOUT)
		{
			WSANETWORKEVENTS netEvent;
			WSAEnumNetworkEvents(m_socketICMP, m_event, &netEvent);

			if (netEvent.lNetworkEvents & FD_READ)
			{
				ULONG nRecvTimestamp = GetTickCountCalibrate();
				int nPacketSize = recvfrom(m_socketICMP, recvbuf, 256, 0, (struct sockaddr*)&sockaddrDest, &nSockaddrDestSize);
				if (nPacketSize != SOCKET_ERROR)
				{
					IPHeader *pIPHeader = (IPHeader*)recvbuf;
					USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4);
					ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen);

					if (pICMPHeader->m_usID == m_usCurrentProcID //是當前程序發出的報文
						&& pICMPHeader->m_byType == ECHO_REPLY //是ICMP響應報文
						&& pICMPHeader->m_usSeq == usSeq //是本次請求報文的響應報文
						)
					{
						/*pPingReply->m_usSeq = usSeq;
						pPingReply->m_dwRoundTripTime = nRecvTimestamp - pICMPHeader->m_ulTimeStamp;
						pPingReply->m_dwBytes = nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);
						pPingReply->m_dwTTL = pIPHeader->m_byTTL;
						return TRUE;*/
						if(0 != m_dwReceiverMsgID &&
							NULL != m_hReceiverHwnd)
						{
							PostMessage(m_hReceiverHwnd, m_dwReceiverMsgID, _pTask->ulInetIP, nRecvTimestamp - pICMPHeader->m_ulTimeStamp);
							return true;
						}
					}
				}
			}
		}
		//超時
		if (GetTickCountCalibrate() - ulSendTimestamp >= m_dwPingTimeout)
		{
			PostMessage(m_hReceiverHwnd, m_dwReceiverMsgID, _pTask->ulInetIP, -1);
			return false;
		}
	}
}

USHORT PingThread::CalCheckSum(USHORT *pBuffer, int nSize)
{
	unsigned long ulCheckSum=0; 
	while(nSize > 1) 
	{ 
		ulCheckSum += *pBuffer++; 
		nSize -= sizeof(USHORT); 
	}
	if(nSize ) 
	{ 
		ulCheckSum += *(UCHAR*)pBuffer; 
	} 

	ulCheckSum = (ulCheckSum >> 16) + (ulCheckSum & 0xffff); 
	ulCheckSum += (ulCheckSum >>16); 

	return (USHORT)(~ulCheckSum); 
}

使用也很簡單

m_xPingThread.SetReceiverHwnd(GetSafeHwnd());
		m_xPingThread.SetReceiverMsgID(WM_USER_PINGMSG);
		m_xPingThread.AddTask("121.40.197.47");
		m_xPingThread.AddTask("11.111.11.111");
		m_xPingThread.SetRunSleepTime(1000);
		m_xPingThread.Run();


只需要處理對應訊息即可。退出的時候注意需要結束執行緒及解除安裝資源

	if(m_xPingThread.GetThreadState() == ThreadRunner::TS_RUN)
	{
		m_xPingThread.Stop();
		ThreadRunner::THREAD_STATE ts = m_xPingThread.GetThreadState();

		while(ts == ThreadRunner::TS_RUN)
		{
			//	nothing
			ts = m_xPingThread.GetThreadState();
			Sleep(1000);
		}
		m_xPingThread.UnInit();
	}



使用效果