注:核心内容使用了此处的实现,我只是做了下小封装
开一个线程专门来刷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();
}