注:核心内容使用了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(); }
使用效果