c++計算網路延遲毫秒數
阿新 • • 發佈:2019-01-29
注:核心內容使用了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();
}
使用效果