分类: C/C++
2012-08-07 13:11:26
class UDP_CONTEXT : protected NET_CONTEXT
{
friend class UdpSer;
protected:
IP_ADDR m_RemoteAddr; //对端地址
enum
{
HEAP_SIZE = 1024 * 1024 * 5,
MAX_IDL_DATA = 10000,
};
public:
UDP_CONTEXT() {}
virtual ~UDP_CONTEXT() {}
void* operator new(size_t nSize);
void operator delete(void* p);
private:
static vector<UDP_CONTEXT* > s_IDLQue;
static CRITICAL_SECTION s_IDLQueLock;
static HANDLE s_hHeap;
};
class DLLENTRY UDP_RCV_DATA
{
friend class UdpSer;
public:
CHAR* m_pData; //数据缓冲区
INT m_nLen; //数据的长度
IP_ADDR m_PeerAddr; //发送报文的地址
UDP_RCV_DATA(const CHAR* szBuf, int nLen, const IP_ADDR& PeerAddr);
~UDP_RCV_DATA();
void* operator new(size_t nSize);
void operator delete(void* p);
enum
{
RCV_HEAP_SIZE = 1024 * 1024 *50, //s_Heap堆的大小
DATA_HEAP_SIZE = 100 * 1024* 1024, //s_DataHeap堆的大小
MAX_IDL_DATA = 250000,
};
private:
static vector<UDP_RCV_DATA* > s_IDLQue;
static CRITICAL_SECTION s_IDLQueLock;
static HANDLE s_DataHeap; //数据缓冲区的堆
static HANDLE s_Heap; //RCV_DATA的堆
};
class DLLENTRY UdpSer
{
public:
UdpSer();
~UdpSer();
/************************************************************************
* Desc : 初始化静态资源,在申请UDP实例对象之前应先调用该函数, 否则程序无法正常运行
************************************************************************/
static void InitReource();
/************************************************************************
* Desc : 在释放UDP实例以后, 掉用该函数释放相关静态资源
************************************************************************/
static void ReleaseReource();
//用指定本地地址和端口进行初始化
BOOL StartServer(const CHAR* szIp = "0.0.0.0", INT nPort = 0);
//从数据队列的头部获取一个接收数据, pCount不为null时返回队列的长度
UDP_RCV_DATA* GetRcvData(DWORD* pCount);
//向对端发送数据
BOOL SendData(const IP_ADDR& PeerAddr, const CHAR* szData, INT nLen);
/****************************************************
* Name : CloseServer()
* Desc : 关闭服务器
****************************************************/
void CloseServer();
protected:
SOCKET m_hSock;
vector<UDP_RCV_DATA* > m_RcvDataQue; //接收数据队列
CRITICAL_SECTION m_RcvDataLock; //访问m_RcvDataQue的互斥锁
long volatile m_bThreadRun; //是否允许后台线程继续运行
BOOL m_bSerRun; //服务器是否正在运行
HANDLE *m_pThreads; //线程数组
HANDLE m_hCompletion; //完成端口句柄
void ReadCompletion(BOOL bSuccess, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped);
/****************************************************
* Name : WorkThread()
* Desc : I/O 后台管理线程
****************************************************/
static UINT WINAPI WorkThread(LPVOID lpParam);
};
BOOL UdpSer::StartServer(const CHAR* szIp /* = */, INT nPort /* = 0 */)
{
BOOL bRet = TRUE;
const int RECV_COUNT = 500;
WSABUF RcvBuf = { NULL, 0 };
DWORD dwBytes = 0;
DWORD dwFlag = 0;
INT nAddrLen = sizeof(IP_ADDR);
INT iErrCode = 0;
try
{
if (m_bSerRun)
{
THROW_LINE;
}
m_bSerRun = TRUE;
m_hSock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (INVALID_SOCKET == m_hSock)
{
THROW_LINE;
}
ULONG ul = 1;
ioctlsocket(m_hSock, FIONBIO, &ul);
//设置为地址重用,优点在于服务器关闭后可以立即启用
int nOpt = 1;
setsockopt(m_hSock, SOL_SOCKET, SO_REUSEADDR, (char*)&nOpt, sizeof(nOpt));
//关闭系统缓存,使用自己的缓存以防止数据的复制操作
INT nZero = 0;
setsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&nZero, sizeof(nZero));
setsockopt(m_hSock, SOL_SOCKET, SO_RCVBUF, (CHAR*)&nZero, sizeof(nZero));
IP_ADDR addr(szIp, nPort);
if (SOCKET_ERROR == bind(m_hSock, (sockaddr*)&addr, sizeof(addr)))
{
closesocket(m_hSock);
THROW_LINE;
}
//将SOCKET绑定到完成端口上
CreateIoCompletionPort((HANDLE)m_hSock, m_hCompletion, 0, 0);
//投递读操作
for (int nIndex = 0; nIndex < RECV_COUNT; nIndex++)
{
UDP_CONTEXT* pRcvContext = new UDP_CONTEXT();
if (pRcvContext && pRcvContext->m_pBuf)
{
dwFlag = 0;
dwBytes = 0;
nAddrLen = sizeof(IP_ADDR);
RcvBuf.buf = pRcvContext->m_pBuf;
RcvBuf.len = UDP_CONTEXT::S_PAGE_SIZE;
pRcvContext->m_hSock = m_hSock;
pRcvContext->m_nOperation = OP_READ;
iErrCode = WSARecvFrom(pRcvContext->m_hSock, &RcvBuf, 1, &dwBytes, &dwFlag, (sockaddr*)(&pRcvContext->m_RemoteAddr)
, &nAddrLen, &(pRcvContext->m_ol), NULL);
if (SOCKET_ERROR == iErrCode && ERROR_IO_PENDING != WSAGetLastError())
{
delete pRcvContext;
pRcvContext = NULL;
}
}
else
{
delete pRcvContext;
}
}
}
catch (const long &lErrLine)
{
bRet = FALSE;
_TRACE("Exp : %s -- %ld ", __FILE__, lErrLine);
}
return bRet;
}
UDP_RCV_DATA *UdpSer::GetRcvData(DWORD* pCount)
{
UDP_RCV_DATA* pRcvData = NULL;
EnterCriticalSection(&m_RcvDataLock);
vector<UDP_RCV_DATA* >::iterator iterRcv = m_RcvDataQue.begin();
if (iterRcv != m_RcvDataQue.end())
{
pRcvData = *iterRcv;
m_RcvDataQue.erase(iterRcv);
}
if (pCount)
{
*pCount = (DWORD)(m_RcvDataQue.size());
}
LeaveCriticalSection(&m_RcvDataLock);
return pRcvData;
}
BOOL UdpSer::SendData(const IP_ADDR& PeerAddr, const CHAR* szData, INT nLen)
{
BOOL bRet = TRUE;
try
{
if (nLen >= 1500)
{
THROW_LINE;
}
UDP_CONTEXT* pSendContext = new UDP_CONTEXT();
if (pSendContext && pSendContext->m_pBuf)
{
pSendContext->m_nOperation = OP_WRITE;
pSendContext->m_RemoteAddr = PeerAddr; 
memcpy(pSendContext->m_pBuf, szData, nLen);
WSABUF SendBuf = { NULL, 0 };
DWORD dwBytes = 0;
SendBuf.buf = pSendContext->m_pBuf;
SendBuf.len = nLen;
INT iErrCode = WSASendTo(m_hSock, &SendBuf, 1, &dwBytes, 0, (sockaddr*)&PeerAddr, sizeof(PeerAddr), &(pSendContext->m_ol), NULL);
if (SOCKET_ERROR == iErrCode && ERROR_IO_PENDING != WSAGetLastError())
{
delete pSendContext;
THROW_LINE;
}
}
else
{
delete pSendContext;
THROW_LINE;
}
}
catch (const long &lErrLine)
{
bRet = FALSE;
_TRACE("Exp : %s -- %ld ", __FILE__, lErrLine);
}
return bRet;
}
void UdpSer::CloseServer()
{
m_bSerRun = FALSE;
closesocket(m_hSock);
}
UINT WINAPI UdpSer::WorkThread(LPVOID lpParam)
{
UdpSer *pThis = (UdpSer *)lpParam;
DWORD dwTrans = 0, dwKey = 0;
LPOVERLAPPED pOl = NULL;
UDP_CONTEXT *pContext = NULL;
while (TRUE)
{
BOOL bOk = GetQueuedCompletionStatus(pThis->m_hCompletion, &dwTrans, &dwKey, (LPOVERLAPPED *)&pOl, WSA_INFINITE);
pContext = CONTAINING_RECORD(pOl, UDP_CONTEXT, m_ol);
if (pContext)
{
switch (pContext->m_nOperation)
{
case OP_READ:
pThis->ReadCompletion(bOk, dwTrans, pOl);
break;
case OP_WRITE:
delete pContext;
pContext = NULL;
break;
}
}
if (FALSE == InterlockedExchangeAdd(&(pThis->m_bThreadRun), 0))
{
break;
}
}
return 0;
}
void UdpSer::ReadCompletion(BOOL bSuccess, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
UDP_CONTEXT* pRcvContext = CONTAINING_RECORD(lpOverlapped, UDP_CONTEXT, m_ol);
WSABUF RcvBuf = { NULL, 0 };
DWORD dwBytes = 0;
DWORD dwFlag = 0;
INT nAddrLen = sizeof(IP_ADDR);
INT iErrCode = 0;
if (TRUE == bSuccess && dwNumberOfBytesTransfered <= UDP_CONTEXT::S_PAGE_SIZE)
{
#ifdef _XML_NET_
EnterCriticalSection(&m_RcvDataLock);
UDP_RCV_DATA* pRcvData = new UDP_RCV_DATA(pRcvContext->m_pBuf, dwNumberOfBytesTransfered, pRcvContext->m_RemoteAddr);
if (pRcvData && pRcvData->m_pData)
{
m_RcvDataQue.push_back(pRcvData);
}
else
{
delete pRcvData;
}
LeaveCriticalSection(&m_RcvDataLock);
#else
if (dwNumberOfBytesTransfered >= sizeof(PACKET_HEAD))
{
EnterCriticalSection(&m_RcvDataLock);
UDP_RCV_DATA* pRcvData = new UDP_RCV_DATA(pRcvContext->m_pBuf, dwNumberOfBytesTransfered, pRcvContext->m_RemoteAddr);
if (pRcvData && pRcvData->m_pData)
{
m_RcvDataQue.push_back(pRcvData);
}
else
{
delete pRcvData;
}
LeaveCriticalSection(&m_RcvDataLock);
}
#endif
//投递下一个接收操作
RcvBuf.buf = pRcvContext->m_pBuf;
RcvBuf.len = UDP_CONTEXT::S_PAGE_SIZE;
iErrCode = WSARecvFrom(pRcvContext->m_hSock, &RcvBuf, 1, &dwBytes, &dwFlag, (sockaddr*)(&pRcvContext->m_RemoteAddr)
, &nAddrLen, &(pRcvContext->m_ol), NULL);
if (SOCKET_ERROR == iErrCode && ERROR_IO_PENDING != WSAGetLastError())
{
ATLTRACE("\r\n%s -- %ld dwNumberOfBytesTransfered = %ld, LAST_ERR = %ld"
, __FILE__, __LINE__, dwNumberOfBytesTransfered, WSAGetLastError());
delete pRcvContext;
pRcvContext = NULL;
}
}
else
{
delete pRcvContext;
}
}