NH 1 year ago
commit
40504ab11f
100 changed files with 41401 additions and 0 deletions
  1. 6 0
      .gitignore
  2. 40 0
      ArqHelper.cpp
  3. 791 0
      ArqHelper.h
  4. 23 0
      CMakeLists.txt
  5. 200 0
      HPSocket-SSL.cpp
  6. 749 0
      HPSocket.cpp
  7. 380 0
      HPSocket4C-SSL.cpp
  8. 3747 0
      HPSocket4C.cpp
  9. 496 0
      HPThreadPool.cpp
  10. 168 0
      HPThreadPool.h
  11. 479 0
      HttpAgent.cpp
  12. 216 0
      HttpAgent.h
  13. 598 0
      HttpClient.cpp
  14. 397 0
      HttpClient.h
  15. 927 0
      HttpCookie.cpp
  16. 194 0
      HttpCookie.h
  17. 469 0
      HttpHelper.cpp
  18. 1385 0
      HttpHelper.h
  19. 615 0
      HttpServer.cpp
  20. 223 0
      HttpServer.h
  21. 132 0
      InternalDef.h
  22. 51 0
      MiscHelper.cpp
  23. 157 0
      MiscHelper.h
  24. 229 0
      SSLAgent.cpp
  25. 107 0
      SSLAgent.h
  26. 150 0
      SSLClient.cpp
  27. 105 0
      SSLClient.h
  28. 1200 0
      SSLHelper.cpp
  29. 496 0
      SSLHelper.h
  30. 229 0
      SSLServer.cpp
  31. 115 0
      SSLServer.h
  32. 1669 0
      SocketHelper.cpp
  33. 959 0
      SocketHelper.h
  34. 770 0
      SocketObject4C.h
  35. 1306 0
      TcpAgent.cpp
  36. 306 0
      TcpAgent.h
  37. 723 0
      TcpClient.cpp
  38. 248 0
      TcpClient.h
  39. 24 0
      TcpPackAgent.cpp
  40. 221 0
      TcpPackAgent.h
  41. 24 0
      TcpPackClient.cpp
  42. 126 0
      TcpPackClient.h
  43. 24 0
      TcpPackServer.cpp
  44. 221 0
      TcpPackServer.h
  45. 24 0
      TcpPullAgent.cpp
  46. 173 0
      TcpPullAgent.h
  47. 24 0
      TcpPullClient.cpp
  48. 89 0
      TcpPullClient.h
  49. 24 0
      TcpPullServer.cpp
  50. 173 0
      TcpPullServer.h
  51. 1187 0
      TcpServer.cpp
  52. 306 0
      TcpServer.h
  53. 186 0
      UdpArqClient.cpp
  54. 114 0
      UdpArqClient.h
  55. 253 0
      UdpArqServer.cpp
  56. 120 0
      UdpArqServer.h
  57. 701 0
      UdpCast.cpp
  58. 219 0
      UdpCast.h
  59. 830 0
      UdpClient.cpp
  60. 234 0
      UdpClient.h
  61. 757 0
      UdpNode.cpp
  62. 198 0
      UdpNode.h
  63. 1231 0
      UdpServer.cpp
  64. 301 0
      UdpServer.h
  65. 409 0
      brotli/decode.h
  66. 501 0
      brotli/encode.h
  67. 305 0
      brotli/port.h
  68. 100 0
      brotli/shared_dictionary.h
  69. 83 0
      brotli/types.h
  70. 298 0
      common/BufferPool.cpp
  71. 869 0
      common/BufferPool.h
  72. 198 0
      common/BufferPtr.h
  73. 291 0
      common/CriSec.h
  74. 24 0
      common/Event.cpp
  75. 530 0
      common/Event.h
  76. 237 0
      common/FileHelper.cpp
  77. 123 0
      common/FileHelper.h
  78. 393 0
      common/FuncHelper.cpp
  79. 426 0
      common/FuncHelper.h
  80. 40 0
      common/GeneralHelper.h
  81. 348 0
      common/IODispatcher.cpp
  82. 272 0
      common/IODispatcher.h
  83. 62 0
      common/PollHelper.cpp
  84. 51 0
      common/PollHelper.h
  85. 152 0
      common/PrivateHeap.h
  86. 228 0
      common/RWLock.cpp
  87. 128 0
      common/RWLock.h
  88. 1731 0
      common/RingBuffer.h
  89. 1068 0
      common/STLHelper.h
  90. 117 0
      common/Semaphore.h
  91. 183 0
      common/SignalHandler.h
  92. 117 0
      common/Singleton.h
  93. 930 0
      common/StringT.h
  94. 66 0
      common/SysHelper.cpp
  95. 186 0
      common/SysHelper.h
  96. 50 0
      common/Thread.cpp
  97. 612 0
      common/Thread.h
  98. 11 0
      common/http/Readme.txt
  99. 903 0
      common/http/llhttp.h
  100. 520 0
      common/http/llhttp_api.c

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+################################################################################
+# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
+################################################################################
+
+/.vs
+/out/build/x64-Debug

+ 40 - 0
ArqHelper.cpp

@@ -0,0 +1,40 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "ArqHelper.h"
+
+#ifdef _UDP_SUPPORT
+
+DWORD GenerateConversationID()
+{
+	static volatile DWORD s_dwConvID = ::TimeGetTime();
+
+	DWORD dwConvID = ::InterlockedIncrement(&s_dwConvID);
+
+	if(dwConvID == 0)
+		dwConvID = ::InterlockedIncrement(&s_dwConvID);
+
+	return dwConvID;
+}
+
+#endif

+ 791 - 0
ArqHelper.h

@@ -0,0 +1,791 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "hpsocket/HPTypeDef.h"
+
+#ifdef _UDP_SUPPORT
+
+#include "SocketHelper.h"
+
+#include "common/FuncHelper.h"
+#include "common/BufferPool.h"
+#include "common/IODispatcher.h"
+#include "common/kcp/ikcp.h"
+
+#include <memory>
+
+using namespace std;
+
+#define DEFAULT_ARQ_NO_DELAY			FALSE
+#define DEFAULT_ARQ_TURNOFF_NC			FALSE
+#define DEFAULT_ARQ_FLUSH_INTERVAL		60
+#define DEFAULT_ARQ_RESEND_BY_ACKS		0
+#define DEFAULT_ARQ_SEND_WND_SIZE		128
+#define DEFAULT_ARQ_RECV_WND_SIZE		512
+#define DEFAULT_ARQ_MIN_RTO				30
+#define DEFAULT_ARQ_FAST_LIMIT			5
+#define DEFAULT_ARQ_MAX_TRANS_UNIT		DEFAULT_UDP_MAX_DATAGRAM_SIZE
+#define DEFAULT_ARQ_MAX_MSG_SIZE		DEFAULT_BUFFER_CACHE_CAPACITY
+#define DEFAULT_ARQ_HANND_SHAKE_TIMEOUT	5000
+
+#define KCP_HEADER_SIZE					24
+#define KCP_MIN_RECV_WND				128
+
+#define ARQ_MAX_HANDSHAKE_INTERVAL		2000
+
+using Fn_ArqOutputProc = int (*)(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv);
+
+DWORD GenerateConversationID();
+
+/************************************************************************
+名称:ARQ 握手状态
+描述:标识当前连接的 ARQ 握手状态
+************************************************************************/
+enum EnArqHandShakeStatus
+{
+	ARQ_HSS_INIT	= 0,	// 初始状态
+	ARQ_HSS_PROC	= 1,	// 正在握手
+	ARQ_HSS_SUCC	= 2,	// 握手成功
+};
+
+struct TArqCmd
+{
+public:
+	static const UINT16 MAGIC			= 0xBB4F;
+	static const UINT8	CMD_HANDSHAKE	= 0x01;
+	static const UINT8	FLAG_COMPLETE	= 0x01;
+	static const int	PACKAGE_LENGTH	= 12;
+
+public:
+	UINT16 magic;
+	UINT8 cmd;
+	UINT8 flag;
+	DWORD selfID;
+	DWORD peerID;
+
+public:
+	static BYTE* MakePackage(UINT8 cmd, UINT8 flag, DWORD selfID, DWORD peerID, UINT16 magic = MAGIC)
+	{
+		BYTE* buff = new BYTE[PACKAGE_LENGTH];
+
+		*((UINT16*)(buff + 0))	= magic;
+		*((UINT8*)(buff + 2))	= cmd;
+		*((UINT8*)(buff + 3))	= flag;
+		*((DWORD*)(buff + 4))	= selfID;
+		*((DWORD*)(buff + 8))	= peerID;
+
+		return buff;
+	}
+
+	BYTE* MakePackage()
+	{
+		return MakePackage(cmd, flag, selfID, peerID, magic);
+	}
+
+	BOOL Parse(const BYTE buff[PACKAGE_LENGTH])
+	{
+		magic	= *((UINT16*)(buff + 0));
+		cmd		= *((UINT8*) (buff + 2));
+		flag	= *((UINT8*) (buff + 3));
+		selfID	= *((DWORD*) (buff + 4));
+		peerID	= *((DWORD*) (buff + 8));
+
+		return IsValid();
+	}
+
+	BOOL IsValid()	{return (magic == MAGIC && cmd == CMD_HANDSHAKE && (flag & 0xFE) == 0);}
+
+public:
+	TArqCmd()
+	{
+		::ZeroMemory(this, sizeof(TArqCmd));
+	}
+
+	TArqCmd(UINT8 c, UINT8 f, DWORD sid, DWORD pid)
+	: magic	(MAGIC)
+	, cmd	(c)
+	, flag	(f)
+	, selfID(sid)
+	, peerID(pid)
+	{
+
+	}
+};
+
+struct TArqAttr
+{
+	BOOL	bNoDelay;
+	BOOL	bTurnoffNc;
+	DWORD	dwResendByAcks;
+	DWORD	dwFlushInterval;
+	DWORD	dwSendWndSize;
+	DWORD	dwRecvWndSize;
+	DWORD	dwMinRto;
+	DWORD	dwMtu;
+	DWORD	dwFastLimit;
+	DWORD	dwMaxMessageSize;
+	DWORD	dwHandShakeTimeout;
+
+public:
+	TArqAttr( BOOL no_delay				= DEFAULT_ARQ_NO_DELAY
+			, BOOL turnoff_nc			= DEFAULT_ARQ_TURNOFF_NC
+			, DWORD resend_by_acks		= DEFAULT_ARQ_RESEND_BY_ACKS
+			, DWORD flush_interval		= DEFAULT_ARQ_FLUSH_INTERVAL
+			, DWORD send_wnd_size		= DEFAULT_ARQ_SEND_WND_SIZE
+			, DWORD recv_wnd_size		= DEFAULT_ARQ_RECV_WND_SIZE
+			, DWORD min_rto				= DEFAULT_ARQ_MIN_RTO
+			, DWORD mtu					= DEFAULT_ARQ_MAX_TRANS_UNIT
+			, DWORD fast_limit			= DEFAULT_ARQ_FAST_LIMIT
+			, DWORD max_msg_size		= DEFAULT_ARQ_MAX_MSG_SIZE
+			, DWORD hand_shake_timeout	= DEFAULT_ARQ_HANND_SHAKE_TIMEOUT
+			)
+	: bNoDelay			(no_delay)
+	, bTurnoffNc		(turnoff_nc)
+	, dwResendByAcks	(resend_by_acks)
+	, dwFlushInterval	(flush_interval)
+	, dwSendWndSize		(send_wnd_size)
+	, dwRecvWndSize		(recv_wnd_size)
+	, dwMinRto			(min_rto)
+	, dwMtu				(mtu)
+	, dwFastLimit		(fast_limit)
+	, dwMaxMessageSize	(max_msg_size)
+	, dwHandShakeTimeout(hand_shake_timeout)
+	{
+		ASSERT(IsValid());
+	}
+
+	BOOL IsValid() const
+	{
+		return 	((int)dwResendByAcks >= 0)																				&&
+				((int)dwFlushInterval > 0)																				&&
+				((int)dwSendWndSize > 0)																				&&
+				((int)dwRecvWndSize > 0)																				&&
+				((int)dwMinRto > 0)																						&&
+				((int)dwFastLimit >= 0)																					&&
+				((int)dwHandShakeTimeout > 2 * (int)dwMinRto)															&&
+				((int)dwMtu >= 3 * KCP_HEADER_SIZE && dwMtu <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE)							&&
+				((int)dwMaxMessageSize > 0 && dwMaxMessageSize < ((KCP_MIN_RECV_WND - 1) * (dwMtu - KCP_HEADER_SIZE)))	;
+	}
+
+};
+
+template<class T, class S> class CArqSessionT
+{
+public:
+	CArqSessionT* Renew(T* pContext, S* pSocket, const TArqAttr& attr, DWORD dwPeerConvID = 0)
+	{
+		m_pContext		= pContext;
+		m_pSocket		= pSocket;
+		m_dwSelfConvID	= ::GenerateConversationID();
+
+		DoRenew(attr, dwPeerConvID);
+		RenewExtra(attr);
+
+		m_dwCreateTime	= ::TimeGetTime();
+		m_dwHSNextTime	= m_dwCreateTime;
+		m_dwHSSndCount	= 0;
+		m_bHSComplete	= FALSE;
+		m_enStatus		= ARQ_HSS_PROC;
+
+		Check();
+
+		return this;
+	}
+
+	BOOL Reset()
+	{
+		if(!IsValid())
+			return FALSE;
+
+		{
+			CReentrantCriSecLock recvlock(m_csRecv);
+			CReentrantCriSecLock sendlock(m_csSend);
+
+			if(!IsValid())
+				return FALSE;
+
+			m_enStatus	= ARQ_HSS_INIT;
+
+			DoReset();
+		}
+
+		ResetExtra();
+
+		return TRUE;
+	}
+
+	BOOL Check()
+	{
+		if(IsReady())
+		{
+			if(m_bHSComplete || DoHandShake())
+				return Flush();
+			else
+				return FALSE;
+		}
+		else if(IsHandShaking())
+			return DoHandShake();
+		else
+		{
+			::SetLastError(ERROR_INVALID_STATE);
+			return FALSE;
+		}
+	}
+
+	BOOL DoHandShake()
+	{
+		if(!IsValid())
+		{
+			::SetLastError(ERROR_INVALID_STATE);
+			return FALSE;
+		}
+
+		unique_ptr<BYTE[]> bufCmdPtr;
+
+		{
+			CReentrantCriSecLock recvlock(m_csRecv);
+
+			if(!IsValid())
+			{
+				::SetLastError(ERROR_INVALID_STATE);
+				return FALSE;
+			}
+
+			DWORD dwCurrent = ::TimeGetTime();
+
+			if(::GetTimeGap32(m_dwCreateTime, dwCurrent) > m_pContext->GetHandShakeTimeout())
+			{
+				::SetLastError(ERROR_TIMEOUT);
+				return FALSE;
+			}
+
+			if((int)(::GetTimeGap32(m_dwHSNextTime, dwCurrent)) < 0)
+				return TRUE;
+
+			m_dwHSNextTime	= dwCurrent + MIN(m_kcp->interval * (++m_dwHSSndCount), ARQ_MAX_HANDSHAKE_INTERVAL);
+			UINT8 iFlag		= IsReady() ? TArqCmd::FLAG_COMPLETE : 0;
+
+			bufCmdPtr.reset(TArqCmd::MakePackage(TArqCmd::CMD_HANDSHAKE, iFlag, m_dwSelfConvID, m_dwPeerConvID));
+		}
+
+		return m_pContext->DoSend(m_pSocket, bufCmdPtr.get(), TArqCmd::PACKAGE_LENGTH);
+	}
+
+	BOOL Flush(BOOL bForce = FALSE)
+	{
+		if(!IsReady())
+		{
+			::SetLastError(ERROR_INVALID_STATE);
+			return FALSE;
+		}
+
+		{
+			CReentrantCriSecTryLock recvlock(m_csRecv);
+
+			if(recvlock.IsValid())
+			{
+				CReentrantCriSecTryLock sendlock(m_csSend);
+
+				if(sendlock.IsValid())
+				{
+					if(!IsReady())
+					{
+						::SetLastError(ERROR_INVALID_STATE);
+						return FALSE;
+					}
+
+					if(bForce)
+						::ikcp_flush(m_kcp);
+					else
+						::ikcp_update(m_kcp, ::TimeGetTime());
+				}
+			}
+		}
+
+		return TRUE;
+	}
+
+	int Send(const BYTE* pBuffer, int iLength)
+	{
+		if(!IsReady())
+			return ERROR_INVALID_STATE;
+
+		int rs = NO_ERROR;
+
+		{
+			CReentrantCriSecLock sendlock(m_csSend);
+
+			if(!IsReady())
+				return ERROR_INVALID_STATE;
+
+			rs = ::ikcp_send(m_kcp, (const char*)pBuffer, iLength);
+			if(rs < 0) rs = ERROR_INCORRECT_SIZE;
+		}
+
+		if(rs == NO_ERROR)
+			Flush(TRUE);
+
+		return rs;
+	}
+
+	int GetWaitingSend()
+	{
+		if(!IsValid())
+		{
+			::SetLastError(ERROR_INVALID_STATE);
+			return -1;
+		}
+
+		CReentrantCriSecLock sendlock(m_csSend);
+
+		if(!IsValid())
+		{
+			::SetLastError(ERROR_INVALID_STATE);
+			return -1;
+		}
+
+		return ::ikcp_waitsnd(m_kcp);
+	}
+
+	EnHandleResult Receive(const BYTE* pData, int iLength, BYTE* pBuffer, int iCapacity)
+	{
+		if(iLength >= KCP_HEADER_SIZE)
+			return ReceiveArq(pData, iLength, pBuffer, iCapacity);
+		else if(iLength == TArqCmd::PACKAGE_LENGTH)
+			return ReceiveHandShake(pData);
+		else
+		{
+			::WSASetLastError(ERROR_INVALID_DATA);
+			return HR_ERROR;
+		}
+	}
+
+	EnHandleResult ReceiveHandShake(const BYTE* pBuffer)
+	{
+		TArqCmd cmd;
+		cmd.Parse(pBuffer);
+
+		if(!cmd.IsValid())
+		{
+			::WSASetLastError(ERROR_INVALID_DATA);
+			return HR_ERROR;
+		}
+
+		{
+			CReentrantCriSecLock recvlock(m_csRecv);
+
+			if(!IsValid())
+			{
+				::WSASetLastError(ERROR_INVALID_STATE);
+				return HR_ERROR;
+			}
+			
+			if(IsReady())
+			{
+				BOOL bReset = FALSE;
+
+				if(cmd.selfID != m_dwPeerConvID)
+					bReset = TRUE;
+				else if(cmd.peerID != m_dwSelfConvID)
+				{
+					if(cmd.peerID != 0)
+						bReset = TRUE;
+					else
+					{
+						if(::GetTimeGap32(m_dwCreateTime) > 2 * m_pContext->GetHandShakeTimeout())
+							bReset = TRUE;
+					}
+				}
+
+				if(bReset)
+				{
+					::WSASetLastError(ERROR_CONNRESET);
+					return HR_ERROR;
+				}
+			}
+			else
+			{
+				if(m_dwPeerConvID == 0)
+				{
+					m_dwPeerConvID = cmd.selfID;
+					m_dwHSNextTime = ::TimeGetTime();
+					m_dwHSSndCount = 0;
+				}
+				else if(cmd.selfID != m_dwPeerConvID)
+				{
+					::WSASetLastError(ERROR_CONNRESET);
+					return HR_ERROR;
+				}
+
+				if(cmd.peerID == m_dwSelfConvID)
+				{
+					m_enStatus = ARQ_HSS_SUCC;
+					return m_pContext->DoFireHandShake(m_pSocket);
+				}
+				else if(cmd.peerID != 0)
+				{
+					::WSASetLastError(ERROR_CONNRESET);
+					return HR_ERROR;
+				}
+			}
+		}
+
+		if(!m_bHSComplete && cmd.flag == TArqCmd::FLAG_COMPLETE)
+			m_bHSComplete = TRUE;
+
+		DoHandShake();
+
+		return HR_OK;
+	}
+
+	EnHandleResult ReceiveArq(const BYTE* pData, int iLength, BYTE* pBuffer, int iCapacity)
+	{
+		if(!IsReady()) return HR_IGNORE;
+
+		{
+			CReentrantCriSecLock recvlock(m_csRecv);
+
+			if(!IsReady())
+			{
+				::WSASetLastError(ERROR_INVALID_STATE);
+				return HR_ERROR;
+			}
+
+			if(iLength < KCP_HEADER_SIZE)
+			{
+				::WSASetLastError(ERROR_INVALID_DATA);
+				return HR_ERROR;
+			}
+
+			int rs = ::ikcp_input(m_kcp, (const char*)pData, iLength);
+
+			if(rs != NO_ERROR)
+			{
+				::WSASetLastError(ERROR_INVALID_DATA);
+				return HR_ERROR;
+			}
+
+			while(TRUE)
+			{
+				int iRead = ::ikcp_recv(m_kcp, (char*)pBuffer, iCapacity);
+
+				if(iRead >= 0)
+				{
+					EnHandleResult result = m_pContext->DoFireReceive(m_pSocket, pBuffer, iRead);
+
+					if(result == HR_ERROR)
+						return result;
+				}
+				else if(iRead == -3)
+				{
+					::WSASetLastError(ERROR_INCORRECT_SIZE);
+					return HR_ERROR;
+				}
+				else
+					break;
+			}
+		}
+
+		Flush(TRUE);
+
+		return HR_OK;
+	}
+
+private:
+	void DoRenew(const TArqAttr& attr, DWORD dwPeerConvID = 0)
+	{
+		ASSERT(attr.IsValid());
+
+		DoReset();
+
+		m_dwPeerConvID	= dwPeerConvID;
+		m_kcp			= ::ikcp_create(m_dwSelfConvID, m_pSocket);
+
+		::ikcp_nodelay(m_kcp, attr.bNoDelay ? 1 : 0, (int)attr.dwFlushInterval, (int)attr.dwResendByAcks, attr.bTurnoffNc ? 1 : 0);
+		::ikcp_wndsize(m_kcp, (int)attr.dwSendWndSize, (int)attr.dwRecvWndSize);
+		::ikcp_setmtu(m_kcp, attr.dwMtu);
+
+		m_kcp->rx_minrto	= (int)attr.dwMinRto;
+		m_kcp->fastlimit	= (int)attr.dwFastLimit;
+		m_kcp->output		= m_pContext->GetArqOutputProc();
+	}
+
+	void DoReset()
+	{
+		if(m_kcp != nullptr)
+		{
+			::ikcp_release(m_kcp);
+			m_kcp = nullptr;
+		}
+	}
+
+public:
+	BOOL		IsValid()		const	{return GetStatus() != ARQ_HSS_INIT;}
+	BOOL		IsHandShaking()	const	{return GetStatus() == ARQ_HSS_PROC;}
+	BOOL		IsReady()		const	{return GetStatus() == ARQ_HSS_SUCC;}
+	IKCPCB*		GetKcp()				{return m_kcp;}
+	DWORD		GetConvID()		const	{if(!IsValid()) return 0; return m_kcp->conv;}
+	DWORD		GetSelfConvID()	const	{return m_dwSelfConvID;}
+	DWORD		GetPeerConvID()	const	{return m_dwPeerConvID;}
+	
+	EnArqHandShakeStatus GetStatus() const {return m_enStatus;}
+
+protected:
+	virtual void RenewExtra(const TArqAttr& attr) {}
+	virtual void ResetExtra() {}
+
+public:
+	CArqSessionT()
+	: m_pContext	(nullptr)
+	, m_pSocket		(nullptr)
+	, m_kcp			(nullptr)
+	, m_enStatus	(ARQ_HSS_INIT)
+	, m_dwSelfConvID(0)
+	, m_dwPeerConvID(0)
+	, m_dwCreateTime(0)
+	, m_dwHSNextTime(0)
+	, m_dwHSSndCount(0)
+	, m_bHSComplete	(FALSE)
+	{
+
+	}
+
+	virtual ~CArqSessionT()
+	{
+		Reset();
+	}
+
+	static CArqSessionT* Construct()
+		{return new CArqSessionT();}
+
+	static void Destruct(CArqSessionT* pSession)
+		{if(pSession) delete pSession;}
+
+protected:
+	T*		m_pContext;
+	S*		m_pSocket;
+
+private:
+	BOOL	m_bHSComplete;
+	DWORD	m_dwHSNextTime;
+	DWORD	m_dwHSSndCount;
+	DWORD	m_dwCreateTime;
+	DWORD	m_dwSelfConvID;
+	DWORD	m_dwPeerConvID;
+	EnArqHandShakeStatus m_enStatus;
+
+	CReentrantCriSec m_csRecv;
+	CReentrantCriSec m_csSend;
+	IKCPCB* m_kcp;
+};
+
+template<class T, class S> class CArqSessionPoolT;
+
+template<class T, class S> class CArqSessionExT : public CArqSessionT<T, S>, public CSafeCounter
+{
+	using __super = CArqSessionT<T, S>;
+
+	using __super::Reset;
+
+	friend class CArqSessionPoolT<T, S>;
+
+public:
+	DWORD GetFreeTime	()	const	{return m_dwFreeTime;}
+	FD GetTimer			()	const	{return m_fdTimer;}
+
+protected:
+	virtual void RenewExtra(const TArqAttr& attr)
+	{
+		ResetCount();
+
+		m_fdTimer = m_ioDispatcher.AddTimer(attr.dwFlushInterval, this);
+		ASSERT(IS_VALID_FD(m_fdTimer));
+	}
+
+	virtual void ResetExtra()
+	{
+		m_ioDispatcher.DelTimer(m_fdTimer);
+
+		m_dwFreeTime = ::TimeGetTime();
+		m_fdTimer	 = INVALID_FD;
+	}
+
+public:
+	CArqSessionExT(CIODispatcher& ioDispatcher)
+	: m_ioDispatcher(ioDispatcher)
+	, m_fdTimer		(INVALID_FD)
+	, m_dwFreeTime	(0)
+	{
+
+	}
+
+	virtual ~CArqSessionExT()
+	{
+		Reset();
+	}
+
+	static CArqSessionExT* Construct(CIODispatcher& ioDispatcher)
+		{return new CArqSessionExT(ioDispatcher);}
+
+	static void Destruct(CArqSessionExT* pSession)
+		{if(pSession) delete pSession;}
+
+private:
+	CIODispatcher& m_ioDispatcher;
+
+	FD		m_fdTimer;
+	DWORD	m_dwFreeTime;
+};
+
+template<class T, class S> class CArqSessionPoolT : private CIOHandler
+{
+	using CArqSessionEx		= CArqSessionExT<T, S>;
+	using TArqSessionList	= CRingPool<CArqSessionEx>;
+	using TArqSessionQueue	= CCASQueue<CArqSessionEx>;
+
+public:
+	CArqSessionEx* PickFreeSession(T* pContext, S* pSocket, const TArqAttr& attr)
+	{
+		DWORD dwIndex;
+		CArqSessionEx* pSession = nullptr;
+
+		if(m_lsFreeSession.TryLock(&pSession, dwIndex))
+		{
+			if(::GetTimeGap32(pSession->GetFreeTime()) >= m_dwSessionLockTime)
+				ENSURE(m_lsFreeSession.ReleaseLock(nullptr, dwIndex));
+			else
+			{
+				ENSURE(m_lsFreeSession.ReleaseLock(pSession, dwIndex));
+				pSession = nullptr;
+			}
+		}
+
+		if(!pSession) pSession = CArqSessionEx::Construct(m_ioDispatcher);
+
+		ASSERT(pSession);
+		return (CArqSessionEx*)pSession->Renew(pContext, pSocket, attr);
+	}
+
+	void PutFreeSession(CArqSessionEx* pSession)
+	{
+		if(pSession->Reset())
+		{
+#ifndef USE_EXTERNAL_GC
+			ReleaseGCSession();
+#endif
+			if(!m_lsFreeSession.TryPut(pSession))
+				m_lsGCSession.PushBack(pSession);
+		}
+	}
+
+	void Prepare()
+	{
+		m_lsFreeSession.Reset(m_dwSessionPoolSize);
+
+		m_ioDispatcher.Start(this, m_pContext->GetPostReceiveCount(), m_pContext->GetWorkerThreadCount());
+	}
+
+	void Clear()
+	{
+		m_ioDispatcher.Stop();
+
+		m_lsFreeSession.Clear();
+
+		ReleaseGCSession(TRUE);
+		ENSURE(m_lsGCSession.IsEmpty());
+	}
+
+	void ReleaseGCSession(BOOL bForce = FALSE)
+	{
+		::ReleaseGCObj(m_lsGCSession, m_dwSessionLockTime, bForce);
+	}
+
+	virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) override
+	{
+
+		if(events & _EPOLL_ALL_ERROR_EVENTS)
+			return FALSE;
+
+		CArqSessionEx* pSession = (CArqSessionEx*)pv;
+
+		CLocalSafeCounter localcounter(*pSession);
+
+		if(!pSession->Check() && pSession->IsValid() && TUdpSocketObj::IsValid(pSession->m_pSocket))
+			pSession->m_pContext->Disconnect(pSession->m_pSocket->connID);
+
+		::ReadTimer(pSession->GetTimer());
+
+		return TRUE;
+	}
+
+public:
+	void SetSessionLockTime	(DWORD dwSessionLockTime)	{m_dwSessionLockTime = dwSessionLockTime;}
+	void SetSessionPoolSize	(DWORD dwSessionPoolSize)	{m_dwSessionPoolSize = dwSessionPoolSize;}
+	void SetSessionPoolHold	(DWORD dwSessionPoolHold)	{m_dwSessionPoolHold = dwSessionPoolHold;}
+
+	DWORD GetSessionLockTime()	{return m_dwSessionLockTime;}
+	DWORD GetSessionPoolSize()	{return m_dwSessionPoolSize;}
+	DWORD GetSessionPoolHold()	{return m_dwSessionPoolHold;}
+
+public:
+	CArqSessionPoolT(T* pContext,
+					DWORD dwPoolSize = DEFAULT_SESSION_POOL_SIZE,
+					DWORD dwPoolHold = DEFAULT_SESSION_POOL_HOLD,
+					DWORD dwLockTime = DEFAULT_SESSION_LOCK_TIME)
+	: m_pContext(pContext)
+	, m_dwSessionPoolSize(dwPoolSize)
+	, m_dwSessionPoolHold(dwPoolHold)
+	, m_dwSessionLockTime(dwLockTime)
+	{
+
+	}
+
+	~CArqSessionPoolT()	{Clear();}
+
+	DECLARE_NO_COPY_CLASS(CArqSessionPoolT)
+
+public:
+	static const DWORD DEFAULT_SESSION_LOCK_TIME;
+	static const DWORD DEFAULT_SESSION_POOL_SIZE;
+	static const DWORD DEFAULT_SESSION_POOL_HOLD;
+
+private:
+	T*					m_pContext;
+
+	DWORD				m_dwSessionLockTime;
+	DWORD				m_dwSessionPoolSize;
+	DWORD				m_dwSessionPoolHold;
+
+	TArqSessionList		m_lsFreeSession;
+	TArqSessionQueue	m_lsGCSession;
+
+	CIODispatcher		m_ioDispatcher;
+};
+
+template<class T, class S> const DWORD CArqSessionPoolT<T, S>::DEFAULT_SESSION_LOCK_TIME	= DEFAULT_OBJECT_CACHE_LOCK_TIME;
+template<class T, class S> const DWORD CArqSessionPoolT<T, S>::DEFAULT_SESSION_POOL_SIZE	= DEFAULT_OBJECT_CACHE_POOL_SIZE;
+template<class T, class S> const DWORD CArqSessionPoolT<T, S>::DEFAULT_SESSION_POOL_HOLD	= DEFAULT_OBJECT_CACHE_POOL_HOLD;
+
+#endif

+ 23 - 0
CMakeLists.txt

@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.10)
+project(hpsocket)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+file(GLOB_RECURSE ALL_SRC_FILES CONFIGURE_DEPENDS
+    "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/*.c"
+)
+
+add_library(hpsocket STATIC ${ALL_SRC_FILES})
+target_include_directories(hpsocket PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+# 解包 brotli 的 .a 为 .o 文件
+file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/brotli_objects)
+execute_process(
+    COMMAND ${CMAKE_AR} -x ${CMAKE_CURRENT_SOURCE_DIR}/libbrotli.a
+    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/brotli_objects
+)
+
+file(GLOB BROTLI_OBJECTS "${CMAKE_BINARY_DIR}/brotli_objects/*.o")
+target_sources(hpsocket PRIVATE ${BROTLI_OBJECTS})

+ 200 - 0
HPSocket-SSL.cpp

@@ -0,0 +1,200 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "hpsocket/HPSocket-SSL.h"
+
+#ifdef _SSL_SUPPORT
+
+#include "TcpServer.h"
+#include "TcpAgent.h"
+#include "TcpClient.h"
+#include "TcpPullServer.h"
+#include "TcpPullClient.h"
+#include "TcpPullAgent.h"
+#include "TcpPackServer.h"
+#include "TcpPackClient.h"
+#include "TcpPackAgent.h"
+
+#ifdef _HTTP_SUPPORT
+#include "HttpServer.h"
+#include "HttpAgent.h"
+#include "HttpClient.h"
+#endif
+
+/*****************************************************************************************************************************************************/
+/******************************************************************** SSL Exports ********************************************************************/
+/*****************************************************************************************************************************************************/
+
+HPSOCKET_API ITcpServer* HP_Create_SSLServer(ITcpServerListener* pListener)
+{
+	return new CSSLServer(pListener);
+}
+
+HPSOCKET_API ITcpAgent* HP_Create_SSLAgent(ITcpAgentListener* pListener)
+{
+	return new CSSLAgent(pListener);
+}
+
+HPSOCKET_API ITcpClient* HP_Create_SSLClient(ITcpClientListener* pListener)
+{
+	return new CSSLClient(pListener);
+}
+
+HPSOCKET_API ITcpPullServer* HP_Create_SSLPullServer(ITcpServerListener* pListener)
+{
+	return (ITcpPullServer*)(new CSSLPullServer(pListener));
+}
+
+HPSOCKET_API ITcpPullAgent* HP_Create_SSLPullAgent(ITcpAgentListener* pListener)
+{
+	return (ITcpPullAgent*)(new CSSLPullAgent(pListener));
+}
+
+HPSOCKET_API ITcpPullClient* HP_Create_SSLPullClient(ITcpClientListener* pListener)
+{
+	return (ITcpPullClient*)(new CSSLPullClient(pListener));
+}
+
+HPSOCKET_API ITcpPackServer* HP_Create_SSLPackServer(ITcpServerListener* pListener)
+{
+	return (ITcpPackServer*)(new CSSLPackServer(pListener));
+}
+
+HPSOCKET_API ITcpPackAgent* HP_Create_SSLPackAgent(ITcpAgentListener* pListener)
+{
+	return (ITcpPackAgent*)(new CSSLPackAgent(pListener));
+}
+
+HPSOCKET_API ITcpPackClient* HP_Create_SSLPackClient(ITcpClientListener* pListener)
+{
+	return (ITcpPackClient*)(new CSSLPackClient(pListener));
+}
+
+HPSOCKET_API void HP_Destroy_SSLServer(ITcpServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_SSLAgent(ITcpAgent* pAgent)
+{
+	delete pAgent;
+}
+
+HPSOCKET_API void HP_Destroy_SSLClient(ITcpClient* pClient)
+{
+	delete pClient;
+}
+
+HPSOCKET_API void HP_Destroy_SSLPullServer(ITcpPullServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_SSLPullAgent(ITcpPullAgent* pAgent)
+{
+	delete pAgent;
+}
+
+HPSOCKET_API void HP_Destroy_SSLPullClient(ITcpPullClient* pClient)
+{
+	delete pClient;
+}
+
+HPSOCKET_API void HP_Destroy_SSLPackServer(ITcpPackServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_SSLPackAgent(ITcpPackAgent* pAgent)
+{
+	delete pAgent;
+}
+
+HPSOCKET_API void HP_Destroy_SSLPackClient(ITcpPackClient* pClient)
+{
+	delete pClient;
+}
+
+/*****************************************************************************************************************************************************/
+/*************************************************************** Global Function Exports *************************************************************/
+/*****************************************************************************************************************************************************/
+
+HPSOCKET_API int __HP_CALL HP_SSL_DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext)
+{
+	return CSSLContext::DefaultServerNameCallback(lpszServerName, pContext);
+}
+
+HPSOCKET_API void HP_SSL_RemoveThreadLocalState(THR_ID dwThreadID)
+{
+	CSSLContext::RemoveThreadLocalState(dwThreadID);
+}
+
+/*****************************************************************************************************************************************************/
+/******************************************************************** HTTPS Exports ******************************************************************/
+/*****************************************************************************************************************************************************/
+
+#ifdef _HTTP_SUPPORT
+
+HPSOCKET_API IHttpServer* HP_Create_HttpsServer(IHttpServerListener* pListener)
+{
+	return (IHttpServer*)(new CHttpsServer(pListener));
+}
+
+HPSOCKET_API IHttpAgent* HP_Create_HttpsAgent(IHttpAgentListener* pListener)
+{
+	return (IHttpAgent*)(new CHttpsAgent(pListener));
+}
+
+HPSOCKET_API IHttpClient* HP_Create_HttpsClient(IHttpClientListener* pListener)
+{
+	return (IHttpClient*)(new CHttpsClient(pListener));
+}
+
+HPSOCKET_API IHttpSyncClient* HP_Create_HttpsSyncClient(IHttpClientListener* pListener)
+{
+	return (IHttpSyncClient*)(new CHttpsSyncClient(pListener));
+}
+
+HPSOCKET_API void HP_Destroy_HttpsServer(IHttpServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_HttpsAgent(IHttpAgent* pAgent)
+{
+	delete pAgent;
+}
+
+HPSOCKET_API void HP_Destroy_HttpsClient(IHttpClient* pClient)
+{
+	delete pClient;
+}
+
+HPSOCKET_API void HP_Destroy_HttpsSyncClient(IHttpSyncClient* pClient)
+{
+	delete pClient;
+}
+
+#endif
+
+#endif

+ 749 - 0
HPSocket.cpp

@@ -0,0 +1,749 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "hpsocket/HPSocket.h"
+#include "TcpServer.h"
+#include "TcpAgent.h"
+#include "TcpClient.h"
+#include "TcpPullServer.h"
+#include "TcpPullClient.h"
+#include "TcpPullAgent.h"
+#include "TcpPackServer.h"
+#include "TcpPackClient.h"
+#include "TcpPackAgent.h"
+#include "HPThreadPool.h"
+
+#ifdef _UDP_SUPPORT
+#include "UdpServer.h"
+#include "UdpClient.h"
+#include "UdpCast.h"
+#include "UdpNode.h"
+#include "UdpArqServer.h"
+#include "UdpArqClient.h"
+#endif
+
+#ifdef _HTTP_SUPPORT
+#include "HttpServer.h"
+#include "HttpAgent.h"
+#include "HttpClient.h"
+#endif
+
+/*****************************************************************************************************************************************************/
+/****************************************************************** TCP/UDP Exports ******************************************************************/
+/*****************************************************************************************************************************************************/
+
+HPSOCKET_API ITcpServer* HP_Create_TcpServer(ITcpServerListener* pListener)
+{
+	return new CTcpServer(pListener);
+}
+
+HPSOCKET_API ITcpAgent* HP_Create_TcpAgent(ITcpAgentListener* pListener)
+{
+	return new CTcpAgent(pListener);
+}
+
+HPSOCKET_API ITcpClient* HP_Create_TcpClient(ITcpClientListener* pListener)
+{
+	return new CTcpClient(pListener);
+}
+
+HPSOCKET_API ITcpPullServer* HP_Create_TcpPullServer(ITcpServerListener* pListener)
+{
+	return (ITcpPullServer*)(new CTcpPullServer(pListener));
+}
+
+HPSOCKET_API ITcpPullAgent* HP_Create_TcpPullAgent(ITcpAgentListener* pListener)
+{
+	return (ITcpPullAgent*)(new CTcpPullAgent(pListener));
+}
+
+HPSOCKET_API ITcpPullClient* HP_Create_TcpPullClient(ITcpClientListener* pListener)
+{
+	return (ITcpPullClient*)(new CTcpPullClient(pListener));
+}
+
+HPSOCKET_API ITcpPackServer* HP_Create_TcpPackServer(ITcpServerListener* pListener)
+{
+	return (ITcpPackServer*)(new CTcpPackServer(pListener));
+}
+
+HPSOCKET_API ITcpPackAgent* HP_Create_TcpPackAgent(ITcpAgentListener* pListener)
+{
+	return (ITcpPackAgent*)(new CTcpPackAgent(pListener));
+}
+
+HPSOCKET_API ITcpPackClient* HP_Create_TcpPackClient(ITcpClientListener* pListener)
+{
+	return (ITcpPackClient*)(new CTcpPackClient(pListener));
+}
+
+HPSOCKET_API void HP_Destroy_TcpServer(ITcpServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_TcpAgent(ITcpAgent* pAgent)
+{
+	delete pAgent;
+}
+
+HPSOCKET_API void HP_Destroy_TcpClient(ITcpClient* pClient)
+{
+	delete pClient;
+}
+
+HPSOCKET_API void HP_Destroy_TcpPullServer(ITcpPullServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_TcpPullAgent(ITcpPullAgent* pAgent)
+{
+	delete pAgent;
+}
+
+HPSOCKET_API void HP_Destroy_TcpPullClient(ITcpPullClient* pClient)
+{
+	delete pClient;
+}
+
+HPSOCKET_API void HP_Destroy_TcpPackServer(ITcpPackServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_TcpPackAgent(ITcpPackAgent* pAgent)
+{
+	delete pAgent;
+}
+
+HPSOCKET_API void HP_Destroy_TcpPackClient(ITcpPackClient* pClient)
+{
+	delete pClient;
+}
+
+#ifdef _UDP_SUPPORT
+
+HPSOCKET_API IUdpServer* HP_Create_UdpServer(IUdpServerListener* pListener)
+{
+	return new CUdpServer(pListener);
+}
+
+HPSOCKET_API IUdpClient* HP_Create_UdpClient(IUdpClientListener* pListener)
+{
+	return new CUdpClient(pListener);
+}
+
+HPSOCKET_API IUdpCast* HP_Create_UdpCast(IUdpCastListener* pListener)
+{
+	return new CUdpCast(pListener);
+}
+
+HPSOCKET_API IUdpNode* HP_Create_UdpNode(IUdpNodeListener* pListener)
+{
+	return new CUdpNode(pListener);
+}
+
+HPSOCKET_API IUdpArqServer* HP_Create_UdpArqServer(IUdpServerListener* pListener)
+{
+	return (IUdpArqServer*)(new CUdpArqServer(pListener));
+}
+
+HPSOCKET_API IUdpArqClient* HP_Create_UdpArqClient(IUdpClientListener* pListener)
+{
+	return (IUdpArqClient*)(new CUdpArqClient(pListener));
+}
+
+HPSOCKET_API void HP_Destroy_UdpServer(IUdpServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_UdpClient(IUdpClient* pClient)
+{
+	delete pClient;
+}
+
+HPSOCKET_API void HP_Destroy_UdpCast(IUdpCast* pCast)
+{
+	delete pCast;
+}
+
+HPSOCKET_API void HP_Destroy_UdpNode(IUdpNode* pNode)
+{
+	delete pNode;
+}
+
+HPSOCKET_API void HP_Destroy_UdpArqServer(IUdpArqServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_UdpArqClient(IUdpArqClient* pClient)
+{
+	delete pClient;
+}
+
+#endif
+
+/*****************************************************************************************************************************************************/
+/*************************************************************** Global Function Exports *************************************************************/
+/*****************************************************************************************************************************************************/
+
+HPSOCKET_API DWORD HP_GetHPSocketVersion()
+{
+	return ::GetHPSocketVersion();
+}
+
+HPSOCKET_API LPCTSTR HP_GetSocketErrorDesc(EnSocketError enCode)
+{
+	return ::GetSocketErrorDesc(enCode);
+}
+
+HPSOCKET_API DWORD SYS_GetLastError()
+{
+	return ::GetLastError();
+}
+
+HPSOCKET_API LPCSTR SYS_GetLastErrorStr()
+{
+	return ::GetLastErrorStr();
+}
+
+HPSOCKET_API int SYS_SetSocketOption(SOCKET sock, int level, int name, LPVOID val, int len)
+{
+	return ::SSO_SetSocketOption(sock, level, name, val, len);
+}
+
+HPSOCKET_API int SYS_GetSocketOption(SOCKET sock, int level, int name, LPVOID val, int* len)
+{
+	return ::SSO_GetSocketOption(sock, level, name, val, len);
+}
+
+HPSOCKET_API int SYS_IoctlSocket(SOCKET sock, long cmd, ULONG* arg)
+{
+	return ::SSO_IoctlSocket(sock, cmd, arg);
+}
+
+HPSOCKET_API BOOL SYS_fcntl_SETFL(FD fd, INT fl, BOOL bSet)
+{
+	return ::fcntl_SETFL(fd, fl, bSet);
+}
+
+HPSOCKET_API int SYS_SSO_NoBlock(SOCKET sock, BOOL bNoBlock)
+{
+	return ::SSO_NoBlock(sock, bNoBlock);
+}
+
+HPSOCKET_API int SYS_SSO_NoDelay(SOCKET sock, BOOL bNoDelay)
+{
+	return ::SSO_NoDelay(sock, bNoDelay);
+}
+
+HPSOCKET_API int SYS_SSO_DontLinger(SOCKET sock, BOOL bDont)
+{
+	return ::SSO_DontLinger(sock, bDont);
+}
+
+HPSOCKET_API int SYS_SSO_Linger(SOCKET sock, USHORT l_onoff, USHORT l_linger)
+{
+	return ::SSO_Linger(sock, l_onoff, l_linger);
+}
+
+HPSOCKET_API int SYS_SSO_RecvBuffSize(SOCKET sock, int size)
+{
+	return ::SSO_RecvBuffSize(sock, size);
+}
+
+HPSOCKET_API int SYS_SSO_SendBuffSize(SOCKET sock, int size)
+{
+	return ::SSO_SendBuffSize(sock, size);
+}
+
+HPSOCKET_API int SYS_SSO_RecvTimeOut(SOCKET sock, int ms)
+{
+	return ::SSO_RecvTimeOut(sock, ms);
+}
+
+HPSOCKET_API int SYS_SSO_SendTimeOut(SOCKET sock, int ms)
+{
+	return ::SSO_SendTimeOut(sock, ms);
+}
+
+HPSOCKET_API int SYS_SSO_ReuseAddress(SOCKET sock, EnReuseAddressPolicy opt)
+{
+	return ::SSO_ReuseAddress(sock, opt);
+}
+
+HPSOCKET_API BOOL SYS_GetSocketLocalAddress(SOCKET socket, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	return ::GetSocketLocalAddress(socket, lpszAddress, iAddressLen, usPort);
+}
+
+HPSOCKET_API BOOL SYS_GetSocketRemoteAddress(SOCKET socket, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	return ::GetSocketRemoteAddress(socket, lpszAddress, iAddressLen, usPort);
+}
+
+HPSOCKET_API BOOL SYS_EnumHostIPAddresses(LPCTSTR lpszHost, EnIPAddrType enType, LPTIPAddr** lpppIPAddr, int& iIPAddrCount)
+{
+	return ::EnumHostIPAddresses(lpszHost, enType, lpppIPAddr, iIPAddrCount);
+}
+
+HPSOCKET_API BOOL SYS_FreeHostIPAddresses(LPTIPAddr* lppIPAddr)
+{
+	return ::FreeHostIPAddresses(lppIPAddr);
+}
+
+HPSOCKET_API BOOL SYS_IsIPAddress(LPCTSTR lpszAddress, EnIPAddrType* penType)
+{
+	return ::IsIPAddress(lpszAddress, penType);
+}
+
+HPSOCKET_API BOOL SYS_GetIPAddress(LPCTSTR lpszHost, TCHAR lpszIP[], int& iIPLenth, EnIPAddrType& enType)
+{
+	return ::GetIPAddress(lpszHost, lpszIP, iIPLenth, enType);
+}
+
+HPSOCKET_API ULONGLONG SYS_NToH64(ULONGLONG value)
+{
+	return ::NToH64(value);
+}
+
+HPSOCKET_API ULONGLONG SYS_HToN64(ULONGLONG value)
+{
+	return ::HToN64(value);
+}
+
+HPSOCKET_API USHORT SYS_SwapEndian16(USHORT value)
+{
+	return ENDIAN_SWAP_16(value);
+}
+
+HPSOCKET_API DWORD SYS_SwapEndian32(DWORD value)
+{
+	return ENDIAN_SWAP_32(value);
+}
+
+HPSOCKET_API BOOL SYS_IsLittleEndian()
+{
+	return ::IsLittleEndian();
+}
+
+HPSOCKET_API LPBYTE SYS_Malloc(int size)
+{
+	return MALLOC(BYTE, size);
+}
+
+HPSOCKET_API LPBYTE SYS_Realloc(LPBYTE p, int size)
+{
+	return REALLOC(BYTE, p, size);
+}
+
+HPSOCKET_API VOID SYS_Free(LPBYTE p)
+{
+	FREE(p);
+}
+
+HPSOCKET_API LPVOID SYS_Calloc(int number, int size)
+{
+	return CALLOC(number, size);
+}
+
+HPSOCKET_API DWORD SYS_GuessBase64EncodeBound(DWORD dwSrcLen)
+{
+	return ::GuessBase64EncodeBound(dwSrcLen);
+}
+
+HPSOCKET_API DWORD SYS_GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	return ::GuessBase64DecodeBound(lpszSrc, dwSrcLen);
+}
+
+HPSOCKET_API int SYS_Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::Base64Encode(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+HPSOCKET_API int SYS_Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::Base64Decode(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+HPSOCKET_API DWORD SYS_GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	return ::GuessUrlEncodeBound(lpszSrc, dwSrcLen);
+}
+
+HPSOCKET_API DWORD SYS_GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	return ::GuessUrlDecodeBound(lpszSrc, dwSrcLen);
+}
+
+HPSOCKET_API int SYS_UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::UrlEncode(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+HPSOCKET_API int SYS_UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::UrlDecode(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+#ifdef _ZLIB_SUPPORT
+
+HPSOCKET_API int SYS_Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::Compress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+HPSOCKET_API int SYS_CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iLevel, int iMethod, int iWindowBits, int iMemLevel, int iStrategy)
+{
+	return ::CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy);
+}
+
+HPSOCKET_API int SYS_Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::Uncompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+HPSOCKET_API int SYS_UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iWindowBits)
+{
+	return ::UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, iWindowBits);
+}
+
+HPSOCKET_API DWORD SYS_GuessCompressBound(DWORD dwSrcLen, BOOL bGZip)
+{
+	return ::GuessCompressBound(dwSrcLen, bGZip);
+}
+
+HPSOCKET_API int SYS_GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::GZipCompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+HPSOCKET_API int SYS_GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::GZipUncompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+HPSOCKET_API DWORD SYS_GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	return ::GZipGuessUncompressBound(lpszSrc, dwSrcLen);
+}
+
+#endif
+
+#ifdef _BROTLI_SUPPORT
+
+HPSOCKET_API int SYS_BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::BrotliCompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+HPSOCKET_API int SYS_BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iQuality, int iWindow, int iMode)
+{
+	return ::BrotliCompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, iQuality, iWindow, (BrotliEncoderMode)iMode);
+}
+
+HPSOCKET_API int SYS_BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return ::BrotliUncompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+HPSOCKET_API DWORD SYS_BrotliGuessCompressBound(DWORD dwSrcLen)
+{
+	return ::BrotliGuessCompressBound(dwSrcLen);
+}
+
+#endif
+
+#ifdef _ICONV_SUPPORT
+
+HPSOCKET_API BOOL SYS_CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int& iOutBufLen)
+{
+	return ::CharsetConvert(lpszFromCharset, lpszToCharset, lpszInBuf, iInBufLen, lpszOutBuf, iOutBufLen);
+}
+
+HPSOCKET_API BOOL SYS_GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength)
+{
+	return ::GbkToUnicodeEx(szSrc, iSrcLength, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength)
+{
+	return ::UnicodeToGbkEx(szSrc, iSrcLength, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength)
+{
+	return ::Utf8ToUnicodeEx(szSrc, iSrcLength, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength)
+{
+	return ::UnicodeToUtf8Ex(szSrc, iSrcLength, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength)
+{
+	return ::GbkToUtf8Ex(szSrc, iSrcLength, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength)
+{
+	return ::Utf8ToGbkEx(szSrc, iSrcLength, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_GbkToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength)
+{
+	return ::GbkToUnicode(szSrc, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_UnicodeToGbk(const WCHAR szSrc[], char szDest[], int& iDestLength)
+{
+	return ::UnicodeToGbk(szSrc, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength)
+{
+	return ::Utf8ToUnicode(szSrc, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int& iDestLength)
+{
+	return ::UnicodeToUtf8(szSrc, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_GbkToUtf8(const char szSrc[], char szDest[], int& iDestLength)
+{
+	return ::GbkToUtf8(szSrc, szDest, iDestLength);
+}
+
+HPSOCKET_API BOOL SYS_Utf8ToGbk(const char szSrc[], char szDest[], int& iDestLength)
+{
+	return ::Utf8ToGbk(szSrc, szDest, iDestLength);
+}
+
+#endif
+
+/*****************************************************************************************************************************************************/
+/******************************************************************** HTTP Exports *******************************************************************/
+/*****************************************************************************************************************************************************/
+
+#ifdef _HTTP_SUPPORT
+
+HPSOCKET_API IHttpServer* HP_Create_HttpServer(IHttpServerListener* pListener)
+{
+	return (IHttpServer*)(new CHttpServer(pListener));
+}
+
+HPSOCKET_API IHttpAgent* HP_Create_HttpAgent(IHttpAgentListener* pListener)
+{
+	return (IHttpAgent*)(new CHttpAgent(pListener));
+}
+
+HPSOCKET_API IHttpClient* HP_Create_HttpClient(IHttpClientListener* pListener)
+{
+	return (IHttpClient*)(new CHttpClient(pListener));
+}
+
+HPSOCKET_API IHttpSyncClient* HP_Create_HttpSyncClient(IHttpClientListener* pListener)
+{
+	return (IHttpSyncClient*)(new CHttpSyncClient(pListener));
+}
+
+HPSOCKET_API void HP_Destroy_HttpServer(IHttpServer* pServer)
+{
+	delete pServer;
+}
+
+HPSOCKET_API void HP_Destroy_HttpAgent(IHttpAgent* pAgent)
+{
+	delete pAgent;
+}
+
+HPSOCKET_API void HP_Destroy_HttpClient(IHttpClient* pClient)
+{
+	delete pClient;
+}
+
+HPSOCKET_API void HP_Destroy_HttpSyncClient(IHttpSyncClient* pClient)
+{
+	delete pClient;
+}
+
+/**************************************************************************/
+/*************************** HTTP Cookie 管理方法 **************************/
+
+HPSOCKET_API BOOL HP_HttpCookie_MGR_LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists)
+{
+	return g_CookieMgr.LoadFromFile(lpszFile, bKeepExists);
+}
+
+HPSOCKET_API BOOL HP_HttpCookie_MGR_SaveToFile(LPCSTR lpszFile, BOOL bKeepExists)
+{
+	return g_CookieMgr.SaveToFile(lpszFile, bKeepExists);
+}
+
+HPSOCKET_API BOOL HP_HttpCookie_MGR_ClearCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
+{
+	return g_CookieMgr.ClearCookies(lpszDomain, lpszPath);
+}
+
+HPSOCKET_API BOOL HP_HttpCookie_MGR_RemoveExpiredCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
+{
+	return g_CookieMgr.RemoveExpiredCookies(lpszDomain, lpszPath);
+}
+
+HPSOCKET_API BOOL HP_HttpCookie_MGR_SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, int enSameSite, BOOL bOnlyUpdateValueIfExists)
+{
+	return g_CookieMgr.SetCookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, (CCookie::EnSameSite)enSameSite, bOnlyUpdateValueIfExists);
+}
+
+HPSOCKET_API BOOL HP_HttpCookie_MGR_DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
+{
+	return g_CookieMgr.DeleteCookie(lpszDomain, lpszPath, lpszName);
+}
+
+HPSOCKET_API void HP_HttpCookie_MGR_SetEnableThirdPartyCookie(BOOL bEnableThirdPartyCookie)
+{
+	g_CookieMgr.SetEnableThirdPartyCookie(bEnableThirdPartyCookie);
+}
+
+HPSOCKET_API BOOL HP_HttpCookie_MGR_IsEnableThirdPartyCookie()
+{
+	return g_CookieMgr.IsEnableThirdPartyCookie();
+}
+
+HPSOCKET_API BOOL HP_HttpCookie_HLP_ParseExpires(LPCSTR lpszExpires, __time64_t& tmExpires)
+{
+	return CCookie::ParseExpires(lpszExpires, tmExpires);
+}
+
+HPSOCKET_API BOOL HP_HttpCookie_HLP_MakeExpiresStr(char lpszBuff[], int& iBuffLen, __time64_t tmExpires)
+{
+	return CCookie::MakeExpiresStr(lpszBuff, iBuffLen, tmExpires);
+}
+
+HPSOCKET_API BOOL HP_HttpCookie_HLP_ToString(char lpszBuff[], int& iBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, int enSameSite)
+{
+	return CCookie::ToString(lpszBuff, iBuffLen, lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, (CCookie::EnSameSite)enSameSite);
+}
+
+HPSOCKET_API __time64_t HP_HttpCookie_HLP_CurrentUTCTime()
+{
+	return CCookie::CurrentUTCTime();
+}
+
+HPSOCKET_API __time64_t HP_HttpCookie_HLP_MaxAgeToExpires(int iMaxAge)
+{
+	return CCookie::MaxAgeToExpires(iMaxAge);
+}
+
+HPSOCKET_API int HP_HttpCookie_HLP_ExpiresToMaxAge(__time64_t tmExpires)
+{
+	return CCookie::ExpiresToMaxAge(tmExpires);
+}
+
+/*****************************************************************************************************************************************************/
+/************************************************************ HTTP Global Function Exports ***********************************************************/
+/*****************************************************************************************************************************************************/
+
+#endif
+
+/*****************************************************************************************************************************************************/
+/**************************************************************** Thread Pool Exports ****************************************************************/
+/*****************************************************************************************************************************************************/
+
+HPSOCKET_API IHPThreadPool* HP_Create_ThreadPool(IHPThreadPoolListener* pListener)
+{
+	return new CHPThreadPool(pListener);
+}
+
+HPSOCKET_API void HP_Destroy_ThreadPool(IHPThreadPool* pThreadPool)
+{
+	delete pThreadPool;
+}
+
+HPSOCKET_API LPTSocketTask HP_Create_SocketTaskObj(Fn_SocketTaskProc fnTaskProc, PVOID pSender, CONNID dwConnID, LPCBYTE pBuffer, INT iBuffLen, EnTaskBufferType enBuffType, WPARAM wParam, LPARAM lParam)
+{
+	return ::CreateSocketTaskObj(fnTaskProc, pSender, dwConnID, pBuffer, iBuffLen, enBuffType, wParam, lParam);
+}
+
+HPSOCKET_API void HP_Destroy_SocketTaskObj(LPTSocketTask pTask)
+{
+	::DestroySocketTaskObj(pTask);
+}
+
+/*****************************************************************************************************************************************************/
+/********************************************************* Compressor / Decompressor Exports *********************************************************/
+/*****************************************************************************************************************************************************/
+
+HPSOCKET_API void HP_Destroy_Compressor(IHPCompressor* pCompressor)
+{
+	::DestroyCompressor(pCompressor);
+}
+
+HPSOCKET_API void HP_Destroy_Decompressor(IHPDecompressor* pDecompressor)
+{
+	::DestroyDecompressor(pDecompressor);
+}
+
+#ifdef _ZLIB_SUPPORT
+
+HPSOCKET_API IHPCompressor* HP_Create_ZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize)
+{
+	return ::CreateZLibCompressor(fnCallback, iWindowBits, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize);
+}
+
+HPSOCKET_API IHPCompressor* HP_Create_GZipCompressor(Fn_CompressDataCallback fnCallback, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize)
+{
+	return ::CreateGZipCompressor(fnCallback, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize);
+}
+
+HPSOCKET_API IHPDecompressor* HP_Create_ZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits, DWORD dwBuffSize)
+{
+	return ::CreateZLibDecompressor(fnCallback, iWindowBits, dwBuffSize);
+}
+
+HPSOCKET_API IHPDecompressor* HP_Create_GZipDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize)
+{
+	return ::CreateGZipDecompressor(fnCallback, dwBuffSize);
+}
+
+#endif
+
+#ifdef _BROTLI_SUPPORT
+
+HPSOCKET_API IHPCompressor* HP_Create_BrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality, int iWindow, int iMode, DWORD dwBuffSize)
+{
+	return ::CreateBrotliCompressor(fnCallback, iQuality, iWindow, iMode, dwBuffSize);
+}
+
+HPSOCKET_API IHPDecompressor* HP_Create_BrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize)
+{
+	return ::CreateBrotliDecompressor(fnCallback, dwBuffSize);
+}
+
+#endif

+ 380 - 0
HPSocket4C-SSL.cpp

@@ -0,0 +1,380 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "hpsocket/HPSocket4C-SSL.h"
+
+#ifdef _SSL_SUPPORT
+
+#include "SocketObject4C.h"
+#include "TcpServer.h"
+#include "TcpClient.h"
+#include "TcpAgent.h"
+#include "TcpPullServer.h"
+#include "TcpPullClient.h"
+#include "TcpPullAgent.h"
+#include "TcpPackServer.h"
+#include "TcpPackClient.h"
+#include "TcpPackAgent.h"
+
+#ifdef _HTTP_SUPPORT
+#include "HttpServer.h"
+#include "HttpAgent.h"
+#include "HttpClient.h"
+#endif
+
+/*****************************************************************************************************************************************************/
+/******************************************************************** SSL Exports ********************************************************************/
+/*****************************************************************************************************************************************************/
+
+typedef C_HP_ObjectT<CSSLServer, ITcpServerListener>							C_HP_SSLServer;
+typedef C_HP_ObjectT<CSSLPullServer, ITcpServerListener, sizeof(IPullSocket)>	C_HP_SSLPullServer;
+typedef C_HP_ObjectT<CSSLPackServer, ITcpServerListener, sizeof(IPackSocket)>	C_HP_SSLPackServer;
+
+typedef C_HP_ObjectT<CSSLAgent, ITcpAgentListener>								C_HP_SSLAgent;
+typedef C_HP_ObjectT<CSSLPullAgent, ITcpAgentListener, sizeof(IPullSocket)>		C_HP_SSLPullAgent;
+typedef C_HP_ObjectT<CSSLPackAgent, ITcpAgentListener, sizeof(IPackSocket)>		C_HP_SSLPackAgent;
+
+typedef C_HP_ObjectT<CSSLClient, ITcpClientListener>							C_HP_SSLClient;
+typedef C_HP_ObjectT<CSSLPullClient, ITcpClientListener, sizeof(IPullClient)>	C_HP_SSLPullClient;
+typedef C_HP_ObjectT<CSSLPackClient, ITcpClientListener, sizeof(IPackClient)>	C_HP_SSLPackClient;
+
+/********************************************************/
+/************** HPSocket4C-SSL 对象创建函数 **************/
+
+HPSOCKET_API HP_SSLServer __HP_CALL Create_HP_SSLServer(HP_TcpServerListener pListener)
+{
+	return (HP_SSLServer)(new C_HP_SSLServer((ITcpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_SSLAgent __HP_CALL Create_HP_SSLAgent(HP_TcpAgentListener pListener)
+{
+	return (HP_SSLAgent)(new C_HP_SSLAgent((ITcpAgentListener*)pListener));
+}
+
+HPSOCKET_API HP_SSLClient __HP_CALL Create_HP_SSLClient(HP_TcpClientListener pListener)
+{
+	return (HP_SSLClient)(new C_HP_SSLClient((ITcpClientListener*)pListener));
+}
+
+HPSOCKET_API HP_SSLPullServer __HP_CALL Create_HP_SSLPullServer(HP_TcpPullServerListener pListener)
+{
+	return (HP_SSLPullServer)(new C_HP_SSLPullServer((ITcpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_SSLPullAgent __HP_CALL Create_HP_SSLPullAgent(HP_TcpPullAgentListener pListener)
+{
+	return (HP_SSLPullAgent)(new C_HP_SSLPullAgent((ITcpAgentListener*)pListener));
+}
+
+HPSOCKET_API HP_SSLPullClient __HP_CALL Create_HP_SSLPullClient(HP_TcpPullClientListener pListener)
+{
+	return (HP_SSLPullClient)(new C_HP_SSLPullClient((ITcpClientListener*)pListener));
+}
+
+HPSOCKET_API HP_SSLPackServer __HP_CALL Create_HP_SSLPackServer(HP_TcpServerListener pListener)
+{
+	return (HP_SSLPackServer)(new C_HP_SSLPackServer((ITcpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_SSLPackAgent __HP_CALL Create_HP_SSLPackAgent(HP_TcpAgentListener pListener)
+{
+	return (HP_SSLPackAgent)(new C_HP_SSLPackAgent((ITcpAgentListener*)pListener));
+}
+
+HPSOCKET_API HP_SSLPackClient __HP_CALL Create_HP_SSLPackClient(HP_TcpClientListener pListener)
+{
+	return (HP_SSLPackClient)(new C_HP_SSLPackClient((ITcpClientListener*)pListener));
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SSLServer(HP_SSLServer pServer)
+{
+	delete (C_HP_SSLServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SSLAgent(HP_SSLAgent pAgent)
+{
+	delete (C_HP_SSLAgent*)pAgent;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SSLClient(HP_SSLClient pClient)
+{
+	delete (C_HP_SSLClient*)pClient;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SSLPullServer(HP_SSLPullServer pServer)
+{
+	delete (C_HP_SSLPullServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SSLPullAgent(HP_SSLPullAgent pAgent)
+{
+	delete (C_HP_SSLPullAgent*)pAgent;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SSLPullClient(HP_SSLPullClient pClient)
+{
+	delete (C_HP_SSLPullClient*)pClient;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SSLPackServer(HP_SSLPackServer pServer)
+{
+	delete (C_HP_SSLPackServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SSLPackAgent(HP_SSLPackAgent pAgent)
+{
+	delete (C_HP_SSLPackAgent*)pAgent;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SSLPackClient(HP_SSLPackClient pClient)
+{
+	delete (C_HP_SSLPackClient*)pClient;
+}
+
+/*****************************************************************************************************************************************************/
+/*************************************************************** Global Function Exports *************************************************************/
+/*****************************************************************************************************************************************************/
+
+/***************************************************************************************/
+/************************************ SSL 初始化方法 ************************************/
+
+HPSOCKET_API int __HP_CALL HP_SSL_DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext)
+{
+	return CSSLContext::DefaultServerNameCallback(lpszServerName, pContext);
+}
+
+HPSOCKET_API void __HP_CALL HP_SSL_RemoveThreadLocalState(THR_ID dwThreadID)
+{
+	CSSLContext::RemoveThreadLocalState(dwThreadID);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLServer_SetupSSLContext(HP_SSLServer pServer, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath, HP_Fn_SNI_ServerNameCallback fnServerNameCallback)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->SetupSSLContext(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPassword, lpszCAPemCertFileOrPath, fnServerNameCallback);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLServer_SetupSSLContextByMemory(HP_SSLServer pServer, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert, HP_Fn_SNI_ServerNameCallback fnServerNameCallback)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->SetupSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert, fnServerNameCallback);
+}
+
+HPSOCKET_API int __HP_CALL HP_SSLServer_AddSSLContext(HP_SSLServer pServer, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->AddSSLContext(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPassword, lpszCAPemCertFileOrPath);
+}
+
+HPSOCKET_API int __HP_CALL HP_SSLServer_AddSSLContextByMemory(HP_SSLServer pServer, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->AddSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLServer_BindSSLServerName(HP_SSLServer pServer, LPCTSTR lpszServerName, int iContextIndex)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->BindSSLServerName(lpszServerName, iContextIndex);
+}
+
+HPSOCKET_API void __HP_CALL HP_SSLServer_CleanupSSLContext(HP_SSLServer pServer)
+{
+	C_HP_Object::ToSecond<ITcpServer>(pServer)->CleanupSSLContext();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_SetupSSLContext(HP_SSLAgent pAgent, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetupSSLContext(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPassword, lpszCAPemCertFileOrPath);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_SetupSSLContextByMemory(HP_SSLAgent pAgent, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetupSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert);
+}
+
+HPSOCKET_API void __HP_CALL HP_SSLAgent_CleanupSSLContext(HP_SSLAgent pAgent)
+{
+	C_HP_Object::ToSecond<ITcpAgent>(pAgent)->CleanupSSLContext();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLClient_SetupSSLContext(HP_SSLClient pClient, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->SetupSSLContext(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPassword, lpszCAPemCertFileOrPath);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLClient_SetupSSLContextByMemory(HP_SSLClient pClient, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->SetupSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert);
+}
+
+HPSOCKET_API void __HP_CALL HP_SSLClient_CleanupSSLContext(HP_SSLClient pClient)
+{
+	C_HP_Object::ToSecond<ITcpClient>(pClient)->CleanupSSLContext();
+}
+
+/***************************************************************************************/
+/************************************* SSL 操作方法 ************************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLServer_StartSSLHandShake(HP_SSLServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->StartSSLHandShake(dwConnID);
+}
+
+HPSOCKET_API void __HP_CALL HP_SSLServer_SetSSLAutoHandShake(HP_SSLServer pServer, BOOL bAutoHandShake)
+{
+	C_HP_Object::ToSecond<ITcpServer>(pServer)->SetSSLAutoHandShake(bAutoHandShake);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLServer_IsSSLAutoHandShake(HP_SSLServer pServer)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->IsSSLAutoHandShake();
+}
+
+HPSOCKET_API void __HP_CALL HP_SSLServer_SetSSLCipherList(HP_SSLServer pServer, LPCTSTR lpszCipherList)
+{
+	C_HP_Object::ToSecond<ITcpServer>(pServer)->SetSSLCipherList(lpszCipherList);
+}
+
+HPSOCKET_API LPCTSTR __HP_CALL HP_SSLServer_GetSSLCipherList(HP_SSLServer pServer)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->GetSSLCipherList();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLServer_GetSSLSessionInfo(HP_SSLServer pServer, HP_CONNID dwConnID, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->GetSSLSessionInfo(dwConnID, enInfo, lppInfo);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_StartSSLHandShake(HP_SSLAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->StartSSLHandShake(dwConnID);
+}
+
+HPSOCKET_API void __HP_CALL HP_SSLAgent_SetSSLAutoHandShake(HP_SSLAgent pAgent, BOOL bAutoHandShake)
+{
+	C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetSSLAutoHandShake(bAutoHandShake);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_IsSSLAutoHandShake(HP_SSLAgent pAgent)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->IsSSLAutoHandShake();
+}
+
+HPSOCKET_API void __HP_CALL HP_SSLAgent_SetSSLCipherList(HP_SSLAgent pAgent, LPCTSTR lpszCipherList)
+{
+	C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetSSLCipherList(lpszCipherList);
+}
+
+HPSOCKET_API LPCTSTR __HP_CALL HP_SSLAgent_GetSSLCipherList(HP_SSLAgent pAgent)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->GetSSLCipherList();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_GetSSLSessionInfo(HP_SSLAgent pAgent, HP_CONNID dwConnID, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->GetSSLSessionInfo(dwConnID, enInfo, lppInfo);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLClient_StartSSLHandShake(HP_SSLClient pClient)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->StartSSLHandShake();
+}
+
+HPSOCKET_API void __HP_CALL HP_SSLClient_SetSSLAutoHandShake(HP_SSLClient pClient, BOOL bAutoHandShake)
+{
+	C_HP_Object::ToSecond<ITcpClient>(pClient)->SetSSLAutoHandShake(bAutoHandShake);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLClient_IsSSLAutoHandShake(HP_SSLClient pClient)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->IsSSLAutoHandShake();
+}
+
+HPSOCKET_API void __HP_CALL HP_SSLClient_SetSSLCipherList(HP_SSLClient pClient, LPCTSTR lpszCipherList)
+{
+	C_HP_Object::ToSecond<ITcpClient>(pClient)->SetSSLCipherList(lpszCipherList);
+}
+
+HPSOCKET_API LPCTSTR __HP_CALL HP_SSLClient_GetSSLCipherList(HP_SSLClient pClient)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->GetSSLCipherList();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_SSLClient_GetSSLSessionInfo(HP_SSLClient pClient, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->GetSSLSessionInfo(enInfo, lppInfo);
+}
+
+/*****************************************************************************************************************************************************/
+/******************************************************************** HTTPS Exports ******************************************************************/
+/*****************************************************************************************************************************************************/
+
+#ifdef _HTTP_SUPPORT
+
+typedef C_HP_ObjectT<CHttpsServer, IHttpServerListener, sizeof(IComplexHttpResponder)>		C_HP_HttpsServer;
+typedef C_HP_ObjectT<CHttpsAgent, IHttpAgentListener, sizeof(IComplexHttpRequester)>		C_HP_HttpsAgent;
+typedef C_HP_ObjectT<CHttpsClient, IHttpClientListener, sizeof(IHttpRequester)>				C_HP_HttpsClient;
+typedef C_HP_ObjectT<CHttpsSyncClient, IHttpClientListener, sizeof(IHttpSyncRequester)>		C_HP_HttpsSyncClient;
+
+/****************************************************/
+/**************** HTTPS 对象创建函数 *****************/
+
+HPSOCKET_API HP_HttpsServer __HP_CALL Create_HP_HttpsServer(HP_HttpServerListener pListener)
+{
+	return (HP_HttpsServer)(new C_HP_HttpsServer((IHttpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_HttpsAgent __HP_CALL Create_HP_HttpsAgent(HP_HttpAgentListener pListener)
+{
+	return (HP_HttpsAgent)(new C_HP_HttpsAgent((IHttpAgentListener*)pListener));
+}
+
+HPSOCKET_API HP_HttpsClient __HP_CALL Create_HP_HttpsClient(HP_HttpClientListener pListener)
+{
+	return (HP_HttpsClient)(new C_HP_HttpsClient((IHttpClientListener*)pListener));
+}
+
+HPSOCKET_API HP_HttpsSyncClient __HP_CALL Create_HP_HttpsSyncClient(HP_HttpClientListener pListener)
+{
+	return (HP_HttpsSyncClient)(new C_HP_HttpsSyncClient((IHttpClientListener*)pListener));
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpsServer(HP_HttpsServer pServer)
+{
+	delete (C_HP_HttpsServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpsAgent(HP_HttpsAgent pAgent)
+{
+	delete (C_HP_HttpsAgent*)pAgent;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpsClient(HP_HttpsClient pClient)
+{
+	delete (C_HP_HttpsClient*)pClient;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpsSyncClient(HP_HttpsSyncClient pClient)
+{
+	delete (C_HP_HttpsSyncClient*)pClient;
+}
+
+#endif
+
+#endif

+ 3747 - 0
HPSocket4C.cpp

@@ -0,0 +1,3747 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "SocketObject4C.h"
+#include "TcpServer.h"
+#include "TcpClient.h"
+#include "TcpAgent.h"
+#include "TcpPullServer.h"
+#include "TcpPullClient.h"
+#include "TcpPullAgent.h"
+#include "TcpPackServer.h"
+#include "TcpPackClient.h"
+#include "TcpPackAgent.h"
+#include "HPThreadPool.h"
+
+#ifdef _UDP_SUPPORT
+#include "UdpServer.h"
+#include "UdpClient.h"
+#include "UdpCast.h"
+#include "UdpNode.h"
+#include "UdpArqServer.h"
+#include "UdpArqClient.h"
+#endif
+
+#ifdef _HTTP_SUPPORT
+#include "HttpServer.h"
+#include "HttpAgent.h"
+#include "HttpClient.h"
+#endif
+
+/*****************************************************************************************************************************************************/
+/****************************************************************** TCP/UDP Exports ******************************************************************/
+/*****************************************************************************************************************************************************/
+
+typedef C_HP_ObjectT<CTcpServer, ITcpServerListener>							C_HP_TcpServer;
+typedef C_HP_ObjectT<CTcpPullServer, ITcpServerListener, sizeof(IPullSocket)>	C_HP_TcpPullServer;
+typedef C_HP_ObjectT<CTcpPackServer, ITcpServerListener, sizeof(IPackSocket)>	C_HP_TcpPackServer;
+
+typedef C_HP_ObjectT<CTcpAgent, ITcpAgentListener>								C_HP_TcpAgent;
+typedef C_HP_ObjectT<CTcpPullAgent, ITcpAgentListener, sizeof(IPullSocket)>		C_HP_TcpPullAgent;
+typedef C_HP_ObjectT<CTcpPackAgent, ITcpAgentListener, sizeof(IPackSocket)>		C_HP_TcpPackAgent;
+
+typedef C_HP_ObjectT<CTcpClient, ITcpClientListener>							C_HP_TcpClient;
+typedef C_HP_ObjectT<CTcpPullClient, ITcpClientListener, sizeof(IPullClient)>	C_HP_TcpPullClient;
+typedef C_HP_ObjectT<CTcpPackClient, ITcpClientListener, sizeof(IPackClient)>	C_HP_TcpPackClient;
+
+#ifdef _UDP_SUPPORT
+
+typedef C_HP_ObjectT<CUdpServer, IUdpServerListener>							C_HP_UdpServer;
+typedef C_HP_ObjectT<CUdpClient, IUdpClientListener>							C_HP_UdpClient;
+typedef C_HP_ObjectT<CUdpCast, IUdpCastListener>								C_HP_UdpCast;
+typedef C_HP_ObjectT<CUdpNode, IUdpNodeListener>								C_HP_UdpNode;
+
+typedef C_HP_ObjectT<CUdpArqServer, IUdpServerListener, sizeof(IArqSocket)>		C_HP_UdpArqServer;
+typedef C_HP_ObjectT<CUdpArqClient, IUdpClientListener, sizeof(IArqClient)>		C_HP_UdpArqClient;
+
+#endif
+
+/****************************************************/
+/**************** TCP/UDP 对象创建函数 ***************/
+
+HPSOCKET_API HP_TcpServer __HP_CALL Create_HP_TcpServer(HP_TcpServerListener pListener)
+{
+	return (HP_TcpServer)(new C_HP_TcpServer((ITcpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_TcpAgent __HP_CALL Create_HP_TcpAgent(HP_TcpAgentListener pListener)
+{
+	return (HP_TcpAgent)(new C_HP_TcpAgent((ITcpAgentListener*)pListener));
+}
+
+HPSOCKET_API HP_TcpClient __HP_CALL Create_HP_TcpClient(HP_TcpClientListener pListener)
+{
+	return (HP_TcpClient)(new C_HP_TcpClient((ITcpClientListener*)pListener));
+}
+
+HPSOCKET_API HP_TcpPullServer __HP_CALL Create_HP_TcpPullServer(HP_TcpPullServerListener pListener)
+{
+	return (HP_TcpPullServer)(new C_HP_TcpPullServer((ITcpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_TcpPullAgent __HP_CALL Create_HP_TcpPullAgent(HP_TcpPullAgentListener pListener)
+{
+	return (HP_TcpPullAgent)(new C_HP_TcpPullAgent((ITcpAgentListener*)pListener));
+}
+
+HPSOCKET_API HP_TcpPullClient __HP_CALL Create_HP_TcpPullClient(HP_TcpPullClientListener pListener)
+{
+	return (HP_TcpPullClient)(new C_HP_TcpPullClient((ITcpClientListener*)pListener));
+}
+
+HPSOCKET_API HP_TcpPackServer __HP_CALL Create_HP_TcpPackServer(HP_TcpServerListener pListener)
+{
+	return (HP_TcpPackServer)(new C_HP_TcpPackServer((ITcpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_TcpPackAgent __HP_CALL Create_HP_TcpPackAgent(HP_TcpAgentListener pListener)
+{
+	return (HP_TcpPackAgent)(new C_HP_TcpPackAgent((ITcpAgentListener*)pListener));
+}
+
+HPSOCKET_API HP_TcpPackClient __HP_CALL Create_HP_TcpPackClient(HP_TcpClientListener pListener)
+{
+	return (HP_TcpPackClient)(new C_HP_TcpPackClient((ITcpClientListener*)pListener));
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpServer(HP_TcpServer pServer)
+{
+	delete (C_HP_TcpServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpAgent(HP_TcpAgent pAgent)
+{
+	delete (C_HP_TcpAgent*)pAgent;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpClient(HP_TcpClient pClient)
+{
+	delete (C_HP_TcpClient*)pClient;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullServer(HP_TcpPullServer pServer)
+{
+	delete (C_HP_TcpPullServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullAgent(HP_TcpPullAgent pAgent)
+{
+	delete (C_HP_TcpPullAgent*)pAgent;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullClient(HP_TcpPullClient pClient)
+{
+	delete (C_HP_TcpPullClient*)pClient;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackServer(HP_TcpPackServer pServer)
+{
+	delete (C_HP_TcpPackServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackAgent(HP_TcpPackAgent pAgent)
+{
+	delete (C_HP_TcpPackAgent*)pAgent;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackClient(HP_TcpPackClient pClient)
+{
+	delete (C_HP_TcpPackClient*)pClient;
+}
+
+HPSOCKET_API HP_TcpServerListener __HP_CALL Create_HP_TcpServerListener()
+{
+	return (HP_TcpServerListener)(new C_HP_TcpServerListener);
+}
+
+HPSOCKET_API HP_TcpAgentListener __HP_CALL Create_HP_TcpAgentListener()
+{
+	return (HP_TcpAgentListener)(new C_HP_TcpAgentListener);
+}
+
+HPSOCKET_API HP_TcpClientListener __HP_CALL Create_HP_TcpClientListener()
+{
+	return (HP_TcpClientListener)(new C_HP_TcpClientListener);
+}
+
+HPSOCKET_API HP_TcpPullServerListener __HP_CALL Create_HP_TcpPullServerListener()
+{
+	return (HP_TcpPullServerListener)(new C_HP_TcpPullServerListener);
+}
+
+HPSOCKET_API HP_TcpPullAgentListener __HP_CALL Create_HP_TcpPullAgentListener()
+{
+	return (HP_TcpPullAgentListener)(new C_HP_TcpPullAgentListener);
+}
+
+HPSOCKET_API HP_TcpPullClientListener __HP_CALL Create_HP_TcpPullClientListener()
+{
+	return (HP_TcpPullClientListener)(new C_HP_TcpPullClientListener);
+}
+
+HPSOCKET_API HP_TcpPackServerListener __HP_CALL Create_HP_TcpPackServerListener()
+{
+	return (HP_TcpPackServerListener)(new C_HP_TcpPackServerListener);
+}
+
+HPSOCKET_API HP_TcpPackAgentListener __HP_CALL Create_HP_TcpPackAgentListener()
+{
+	return (HP_TcpPackAgentListener)(new C_HP_TcpPackAgentListener);
+}
+
+HPSOCKET_API HP_TcpPackClientListener __HP_CALL Create_HP_TcpPackClientListener()
+{
+	return (HP_TcpPackClientListener)(new C_HP_TcpPackClientListener);
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpServerListener(HP_TcpServerListener pListener)
+{
+	delete (C_HP_TcpServerListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpAgentListener(HP_TcpAgentListener pListener)
+{
+	delete (C_HP_TcpAgentListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpClientListener(HP_TcpClientListener pListener)
+{
+	delete (C_HP_TcpClientListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullServerListener(HP_TcpPullServerListener pListener)
+{
+	delete (C_HP_TcpPullServerListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullAgentListener(HP_TcpPullAgentListener pListener)
+{
+	delete (C_HP_TcpPullAgentListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullClientListener(HP_TcpPullClientListener pListener)
+{
+	delete (C_HP_TcpPullClientListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackServerListener(HP_TcpPackServerListener pListener)
+{
+	delete (C_HP_TcpPackServerListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackAgentListener(HP_TcpPackAgentListener pListener)
+{
+	delete (C_HP_TcpPackAgentListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackClientListener(HP_TcpPackClientListener pListener)
+{
+	delete (C_HP_TcpPackClientListener*)pListener;
+}
+
+#ifdef _UDP_SUPPORT
+
+HPSOCKET_API HP_UdpServer __HP_CALL Create_HP_UdpServer(HP_UdpServerListener pListener)
+{
+	return (HP_UdpServer)(new C_HP_UdpServer((IUdpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_UdpClient __HP_CALL Create_HP_UdpClient(HP_UdpClientListener pListener)
+{
+	return (HP_UdpClient)(new C_HP_UdpClient((IUdpClientListener*)pListener));
+}
+
+HPSOCKET_API HP_UdpCast __HP_CALL Create_HP_UdpCast(HP_UdpCastListener pListener)
+{
+	return (HP_UdpCast)(new C_HP_UdpCast((IUdpCastListener*)pListener));
+}
+
+HPSOCKET_API HP_UdpNode __HP_CALL Create_HP_UdpNode(HP_UdpNodeListener pListener)
+{
+	return (HP_UdpNode)(new C_HP_UdpNode((IUdpNodeListener*)pListener));
+}
+
+HPSOCKET_API HP_UdpArqServer __HP_CALL Create_HP_UdpArqServer(HP_UdpServerListener pListener)
+{
+	return (HP_UdpArqServer)(new C_HP_UdpArqServer((IUdpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_UdpArqClient __HP_CALL Create_HP_UdpArqClient(HP_UdpClientListener pListener)
+{
+	return (HP_UdpArqClient)(new C_HP_UdpArqClient((IUdpClientListener*)pListener));
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpServer(HP_UdpServer pServer)
+{
+	delete (C_HP_UdpServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpClient(HP_UdpClient pClient)
+{
+	delete (C_HP_UdpClient*)pClient;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpCast(HP_UdpCast pCast)
+{
+	delete (C_HP_UdpCast*)pCast;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpNode(HP_UdpNode pNode)
+{
+	delete (C_HP_UdpNode*)pNode;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqServer(HP_UdpArqServer pServer)
+{
+	delete (C_HP_UdpArqServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqClient(HP_UdpArqClient pClient)
+{
+	delete (C_HP_UdpArqClient*)pClient;
+}
+
+HPSOCKET_API HP_UdpServerListener __HP_CALL Create_HP_UdpServerListener()
+{
+	return (HP_UdpServerListener)(new C_HP_UdpServerListener);
+}
+
+HPSOCKET_API HP_UdpClientListener __HP_CALL Create_HP_UdpClientListener()
+{
+	return (HP_UdpClientListener)(new C_HP_UdpClientListener);
+}
+
+HPSOCKET_API HP_UdpCastListener __HP_CALL Create_HP_UdpCastListener()
+{
+	return (HP_UdpCastListener)(new C_HP_UdpCastListener);
+}
+
+HPSOCKET_API HP_UdpNodeListener __HP_CALL Create_HP_UdpNodeListener()
+{
+	return (HP_UdpNodeListener)(new C_HP_UdpNodeListener);
+}
+
+HPSOCKET_API HP_UdpArqServerListener __HP_CALL Create_HP_UdpArqServerListener()
+{
+	return (HP_UdpArqServerListener)(new C_HP_UdpArqServerListener);
+}
+
+HPSOCKET_API HP_UdpArqClientListener __HP_CALL Create_HP_UdpArqClientListener()
+{
+	return (HP_UdpArqClientListener)(new C_HP_UdpArqClientListener);
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpServerListener(HP_UdpServerListener pListener)
+{
+	delete (C_HP_UdpServerListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpClientListener(HP_UdpClientListener pListener)
+{
+	delete (C_HP_UdpClientListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpCastListener(HP_UdpCastListener pListener)
+{
+	delete (C_HP_UdpClientListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpNodeListener(HP_UdpNodeListener pListener)
+{
+	delete (C_HP_UdpNodeListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqServerListener(HP_UdpArqServerListener pListener)
+{
+	delete (C_HP_UdpArqServerListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqClientListener(HP_UdpArqClientListener pListener)
+{
+	delete (C_HP_UdpArqClientListener*)pListener;
+}
+
+#endif
+
+/**********************************************************************************/
+/***************************** Server 回调函数设置方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnPrepareListen(HP_ServerListener pListener, HP_FN_Server_OnPrepareListen fn)
+{
+	((C_HP_TcpServerListener*)pListener)->m_fnOnPrepareListen = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnAccept(HP_ServerListener pListener, HP_FN_Server_OnAccept fn)
+{
+	((C_HP_TcpServerListener*)pListener)->m_fnOnAccept = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnHandShake(HP_ServerListener pListener, HP_FN_Server_OnHandShake fn)
+{
+	((C_HP_TcpServerListener*)pListener)->m_fnOnHandShake = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnSend(HP_ServerListener pListener, HP_FN_Server_OnSend fn)
+{
+	((C_HP_TcpServerListener*)pListener)->m_fnOnSend = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnReceive(HP_ServerListener pListener, HP_FN_Server_OnReceive fn)
+{
+	((C_HP_TcpServerListener*)pListener)->m_fnOnReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnPullReceive(HP_ServerListener pListener, HP_FN_Server_OnPullReceive fn)
+{
+	((C_HP_TcpServerListener*)pListener)->m_fnOnPullReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnClose(HP_ServerListener pListener, HP_FN_Server_OnClose fn)
+{
+	((C_HP_TcpServerListener*)pListener)->m_fnOnClose = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnShutdown(HP_ServerListener pListener, HP_FN_Server_OnShutdown fn)
+{
+	((C_HP_TcpServerListener*)pListener)->m_fnOnShutdown = fn;
+}
+
+/**********************************************************************************/
+/***************************** Agent 回调函数设置方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnPrepareConnect(HP_AgentListener pListener, HP_FN_Agent_OnPrepareConnect fn)
+{
+	((C_HP_TcpAgentListener*)pListener)->m_fnOnPrepareConnect = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnConnect(HP_AgentListener pListener, HP_FN_Agent_OnConnect fn)
+{
+	((C_HP_TcpAgentListener*)pListener)->m_fnOnConnect = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnHandShake(HP_AgentListener pListener, HP_FN_Agent_OnHandShake fn)
+{
+	((C_HP_TcpAgentListener*)pListener)->m_fnOnHandShake = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnSend(HP_AgentListener pListener, HP_FN_Agent_OnSend fn)
+{
+	((C_HP_TcpAgentListener*)pListener)->m_fnOnSend = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnReceive(HP_AgentListener pListener, HP_FN_Agent_OnReceive fn)
+{
+	((C_HP_TcpAgentListener*)pListener)->m_fnOnReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnPullReceive(HP_AgentListener pListener, HP_FN_Agent_OnPullReceive fn)
+{
+	((C_HP_TcpAgentListener*)pListener)->m_fnOnPullReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnClose(HP_AgentListener pListener, HP_FN_Agent_OnClose fn)
+{
+	((C_HP_TcpAgentListener*)pListener)->m_fnOnClose = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnShutdown(HP_AgentListener pListener, HP_FN_Agent_OnShutdown fn)
+{
+	((C_HP_TcpAgentListener*)pListener)->m_fnOnShutdown = fn;
+}
+
+/**********************************************************************************/
+/***************************** Client 回调函数设置方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnPrepareConnect(HP_ClientListener pListener, HP_FN_Client_OnPrepareConnect fn)
+{
+	((C_HP_TcpClientListener*)pListener)->m_fnOnPrepareConnect = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnConnect(HP_ClientListener pListener, HP_FN_Client_OnConnect fn)
+{
+	((C_HP_TcpClientListener*)pListener)->m_fnOnConnect = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnHandShake(HP_ClientListener pListener, HP_FN_Client_OnHandShake fn)
+{
+	((C_HP_TcpClientListener*)pListener)->m_fnOnHandShake = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnSend(HP_ClientListener pListener, HP_FN_Client_OnSend fn)
+{
+	((C_HP_TcpClientListener*)pListener)->m_fnOnSend = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnReceive(HP_ClientListener pListener, HP_FN_Client_OnReceive fn)
+{
+	((C_HP_TcpClientListener*)pListener)->m_fnOnReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnPullReceive(HP_ClientListener pListener, HP_FN_Client_OnPullReceive fn)
+{
+	((C_HP_TcpClientListener*)pListener)->m_fnOnPullReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnClose(HP_ClientListener pListener, HP_FN_Client_OnClose fn)
+{
+	((C_HP_TcpClientListener*)pListener)->m_fnOnClose = fn;
+}
+
+#ifdef _UDP_SUPPORT
+
+/**********************************************************************************/
+/***************************** UdpNode 回调函数设置方法 *****************************/
+
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnPrepareListen(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnPrepareListen fn)
+{
+	((C_HP_UdpNodeListener*)pListener)->m_fnOnPrepareListen = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnSend(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnSend fn)
+{
+	((C_HP_UdpNodeListener*)pListener)->m_fnOnSend = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnReceive(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnReceive fn)
+{
+	((C_HP_UdpNodeListener*)pListener)->m_fnOnReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnError(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnError fn)
+{
+	((C_HP_UdpNodeListener*)pListener)->m_fnOnError = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnShutdown(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnShutdown fn)
+{
+	((C_HP_UdpNodeListener*)pListener)->m_fnOnShutdown = fn;
+}
+
+#endif
+
+/**************************************************************************/
+/***************************** Server 操作方法 *****************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_Start(HP_Server pServer, LPCTSTR lpszBindAddress, USHORT usPort)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->Start(lpszBindAddress, usPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_Stop(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->Stop();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_Wait(HP_Server pServer, DWORD dwMilliseconds)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->Wait(dwMilliseconds);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_Send(HP_Server pServer, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->Send(dwConnID, pBuffer, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_SendPart(HP_Server pServer, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->Send(dwConnID, pBuffer, iLength, iOffset);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_SendPackets(HP_Server pServer, HP_CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->SendPackets(dwConnID, pBuffers, iCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_PauseReceive(HP_Server pServer, HP_CONNID dwConnID, BOOL bPause)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->PauseReceive(dwConnID, bPause);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_Disconnect(HP_Server pServer, HP_CONNID dwConnID, BOOL bForce)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->Disconnect(dwConnID, bForce);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_DisconnectLongConnections(HP_Server pServer, DWORD dwPeriod, BOOL bForce)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->DisconnectLongConnections(dwPeriod, bForce);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_DisconnectSilenceConnections(HP_Server pServer, DWORD dwPeriod, BOOL bForce)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->DisconnectSilenceConnections(dwPeriod, bForce);
+}
+
+/******************************************************************************/
+/***************************** Server 属性访问方法 *****************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_SetConnectionExtra(HP_Server pServer, HP_CONNID dwConnID, PVOID pExtra)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->SetConnectionExtra(dwConnID, pExtra);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_GetConnectionExtra(HP_Server pServer, HP_CONNID dwConnID, PVOID* ppExtra)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetConnectionExtra(dwConnID, ppExtra);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_IsSecure(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->IsSecure();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_HasStarted(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->HasStarted();
+}
+
+HPSOCKET_API En_HP_ServiceState __HP_CALL HP_Server_GetState(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetState();
+}
+
+HPSOCKET_API En_HP_SocketError __HP_CALL HP_Server_GetLastError(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetLastError();
+}
+
+HPSOCKET_API LPCTSTR __HP_CALL HP_Server_GetLastErrorDesc(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetLastErrorDesc();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_GetPendingDataLength(HP_Server pServer, HP_CONNID dwConnID, int* piPending)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetPendingDataLength(dwConnID, *piPending);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_IsPauseReceive(HP_Server pServer, HP_CONNID dwConnID, BOOL* pbPaused)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->IsPauseReceive(dwConnID, *pbPaused);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_IsConnected(HP_Server pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->IsConnected(dwConnID);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Server_GetConnectionCount(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetConnectionCount();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_GetAllConnectionIDs(HP_Server pServer, HP_CONNID pIDs[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetAllConnectionIDs(pIDs, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_GetConnectPeriod(HP_Server pServer, HP_CONNID dwConnID, DWORD* pdwPeriod)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetConnectPeriod(dwConnID, *pdwPeriod);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_GetSilencePeriod(HP_Server pServer, HP_CONNID dwConnID, DWORD* pdwPeriod)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetSilencePeriod(dwConnID, *pdwPeriod);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_GetListenAddress(HP_Server pServer, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetListenAddress(lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_GetLocalAddress(HP_Server pServer, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetLocalAddress(dwConnID, lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_GetRemoteAddress(HP_Server pServer, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetRemoteAddress(dwConnID, lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetReuseAddressPolicy(HP_Server pServer, En_HP_ReuseAddressPolicy enReusePolicy)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetReuseAddressPolicy(enReusePolicy);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetSendPolicy(HP_Server pServer, En_HP_SendPolicy enSendPolicy)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetSendPolicy(enSendPolicy);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetOnSendSyncPolicy(HP_Server pServer, En_HP_OnSendSyncPolicy enSyncPolicy)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetOnSendSyncPolicy(enSyncPolicy);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetFreeSocketObjLockTime(HP_Server pServer, DWORD dwFreeSocketObjLockTime)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetFreeSocketObjLockTime(dwFreeSocketObjLockTime);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetFreeSocketObjPool(HP_Server pServer, DWORD dwFreeSocketObjPool)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetFreeSocketObjPool(dwFreeSocketObjPool);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetFreeBufferObjPool(HP_Server pServer, DWORD dwFreeBufferObjPool)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetFreeBufferObjPool(dwFreeBufferObjPool);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetFreeSocketObjHold(HP_Server pServer, DWORD dwFreeSocketObjHold)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetFreeSocketObjHold(dwFreeSocketObjHold);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetFreeBufferObjHold(HP_Server pServer, DWORD dwFreeBufferObjHold)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetFreeBufferObjHold(dwFreeBufferObjHold);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetMaxConnectionCount(HP_Server pServer, DWORD dwMaxConnectionCount)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetMaxConnectionCount(dwMaxConnectionCount);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetWorkerThreadCount(HP_Server pServer, DWORD dwWorkerThreadCount)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetWorkerThreadCount(dwWorkerThreadCount);
+}
+
+HPSOCKET_API void __HP_CALL HP_Server_SetMarkSilence(HP_Server pServer, BOOL bMarkSilence)
+{
+	C_HP_Object::ToSecond<IServer>(pServer)->SetMarkSilence(bMarkSilence);
+}
+
+HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_Server_GetReuseAddressPolicy(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetReuseAddressPolicy();
+}
+
+HPSOCKET_API En_HP_SendPolicy __HP_CALL HP_Server_GetSendPolicy(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetSendPolicy();
+}
+
+HPSOCKET_API En_HP_OnSendSyncPolicy __HP_CALL HP_Server_GetOnSendSyncPolicy(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetOnSendSyncPolicy();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeSocketObjLockTime(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetFreeSocketObjLockTime();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeSocketObjPool(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetFreeSocketObjPool();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeBufferObjPool(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetFreeBufferObjPool();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeSocketObjHold(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetFreeSocketObjHold();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeBufferObjHold(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetFreeBufferObjHold();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Server_GetMaxConnectionCount(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetMaxConnectionCount();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Server_GetWorkerThreadCount(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->GetWorkerThreadCount();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Server_IsMarkSilence(HP_Server pServer)
+{
+	return C_HP_Object::ToSecond<IServer>(pServer)->IsMarkSilence();
+}
+
+/**********************************************************************************/
+/******************************* TCP Server 操作方法 *******************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_TcpServer_SendSmallFile(HP_Server pServer, HP_CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->SendSmallFile(dwConnID, lpszFileName, pHead, pTail);
+}
+
+/**********************************************************************************/
+/***************************** TCP Server 属性访问方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_TcpServer_SetAcceptSocketCount(HP_TcpServer pServer, DWORD dwAcceptSocketCount)
+{
+	C_HP_Object::ToSecond<ITcpServer>(pServer)->SetAcceptSocketCount(dwAcceptSocketCount);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpServer_SetSocketBufferSize(HP_TcpServer pServer, DWORD dwSocketBufferSize)
+{
+	C_HP_Object::ToSecond<ITcpServer>(pServer)->SetSocketBufferSize(dwSocketBufferSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpServer_SetSocketListenQueue(HP_TcpServer pServer, DWORD dwSocketListenQueue)
+{
+	C_HP_Object::ToSecond<ITcpServer>(pServer)->SetSocketListenQueue(dwSocketListenQueue);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpServer_SetKeepAliveTime(HP_TcpServer pServer, DWORD dwKeepAliveTime)
+{
+	C_HP_Object::ToSecond<ITcpServer>(pServer)->SetKeepAliveTime(dwKeepAliveTime);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpServer_SetKeepAliveInterval(HP_TcpServer pServer, DWORD dwKeepAliveInterval)
+{
+	C_HP_Object::ToSecond<ITcpServer>(pServer)->SetKeepAliveInterval(dwKeepAliveInterval);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpServer_SetNoDelay(HP_TcpServer pServer, BOOL bNoDelay)
+{
+	C_HP_Object::ToSecond<ITcpServer>(pServer)->SetNoDelay(bNoDelay);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetAcceptSocketCount(HP_TcpServer pServer)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->GetAcceptSocketCount();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetSocketBufferSize(HP_TcpServer pServer)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->GetSocketBufferSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetSocketListenQueue(HP_TcpServer pServer)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->GetSocketListenQueue();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetKeepAliveTime(HP_TcpServer pServer)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->GetKeepAliveTime();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetKeepAliveInterval(HP_TcpServer pServer)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->GetKeepAliveInterval();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_TcpServer_IsNoDelay(HP_TcpServer pServer)
+{
+	return C_HP_Object::ToSecond<ITcpServer>(pServer)->IsNoDelay();
+}
+
+#ifdef _UDP_SUPPORT
+
+/**********************************************************************************/
+/***************************** UDP Server 属性访问方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_UdpServer_SetMaxDatagramSize(HP_UdpServer pServer, DWORD dwMaxDatagramSize)
+{
+	C_HP_Object::ToSecond<IUdpServer>(pServer)->SetMaxDatagramSize(dwMaxDatagramSize);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetMaxDatagramSize(HP_UdpServer pServer)
+{
+	return C_HP_Object::ToSecond<IUdpServer>(pServer)->GetMaxDatagramSize();
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpServer_SetPostReceiveCount(HP_UdpServer pServer, DWORD dwPostReceiveCount)
+{
+	C_HP_Object::ToSecond<IUdpServer>(pServer)->SetPostReceiveCount(dwPostReceiveCount);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetPostReceiveCount(HP_UdpServer pServer)
+{
+	return C_HP_Object::ToSecond<IUdpServer>(pServer)->GetPostReceiveCount();
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpServer_SetDetectAttempts(HP_UdpServer pServer, DWORD dwDetectAttempts)
+{
+	C_HP_Object::ToSecond<IUdpServer>(pServer)->SetDetectAttempts(dwDetectAttempts);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpServer_SetDetectInterval(HP_UdpServer pServer, DWORD dwDetectInterval)
+{
+	C_HP_Object::ToSecond<IUdpServer>(pServer)->SetDetectInterval(dwDetectInterval);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetDetectAttempts(HP_UdpServer pServer)
+{
+	return C_HP_Object::ToSecond<IUdpServer>(pServer)->GetDetectAttempts();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetDetectInterval(HP_UdpServer pServer)
+{
+	return C_HP_Object::ToSecond<IUdpServer>(pServer)->GetDetectInterval();
+}
+
+/**********************************************************************************/
+/*************************** UDP ARQ Server 属性访问方法 ***************************/
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetNoDelay(HP_UdpArqServer pServer, BOOL bNoDelay)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetNoDelay(bNoDelay);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetTurnoffCongestCtrl(HP_UdpArqServer pServer, BOOL bTurnOff)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetTurnoffCongestCtrl(bTurnOff);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetFlushInterval(HP_UdpArqServer pServer, DWORD dwFlushInterval)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetFlushInterval(dwFlushInterval);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetResendByAcks(HP_UdpArqServer pServer, DWORD dwResendByAcks)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetResendByAcks(dwResendByAcks);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetSendWndSize(HP_UdpArqServer pServer, DWORD dwSendWndSize)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetSendWndSize(dwSendWndSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetRecvWndSize(HP_UdpArqServer pServer, DWORD dwRecvWndSize)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetRecvWndSize(dwRecvWndSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetMinRto(HP_UdpArqServer pServer, DWORD dwMinRto)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetMinRto(dwMinRto);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetFastLimit(HP_UdpArqServer pServer, DWORD dwFastLimit)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetFastLimit(dwFastLimit);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetMaxTransUnit(HP_UdpArqServer pServer, DWORD dwMaxTransUnit)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetMaxTransUnit(dwMaxTransUnit);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetMaxMessageSize(HP_UdpArqServer pServer, DWORD dwMaxMessageSize)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetMaxMessageSize(dwMaxMessageSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetHandShakeTimeout(HP_UdpArqServer pServer, DWORD dwHandShakeTimeout)
+{
+	C_HP_Object::ToFirst<IArqSocket>(pServer)->SetHandShakeTimeout(dwHandShakeTimeout);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpArqServer_IsNoDelay(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->IsNoDelay();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpArqServer_IsTurnoffCongestCtrl(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->IsTurnoffCongestCtrl();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetFlushInterval(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetFlushInterval();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetResendByAcks(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetResendByAcks();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetSendWndSize(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetSendWndSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetRecvWndSize(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetRecvWndSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetMinRto(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetMinRto();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetFastLimit(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetFastLimit();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetMaxTransUnit(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetMaxTransUnit();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetMaxMessageSize(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetMaxMessageSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetHandShakeTimeout(HP_UdpArqServer pServer)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetHandShakeTimeout();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpArqServer_GetWaitingSendMessageCount(HP_UdpArqServer pServer, HP_CONNID dwConnID, int* piCount)
+{
+	return C_HP_Object::ToFirst<IArqSocket>(pServer)->GetWaitingSendMessageCount(dwConnID, *piCount);
+}
+
+#endif
+
+/**************************************************************************/
+/***************************** Agent 操作方法 *****************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_Start(HP_Agent pAgent, LPCTSTR lpszBindAddress, BOOL bAsyncConnect)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Start(lpszBindAddress, bAsyncConnect);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_Stop(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Stop();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_Wait(HP_Agent pAgent, DWORD dwMilliseconds)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Wait(dwMilliseconds);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_Connect(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithExtra(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, PVOID pExtra)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, pExtra);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithLocalPort(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, USHORT usLocalPort)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, nullptr, usLocalPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithLocalAddress(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, LPCTSTR lpszLocalAddress)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, nullptr, 0, lpszLocalAddress);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithExtraAndLocalPort(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, PVOID pExtra, USHORT usLocalPort)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, pExtra, usLocalPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithExtraAndLocalAddressPort(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, PVOID pExtra, USHORT usLocalPort, LPCTSTR lpszLocalAddress)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, pExtra, usLocalPort, lpszLocalAddress);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_Send(HP_Agent pAgent, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Send(dwConnID, pBuffer, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_SendPart(HP_Agent pAgent, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Send(dwConnID, pBuffer, iLength, iOffset);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_SendPackets(HP_Agent pAgent, HP_CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->SendPackets(dwConnID, pBuffers, iCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_PauseReceive(HP_Agent pAgent, HP_CONNID dwConnID, BOOL bPause)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->PauseReceive(dwConnID, bPause);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_Disconnect(HP_Agent pAgent, HP_CONNID dwConnID, BOOL bForce)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->Disconnect(dwConnID, bForce);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_DisconnectLongConnections(HP_Agent pAgent, DWORD dwPeriod, BOOL bForce)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->DisconnectLongConnections(dwPeriod, bForce);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_DisconnectSilenceConnections(HP_Agent pAgent, DWORD dwPeriod, BOOL bForce)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->DisconnectSilenceConnections(dwPeriod, bForce);
+}
+
+/******************************************************************************/
+/***************************** Agent 属性访问方法 *****************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_SetConnectionExtra(HP_Agent pAgent, HP_CONNID dwConnID, PVOID pExtra)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->SetConnectionExtra(dwConnID, pExtra);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_GetConnectionExtra(HP_Agent pAgent, HP_CONNID dwConnID, PVOID* ppExtra)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetConnectionExtra(dwConnID, ppExtra);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_IsSecure(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->IsSecure();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_HasStarted(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->HasStarted();
+}
+
+HPSOCKET_API En_HP_ServiceState __HP_CALL HP_Agent_GetState(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetState();
+}
+
+HPSOCKET_API En_HP_SocketError __HP_CALL HP_Agent_GetLastError(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetLastError();
+}
+
+HPSOCKET_API LPCTSTR __HP_CALL HP_Agent_GetLastErrorDesc(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetLastErrorDesc();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_GetPendingDataLength(HP_Agent pAgent, HP_CONNID dwConnID, int* piPending)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetPendingDataLength(dwConnID, *piPending);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_IsPauseReceive(HP_Agent pAgent, HP_CONNID dwConnID, BOOL* pbPaused)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->IsPauseReceive(dwConnID, *pbPaused);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_IsConnected(HP_Agent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->IsConnected(dwConnID);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Agent_GetConnectionCount(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetConnectionCount();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_GetAllConnectionIDs(HP_Agent pAgent, HP_CONNID pIDs[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetAllConnectionIDs(pIDs, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_GetConnectPeriod(HP_Agent pAgent, HP_CONNID dwConnID, DWORD* pdwPeriod)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetConnectPeriod(dwConnID, *pdwPeriod);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_GetSilencePeriod(HP_Agent pAgent, HP_CONNID dwConnID, DWORD* pdwPeriod)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetSilencePeriod(dwConnID, *pdwPeriod);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_GetLocalAddress(HP_Agent pAgent, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetLocalAddress(dwConnID, lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_GetRemoteAddress(HP_Agent pAgent, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetRemoteAddress(dwConnID, lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_GetRemoteHost(HP_Agent pAgent, HP_CONNID dwConnID, TCHAR lpszHost[], int* piHostLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetRemoteHost(dwConnID, lpszHost, *piHostLen, *pusPort);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetReuseAddressPolicy(HP_Agent pAgent, En_HP_ReuseAddressPolicy enReusePolicy)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetReuseAddressPolicy(enReusePolicy);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetSendPolicy(HP_Agent pAgent, En_HP_SendPolicy enSendPolicy)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetSendPolicy(enSendPolicy);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetOnSendSyncPolicy(HP_Agent pAgent, En_HP_OnSendSyncPolicy enSyncPolicy)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetOnSendSyncPolicy(enSyncPolicy);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetFreeSocketObjLockTime(HP_Agent pAgent, DWORD dwFreeSocketObjLockTime)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetFreeSocketObjLockTime(dwFreeSocketObjLockTime);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetFreeSocketObjPool(HP_Agent pAgent, DWORD dwFreeSocketObjPool)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetFreeSocketObjPool(dwFreeSocketObjPool);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetFreeBufferObjPool(HP_Agent pAgent, DWORD dwFreeBufferObjPool)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetFreeBufferObjPool(dwFreeBufferObjPool);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetFreeSocketObjHold(HP_Agent pAgent, DWORD dwFreeSocketObjHold)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetFreeSocketObjHold(dwFreeSocketObjHold);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetFreeBufferObjHold(HP_Agent pAgent, DWORD dwFreeBufferObjHold)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetFreeBufferObjHold(dwFreeBufferObjHold);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetMaxConnectionCount(HP_Agent pAgent, DWORD dwMaxConnectionCount)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetMaxConnectionCount(dwMaxConnectionCount);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetWorkerThreadCount(HP_Agent pAgent, DWORD dwWorkerThreadCount)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetWorkerThreadCount(dwWorkerThreadCount);
+}
+
+HPSOCKET_API void __HP_CALL HP_Agent_SetMarkSilence(HP_Agent pAgent, BOOL bMarkSilence)
+{
+	C_HP_Object::ToSecond<IAgent>(pAgent)->SetMarkSilence(bMarkSilence);
+}
+
+HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_Agent_GetReuseAddressPolicy(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetReuseAddressPolicy();
+}
+
+HPSOCKET_API En_HP_SendPolicy __HP_CALL HP_Agent_GetSendPolicy(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetSendPolicy();
+}
+
+HPSOCKET_API En_HP_OnSendSyncPolicy __HP_CALL HP_Agent_GetOnSendSyncPolicy(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetOnSendSyncPolicy();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeSocketObjLockTime(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetFreeSocketObjLockTime();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeSocketObjPool(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetFreeSocketObjPool();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeBufferObjPool(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetFreeBufferObjPool();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeSocketObjHold(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetFreeSocketObjHold();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeBufferObjHold(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetFreeBufferObjHold();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Agent_GetMaxConnectionCount(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetMaxConnectionCount();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Agent_GetWorkerThreadCount(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->GetWorkerThreadCount();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Agent_IsMarkSilence(HP_Agent pAgent)
+{
+	return C_HP_Object::ToSecond<IAgent>(pAgent)->IsMarkSilence();
+}
+
+/**********************************************************************************/
+/******************************* TCP Agent 操作方法 *******************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_TcpAgent_SendSmallFile(HP_Agent pAgent, HP_CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SendSmallFile(dwConnID, lpszFileName, pHead, pTail);
+}
+
+/**********************************************************************************/
+/***************************** TCP Agent 属性访问方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_TcpAgent_SetSyncConnectTimeout(HP_TcpAgent pAgent, DWORD dwSyncConnectTimeout)
+{
+	C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetSyncConnectTimeout(dwSyncConnectTimeout);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpAgent_SetSocketBufferSize(HP_TcpAgent pAgent, DWORD dwSocketBufferSize)
+{
+	C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetSocketBufferSize(dwSocketBufferSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpAgent_SetKeepAliveTime(HP_TcpAgent pAgent, DWORD dwKeepAliveTime)
+{
+	C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetKeepAliveTime(dwKeepAliveTime);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpAgent_SetKeepAliveInterval(HP_TcpAgent pAgent, DWORD dwKeepAliveInterval)
+{
+	C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetKeepAliveInterval(dwKeepAliveInterval);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpAgent_SetNoDelay(HP_TcpAgent pAgent, BOOL bNoDelay)
+{
+	C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetNoDelay(bNoDelay);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetSyncConnectTimeout(HP_TcpAgent pAgent)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->GetSyncConnectTimeout();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetSocketBufferSize(HP_TcpAgent pAgent)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->GetSocketBufferSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetKeepAliveTime(HP_TcpAgent pAgent)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->GetKeepAliveTime();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetKeepAliveInterval(HP_TcpAgent pAgent)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->GetKeepAliveInterval();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_TcpAgent_IsNoDelay(HP_TcpAgent pAgent)
+{
+	return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->IsNoDelay();
+}
+
+/******************************************************************************/
+/***************************** Client 组件操作方法 *****************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_Start(HP_Client pClient, LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->Start(lpszRemoteAddress, usPort, bAsyncConnect);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_StartWithBindAddress(HP_Client pClient, LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->Start(lpszRemoteAddress, usPort, bAsyncConnect, lpszBindAddress);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_StartWithBindAddressAndLocalPort(HP_Client pClient, LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->Start(lpszRemoteAddress, usPort, bAsyncConnect, lpszBindAddress, usLocalPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_Stop(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->Stop();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_Send(HP_Client pClient, const BYTE* pBuffer, int iLength)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->Send(pBuffer, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_SendPart(HP_Client pClient, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->Send(pBuffer, iLength, iOffset);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_SendPackets(HP_Client pClient, const WSABUF pBuffers[], int iCount)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->SendPackets(pBuffers, iCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_PauseReceive(HP_Client pClient, BOOL bPause)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->PauseReceive(bPause);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_Wait(HP_Client pClient, DWORD dwMilliseconds)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->Wait(dwMilliseconds);
+}
+
+/******************************************************************************/
+/***************************** Client 属性访问方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_Client_SetExtra(HP_Client pClient, PVOID pExtra)
+{
+	C_HP_Object::ToSecond<IClient>(pClient)->SetExtra(pExtra);
+}
+
+HPSOCKET_API PVOID __HP_CALL HP_Client_GetExtra(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetExtra();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_IsSecure(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->IsSecure();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_HasStarted(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->HasStarted();
+}
+
+HPSOCKET_API En_HP_ServiceState	__HP_CALL HP_Client_GetState(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetState();
+}
+
+HPSOCKET_API En_HP_SocketError	__HP_CALL HP_Client_GetLastError(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetLastError();
+}
+
+HPSOCKET_API LPCTSTR __HP_CALL HP_Client_GetLastErrorDesc(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetLastErrorDesc();
+}
+
+HPSOCKET_API HP_CONNID __HP_CALL HP_Client_GetConnectionID(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetConnectionID();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_GetLocalAddress(HP_Client pClient, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetLocalAddress(lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_GetRemoteHost(HP_Client pClient, TCHAR lpszHost[], int* piHostLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetRemoteHost(lpszHost, *piHostLen, *pusPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_GetPendingDataLength(HP_Client pClient, int* piPending)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetPendingDataLength(*piPending);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_IsPauseReceive(HP_Client pClient, BOOL* pbPaused)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->IsPauseReceive(*pbPaused);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Client_IsConnected(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->IsConnected();
+}
+
+HPSOCKET_API void __HP_CALL HP_Client_SetReuseAddressPolicy(HP_Client pClient, En_HP_ReuseAddressPolicy enReusePolicy)
+{
+	C_HP_Object::ToSecond<IClient>(pClient)->SetReuseAddressPolicy(enReusePolicy);
+}
+
+HPSOCKET_API void __HP_CALL HP_Client_SetFreeBufferPoolSize(HP_Client pClient, DWORD dwFreeBufferPoolSize)
+{
+	C_HP_Object::ToSecond<IClient>(pClient)->SetFreeBufferPoolSize(dwFreeBufferPoolSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_Client_SetFreeBufferPoolHold(HP_Client pClient, DWORD dwFreeBufferPoolHold)
+{
+	C_HP_Object::ToSecond<IClient>(pClient)->SetFreeBufferPoolHold(dwFreeBufferPoolHold);
+}
+
+HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_Client_GetReuseAddressPolicy(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetReuseAddressPolicy();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Client_GetFreeBufferPoolSize(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetFreeBufferPoolSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_Client_GetFreeBufferPoolHold(HP_Client pClient)
+{
+	return C_HP_Object::ToSecond<IClient>(pClient)->GetFreeBufferPoolHold();
+}
+
+/**********************************************************************************/
+/******************************* TCP Client 操作方法 *******************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_TcpClient_SendSmallFile(HP_Client pClient, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->SendSmallFile(lpszFileName, pHead, pTail);
+}
+
+/**********************************************************************************/
+/***************************** TCP Client 属性访问方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_TcpClient_SetSyncConnectTimeout(HP_TcpClient pClient, DWORD dwSyncConnectTimeout)
+{
+	C_HP_Object::ToSecond<ITcpClient>(pClient)->SetSyncConnectTimeout(dwSyncConnectTimeout);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpClient_SetSocketBufferSize(HP_TcpClient pClient, DWORD dwSocketBufferSize)
+{
+	C_HP_Object::ToSecond<ITcpClient>(pClient)->SetSocketBufferSize(dwSocketBufferSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpClient_SetKeepAliveTime(HP_TcpClient pClient, DWORD dwKeepAliveTime)
+{
+	C_HP_Object::ToSecond<ITcpClient>(pClient)->SetKeepAliveTime(dwKeepAliveTime);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpClient_SetKeepAliveInterval(HP_TcpClient pClient, DWORD dwKeepAliveInterval)
+{
+	C_HP_Object::ToSecond<ITcpClient>(pClient)->SetKeepAliveInterval(dwKeepAliveInterval);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpClient_SetNoDelay(HP_TcpClient pClient, BOOL bNoDelay)
+{
+	C_HP_Object::ToSecond<ITcpClient>(pClient)->SetNoDelay(bNoDelay);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetSyncConnectTimeout(HP_TcpClient pClient)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->GetSyncConnectTimeout();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetSocketBufferSize(HP_TcpClient pClient)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->GetSocketBufferSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetKeepAliveTime(HP_TcpClient pClient)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->GetKeepAliveTime();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetKeepAliveInterval(HP_TcpClient pClient)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->GetKeepAliveInterval();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_TcpClient_IsNoDelay(HP_TcpClient pClient)
+{
+	return C_HP_Object::ToSecond<ITcpClient>(pClient)->IsNoDelay();
+}
+
+#ifdef _UDP_SUPPORT
+
+/**********************************************************************************/
+/***************************** UDP Client 属性访问方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_UdpClient_SetMaxDatagramSize(HP_UdpClient pClient, DWORD dwMaxDatagramSize)
+{
+	C_HP_Object::ToSecond<IUdpClient>(pClient)->SetMaxDatagramSize(dwMaxDatagramSize);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpClient_GetMaxDatagramSize(HP_UdpClient pClient)
+{
+	return C_HP_Object::ToSecond<IUdpClient>(pClient)->GetMaxDatagramSize();
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpClient_SetDetectAttempts(HP_UdpClient pClient, DWORD dwDetectAttempts)
+{
+	C_HP_Object::ToSecond<IUdpClient>(pClient)->SetDetectAttempts(dwDetectAttempts);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpClient_SetDetectInterval(HP_UdpClient pClient, DWORD dwDetectInterval)
+{
+	C_HP_Object::ToSecond<IUdpClient>(pClient)->SetDetectInterval(dwDetectInterval);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpClient_GetDetectAttempts(HP_UdpClient pClient)
+{
+	return C_HP_Object::ToSecond<IUdpClient>(pClient)->GetDetectAttempts();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpClient_GetDetectInterval(HP_UdpClient pClient)
+{
+	return C_HP_Object::ToSecond<IUdpClient>(pClient)->GetDetectInterval();
+}
+
+/**********************************************************************************/
+/*************************** UDP ARQ Client 属性访问方法 ***************************/
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetNoDelay(HP_UdpArqClient pClient, BOOL bNoDelay)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetNoDelay(bNoDelay);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetTurnoffCongestCtrl(HP_UdpArqClient pClient, BOOL bTurnOff)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetTurnoffCongestCtrl(bTurnOff);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetFlushInterval(HP_UdpArqClient pClient, DWORD dwFlushInterval)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetFlushInterval(dwFlushInterval);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetResendByAcks(HP_UdpArqClient pClient, DWORD dwResendByAcks)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetResendByAcks(dwResendByAcks);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetSendWndSize(HP_UdpArqClient pClient, DWORD dwSendWndSize)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetSendWndSize(dwSendWndSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetRecvWndSize(HP_UdpArqClient pClient, DWORD dwRecvWndSize)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetRecvWndSize(dwRecvWndSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetMinRto(HP_UdpArqClient pClient, DWORD dwMinRto)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetMinRto(dwMinRto);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetFastLimit(HP_UdpArqClient pClient, DWORD dwFastLimit)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetFastLimit(dwFastLimit);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetMaxTransUnit(HP_UdpArqClient pClient, DWORD dwMaxTransUnit)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetMaxTransUnit(dwMaxTransUnit);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetMaxMessageSize(HP_UdpArqClient pClient, DWORD dwMaxMessageSize)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetMaxMessageSize(dwMaxMessageSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetHandShakeTimeout(HP_UdpArqClient pClient, DWORD dwHandShakeTimeout)
+{
+	C_HP_Object::ToFirst<IArqClient>(pClient)->SetHandShakeTimeout(dwHandShakeTimeout);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpArqClient_IsNoDelay(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->IsNoDelay();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpArqClient_IsTurnoffCongestCtrl(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->IsTurnoffCongestCtrl();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetFlushInterval(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetFlushInterval();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetResendByAcks(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetResendByAcks();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetSendWndSize(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetSendWndSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetRecvWndSize(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetRecvWndSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetMinRto(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetMinRto();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetFastLimit(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetFastLimit();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetMaxTransUnit(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetMaxTransUnit();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetMaxMessageSize(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetMaxMessageSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetHandShakeTimeout(HP_UdpArqClient pClient)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetHandShakeTimeout();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpArqClient_GetWaitingSendMessageCount(HP_UdpArqClient pClient, int* piCount)
+{
+	return C_HP_Object::ToFirst<IArqClient>(pClient)->GetWaitingSendMessageCount(*piCount);
+}
+
+/**********************************************************************************/
+/****************************** UDP Cast 属性访问方法 ******************************/
+
+HPSOCKET_API void __HP_CALL HP_UdpCast_SetMaxDatagramSize(HP_UdpCast pCast, DWORD dwMaxDatagramSize)
+{
+	C_HP_Object::ToSecond<IUdpCast>(pCast)->SetMaxDatagramSize(dwMaxDatagramSize);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpCast_GetMaxDatagramSize(HP_UdpCast pCast)
+{
+	return C_HP_Object::ToSecond<IUdpCast>(pCast)->GetMaxDatagramSize();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpCast_GetRemoteAddress(HP_UdpCast pCast, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IUdpCast>(pCast)->GetRemoteAddress(lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpCast_SetCastMode(HP_UdpCast pCast, En_HP_CastMode enCastMode)
+{
+	C_HP_Object::ToSecond<IUdpCast>(pCast)->SetCastMode(enCastMode);
+}
+
+HPSOCKET_API En_HP_CastMode __HP_CALL HP_UdpCast_GetCastMode(HP_UdpCast pCast)
+{
+	return C_HP_Object::ToSecond<IUdpCast>(pCast)->GetCastMode();
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpCast_SetMultiCastTtl(HP_UdpCast pCast, int iMCTtl)
+{
+	C_HP_Object::ToSecond<IUdpCast>(pCast)->SetMultiCastTtl(iMCTtl);
+}
+
+HPSOCKET_API int __HP_CALL HP_UdpCast_GetMultiCastTtl(HP_UdpCast pCast)
+{
+	return C_HP_Object::ToSecond<IUdpCast>(pCast)->GetMultiCastTtl();
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpCast_SetMultiCastLoop(HP_UdpCast pCast, BOOL bMCLoop)
+{
+	C_HP_Object::ToSecond<IUdpCast>(pCast)->SetMultiCastLoop(bMCLoop);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpCast_IsMultiCastLoop(HP_UdpCast pCast)
+{
+	return C_HP_Object::ToSecond<IUdpCast>(pCast)->IsMultiCastLoop();
+}
+
+/**********************************************************************************/
+/****************************** UDP Node 组件操作方法 ******************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Start(HP_UdpNode pNode, LPCTSTR lpszBindAddress, USHORT usPort)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->Start(lpszBindAddress, usPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_StartWithCast(HP_UdpNode pNode, LPCTSTR lpszBindAddress, USHORT usPort, En_HP_CastMode enCastMode, LPCTSTR lpszCastAddress)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->Start(lpszBindAddress, usPort, enCastMode, lpszCastAddress);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Stop(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->Stop();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Wait(HP_UdpNode pNode, DWORD dwMilliseconds)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->Wait(dwMilliseconds);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Send(HP_UdpNode pNode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->Send(lpszRemoteAddress, usRemotePort, pBuffer, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendPart(HP_UdpNode pNode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->Send(lpszRemoteAddress, usRemotePort, pBuffer, iLength, iOffset);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendPackets(HP_UdpNode pNode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const WSABUF pBuffers[], int iCount)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->SendPackets(lpszRemoteAddress, usRemotePort, pBuffers, iCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendCast(HP_UdpNode pNode, const BYTE* pBuffer, int iLength)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->SendCast(pBuffer, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendCastPart(HP_UdpNode pNode, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->SendCast(pBuffer, iLength, iOffset);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendCastPackets(HP_UdpNode pNode, const WSABUF pBuffers[], int iCount)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->SendCastPackets(pBuffers, iCount);
+}
+
+/**********************************************************************************/
+/****************************** UDP Node 属性访问方法 ******************************/
+
+HPSOCKET_API void __HP_CALL HP_UdpNode_SetExtra(HP_UdpNode pNode, PVOID pExtra)
+{
+	C_HP_Object::ToSecond<IUdpNode>(pNode)->SetExtra(pExtra);
+}
+
+HPSOCKET_API PVOID __HP_CALL HP_UdpNode_GetExtra(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetExtra();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_HasStarted(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->HasStarted();
+}
+
+HPSOCKET_API En_HP_ServiceState __HP_CALL HP_UdpNode_GetState(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetState();
+}
+
+HPSOCKET_API En_HP_SocketError __HP_CALL HP_UdpNode_GetLastError(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetLastError();
+}
+
+HPSOCKET_API LPCTSTR __HP_CALL HP_UdpNode_GetLastErrorDesc(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetLastErrorDesc();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_GetLocalAddress(HP_UdpNode pNode, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetLocalAddress(lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_GetCastAddress(HP_UdpNode pNode, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetCastAddress(lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API En_HP_CastMode __HP_CALL HP_UdpNode_GetCastMode(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetCastMode();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_GetPendingDataLength(HP_UdpNode pNode, int* piPending)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetPendingDataLength(*piPending);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpNode_SetMaxDatagramSize(HP_UdpNode pNode, DWORD dwMaxDatagramSize)
+{
+	C_HP_Object::ToSecond<IUdpNode>(pNode)->SetMaxDatagramSize(dwMaxDatagramSize);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetMaxDatagramSize(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetMaxDatagramSize();
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpNode_SetMultiCastTtl(HP_UdpNode pNode, int iMCTtl)
+{
+	C_HP_Object::ToSecond<IUdpNode>(pNode)->SetMultiCastTtl(iMCTtl);
+}
+
+HPSOCKET_API int __HP_CALL HP_UdpNode_GetMultiCastTtl(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetMultiCastTtl();
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpNode_SetMultiCastLoop(HP_UdpNode pNode, BOOL bMCLoop)
+{
+	C_HP_Object::ToSecond<IUdpNode>(pNode)->SetMultiCastLoop(bMCLoop);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_UdpNode_IsMultiCastLoop(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->IsMultiCastLoop();
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpNode_SetReuseAddressPolicy(HP_UdpNode pNode, En_HP_ReuseAddressPolicy enReusePolicy)
+{
+	C_HP_Object::ToSecond<IUdpNode>(pNode)->SetReuseAddressPolicy(enReusePolicy);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpNode_SetWorkerThreadCount(HP_UdpNode pNode, DWORD dwWorkerThreadCount)
+{
+	C_HP_Object::ToSecond<IUdpNode>(pNode)->SetWorkerThreadCount(dwWorkerThreadCount);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpNode_SetPostReceiveCount(HP_UdpNode pNode, DWORD dwPostReceiveCount)
+{
+	C_HP_Object::ToSecond<IUdpNode>(pNode)->SetPostReceiveCount(dwPostReceiveCount);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpNode_SetFreeBufferPoolSize(HP_UdpNode pNode, DWORD dwFreeBufferPoolSize)
+{
+	C_HP_Object::ToSecond<IUdpNode>(pNode)->SetFreeBufferPoolSize(dwFreeBufferPoolSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_UdpNode_SetFreeBufferPoolHold(HP_UdpNode pNode, DWORD dwFreeBufferPoolHold)
+{
+	C_HP_Object::ToSecond<IUdpNode>(pNode)->SetFreeBufferPoolHold(dwFreeBufferPoolHold);
+}
+
+HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_UdpNode_GetReuseAddressPolicy(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetReuseAddressPolicy();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetWorkerThreadCount(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetWorkerThreadCount();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetPostReceiveCount(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetPostReceiveCount();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetFreeBufferPoolSize(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetFreeBufferPoolSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetFreeBufferPoolHold(HP_UdpNode pNode)
+{
+	return C_HP_Object::ToSecond<IUdpNode>(pNode)->GetFreeBufferPoolHold();
+}
+
+#endif
+
+/***************************************************************************************/
+/***************************** TCP Pull Server 组件操作方法 *****************************/
+
+HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullServer_Fetch(HP_TcpPullServer pServer, HP_CONNID dwConnID, BYTE* pData, int iLength)
+{
+	return C_HP_Object::ToFirst<IPullSocket>(pServer)->Fetch(dwConnID, pData, iLength);
+}
+
+HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullServer_Peek(HP_TcpPullServer pServer, HP_CONNID dwConnID, BYTE* pData, int iLength)
+{
+	return C_HP_Object::ToFirst<IPullSocket>(pServer)->Peek(dwConnID, pData, iLength);
+}
+
+/***************************************************************************************/
+/***************************** TCP Pull Server 属性访问方法 *****************************/
+
+/***************************************************************************************/
+/***************************** TCP Pull Agent 组件操作方法 *****************************/
+
+HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullAgent_Fetch(HP_TcpPullAgent pAgent, HP_CONNID dwConnID, BYTE* pData, int iLength)
+{
+	return C_HP_Object::ToFirst<IPullSocket>(pAgent)->Fetch(dwConnID, pData, iLength);
+}
+
+HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullAgent_Peek(HP_TcpPullAgent pAgent, HP_CONNID dwConnID, BYTE* pData, int iLength)
+{
+	return C_HP_Object::ToFirst<IPullSocket>(pAgent)->Peek(dwConnID, pData, iLength);
+}
+
+/***************************************************************************************/
+/***************************** TCP Pull Agent 属性访问方法 *****************************/
+
+/***************************************************************************************/
+/***************************** TCP Pull Client 组件操作方法 *****************************/
+
+HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullClient_Fetch(HP_TcpPullClient pClient, BYTE* pData, int iLength)
+{
+	return C_HP_Object::ToFirst<IPullClient>(pClient)->Fetch(pData, iLength);
+}
+
+HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullClient_Peek(HP_TcpPullClient pClient, BYTE* pData, int iLength)
+{
+	return C_HP_Object::ToFirst<IPullClient>(pClient)->Peek(pData, iLength);
+}
+
+/***************************************************************************************/
+/***************************** TCP Pull Client 属性访问方法 *****************************/
+
+/***************************************************************************************/
+/***************************** TCP Pack Server 组件操作方法 *****************************/
+
+/***************************************************************************************/
+/***************************** TCP Pack Server 属性访问方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_TcpPackServer_SetMaxPackSize(HP_TcpPackServer pServer, DWORD dwMaxPackSize)
+{
+	C_HP_Object::ToFirst<IPackSocket>(pServer)->SetMaxPackSize(dwMaxPackSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpPackServer_SetPackHeaderFlag(HP_TcpPackServer pServer, USHORT usPackHeaderFlag)
+{
+	C_HP_Object::ToFirst<IPackSocket>(pServer)->SetPackHeaderFlag(usPackHeaderFlag);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpPackServer_GetMaxPackSize(HP_TcpPackServer pServer)
+{
+	return C_HP_Object::ToFirst<IPackSocket>(pServer)->GetMaxPackSize();
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_TcpPackServer_GetPackHeaderFlag(HP_TcpPackServer pServer)
+{
+	return C_HP_Object::ToFirst<IPackSocket>(pServer)->GetPackHeaderFlag();
+}
+
+/***************************************************************************************/
+/***************************** TCP Pack Agent 组件操作方法 *****************************/
+
+/***************************************************************************************/
+/***************************** TCP Pack Agent 属性访问方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_TcpPackAgent_SetMaxPackSize(HP_TcpPackAgent pAgent, DWORD dwMaxPackSize)
+{
+	C_HP_Object::ToFirst<IPackSocket>(pAgent)->SetMaxPackSize(dwMaxPackSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpPackAgent_SetPackHeaderFlag(HP_TcpPackAgent pAgent, USHORT usPackHeaderFlag)
+{
+	C_HP_Object::ToFirst<IPackSocket>(pAgent)->SetPackHeaderFlag(usPackHeaderFlag);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpPackAgent_GetMaxPackSize(HP_TcpPackAgent pAgent)
+{
+	return C_HP_Object::ToFirst<IPackSocket>(pAgent)->GetMaxPackSize();
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_TcpPackAgent_GetPackHeaderFlag(HP_TcpPackAgent pAgent)
+{
+	return C_HP_Object::ToFirst<IPackSocket>(pAgent)->GetPackHeaderFlag();
+}
+
+/***************************************************************************************/
+/***************************** TCP Pack Client 组件操作方法 *****************************/
+
+/***************************************************************************************/
+/***************************** TCP Pack Client 属性访问方法 *****************************/
+
+HPSOCKET_API void __HP_CALL HP_TcpPackClient_SetMaxPackSize(HP_TcpPackClient pClient, DWORD dwMaxPackSize)
+{
+	C_HP_Object::ToFirst<IPackClient>(pClient)->SetMaxPackSize(dwMaxPackSize);
+}
+
+HPSOCKET_API void __HP_CALL HP_TcpPackClient_SetPackHeaderFlag(HP_TcpPackClient pClient, USHORT usPackHeaderFlag)
+{
+	C_HP_Object::ToFirst<IPackClient>(pClient)->SetPackHeaderFlag(usPackHeaderFlag);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_TcpPackClient_GetMaxPackSize(HP_TcpPackClient pClient)
+{
+	return C_HP_Object::ToFirst<IPackClient>(pClient)->GetMaxPackSize();
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_TcpPackClient_GetPackHeaderFlag(HP_TcpPackClient pClient)
+{
+	return C_HP_Object::ToFirst<IPackClient>(pClient)->GetPackHeaderFlag();
+}
+
+/*****************************************************************************************************************************************************/
+/*************************************************************** Global Function Exports *************************************************************/
+/*****************************************************************************************************************************************************/
+
+HPSOCKET_API DWORD __HP_CALL HP_GetHPSocketVersion()
+{
+	return ::GetHPSocketVersion();
+}
+
+HPSOCKET_API LPCTSTR __HP_CALL HP_GetSocketErrorDesc(En_HP_SocketError enCode)
+{
+	return ::GetSocketErrorDesc(enCode);
+}
+
+HPSOCKET_API DWORD __HP_CALL SYS_GetLastError()
+{
+	return ::GetLastError();
+}
+
+HPSOCKET_API LPCSTR __HP_CALL SYS_GetLastErrorStr()
+{
+	return ::GetLastErrorStr();
+}
+
+HPSOCKET_API int __HP_CALL SYS_SetSocketOption(SOCKET sock, int level, int name, LPVOID val, int len)
+{
+	return ::SSO_SetSocketOption(sock, level, name, val, len);
+}
+
+HPSOCKET_API int __HP_CALL SYS_GetSocketOption(SOCKET sock, int level, int name, LPVOID val, int* len)
+{
+	return ::SSO_GetSocketOption(sock, level, name, val, len);
+}
+
+HPSOCKET_API int __HP_CALL SYS_IoctlSocket(SOCKET sock, long cmd, ULONG* arg)
+{
+	return ::SSO_IoctlSocket(sock, cmd, arg);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_fcntl_SETFL(FD fd, INT fl, BOOL bSet)
+{
+	return ::fcntl_SETFL(fd, fl, bSet);
+}
+
+HPSOCKET_API int __HP_CALL SYS_SSO_NoBlock(SOCKET sock, BOOL bNoBlock)
+{
+	return ::SSO_NoBlock(sock, bNoBlock);
+}
+
+HPSOCKET_API int __HP_CALL SYS_SSO_NoDelay(SOCKET sock, BOOL bNoDelay)
+{
+	return ::SSO_NoDelay(sock, bNoDelay);
+}
+
+HPSOCKET_API int __HP_CALL SYS_SSO_DontLinger(SOCKET sock, BOOL bDont)
+{
+	return ::SSO_DontLinger(sock, bDont);
+}
+
+HPSOCKET_API int __HP_CALL SYS_SSO_Linger(SOCKET sock, USHORT l_onoff, USHORT l_linger)
+{
+	return ::SSO_Linger(sock, l_onoff, l_linger);
+}
+
+HPSOCKET_API int __HP_CALL SYS_SSO_RecvBuffSize(SOCKET sock, int size)
+{
+	return ::SSO_RecvBuffSize(sock, size);
+}
+
+HPSOCKET_API int __HP_CALL SYS_SSO_SendBuffSize(SOCKET sock, int size)
+{
+	return ::SSO_SendBuffSize(sock, size);
+}
+
+HPSOCKET_API int __HP_CALL SYS_SSO_RecvTimeOut(SOCKET sock, int ms)
+{
+	return ::SSO_RecvTimeOut(sock, ms);
+}
+
+HPSOCKET_API int __HP_CALL SYS_SSO_SendTimeOut(SOCKET sock, int ms)
+{
+	return ::SSO_SendTimeOut(sock, ms);
+}
+
+HPSOCKET_API int __HP_CALL SYS_SSO_ReuseAddress(SOCKET sock, En_HP_ReuseAddressPolicy opt)
+{
+	return ::SSO_ReuseAddress(sock, opt);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_GetSocketLocalAddress(SOCKET socket, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return ::GetSocketLocalAddress(socket, lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_GetSocketRemoteAddress(SOCKET socket, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort)
+{
+	return ::GetSocketRemoteAddress(socket, lpszAddress, *piAddressLen, *pusPort);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_EnumHostIPAddresses(LPCTSTR lpszHost, En_HP_IPAddrType enType, HP_LPTIPAddr** lpppIPAddr, int* piIPAddrCount)
+{
+	return ::EnumHostIPAddresses(lpszHost, enType, lpppIPAddr, *piIPAddrCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_FreeHostIPAddresses(HP_LPTIPAddr* lppIPAddr)
+{
+	return ::FreeHostIPAddresses(lppIPAddr);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_IsIPAddress(LPCTSTR lpszAddress, En_HP_IPAddrType* penType)
+{
+	return ::IsIPAddress(lpszAddress, penType);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_GetIPAddress(LPCTSTR lpszHost, TCHAR lpszIP[], int* piIPLenth, En_HP_IPAddrType* penType)
+{
+	return ::GetIPAddress(lpszHost, lpszIP, *piIPLenth, *penType);
+}
+
+HPSOCKET_API ULONGLONG __HP_CALL SYS_NToH64(ULONGLONG value)
+{
+	return ::NToH64(value);
+}
+
+HPSOCKET_API ULONGLONG __HP_CALL SYS_HToN64(ULONGLONG value)
+{
+	return ::HToN64(value);
+}
+
+HPSOCKET_API USHORT __HP_CALL SYS_SwapEndian16(USHORT value)
+{
+	return ENDIAN_SWAP_16(value);
+}
+
+HPSOCKET_API DWORD __HP_CALL SYS_SwapEndian32(DWORD value)
+{
+	return ENDIAN_SWAP_32(value);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_IsLittleEndian()
+{
+	return ::IsLittleEndian();
+}
+
+HPSOCKET_API LPBYTE __HP_CALL SYS_Malloc(int size)
+{
+	return MALLOC(BYTE, size);
+}
+
+HPSOCKET_API LPBYTE __HP_CALL SYS_Realloc(LPBYTE p, int size)
+{
+	return REALLOC(BYTE, p, size);
+}
+
+HPSOCKET_API VOID __HP_CALL SYS_Free(LPBYTE p)
+{
+	FREE(p);
+}
+
+HPSOCKET_API LPVOID __HP_CALL SYS_Calloc(int number, int size)
+{
+	return CALLOC(number, size);
+}
+
+HPSOCKET_API DWORD __HP_CALL SYS_GuessBase64EncodeBound(DWORD dwSrcLen)
+{
+	return ::GuessBase64EncodeBound(dwSrcLen);
+}
+
+HPSOCKET_API DWORD __HP_CALL SYS_GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	return ::GuessBase64DecodeBound(lpszSrc, dwSrcLen);
+}
+
+HPSOCKET_API int __HP_CALL SYS_Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::Base64Encode(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+HPSOCKET_API int __HP_CALL SYS_Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::Base64Decode(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+HPSOCKET_API DWORD __HP_CALL SYS_GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	return ::GuessUrlEncodeBound(lpszSrc, dwSrcLen);
+}
+
+HPSOCKET_API DWORD __HP_CALL SYS_GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	return ::GuessUrlDecodeBound(lpszSrc, dwSrcLen);
+}
+
+HPSOCKET_API int __HP_CALL SYS_UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::UrlEncode(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+HPSOCKET_API int __HP_CALL SYS_UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::UrlDecode(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+#ifdef _ZLIB_SUPPORT
+
+HPSOCKET_API int __HP_CALL SYS_Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::Compress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+HPSOCKET_API int __HP_CALL SYS_CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen, int iLevel, int iMethod, int iWindowBits, int iMemLevel, int iStrategy)
+{
+	return ::CompressEx(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy);
+}
+
+HPSOCKET_API int __HP_CALL SYS_Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::Uncompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+HPSOCKET_API int __HP_CALL SYS_UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen, int iWindowBits)
+{
+	return ::UncompressEx(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen, iWindowBits);
+}
+
+HPSOCKET_API DWORD __HP_CALL SYS_GuessCompressBound(DWORD dwSrcLen, BOOL bGZip)
+{
+	return ::GuessCompressBound(dwSrcLen, bGZip);
+}
+
+HPSOCKET_API int __HP_CALL SYS_GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::GZipCompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+HPSOCKET_API int __HP_CALL SYS_GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::GZipUncompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+HPSOCKET_API DWORD __HP_CALL SYS_GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	return ::GZipGuessUncompressBound(lpszSrc, dwSrcLen);
+}
+
+#endif
+
+#ifdef _BROTLI_SUPPORT
+
+HPSOCKET_API int __HP_CALL SYS_BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::BrotliCompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+HPSOCKET_API int __HP_CALL SYS_BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen, int iQuality, int iWindow, int iMode)
+{
+	return ::BrotliCompressEx(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen, iQuality, iWindow , iMode);
+}
+
+HPSOCKET_API int __HP_CALL SYS_BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen)
+{
+	return ::BrotliUncompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen);
+}
+
+HPSOCKET_API DWORD __HP_CALL SYS_BrotliGuessCompressBound(DWORD dwSrcLen)
+{
+	return ::BrotliGuessCompressBound(dwSrcLen);
+}
+
+#endif
+
+#ifdef _ICONV_SUPPORT
+
+HPSOCKET_API BOOL __HP_CALL SYS_CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int* piOutBufLen)
+{
+	return ::CharsetConvert(lpszFromCharset, lpszToCharset, lpszInBuf, iInBufLen, lpszOutBuf, *piOutBufLen);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int* piDestLength)
+{
+	return ::GbkToUnicodeEx(szSrc, iSrcLength, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int* piDestLength)
+{
+	return ::UnicodeToGbkEx(szSrc, iSrcLength, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int* piDestLength)
+{
+	return ::Utf8ToUnicodeEx(szSrc, iSrcLength, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int* piDestLength)
+{
+	return ::UnicodeToUtf8Ex(szSrc, iSrcLength, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int* piDestLength)
+{
+	return ::GbkToUtf8Ex(szSrc, iSrcLength, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int* piDestLength)
+{
+	return ::Utf8ToGbkEx(szSrc, iSrcLength, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_GbkToUnicode(const char szSrc[], WCHAR szDest[], int* piDestLength)
+{
+	return ::GbkToUnicode(szSrc, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToGbk(const WCHAR szSrc[], char szDest[], int* piDestLength)
+{
+	return ::UnicodeToGbk(szSrc, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int* piDestLength)
+{
+	return ::Utf8ToUnicode(szSrc, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int* piDestLength)
+{
+	return ::UnicodeToUtf8(szSrc, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_GbkToUtf8(const char szSrc[], char szDest[], int* piDestLength)
+{
+	return ::GbkToUtf8(szSrc, szDest, *piDestLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToGbk(const char szSrc[], char szDest[], int* piDestLength)
+{
+	return ::Utf8ToGbk(szSrc, szDest, *piDestLength);
+}
+
+#endif
+
+/*****************************************************************************************************************************************************/
+/******************************************************************** HTTP Exports *******************************************************************/
+/*****************************************************************************************************************************************************/
+
+#ifdef _HTTP_SUPPORT
+
+typedef C_HP_ObjectT<CHttpServer, IHttpServerListener, sizeof(IComplexHttpResponder)>	C_HP_HttpServer;
+typedef C_HP_ObjectT<CHttpAgent, IHttpAgentListener, sizeof(IComplexHttpRequester)>		C_HP_HttpAgent;
+typedef C_HP_ObjectT<CHttpClient, IHttpClientListener, sizeof(IHttpRequester)>			C_HP_HttpClient;
+typedef C_HP_ObjectT<CHttpSyncClient, IHttpClientListener, sizeof(IHttpSyncRequester)>	C_HP_HttpSyncClient;
+
+/****************************************************/
+/***************** HTTP 对象创建函数 *****************/
+
+HPSOCKET_API HP_HttpServer __HP_CALL Create_HP_HttpServer(HP_HttpServerListener pListener)
+{
+	return (HP_HttpServer)(new C_HP_HttpServer((IHttpServerListener*)pListener));
+}
+
+HPSOCKET_API HP_HttpAgent __HP_CALL Create_HP_HttpAgent(HP_HttpAgentListener pListener)
+{
+	return (HP_HttpAgent)(new C_HP_HttpAgent((IHttpAgentListener*)pListener));
+}
+
+HPSOCKET_API HP_HttpClient __HP_CALL Create_HP_HttpClient(HP_HttpClientListener pListener)
+{
+	return (HP_HttpClient)(new C_HP_HttpClient((IHttpClientListener*)pListener));
+}
+
+HPSOCKET_API HP_HttpSyncClient __HP_CALL Create_HP_HttpSyncClient(HP_HttpClientListener pListener)
+{
+	return (HP_HttpSyncClient)(new C_HP_HttpSyncClient((IHttpClientListener*)pListener));
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpServer(HP_HttpServer pServer)
+{
+	delete (C_HP_HttpServer*)pServer;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpAgent(HP_HttpAgent pAgent)
+{
+	delete (C_HP_HttpAgent*)pAgent;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpClient(HP_HttpClient pClient)
+{
+	delete (C_HP_HttpClient*)pClient;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpSyncClient(HP_HttpSyncClient pClient)
+{
+	delete (C_HP_HttpSyncClient*)pClient;
+}
+
+HPSOCKET_API HP_HttpServerListener __HP_CALL Create_HP_HttpServerListener()
+{
+	return (HP_HttpServerListener)(new C_HP_HttpServerListener);
+}
+
+HPSOCKET_API HP_HttpAgentListener __HP_CALL Create_HP_HttpAgentListener()
+{
+	return (HP_HttpAgentListener)(new C_HP_HttpAgentListener);
+}
+
+HPSOCKET_API HP_HttpClientListener __HP_CALL Create_HP_HttpClientListener()
+{
+	return (HP_HttpClientListener)(new C_HP_HttpClientListener);
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpServerListener(HP_HttpServerListener pListener)
+{
+	delete (C_HP_HttpServerListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpAgentListener(HP_HttpAgentListener pListener)
+{
+	delete (C_HP_HttpAgentListener*)pListener;
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_HttpClientListener(HP_HttpClientListener pListener)
+{
+	delete (C_HP_HttpClientListener*)pListener;
+}
+
+/**********************************************************************************/
+/*************************** HTTP Server 回调函数设置方法 **************************/
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnMessageBegin(HP_HttpServerListener pListener, HP_FN_HttpServer_OnMessageBegin fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnMessageBegin = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnRequestLine(HP_HttpServerListener pListener, HP_FN_HttpServer_OnRequestLine fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnRequestLine = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnHeader(HP_HttpServerListener pListener, HP_FN_HttpServer_OnHeader fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnHeader = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnHeadersComplete(HP_HttpServerListener pListener, HP_FN_HttpServer_OnHeadersComplete fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnHeadersComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnBody(HP_HttpServerListener pListener, HP_FN_HttpServer_OnBody fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnBody = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnChunkHeader(HP_HttpServerListener pListener, HP_FN_HttpServer_OnChunkHeader fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnChunkHeader = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnChunkComplete(HP_HttpServerListener pListener, HP_FN_HttpServer_OnChunkComplete fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnChunkComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnMessageComplete(HP_HttpServerListener pListener, HP_FN_HttpServer_OnMessageComplete fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnMessageComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnUpgrade(HP_HttpServerListener pListener, HP_FN_HttpServer_OnUpgrade fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnUpgrade = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnParseError(HP_HttpServerListener pListener, HP_FN_HttpServer_OnParseError fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnParseError = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnWSMessageHeader(HP_HttpServerListener pListener, HP_FN_HttpServer_OnWSMessageHeader fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnWSMessageHeader = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnWSMessageBody(HP_HttpServerListener pListener, HP_FN_HttpServer_OnWSMessageBody fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnWSMessageBody = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnWSMessageComplete(HP_HttpServerListener pListener, HP_FN_HttpServer_OnWSMessageComplete fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnWSMessageComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnPrepareListen(HP_HttpServerListener pListener, HP_FN_HttpServer_OnPrepareListen fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnPrepareListen = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnAccept(HP_HttpServerListener pListener, HP_FN_HttpServer_OnAccept fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnAccept = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnHandShake(HP_HttpServerListener pListener, HP_FN_HttpServer_OnHandShake fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnHandShake = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnSend(HP_HttpServerListener pListener, HP_FN_HttpServer_OnSend fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnSend = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnReceive(HP_HttpServerListener pListener, HP_FN_HttpServer_OnReceive fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnClose(HP_HttpServerListener pListener, HP_FN_HttpServer_OnClose fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnClose = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnShutdown(HP_HttpServerListener pListener, HP_FN_HttpServer_OnShutdown fn)
+{
+	((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnShutdown = fn;
+}
+
+/**********************************************************************************/
+/**************************** HTTP Agent 回调函数设置方法 **************************/
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnMessageBegin(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnMessageBegin fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnMessageBegin = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnStatusLine(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnStatusLine fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnStatusLine = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnHeader(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnHeader fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnHeader = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnHeadersComplete(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnHeadersComplete fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnHeadersComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnBody(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnBody fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnBody = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnChunkHeader(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnChunkHeader fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnChunkHeader = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnChunkComplete(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnChunkComplete fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnChunkComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnMessageComplete(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnMessageComplete fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnMessageComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnUpgrade(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnUpgrade fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnUpgrade = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnParseError(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnParseError fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnParseError = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnWSMessageHeader(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnWSMessageHeader fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnWSMessageHeader = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnWSMessageBody(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnWSMessageBody fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnWSMessageBody = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnWSMessageComplete(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnWSMessageComplete fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnWSMessageComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnPrepareConnect(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnPrepareConnect fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnPrepareConnect = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnConnect(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnConnect fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnConnect = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnHandShake(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnHandShake fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnHandShake = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnReceive(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnReceive fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnSend(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnSend fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnSend = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnClose(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnClose fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnClose = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnShutdown(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnShutdown fn)
+{
+	((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnShutdown = fn;
+}
+
+/**********************************************************************************/
+/*************************** HTTP Client 回调函数设置方法 **************************/
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnMessageBegin(HP_HttpClientListener pListener, HP_FN_HttpClient_OnMessageBegin fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnMessageBegin = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnStatusLine(HP_HttpClientListener pListener, HP_FN_HttpClient_OnStatusLine fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnStatusLine = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnHeader(HP_HttpClientListener pListener, HP_FN_HttpClient_OnHeader fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnHeader = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnHeadersComplete(HP_HttpClientListener pListener, HP_FN_HttpClient_OnHeadersComplete fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnHeadersComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnBody(HP_HttpClientListener pListener, HP_FN_HttpClient_OnBody fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnBody = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnChunkHeader(HP_HttpClientListener pListener, HP_FN_HttpClient_OnChunkHeader fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnChunkHeader = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnChunkComplete(HP_HttpClientListener pListener, HP_FN_HttpClient_OnChunkComplete fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnChunkComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnMessageComplete(HP_HttpClientListener pListener, HP_FN_HttpClient_OnMessageComplete fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnMessageComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnUpgrade(HP_HttpClientListener pListener, HP_FN_HttpClient_OnUpgrade fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnUpgrade = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnParseError(HP_HttpClientListener pListener, HP_FN_HttpClient_OnParseError fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnParseError = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnWSMessageHeader(HP_HttpClientListener pListener, HP_FN_HttpClient_OnWSMessageHeader fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnWSMessageHeader = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnWSMessageBody(HP_HttpClientListener pListener, HP_FN_HttpClient_OnWSMessageBody fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnWSMessageBody = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnWSMessageComplete(HP_HttpClientListener pListener, HP_FN_HttpClient_OnWSMessageComplete fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnWSMessageComplete = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnPrepareConnect(HP_HttpClientListener pListener, HP_FN_HttpClient_OnPrepareConnect fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnPrepareConnect = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnConnect(HP_HttpClientListener pListener, HP_FN_HttpClient_OnConnect fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnConnect = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnHandShake(HP_HttpClientListener pListener, HP_FN_HttpClient_OnHandShake fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnHandShake = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnReceive(HP_HttpClientListener pListener, HP_FN_HttpClient_OnReceive fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnReceive = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnSend(HP_HttpClientListener pListener, HP_FN_HttpClient_OnSend fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnSend = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnClose(HP_HttpClientListener pListener, HP_FN_HttpClient_OnClose fn)
+{
+	((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnClose = fn;
+}
+
+/**************************************************************************/
+/*************************** HTTP Server 操作方法 **************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendResponse(HP_HttpServer pServer, HP_CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pData, int iLength)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->SendResponse(dwConnID, usStatusCode, lpszDesc, lpHeaders, iHeaderCount, pData, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendLocalFile(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszFileName, USHORT usStatusCode, LPCSTR lpszDesc, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->SendLocalFile(dwConnID, lpszFileName, usStatusCode, lpszDesc, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendChunkData(HP_HttpServer pServer, HP_CONNID dwConnID, const BYTE* pData, int iLength, LPCSTR lpszExtensions)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->SendChunkData(dwConnID, pData, iLength, lpszExtensions);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendWSMessage(HP_HttpServer pServer, HP_CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->SendWSMessage(dwConnID, bFinal, iReserved, iOperationCode, pData, iLength, ullBodyLen);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_Release(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->Release(dwConnID);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_StartHttp(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->StartHttp(dwConnID);
+}
+
+/******************************************************************************/
+/*************************** HTTP Server 属性访问方法 **************************/
+
+HPSOCKET_API void __HP_CALL HP_HttpServer_SetReleaseDelay(HP_HttpServer pServer, DWORD dwReleaseDelay)
+{
+	C_HP_Object::ToFirst<IHttpServer>(pServer)->SetReleaseDelay(dwReleaseDelay);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_HttpServer_GetReleaseDelay(HP_HttpServer pServer)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetReleaseDelay();
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_HttpServer_GetUrlFieldSet(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetUrlFieldSet(dwConnID);
+}
+
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetUrlField(HP_HttpServer pServer, HP_CONNID dwConnID, En_HP_HttpUrlField enField)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetUrlField(dwConnID, enField);
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetMethod(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetMethod(dwConnID);
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpServer_SetLocalVersion(HP_HttpServer pServer, En_HP_HttpVersion usVersion)
+{
+	C_HP_Object::ToFirst<IHttpServer>(pServer)->SetLocalVersion(usVersion);
+}
+
+HPSOCKET_API En_HP_HttpVersion __HP_CALL HP_HttpServer_GetLocalVersion(HP_HttpServer pServer)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetLocalVersion();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_IsUpgrade(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->IsUpgrade(dwConnID);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_IsKeepAlive(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->IsKeepAlive(dwConnID);
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_HttpServer_GetVersion(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetVersion(dwConnID);
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetHost(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetHost(dwConnID);
+}
+
+HPSOCKET_API ULONGLONG __HP_CALL HP_HttpServer_GetContentLength(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetContentLength(dwConnID);
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetContentType(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetContentType(dwConnID);
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetContentEncoding(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetContentEncoding(dwConnID);
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetTransferEncoding(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetTransferEncoding(dwConnID);
+}
+
+HPSOCKET_API En_HP_HttpUpgradeType __HP_CALL HP_HttpServer_GetUpgradeType(HP_HttpServer pServer, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetUpgradeType(dwConnID);
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_HttpServer_GetParseErrorCode(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR* lpszErrorDesc)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetParseErrorCode(dwConnID, lpszErrorDesc);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetHeader(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetHeader(dwConnID, lpszName, lpszValue);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetHeaders(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetHeaders(dwConnID, lpszName, lpszValue, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetAllHeaders(HP_HttpServer pServer, HP_CONNID dwConnID, HP_THeader lpHeaders[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetAllHeaders(dwConnID, lpHeaders, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetAllHeaderNames(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetAllHeaderNames(dwConnID, lpszName, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetCookie(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetCookie(dwConnID, lpszName, lpszValue);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetAllCookies(HP_HttpServer pServer, HP_CONNID dwConnID, HP_TCookie lpCookies[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetAllCookies(dwConnID, lpCookies, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetWSMessageState(HP_HttpServer pServer, HP_CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->GetWSMessageState(dwConnID, lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpServer_SetHttpAutoStart(HP_HttpServer pServer, BOOL bAutoStart)
+{
+	C_HP_Object::ToFirst<IHttpServer>(pServer)->SetHttpAutoStart(bAutoStart);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpServer_IsHttpAutoStart(HP_HttpServer pServer)
+{
+	return C_HP_Object::ToFirst<IHttpServer>(pServer)->IsHttpAutoStart();
+}
+
+/**************************************************************************/
+/*************************** HTTP Agent 操作方法 ***************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendRequest(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendRequest(dwConnID, lpszMethod, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendLocalFile(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendLocalFile(dwConnID, lpszFileName, lpszMethod, lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendChunkData(HP_HttpAgent pAgent, HP_CONNID dwConnID, const BYTE* pData, int iLength, LPCSTR lpszExtensions)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendChunkData(dwConnID, pData, iLength, lpszExtensions);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendPost(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendPost(dwConnID, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendPut(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendPut(dwConnID, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendPatch(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendPatch(dwConnID, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendGet(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendGet(dwConnID, lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendDelete(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendDelete(dwConnID, lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendHead(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendHead(dwConnID, lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendTrace(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendTrace(dwConnID, lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendOptions(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendOptions(dwConnID, lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendConnect(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszHost, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendConnect(dwConnID, lpszHost, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendWSMessage(HP_HttpAgent pAgent, HP_CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SendWSMessage(dwConnID, bFinal, iReserved, iOperationCode, lpszMask, pData, iLength, ullBodyLen);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_StartHttp(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->StartHttp(dwConnID);
+}
+
+/******************************************************************************/
+/*************************** HTTP Agent 属性访问方法 ***************************/
+
+HPSOCKET_API USHORT __HP_CALL HP_HttpAgent_GetStatusCode(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetStatusCode(dwConnID);
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpAgent_SetLocalVersion(HP_HttpAgent pAgent, En_HP_HttpVersion usVersion)
+{
+	C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SetLocalVersion(usVersion);
+}
+
+HPSOCKET_API En_HP_HttpVersion __HP_CALL HP_HttpAgent_GetLocalVersion(HP_HttpAgent pAgent)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetLocalVersion();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsUpgrade(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->IsUpgrade(dwConnID);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsKeepAlive(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->IsKeepAlive(dwConnID);
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_HttpAgent_GetVersion(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetVersion(dwConnID);
+}
+
+HPSOCKET_API ULONGLONG __HP_CALL HP_HttpAgent_GetContentLength(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetContentLength(dwConnID);
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpAgent_GetContentType(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetContentType(dwConnID);
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpAgent_GetContentEncoding(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetContentEncoding(dwConnID);
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpAgent_GetTransferEncoding(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetTransferEncoding(dwConnID);
+}
+
+HPSOCKET_API En_HP_HttpUpgradeType __HP_CALL HP_HttpAgent_GetUpgradeType(HP_HttpAgent pAgent, HP_CONNID dwConnID)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetUpgradeType(dwConnID);
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_HttpAgent_GetParseErrorCode(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR* lpszErrorDesc)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetParseErrorCode(dwConnID, lpszErrorDesc);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetHeader(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetHeader(dwConnID, lpszName, lpszValue);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetHeaders(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetHeaders(dwConnID, lpszName, lpszValue, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetAllHeaders(HP_HttpAgent pAgent, HP_CONNID dwConnID, HP_THeader lpHeaders[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetAllHeaders(dwConnID, lpHeaders, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetAllHeaderNames(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetAllHeaderNames(dwConnID, lpszName, *pdwCount);
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpAgent_SetUseCookie(HP_HttpAgent pAgent, BOOL bUseCookie)
+{
+	C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SetUseCookie(bUseCookie);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsUseCookie(HP_HttpAgent pAgent)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->IsUseCookie();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetCookie(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetCookie(dwConnID, lpszName, lpszValue);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetAllCookies(HP_HttpAgent pAgent, HP_CONNID dwConnID, HP_TCookie lpCookies[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetAllCookies(dwConnID, lpCookies, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetWSMessageState(HP_HttpAgent pAgent, HP_CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->GetWSMessageState(dwConnID, lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpAgent_SetHttpAutoStart(HP_HttpAgent pAgent, BOOL bAutoStart)
+{
+	C_HP_Object::ToFirst<IHttpAgent>(pAgent)->SetHttpAutoStart(bAutoStart);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsHttpAutoStart(HP_HttpAgent pAgent)
+{
+	return C_HP_Object::ToFirst<IHttpAgent>(pAgent)->IsHttpAutoStart();
+}
+
+/**************************************************************************/
+/*************************** HTTP Client 操作方法 **************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendRequest(HP_HttpClient pClient, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendRequest(lpszMethod, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendLocalFile(HP_HttpClient pClient, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendLocalFile(lpszFileName, lpszMethod, lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendChunkData(HP_HttpClient pClient, const BYTE* pData, int iLength, LPCSTR lpszExtensions)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendChunkData(pData, iLength, lpszExtensions);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendPost(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendPost(lpszPath, lpHeaders, iHeaderCount, pBody, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendPut(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendPut(lpszPath, lpHeaders, iHeaderCount, pBody, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendPatch(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendPatch(lpszPath, lpHeaders, iHeaderCount, pBody, iLength);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendGet(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendGet(lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendDelete(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendDelete(lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendHead(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendHead(lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendTrace(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendTrace(lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendOptions(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendOptions(lpszPath, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendConnect(HP_HttpClient pClient, LPCSTR lpszHost, const HP_THeader lpHeaders[], int iHeaderCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendConnect(lpszHost, lpHeaders, iHeaderCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendWSMessage(HP_HttpClient pClient, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->SendWSMessage(bFinal, iReserved, iOperationCode, lpszMask, pData, iLength, ullBodyLen);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_StartHttp(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->StartHttp();
+}
+
+/******************************************************************************/
+/*************************** HTTP Client 属性访问方法 **************************/
+
+HPSOCKET_API USHORT __HP_CALL HP_HttpClient_GetStatusCode(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetStatusCode();
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpClient_SetLocalVersion(HP_HttpClient pClient, En_HP_HttpVersion usVersion)
+{
+	C_HP_Object::ToFirst<IHttpClient>(pClient)->SetLocalVersion(usVersion);
+}
+
+HPSOCKET_API En_HP_HttpVersion __HP_CALL HP_HttpClient_GetLocalVersion(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetLocalVersion();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsUpgrade(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->IsUpgrade();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsKeepAlive(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->IsKeepAlive();
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_HttpClient_GetVersion(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetVersion();
+}
+
+HPSOCKET_API ULONGLONG __HP_CALL HP_HttpClient_GetContentLength(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetContentLength();
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpClient_GetContentType(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetContentType();
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpClient_GetContentEncoding(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetContentEncoding();
+}
+
+HPSOCKET_API LPCSTR __HP_CALL HP_HttpClient_GetTransferEncoding(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetTransferEncoding();
+}
+
+HPSOCKET_API En_HP_HttpUpgradeType __HP_CALL HP_HttpClient_GetUpgradeType(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetUpgradeType();
+}
+
+HPSOCKET_API USHORT __HP_CALL HP_HttpClient_GetParseErrorCode(HP_HttpClient pClient, LPCSTR* lpszErrorDesc)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetParseErrorCode(lpszErrorDesc);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetHeader(HP_HttpClient pClient, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetHeader(lpszName, lpszValue);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetHeaders(HP_HttpClient pClient, LPCSTR lpszName, LPCSTR lpszValue[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetHeaders(lpszName, lpszValue, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetAllHeaders(HP_HttpClient pClient, HP_THeader lpHeaders[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetAllHeaders(lpHeaders, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetAllHeaderNames(HP_HttpClient pClient, LPCSTR lpszName[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetAllHeaderNames(lpszName, *pdwCount);
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpClient_SetUseCookie(HP_HttpClient pClient, BOOL bUseCookie)
+{
+	C_HP_Object::ToFirst<IHttpClient>(pClient)->SetUseCookie(bUseCookie);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsUseCookie(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->IsUseCookie();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetCookie(HP_HttpClient pClient, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetCookie(lpszName, lpszValue);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetAllCookies(HP_HttpClient pClient, HP_TCookie lpCookies[], DWORD* pdwCount)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetAllCookies(lpCookies, *pdwCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetWSMessageState(HP_HttpClient pClient, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpClient_SetHttpAutoStart(HP_HttpClient pClient, BOOL bAutoStart)
+{
+	C_HP_Object::ToFirst<IHttpClient>(pClient)->SetHttpAutoStart(bAutoStart);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsHttpAutoStart(HP_HttpClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpClient>(pClient)->IsHttpAutoStart();
+}
+
+/**************************************************************************/
+/************************ HTTP Sync Client 操作方法 ************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpSyncClient_OpenUrl(HP_HttpSyncClient pClient, LPCSTR lpszMethod, LPCSTR lpszUrl, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength, BOOL bForceReconnect)
+{
+	return C_HP_Object::ToFirst<IHttpSyncClient>(pClient)->OpenUrl(lpszMethod, lpszUrl, lpHeaders, iHeaderCount, pBody, iLength, bForceReconnect);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpSyncClient_CleanupRequestResult(HP_HttpSyncClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpSyncClient>(pClient)->CleanupRequestResult();
+}
+
+/**************************************************************************/
+/********************** HTTP Sync Client 属性访问方法 **********************/
+
+HPSOCKET_API void __HP_CALL HP_HttpSyncClient_SetConnectTimeout(HP_HttpSyncClient pClient, DWORD dwConnectTimeout)
+{
+	C_HP_Object::ToFirst<IHttpSyncClient>(pClient)->SetConnectTimeout(dwConnectTimeout);
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpSyncClient_SetRequestTimeout(HP_HttpSyncClient pClient, DWORD dwRequestTimeout)
+{
+	C_HP_Object::ToFirst<IHttpSyncClient>(pClient)->SetRequestTimeout(dwRequestTimeout);
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_HttpSyncClient_GetConnectTimeout(HP_HttpSyncClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpSyncClient>(pClient)->GetConnectTimeout();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_HttpSyncClient_GetRequestTimeout(HP_HttpSyncClient pClient)
+{
+	return C_HP_Object::ToFirst<IHttpSyncClient>(pClient)->GetRequestTimeout();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpSyncClient_GetResponseBody(HP_HttpSyncClient pClient, LPCBYTE* lpszBody, int* piLength)
+{
+	return C_HP_Object::ToFirst<IHttpSyncClient>(pClient)->GetResponseBody(lpszBody, piLength);
+}
+
+/**************************************************************************/
+/*************************** HTTP Cookie 管理方法 **************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists)
+{
+	return g_CookieMgr.LoadFromFile(lpszFile, bKeepExists);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_SaveToFile(LPCSTR lpszFile, BOOL bKeepExists)
+{
+	return g_CookieMgr.SaveToFile(lpszFile, bKeepExists);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_ClearCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
+{
+	return g_CookieMgr.ClearCookies(lpszDomain, lpszPath);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_RemoveExpiredCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
+{
+	return g_CookieMgr.RemoveExpiredCookies(lpszDomain, lpszPath);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, int enSameSite, BOOL bOnlyUpdateValueIfExists)
+{
+	return g_CookieMgr.SetCookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, (CCookie::EnSameSite)enSameSite, bOnlyUpdateValueIfExists);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
+{
+	return g_CookieMgr.DeleteCookie(lpszDomain, lpszPath, lpszName);
+}
+
+HPSOCKET_API void __HP_CALL HP_HttpCookie_MGR_SetEnableThirdPartyCookie(BOOL bEnableThirdPartyCookie)
+{
+	g_CookieMgr.SetEnableThirdPartyCookie(bEnableThirdPartyCookie);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_IsEnableThirdPartyCookie()
+{
+	return g_CookieMgr.IsEnableThirdPartyCookie();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_HLP_ParseExpires(LPCSTR lpszExpires, __time64_t* ptmExpires)
+{
+	return CCookie::ParseExpires(lpszExpires, *ptmExpires);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_HLP_MakeExpiresStr(char lpszBuff[], int* piBuffLen, __time64_t tmExpires)
+{
+	return CCookie::MakeExpiresStr(lpszBuff, *piBuffLen, tmExpires);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_HLP_ToString(char lpszBuff[], int* piBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, int enSameSite)
+{
+	return CCookie::ToString(lpszBuff, *piBuffLen, lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, (CCookie::EnSameSite)enSameSite);
+}
+
+HPSOCKET_API __time64_t __HP_CALL HP_HttpCookie_HLP_CurrentUTCTime()
+{
+	return CCookie::CurrentUTCTime();
+}
+
+HPSOCKET_API __time64_t __HP_CALL HP_HttpCookie_HLP_MaxAgeToExpires(int iMaxAge)
+{
+	return CCookie::MaxAgeToExpires(iMaxAge);
+}
+
+HPSOCKET_API int __HP_CALL HP_HttpCookie_HLP_ExpiresToMaxAge(__time64_t tmExpires)
+{
+	return CCookie::ExpiresToMaxAge(tmExpires);
+}
+
+/*****************************************************************************************************************************************************/
+/************************************************************* HTTP Global Function Exports **********************************************************/
+/*****************************************************************************************************************************************************/
+
+#endif
+
+/*****************************************************************************************************************************************************/
+/**************************************************************** Thread Pool Exports ****************************************************************/
+/*****************************************************************************************************************************************************/
+
+/**********************************************************************************/
+/************************** Thread Pool 回调函数设置方法 ***************************/
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnStartup(HP_ThreadPoolListener pListener, HP_FN_ThreadPool_OnStartup fn)
+{
+	((C_HP_ThreadPoolListener*)pListener)->m_fnOnStartup = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnShutdown(HP_ThreadPoolListener pListener, HP_FN_ThreadPool_OnShutdown fn)
+{
+	((C_HP_ThreadPoolListener*)pListener)->m_fnOnShutdown = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnWorkerThreadStart(HP_ThreadPoolListener pListener, HP_FN_ThreadPool_OnWorkerThreadStart fn)
+{
+	((C_HP_ThreadPoolListener*)pListener)->m_fnOnWorkerThreadStart = fn;
+}
+
+HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnWorkerThreadEnd(HP_ThreadPoolListener pListener, HP_FN_ThreadPool_OnWorkerThreadEnd fn)
+{
+	((C_HP_ThreadPoolListener*)pListener)->m_fnOnWorkerThreadEnd = fn;
+}
+
+/****************************************************/
+/******************* 对象创建函数 ********************/
+
+HPSOCKET_API HP_ThreadPoolListener __HP_CALL Create_HP_ThreadPoolListener()
+{
+	return (HP_ThreadPoolListener)(new C_HP_ThreadPoolListener);
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_ThreadPoolListener(HP_ThreadPoolListener pListener)
+{
+	delete (IHPThreadPoolListener*)pListener;
+}
+
+HPSOCKET_API HP_ThreadPool __HP_CALL Create_HP_ThreadPool(HP_ThreadPoolListener pListener)
+{
+	return (HP_ThreadPool)(new CHPThreadPool((IHPThreadPoolListener*)pListener));
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_ThreadPool(HP_ThreadPool pThreadPool)
+{
+	delete (IHPThreadPool*)pThreadPool;
+}
+
+HPSOCKET_API LPTSocketTask __HP_CALL Create_HP_SocketTaskObj(Fn_SocketTaskProc fnTaskProc, PVOID pSender, HP_CONNID dwConnID, LPCBYTE pBuffer, INT iBuffLen, En_HP_TaskBufferType enBuffType, WPARAM wParam, LPARAM lParam)
+{
+	return ::CreateSocketTaskObj(fnTaskProc, pSender, dwConnID, pBuffer, iBuffLen, enBuffType, wParam, lParam);
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_SocketTaskObj(LPTSocketTask pTask)
+{
+	::DestroySocketTaskObj(pTask);
+}
+
+/***********************************************************************/
+/***************************** 组件操作方法 *****************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Start(HP_ThreadPool pThreadPool, DWORD dwThreadCount, DWORD dwMaxQueueSize, En_HP_RejectedPolicy enRejectedPolicy, DWORD dwStackSize)
+{
+	return ((IHPThreadPool*)pThreadPool)->Start(dwThreadCount, dwMaxQueueSize, enRejectedPolicy, dwStackSize);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Stop(HP_ThreadPool pThreadPool, DWORD dwMaxWait)
+{
+	return ((IHPThreadPool*)pThreadPool)->Stop(dwMaxWait);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Submit(HP_ThreadPool pThreadPool, HP_Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait)
+{
+	return ((IHPThreadPool*)pThreadPool)->Submit(fnTaskProc, pvArg, dwMaxWait);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Submit_Task(HP_ThreadPool pThreadPool, HP_LPTSocketTask pTask, DWORD dwMaxWait)
+{
+	return ((IHPThreadPool*)pThreadPool)->Submit(pTask, dwMaxWait);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_AdjustThreadCount(HP_ThreadPool pThreadPool, DWORD dwNewThreadCount)
+{
+	return ((IHPThreadPool*)pThreadPool)->AdjustThreadCount(dwNewThreadCount);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Wait(HP_ThreadPool pThreadPool, DWORD dwMilliseconds)
+{
+	return ((IHPThreadPool*)pThreadPool)->Wait(dwMilliseconds);
+}
+
+/***********************************************************************/
+/***************************** 属性访问方法 *****************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_HasStarted(HP_ThreadPool pThreadPool)
+{
+	return ((IHPThreadPool*)pThreadPool)->HasStarted();
+}
+
+HPSOCKET_API En_HP_ServiceState	__HP_CALL HP_ThreadPool_GetState(HP_ThreadPool pThreadPool)
+{
+	return ((IHPThreadPool*)pThreadPool)->GetState();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetQueueSize(HP_ThreadPool pThreadPool)
+{
+	return ((IHPThreadPool*)pThreadPool)->GetQueueSize();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetTaskCount(HP_ThreadPool pThreadPool)
+{
+	return ((IHPThreadPool*)pThreadPool)->GetTaskCount();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetThreadCount(HP_ThreadPool pThreadPool)
+{
+	return ((IHPThreadPool*)pThreadPool)->GetThreadCount();
+}
+
+HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetMaxQueueSize(HP_ThreadPool pThreadPool)
+{
+	return ((IHPThreadPool*)pThreadPool)->GetMaxQueueSize();
+}
+
+HPSOCKET_API En_HP_RejectedPolicy __HP_CALL HP_ThreadPool_GetRejectedPolicy(HP_ThreadPool pThreadPool)
+{
+	return ((IHPThreadPool*)pThreadPool)->GetRejectedPolicy();
+}
+
+/*****************************************************************************************************************************************************/
+/********************************************************* Compressor / Decompressor Exports *********************************************************/
+/*****************************************************************************************************************************************************/
+
+/****************************************************/
+/******************* 对象创建函数 ********************/
+
+HPSOCKET_API void __HP_CALL Destroy_HP_Compressor(HP_Compressor pCompressor)
+{
+	::DestroyCompressor((IHPCompressor*)pCompressor);
+}
+
+HPSOCKET_API void __HP_CALL Destroy_HP_Decompressor(HP_Decompressor pDecompressor)
+{
+	::DestroyDecompressor((IHPDecompressor*)pDecompressor);
+}
+
+#ifdef _ZLIB_SUPPORT
+
+HPSOCKET_API HP_Compressor __HP_CALL Create_HP_ZLibCompressor(HP_Fn_CompressDataCallback fnCallback)
+{
+	return ::CreateZLibCompressor(fnCallback);
+}
+
+HPSOCKET_API HP_Compressor __HP_CALL Create_HP_ZLibCompressorEx(HP_Fn_CompressDataCallback fnCallback, int iWindowBits, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize)
+{
+	return ::CreateZLibCompressor(fnCallback, iWindowBits, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize);
+}
+
+HPSOCKET_API HP_Compressor __HP_CALL Create_HP_GZipCompressor(HP_Fn_CompressDataCallback fnCallback)
+{
+	return ::CreateGZipCompressor(fnCallback);
+}
+
+HPSOCKET_API HP_Compressor __HP_CALL Create_HP_GZipCompressorEx(HP_Fn_CompressDataCallback fnCallback, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize)
+{
+	return ::CreateGZipCompressor(fnCallback, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize);
+}
+
+HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_ZLibDecompressor(HP_Fn_DecompressDataCallback fnCallback)
+{
+	return ::CreateZLibDecompressor(fnCallback);
+}
+
+HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_ZLibDecompressorEx(HP_Fn_DecompressDataCallback fnCallback, int iWindowBits, DWORD dwBuffSize)
+{
+	return ::CreateZLibDecompressor(fnCallback, iWindowBits, dwBuffSize);
+}
+
+HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_GZipDecompressor(HP_Fn_DecompressDataCallback fnCallback)
+{
+	return ::CreateGZipDecompressor(fnCallback);
+}
+
+HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_GZipDecompressorEx(HP_Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize)
+{
+	return ::CreateGZipDecompressor(fnCallback, dwBuffSize);
+}
+
+#endif
+
+#ifdef _BROTLI_SUPPORT
+
+HPSOCKET_API HP_Compressor __HP_CALL Create_HP_BrotliCompressor(HP_Fn_CompressDataCallback fnCallback)
+{
+	return ::CreateBrotliCompressor(fnCallback);
+}
+
+HPSOCKET_API HP_Compressor __HP_CALL Create_HP_BrotliCompressorEx(HP_Fn_CompressDataCallback fnCallback, int iQuality, int iWindow, int iMode, DWORD dwBuffSize)
+{
+	return ::CreateBrotliCompressor(fnCallback, iQuality, iWindow, iMode, dwBuffSize);
+}
+
+HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_BrotliDecompressor(HP_Fn_DecompressDataCallback fnCallback)
+{
+	return ::CreateBrotliDecompressor(fnCallback);
+}
+
+HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_BrotliDecompressorEx(HP_Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize)
+{
+	return ::CreateBrotliDecompressor(fnCallback, dwBuffSize);
+}
+
+#endif
+
+/***********************************************************************/
+/***************************** 组件操作方法 *****************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_Compressor_Process(HP_Compressor pCompressor, const BYTE* pData, int iLength, BOOL bLast, PVOID pContext)
+{
+	return ((IHPCompressor*)pCompressor)->Process(pData, iLength, bLast, pContext);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Compressor_ProcessEx(HP_Compressor pCompressor, const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush, PVOID pContext)
+{
+	return ((IHPCompressor*)pCompressor)->ProcessEx(pData, iLength, bLast, bFlush, pContext);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Compressor_Reset(HP_Compressor pCompressor)
+{
+	return ((IHPCompressor*)pCompressor)->Reset();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Decompressor_Process(HP_Decompressor pDecompressor, const BYTE* pData, int iLength, PVOID pContext)
+{
+	return ((IHPDecompressor*)pDecompressor)->Process(pData, iLength, pContext);
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Decompressor_Reset(HP_Decompressor pDecompressor)
+{
+	return ((IHPDecompressor*)pDecompressor)->Reset();
+}
+
+/***********************************************************************/
+/***************************** 属性访问方法 *****************************/
+
+HPSOCKET_API BOOL __HP_CALL HP_Compressor_IsValid(HP_Compressor pCompressor)
+{
+	return ((IHPCompressor*)pCompressor)->IsValid();
+}
+
+HPSOCKET_API BOOL __HP_CALL HP_Decompressor_IsValid(HP_Decompressor pDecompressor)
+{
+	return ((IHPDecompressor*)pDecompressor)->IsValid();
+}

+ 496 - 0
HPThreadPool.cpp

@@ -0,0 +1,496 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+#include "HPThreadPool.h"
+#include "common/FuncHelper.h"
+
+#include <pthread.h>
+
+LPTSocketTask CreateSocketTaskObj(	Fn_SocketTaskProc fnTaskProc,
+									PVOID pSender, CONNID dwConnID,
+									LPCBYTE pBuffer, INT iBuffLen, EnTaskBufferType enBuffType,
+									WPARAM wParam, LPARAM lParam)
+{
+	ASSERT(fnTaskProc != nullptr);
+	ASSERT(iBuffLen >= 0);
+
+	LPTSocketTask pTask = new TSocketTask;
+
+	pTask->fn		= fnTaskProc;
+	pTask->sender	= pSender;
+	pTask->connID	= dwConnID;
+	pTask->bufLen	= iBuffLen;
+	pTask->bufType	= enBuffType;
+	pTask->wparam	= wParam;
+	pTask->lparam	= lParam;
+
+	if(enBuffType != TBT_COPY || !pBuffer)
+		pTask->buf = pBuffer;
+	else
+	{
+		pTask->buf = MALLOC(BYTE, iBuffLen);
+		::CopyMemory((LPBYTE)pTask->buf, pBuffer, iBuffLen);
+	}
+
+	return pTask;
+}
+
+void DestroySocketTaskObj(LPTSocketTask pTask)
+{
+	if(pTask)
+	{
+		if(pTask->bufType != TBT_REFER && pTask->buf)
+			FREE(pTask->buf);
+
+		delete pTask;
+	}
+}
+
+volatile UINT CHPThreadPool::sm_uiNum		= MAXUINT;
+LPCTSTR CHPThreadPool::POOLED_THREAD_PREFIX	= _T("hp-pool-");
+
+BOOL CHPThreadPool::Start(DWORD dwThreadCount, DWORD dwMaxQueueSize, EnRejectedPolicy enRejectedPolicy, DWORD dwStackSize)
+{
+	if(!CheckStarting())
+		return FALSE;
+
+	m_dwStackSize		= dwStackSize;
+	m_dwMaxQueueSize	= dwMaxQueueSize;
+	m_enRejectedPolicy	= enRejectedPolicy;
+
+	FireStartup();
+
+	if(!InternalAdjustThreadCount(dwThreadCount))
+	{
+		EXECUTE_RESTORE_ERROR(Stop());
+		return FALSE;
+	}
+
+	m_enState = SS_STARTED;
+
+	return TRUE;
+}
+
+BOOL CHPThreadPool::Stop(DWORD dwMaxWait)
+{
+	if(!CheckStoping())
+		return FALSE;
+
+	::WaitFor(15);
+
+	Shutdown(dwMaxWait);
+
+	FireShutdown();
+
+	Reset();
+
+	return TRUE;
+}
+
+BOOL CHPThreadPool::Shutdown(DWORD dwMaxWait)
+{
+	BOOL isOK		 = TRUE;
+	BOOL bLimited	 = (m_dwMaxQueueSize != 0);
+	BOOL bInfinite	 = (dwMaxWait == (DWORD)INFINITE || dwMaxWait == 0);
+	auto prdShutdown = [this]() {return m_stThreads.empty();};
+
+	if(m_enRejectedPolicy == TRP_WAIT_FOR && bLimited)
+		m_evQueue.SyncNotifyAll();
+
+	VERIFY(DoAdjustThreadCount(0));
+
+	if(bInfinite)
+		m_evShutdown.Wait(prdShutdown);
+	else
+		m_evShutdown.WaitFor(dwMaxWait, prdShutdown);
+
+	ASSERT(m_lsTasks.Size()	  == 0);
+	ASSERT(m_stThreads.size() == 0);
+
+	if(!m_lsTasks.IsEmpty())
+	{
+		TTask* pTask = nullptr;
+
+		while(m_lsTasks.PopFront(&pTask))
+		{
+			if(pTask->freeArg)
+				::DestroySocketTaskObj((LPTSocketTask)pTask->arg);
+
+			TTask::Destruct(pTask);
+		}
+
+		::SetLastError(ERROR_CANCELLED);
+		isOK = FALSE;
+	}
+
+	if(!m_stThreads.empty())
+	{
+		CCriSecLock lock(m_csThread);
+
+		if(!m_stThreads.empty())
+		{
+#if !defined(__ANDROID__)
+			for(auto it = m_stThreads.begin(), end = m_stThreads.end(); it != end; ++it)
+				pthread_cancel(*it);
+#endif
+			m_stThreads.clear();
+
+			::SetLastError(ERROR_CANCELLED);
+			isOK = FALSE;
+		}
+	}
+
+	return isOK;
+}
+
+BOOL CHPThreadPool::Submit(Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait)
+{
+	return DoSubmit(fnTaskProc, pvArg, FALSE, dwMaxWait);
+}
+
+BOOL CHPThreadPool::Submit(LPTSocketTask pTask, DWORD dwMaxWait)
+{
+	return DoSubmit((Fn_TaskProc)pTask->fn, (PVOID)pTask, TRUE, dwMaxWait);
+}
+
+BOOL CHPThreadPool::DoSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg, DWORD dwMaxWait)
+{
+	EnSubmitResult sr = DirectSubmit(fnTaskProc, pvArg, bFreeArg);
+
+	if(sr != SUBMIT_FULL)
+		return (sr == SUBMIT_OK);
+
+	if(m_enRejectedPolicy == TRP_CALL_FAIL)
+	{
+		::SetLastError(ERROR_DESTINATION_ELEMENT_FULL);
+		return FALSE;
+	}
+	else if(m_enRejectedPolicy == TRP_WAIT_FOR)
+	{
+		return CycleWaitSubmit(fnTaskProc, pvArg, dwMaxWait, bFreeArg);
+	}
+	else if(m_enRejectedPolicy == TRP_CALLER_RUN)
+	{
+		DoRunTaskProc(fnTaskProc, pvArg, bFreeArg);
+	}
+	else
+	{
+		ASSERT(FALSE);
+
+		::SetLastError(ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void CHPThreadPool::DoRunTaskProc(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg)
+{
+	::InterlockedIncrement(&m_dwTaskCount);
+	fnTaskProc(pvArg);
+	::InterlockedDecrement(&m_dwTaskCount);
+
+	if(bFreeArg)
+		::DestroySocketTaskObj((LPTSocketTask)pvArg);
+}
+
+CHPThreadPool::EnSubmitResult CHPThreadPool::DirectSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg)
+{
+	if(!CheckStarted())
+		return SUBMIT_ERROR;
+
+	BOOL bLimited = (m_dwMaxQueueSize != 0);
+
+	if(bLimited && m_lsTasks.Size() >= m_dwMaxQueueSize)
+		return SUBMIT_FULL;
+	else
+	{
+		TTask* pTask = TTask::Construct(fnTaskProc, pvArg, bFreeArg);
+
+		m_lsTasks.PushBack(pTask);
+		m_evTask.SyncNotifyOne();
+	}
+
+	return SUBMIT_OK;
+}
+
+BOOL CHPThreadPool::CycleWaitSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait, BOOL bFreeArg)
+{
+	ASSERT(m_dwMaxQueueSize != 0);
+
+	DWORD dwTime	= ::TimeGetTime();
+	BOOL bInfinite	= (dwMaxWait == (DWORD)INFINITE || dwMaxWait == 0);
+	auto prdQueue	= [this]() {return	(m_lsTasks.Size() < m_dwMaxQueueSize);};
+
+	while(CheckStarted()) 
+	{
+		EnSubmitResult sr = DirectSubmit(fnTaskProc, pvArg, bFreeArg);
+
+		if(sr == SUBMIT_OK)
+			return TRUE;
+		if(sr == SUBMIT_ERROR)
+			return FALSE;
+
+		if(bInfinite)
+			m_evQueue.Wait(prdQueue);
+		else
+		{
+			DWORD dwNow = ::GetTimeGap32(dwTime);
+
+			if(dwNow > dwMaxWait || !m_evQueue.WaitFor(chrono::milliseconds(dwMaxWait - dwNow), prdQueue))
+			{
+				::SetLastError(ERROR_TIMEOUT);
+				break;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+BOOL CHPThreadPool::AdjustThreadCount(DWORD dwNewThreadCount)
+{
+	if(!CheckStarted())
+		return FALSE;
+
+	return InternalAdjustThreadCount(dwNewThreadCount);
+}
+
+BOOL CHPThreadPool::InternalAdjustThreadCount(DWORD dwNewThreadCount)
+{
+	int iNewThreadCount = (int)dwNewThreadCount;
+
+	if(iNewThreadCount == 0)
+		iNewThreadCount = ::GetDefaultWorkerThreadCount();
+	else if(iNewThreadCount < 0)
+		iNewThreadCount = PROCESSOR_COUNT * (-iNewThreadCount);
+
+	return DoAdjustThreadCount((DWORD)iNewThreadCount);
+}
+
+BOOL CHPThreadPool::DoAdjustThreadCount(DWORD dwNewThreadCount)
+{
+	ASSERT((int)dwNewThreadCount >= 0);
+
+	BOOL bRemove		= FALSE;
+	DWORD dwThreadCount	= 0;
+
+	if(dwNewThreadCount > m_dwThreadCount)
+	{
+		dwThreadCount = dwNewThreadCount - m_dwThreadCount;
+		return CreateWorkerThreads(dwThreadCount);
+	}
+	else if(dwNewThreadCount < m_dwThreadCount)
+	{
+		bRemove		  = TRUE;
+		dwThreadCount = m_dwThreadCount - dwNewThreadCount;
+
+		::InterlockedSub(&m_dwThreadCount, dwThreadCount);
+	}
+
+	if(bRemove)
+	{
+		for(DWORD i = 0; i < dwThreadCount; i++)
+			m_evTask.SyncNotifyOne();
+	}
+
+	return TRUE;
+}
+
+BOOL CHPThreadPool::CreateWorkerThreads(DWORD dwThreadCount)
+{
+	unique_ptr<pthread_attr_t> pThreadAttr;
+
+	if(m_dwStackSize != 0)
+	{
+		pThreadAttr = make_unique<pthread_attr_t>();
+		VERIFY_IS_NO_ERROR(pthread_attr_init(pThreadAttr.get()));
+
+		int rs = pthread_attr_setstacksize(pThreadAttr.get(), m_dwStackSize);
+
+		if(!IS_NO_ERROR(rs))
+		{
+			pthread_attr_destroy(pThreadAttr.get());
+			::SetLastError(rs);
+
+			return FALSE;
+		}
+	}
+
+	BOOL isOK = TRUE;
+
+	for(DWORD i = 0; i < dwThreadCount; i++)
+	{
+		THR_ID dwThreadID;
+		int rs = pthread_create(&dwThreadID, pThreadAttr.get(), ThreadProc, (PVOID)this);
+
+		if(!IS_NO_ERROR(rs))
+		{
+			::SetLastError(rs);
+			isOK = FALSE;
+
+			break;
+		}
+
+		::InterlockedIncrement(&m_dwThreadCount);
+
+		CCriSecLock lock(m_csThread);
+		m_stThreads.emplace(dwThreadID);
+	}
+
+	if(pThreadAttr != nullptr)
+		pthread_attr_destroy(pThreadAttr.get());
+
+	return isOK;
+}
+
+PVOID CHPThreadPool::ThreadProc(LPVOID pv)
+{
+	CHPThreadPool* pThis = (CHPThreadPool*)pv;
+
+	::SetSequenceThreadName(SELF_THREAD_ID, pThis->m_strPrefix, pThis->m_uiSeq);
+
+	pThis->FireWorkerThreadStart();
+	
+	PVOID rs = (PVOID)(UINT_PTR)(pThis->WorkerProc());
+	
+	pThis->FireWorkerThreadEnd();
+
+	return rs;
+}
+
+int CHPThreadPool::WorkerProc()
+{
+	BOOL bLimited	= (m_dwMaxQueueSize != 0);
+	TTask* pTask	= nullptr;
+	auto prdTask	= [this]() {return (!m_lsTasks.IsEmpty()) || (m_dwThreadCount < m_stThreads.size());};
+
+	while(TRUE)
+	{
+		pTask = nullptr;
+
+		while(m_lsTasks.PopFront(&pTask))
+		{
+			if(m_enRejectedPolicy == TRP_WAIT_FOR && bLimited)
+				m_evQueue.SyncNotifyOne();
+
+			DoRunTaskProc(pTask->fn, pTask->arg, pTask->freeArg);
+
+			TTask::Destruct(pTask);
+		}
+		
+		if(CheckWorkerThreadExit())
+			break;
+
+		m_evTask.Wait(prdTask);
+	}
+
+	return 0;
+}
+
+BOOL CHPThreadPool::CheckWorkerThreadExit()
+{
+	BOOL bExit		= FALSE;
+	BOOL bShutdown	= FALSE;
+
+	if(m_dwThreadCount < m_stThreads.size())
+	{
+		CCriSecLock lock(m_csThread);
+
+		if(m_dwThreadCount < m_stThreads.size())
+		{
+			VERIFY(m_stThreads.erase(SELF_THREAD_ID) == 1);
+
+			bExit		= TRUE;
+			bShutdown	= m_stThreads.empty();
+		}
+	}
+
+	if(bExit)
+	{
+		pthread_detach(SELF_THREAD_ID);
+
+		if(bShutdown)
+			m_evShutdown.SyncNotifyOne();
+	}
+
+	return bExit;
+}
+
+BOOL CHPThreadPool::CheckStarting()
+{
+	if(::InterlockedCompareExchange(&m_enState, SS_STARTING, SS_STOPPED) != SS_STOPPED)
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CHPThreadPool::CheckStarted()
+{
+	if(m_enState != SS_STARTED)
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CHPThreadPool::CheckStoping()
+{
+	if( ::InterlockedCompareExchange(&m_enState, SS_STOPPING, SS_STARTED) != SS_STARTED &&
+		::InterlockedCompareExchange(&m_enState, SS_STOPPING, SS_STARTING) != SS_STARTING)
+	{
+		while(m_enState != SS_STOPPED)
+			::WaitFor(5);
+
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void CHPThreadPool::Reset(BOOL bSetWaitEvent)
+{
+	m_uiSeq				= MAXUINT;
+	m_dwStackSize		= 0;
+	m_dwTaskCount		= 0;
+	m_dwThreadCount		= 0;
+	m_dwMaxQueueSize	= 0;
+	m_enRejectedPolicy	= TRP_CALL_FAIL;
+	m_enState			= SS_STOPPED;
+
+	if(bSetWaitEvent)
+		m_evWait.SyncNotifyAll();
+}
+
+void CHPThreadPool::MakePrefix()
+{
+	UINT uiNumber = ::InterlockedIncrement(&sm_uiNum);
+
+	m_strPrefix.Format(_T("%s%u-"), POOLED_THREAD_PREFIX, uiNumber);
+}

+ 168 - 0
HPThreadPool.h

@@ -0,0 +1,168 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/SocketInterface.h"
+#include "common/STLHelper.h"
+#include "common/Semaphore.h"
+#include "common/RingBuffer.h"
+#include "InternalDef.h"
+
+LPTSocketTask CreateSocketTaskObj(	Fn_SocketTaskProc fnTaskProc,
+									PVOID pSender, CONNID dwConnID,
+									LPCBYTE pBuffer, INT iBuffLen, EnTaskBufferType enBuffType = TBT_COPY,
+									WPARAM wParam = 0, LPARAM lParam = 0);
+
+void DestroySocketTaskObj(LPTSocketTask pTask);
+
+class CHPThreadPool : public IHPThreadPool
+{
+private:
+	enum EnSubmitResult{SUBMIT_OK, SUBMIT_FULL, SUBMIT_ERROR};
+
+	struct TTask
+	{
+		Fn_TaskProc	fn;
+		PVOID		arg;
+		BOOL		freeArg;
+
+	public:
+		static TTask* Construct(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg)
+		{
+			return new TTask(fnTaskProc, pvArg, bFreeArg);
+		}
+
+		static void Destruct(TTask* pTask)
+		{
+			if(pTask)
+			{
+				delete pTask;
+			}
+		}
+
+	private:
+		TTask(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg)
+		: fn(fnTaskProc), arg(pvArg), freeArg(bFreeArg)
+		{
+			ASSERT(fn != nullptr);
+		}
+	};
+
+	using CTaskQueue = CCASQueue<TTask>;
+
+public:
+	virtual BOOL Start(DWORD dwThreadCount = 0, DWORD dwMaxQueueSize = 0, EnRejectedPolicy enRejectedPolicy = TRP_CALL_FAIL, DWORD dwStackSize = 0);
+	virtual BOOL Stop(DWORD dwMaxWait = INFINITE);
+	virtual BOOL Wait(DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
+
+	virtual BOOL Submit(Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait = INFINITE);
+	virtual BOOL Submit(LPTSocketTask pTask, DWORD dwMaxWait = INFINITE);
+	virtual BOOL AdjustThreadCount(DWORD dwNewThreadCount);
+
+public:
+	virtual BOOL HasStarted()						{return m_enState == SS_STARTED || m_enState == SS_STARTING;}
+	virtual EnServiceState GetState()				{return m_enState;}
+
+	virtual DWORD GetQueueSize()					{return m_lsTasks.Size();}
+	virtual DWORD GetTaskCount()					{return m_dwTaskCount;}
+	virtual DWORD GetThreadCount()					{return m_dwThreadCount;}
+	virtual DWORD GetMaxQueueSize()					{return m_dwMaxQueueSize;}
+	virtual EnRejectedPolicy GetRejectedPolicy()	{return m_enRejectedPolicy;}
+
+private:
+	BOOL CheckStarting();
+	BOOL CheckStarted();
+	BOOL CheckStoping();
+
+	BOOL InternalAdjustThreadCount(DWORD dwNewThreadCount);
+	BOOL DoAdjustThreadCount(DWORD dwNewThreadCount);
+
+	BOOL CreateWorkerThreads(DWORD dwThreadCount);
+
+	BOOL Shutdown(DWORD dwMaxWait);
+	BOOL CheckWorkerThreadExit();
+
+	static PVOID ThreadProc(LPVOID pv);
+	int WorkerProc();
+
+	EnSubmitResult DirectSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg);
+	BOOL CycleWaitSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait, BOOL bFreeArg);
+	BOOL DoSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg, DWORD dwMaxWait);
+	void DoRunTaskProc(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg);
+
+	void FireStartup()
+		{if(m_pListener != nullptr) m_pListener->OnStartup(this);}
+	void FireShutdown()
+		{if(m_pListener != nullptr) m_pListener->OnShutdown(this);}
+	void FireWorkerThreadStart()
+		{if(m_pListener != nullptr) m_pListener->OnWorkerThreadStart(this, SELF_THREAD_ID);}
+	void FireWorkerThreadEnd()
+		{if(m_pListener != nullptr) m_pListener->OnWorkerThreadEnd(this, SELF_THREAD_ID);}
+
+public:
+	CHPThreadPool(IHPThreadPoolListener* pListener = nullptr)
+	: m_pListener(pListener)
+	{
+		MakePrefix();
+		Reset(FALSE);
+	}
+
+	virtual ~CHPThreadPool()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	void Reset(BOOL bSetWaitEvent = TRUE);
+	void MakePrefix();
+
+private:
+	static LPCTSTR			POOLED_THREAD_PREFIX;
+	static volatile UINT	sm_uiNum;
+
+	volatile UINT			m_uiSeq;
+	CString					m_strPrefix; 
+
+private:
+	IHPThreadPoolListener*	m_pListener;
+
+	DWORD					m_dwStackSize;
+	DWORD					m_dwMaxQueueSize;
+	EnRejectedPolicy		m_enRejectedPolicy;
+
+	volatile DWORD			m_dwTaskCount;
+	volatile DWORD			m_dwThreadCount;
+	volatile EnServiceState	m_enState;
+
+	CSEM					m_evWait;
+	CSEM					m_evShutdown;
+	CSEM					m_evTask;
+	CSEM					m_evQueue;
+	CCriSec					m_csThread;
+
+	CTaskQueue				m_lsTasks;
+	unordered_set<THR_ID>	m_stThreads;
+
+	DECLARE_NO_COPY_CLASS(CHPThreadPool)
+};

+ 479 - 0
HttpAgent.cpp

@@ -0,0 +1,479 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "HttpAgent.h"
+
+#ifdef _HTTP_SUPPORT
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::CheckParams()
+{
+	if(m_enLocalVersion != HV_1_1 && m_enLocalVersion != HV_1_0)
+	{
+		SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	return __super::CheckParams();
+}
+
+template<class T, USHORT default_port> void CHttpAgentT<T, default_port>::PrepareStart()
+{
+	__super::PrepareStart();
+
+	m_objPool.SetHttpObjLockTime(GetFreeSocketObjLockTime());
+	m_objPool.SetHttpObjPoolSize(GetFreeSocketObjPool());
+	m_objPool.SetHttpObjPoolHold(GetFreeSocketObjHold());
+
+	m_objPool.Prepare();
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::SendRequest(CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+	
+	WSABUF szBuffer[2];
+	CStringA strHeader;
+
+	LPCSTR lpszHost	= nullptr;
+	USHORT usPort	= 0;
+	BOOL bConnect	= (stricmp(lpszMethod, HTTP_METHOD_CONNECT) == 0);
+
+	if(!bConnect)
+	{
+		GetRemoteHost(dwConnID, &lpszHost, &usPort);
+		if(usPort == default_port) usPort = 0;
+	}
+
+	CStringA strPath;
+	::AdjustRequestPath(bConnect, lpszPath, strPath);
+
+	pHttpObj->SetRequestPath(lpszMethod, strPath);
+	pHttpObj->ReloadCookies();
+
+	::MakeRequestLine(lpszMethod, strPath, m_enLocalVersion, strHeader);
+	::MakeHeaderLines(lpHeaders, iHeaderCount, &pHttpObj->GetCookieMap(), iLength, TRUE, -1, lpszHost, usPort, strHeader);
+	::MakeHttpPacket(strHeader, pBody, iLength, szBuffer);
+
+	return SendPackets(dwConnID, szBuffer, 2);
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount)
+{
+	CFile file;
+	CFileMapping fmap;
+
+	HRESULT hr = ::ReadSmallFile(CA2T(lpszFileName), file, fmap);
+
+	if(FAILED(hr))
+	{
+		::SetLastError(hr);
+		return FALSE;
+	}
+
+	return SendRequest(dwConnID, lpszMethod, lpszPath, lpHeaders, iHeaderCount, (BYTE*)fmap, (int)fmap.Size());
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::SendChunkData(CONNID dwConnID, const BYTE* pData, int iLength, LPCSTR lpszExtensions)
+{
+	char szLen[12];
+	WSABUF bufs[5];
+
+	int iCount = MakeChunkPackage(pData, iLength, lpszExtensions, szLen, bufs);
+
+	return SendPackets(dwConnID, bufs, iCount);
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::SendWSMessage(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
+{
+	ASSERT(lpszMask);
+
+	WSABUF szBuffer[2];
+	BYTE szHeader[HTTP_MAX_WS_HEADER_LEN];
+
+	unique_ptr<BYTE[]> szData = make_unique<BYTE[]>(iLength);
+	memcpy(szData.get(), pData, iLength);
+
+	if(!::MakeWSPacket(bFinal, iReserved, iOperationCode, lpszMask, szData.get(), iLength, ullBodyLen, szHeader, szBuffer))
+		return FALSE;
+
+	return SendPackets(dwConnID, szBuffer, 2);
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::FireConnect(TAgentSocketObj* pSocketObj)
+{
+	return m_bHttpAutoStart ? __super::FireConnect(pSocketObj) : __super::DoFireConnect(pSocketObj);
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireConnect(TAgentSocketObj* pSocketObj)
+{
+	THttpObj* pHttpObj	  = DoStartHttp(pSocketObj);
+	EnHandleResult result = __super::DoFireConnect(pSocketObj);
+
+	if(result == HR_ERROR)
+	{
+		m_objPool.PutFreeHttpObj(pHttpObj);
+		SetConnectionReserved(pSocketObj, nullptr);
+	}
+
+	return result;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireHandShake(TAgentSocketObj* pSocketObj)
+{
+	EnHandleResult result = __super::DoFireHandShake(pSocketObj);
+
+	if(result == HR_ERROR)
+	{
+		THttpObj* pHttpObj = FindHttpObj(pSocketObj);
+		VERIFY(pHttpObj);
+
+		m_objPool.PutFreeHttpObj(pHttpObj);
+		SetConnectionReserved(pSocketObj, nullptr);
+	}
+
+	return result;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+{
+	THttpObj* pHttpObj = FindHttpObj(pSocketObj);
+
+	if(pHttpObj != nullptr)
+		return pHttpObj->Execute(pData, iLength);
+	else
+		return DoFireSuperReceive(pSocketObj, pData, iLength);
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+{
+	THttpObj* pHttpObj = FindHttpObj(pSocketObj);
+
+	if(pHttpObj != nullptr)
+		pHttpObj->CheckBodyIdentityEof();
+
+	EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
+
+	if(pHttpObj != nullptr)
+	{
+		m_objPool.PutFreeHttpObj(pHttpObj);
+		SetConnectionReserved(pSocketObj, nullptr);
+	}
+
+	return result;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireShutdown()
+{
+	EnHandleResult result = __super::DoFireShutdown();
+
+	m_objPool.Clear();
+
+	return result;
+}
+
+template<class T, USHORT default_port> void CHttpAgentT<T, default_port>::ReleaseGCSocketObj(BOOL bForce)
+{
+	__super::ReleaseGCSocketObj(bForce);
+
+#ifdef USE_EXTERNAL_GC
+	m_objPool.ReleaseGCHttpObj(bForce);
+#endif
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::IsUpgrade(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->IsUpgrade();
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::IsKeepAlive(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->IsKeepAlive();
+}
+
+template<class T, USHORT default_port> USHORT CHttpAgentT<T, default_port>::GetVersion(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return 0;
+
+	return pHttpObj->GetVersion();
+}
+
+template<class T, USHORT default_port> ULONGLONG CHttpAgentT<T, default_port>::GetContentLength(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return 0;
+
+	return pHttpObj->GetContentLength();
+}
+
+template<class T, USHORT default_port> LPCSTR CHttpAgentT<T, default_port>::GetContentType(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return nullptr;
+
+	return pHttpObj->GetContentType();
+}
+
+template<class T, USHORT default_port> LPCSTR CHttpAgentT<T, default_port>::GetContentEncoding(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return nullptr;
+
+	return pHttpObj->GetContentEncoding();
+}
+
+template<class T, USHORT default_port> LPCSTR CHttpAgentT<T, default_port>::GetTransferEncoding(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return nullptr;
+
+	return pHttpObj->GetTransferEncoding();
+}
+
+template<class T, USHORT default_port> EnHttpUpgradeType CHttpAgentT<T, default_port>::GetUpgradeType(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return HUT_NONE;
+
+	return pHttpObj->GetUpgradeType();
+}
+
+template<class T, USHORT default_port> USHORT CHttpAgentT<T, default_port>::GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return 0;
+
+	return pHttpObj->GetParseErrorCode(lpszErrorDesc);
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetHeader(lpszName, lpszValue);
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetHeaders(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetHeaders(lpszName, lpszValue, dwCount);
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetAllHeaders(lpHeaders, dwCount);
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetAllHeaderNames(lpszName, dwCount);
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetCookie(lpszName, lpszValue);
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetAllCookies(lpCookies, dwCount);
+}
+
+template<class T, USHORT default_port> USHORT CHttpAgentT<T, default_port>::GetStatusCode(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return 0;
+
+	return pHttpObj->GetStatusCode();
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetWSMessageState(CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);
+}
+
+template<class T, USHORT default_port> inline typename CHttpAgentT<T, default_port>::THttpObj* CHttpAgentT<T, default_port>::FindHttpObj(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = nullptr;
+	GetConnectionReserved(dwConnID, (PVOID*)&pHttpObj);
+
+	return pHttpObj;
+}
+
+template<class T, USHORT default_port> inline typename CHttpAgentT<T, default_port>::THttpObj* CHttpAgentT<T, default_port>::FindHttpObj(TAgentSocketObj* pSocketObj)
+{
+	THttpObj* pHttpObj = nullptr;
+	GetConnectionReserved(pSocketObj, (PVOID*)&pHttpObj);
+
+	return pHttpObj;
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::StartHttp(CONNID dwConnID)
+{
+	if(IsHttpAutoStart())
+	{
+		::SetLastError(ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return StartHttp(pSocketObj);
+}
+
+template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::StartHttp(TAgentSocketObj* pSocketObj)
+{
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	CReentrantCriSecLock locallock(pSocketObj->csSend);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	THttpObj* pHttpObj = FindHttpObj(pSocketObj);
+
+	if(pHttpObj != nullptr)
+	{
+		::SetLastError(ERROR_ALREADY_INITIALIZED);
+		return FALSE;
+	}
+
+	DoStartHttp(pSocketObj);
+
+	if(!IsSecure())
+		FireHandShake(pSocketObj);
+	else
+	{
+#ifdef _SSL_SUPPORT
+		if(IsSSLAutoHandShake())
+			StartSSLHandShake(pSocketObj);
+#endif
+	}
+
+	return TRUE;
+}
+
+template<class T, USHORT default_port> typename CHttpAgentT<T, default_port>::THttpObj* CHttpAgentT<T, default_port>::DoStartHttp(TAgentSocketObj* pSocketObj)
+{
+	THttpObj* pHttpObj = m_objPool.PickFreeHttpObj(this, pSocketObj);
+	VERIFY(SetConnectionReserved(pSocketObj, pHttpObj));
+
+	return pHttpObj;
+}
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+template class CHttpAgentT<CTcpAgent, HTTP_DEFAULT_PORT>;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLAgent.h"
+
+template class CHttpAgentT<CSSLAgent, HTTPS_DEFAULT_PORT>;
+
+#endif
+
+#endif

+ 216 - 0
HttpAgent.h

@@ -0,0 +1,216 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpAgent.h"
+#include "HttpHelper.h"
+
+#ifdef _HTTP_SUPPORT
+
+template<class T, USHORT default_port> class CHttpAgentT : public IComplexHttpRequester, public T
+{
+	using __super = T;
+	using __super::GetConnectionReserved;
+	using __super::SetConnectionReserved;
+	using __super::SetLastError;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+	using __super::SendPackets;
+	using __super::HasStarted;
+	using __super::GetRemoteHost;
+	using __super::GetFreeSocketObjLockTime;
+	using __super::GetFreeSocketObjPool;
+	using __super::GetFreeSocketObjHold;
+
+	using __super::IsSecure;
+	using __super::FireHandShake;
+	using __super::FindSocketObj;
+
+#ifdef _SSL_SUPPORT
+	using __super::StartSSLHandShake;
+	using __super::IsSSLAutoHandShake;
+#endif
+
+protected:
+	using CHttpObjPool	= CHttpObjPoolT<FALSE, CHttpAgentT, TAgentSocketObj>;
+	using THttpObj		= THttpObjT<CHttpAgentT, TAgentSocketObj>;
+
+	friend typename		CHttpAgentT::THttpObj;
+
+public:
+	virtual BOOL SendRequest(CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0);
+	virtual BOOL SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0);
+	virtual BOOL SendChunkData(CONNID dwConnID, const BYTE* pData = nullptr, int iLength = 0, LPCSTR lpszExtensions = nullptr);
+
+	virtual BOOL SendPost(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+		{return SendRequest(dwConnID, HTTP_METHOD_POST, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
+	virtual BOOL SendPut(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+		{return SendRequest(dwConnID, HTTP_METHOD_PUT, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
+	virtual BOOL SendPatch(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+		{return SendRequest(dwConnID, HTTP_METHOD_PATCH, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
+	virtual BOOL SendGet(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(dwConnID, HTTP_METHOD_GET, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendDelete(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(dwConnID, HTTP_METHOD_DELETE, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendHead(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(dwConnID, HTTP_METHOD_HEAD, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendTrace(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(dwConnID, HTTP_METHOD_TRACE, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendOptions(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(dwConnID, HTTP_METHOD_OPTIONS, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendConnect(CONNID dwConnID, LPCSTR lpszHost, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(dwConnID, HTTP_METHOD_CONNECT, lpszHost, lpHeaders, iHeaderCount);}
+
+	virtual BOOL SendWSMessage(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0);
+
+	virtual BOOL StartHttp(CONNID dwConnID);
+
+public:
+	virtual void SetUseCookie(BOOL bUseCookie)					{ENSURE_HAS_STOPPED(); m_pCookieMgr		= bUseCookie ? &g_CookieMgr : nullptr;}
+	virtual void SetHttpAutoStart(BOOL bAutoStart)				{ENSURE_HAS_STOPPED(); m_bHttpAutoStart	= bAutoStart;}
+	virtual void SetLocalVersion(EnHttpVersion enLocalVersion)	{ENSURE_HAS_STOPPED(); m_enLocalVersion	= enLocalVersion;}
+
+	virtual BOOL IsUseCookie()									{return m_pCookieMgr != nullptr;}
+	virtual BOOL IsHttpAutoStart()								{return m_bHttpAutoStart;}
+	virtual EnHttpVersion GetLocalVersion()						{return m_enLocalVersion;}
+
+	virtual BOOL IsUpgrade(CONNID dwConnID);
+	virtual BOOL IsKeepAlive(CONNID dwConnID);
+	virtual USHORT GetVersion(CONNID dwConnID);
+	virtual ULONGLONG GetContentLength(CONNID dwConnID);
+	virtual LPCSTR GetContentType(CONNID dwConnID);
+	virtual LPCSTR GetContentEncoding(CONNID dwConnID);
+	virtual LPCSTR GetTransferEncoding(CONNID dwConnID);
+	virtual EnHttpUpgradeType GetUpgradeType(CONNID dwConnID);
+	virtual USHORT GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc = nullptr);
+
+	virtual BOOL GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue);
+	virtual BOOL GetHeaders(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount);
+	virtual BOOL GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount);
+	virtual BOOL GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount);
+
+	virtual BOOL GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue);
+	virtual BOOL GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount);
+
+	virtual USHORT GetStatusCode(CONNID dwConnID);
+
+	virtual BOOL GetWSMessageState(CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain);
+
+private:
+	BOOL StartHttp(TAgentSocketObj* pSocketObj);
+	THttpObj* DoStartHttp(TAgentSocketObj* pSocketObj);
+
+private:
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual EnHandleResult FireConnect(TAgentSocketObj* pSocketObj);
+	virtual EnHandleResult DoFireConnect(TAgentSocketObj* pSocketObj);
+	virtual EnHandleResult DoFireHandShake(TAgentSocketObj* pSocketObj);
+	virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength);
+	virtual EnHandleResult DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
+	virtual EnHandleResult DoFireShutdown();
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
+
+	EnHandleResult DoFireSuperReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return __super::DoFireReceive(pSocketObj, pData, iLength);}
+
+	EnHttpParseResult FireMessageBegin(TAgentSocketObj* pSocketObj)
+		{return m_pListener->OnMessageBegin((IHttpAgent*)this, pSocketObj->connID);}
+	EnHttpParseResult FireRequestLine(TAgentSocketObj* pSocketObj, LPCSTR lpszMethod, LPCSTR lpszUrl)
+		{return m_pListener->OnRequestLine((IHttpAgent*)this, pSocketObj->connID, lpszMethod, lpszUrl);}
+	EnHttpParseResult FireStatusLine(TAgentSocketObj* pSocketObj, USHORT usStatusCode, LPCSTR lpszDesc)
+		{return m_pListener->OnStatusLine((IHttpAgent*)this, pSocketObj->connID, usStatusCode, lpszDesc);}
+	EnHttpParseResult FireHeader(TAgentSocketObj* pSocketObj, LPCSTR lpszName, LPCSTR lpszValue)
+		{return m_pListener->OnHeader((IHttpAgent*)this, pSocketObj->connID, lpszName, lpszValue);}
+	EnHttpParseResult FireHeadersComplete(TAgentSocketObj* pSocketObj)
+		{return m_pListener->OnHeadersComplete((IHttpAgent*)this, pSocketObj->connID);}
+	EnHttpParseResult FireBody(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnBody((IHttpAgent*)this, pSocketObj->connID, pData, iLength);}
+	EnHttpParseResult FireChunkHeader(TAgentSocketObj* pSocketObj, int iLength)
+		{return m_pListener->OnChunkHeader((IHttpAgent*)this, pSocketObj->connID, iLength);}
+	EnHttpParseResult FireChunkComplete(TAgentSocketObj* pSocketObj)
+		{return m_pListener->OnChunkComplete((IHttpAgent*)this, pSocketObj->connID);}
+	EnHttpParseResult FireMessageComplete(TAgentSocketObj* pSocketObj)
+		{return m_pListener->OnMessageComplete((IHttpAgent*)this, pSocketObj->connID);}
+	EnHttpParseResult FireUpgrade(TAgentSocketObj* pSocketObj, EnHttpUpgradeType enUpgradeType)
+		{return m_pListener->OnUpgrade((IHttpAgent*)this, pSocketObj->connID, enUpgradeType);}
+	EnHttpParseResult FireParseError(TAgentSocketObj* pSocketObj, int iErrorCode, LPCSTR lpszErrorDesc)
+		{return m_pListener->OnParseError((IHttpAgent*)this, pSocketObj->connID, iErrorCode, lpszErrorDesc);}
+
+	EnHandleResult FireWSMessageHeader(TAgentSocketObj* pSocketObj, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
+		{return m_pListener->OnWSMessageHeader((IHttpAgent*)this, pSocketObj->connID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
+	EnHandleResult FireWSMessageBody(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnWSMessageBody((IHttpAgent*)this, pSocketObj->connID, pData, iLength);}
+	EnHandleResult FireWSMessageComplete(TAgentSocketObj* pSocketObj)
+		{return m_pListener->OnWSMessageComplete((IHttpAgent*)this, pSocketObj->connID);}
+
+	inline THttpObj* FindHttpObj(CONNID dwConnID);
+	inline THttpObj* FindHttpObj(TAgentSocketObj* pSocketObj);
+
+	CCookieMgr* GetCookieMgr()						{return m_pCookieMgr;}
+	LPCSTR GetRemoteDomain(TAgentSocketObj* pSocketObj)	{LPCSTR lpszDomain; pSocketObj->GetRemoteHost(&lpszDomain); return lpszDomain;}
+
+public:
+	CHttpAgentT(IHttpAgentListener* pListener)
+	: T					(pListener)
+	, m_pListener		(pListener)
+	, m_pCookieMgr		(&g_CookieMgr)
+	, m_bHttpAutoStart	(TRUE)
+	, m_enLocalVersion	(DEFAULT_HTTP_VERSION)
+	{
+
+	}
+
+	virtual ~CHttpAgentT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	IHttpAgentListener*	m_pListener;
+	CCookieMgr*			m_pCookieMgr;
+	EnHttpVersion		m_enLocalVersion;
+
+	BOOL				m_bHttpAutoStart;
+
+	CHttpObjPool		m_objPool;
+};
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+typedef CHttpAgentT<CTcpAgent, HTTP_DEFAULT_PORT> CHttpAgent;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLAgent.h"
+
+typedef CHttpAgentT<CSSLAgent, HTTPS_DEFAULT_PORT> CHttpsAgent;
+
+#endif
+
+#endif

+ 598 - 0
HttpClient.cpp

@@ -0,0 +1,598 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "HttpClient.h"
+
+#ifdef _HTTP_SUPPORT
+
+template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::CheckParams()
+{
+	if(m_enLocalVersion != HV_1_1 && m_enLocalVersion != HV_1_0)
+	{
+		SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	return __super::CheckParams();
+}
+
+template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::SendRequest(LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	USES_CONVERSION;
+
+	WSABUF szBuffer[2];
+	CStringA strHeader;
+
+	LPCSTR lpszHost	= nullptr;
+	USHORT usPort	= 0;
+	BOOL bConnect	= (stricmp(lpszMethod, HTTP_METHOD_CONNECT) == 0);
+
+	if(!bConnect)
+	{
+		GetRemoteHost(&lpszHost, &usPort);
+		if(usPort == default_port) usPort = 0;
+	}
+
+	CStringA strPath;
+	::AdjustRequestPath(bConnect, lpszPath, strPath);
+
+	m_objHttp.SetRequestPath(lpszMethod, strPath);
+	m_objHttp.ReloadCookies();
+
+	::MakeRequestLine(lpszMethod, strPath, m_enLocalVersion, strHeader);
+	::MakeHeaderLines(lpHeaders, iHeaderCount, &m_objHttp.GetCookieMap(), iLength, TRUE, -1, lpszHost, usPort, strHeader);
+	::MakeHttpPacket(strHeader, pBody, iLength, szBuffer);
+
+	return SendPackets(szBuffer, 2);
+}
+
+template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::SendLocalFile(LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount)
+{
+	CFile file;
+	CFileMapping fmap;
+
+	HRESULT hr = ::ReadSmallFile(CA2T(lpszFileName), file, fmap);
+
+	if(FAILED(hr))
+	{
+		::SetLastError(hr);
+		return FALSE;
+	}
+
+	return SendRequest(lpszMethod, lpszPath, lpHeaders, iHeaderCount, (BYTE*)fmap, (int)fmap.Size());
+}
+
+template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::SendChunkData(const BYTE* pData, int iLength, LPCSTR lpszExtensions)
+{
+	char szLen[12];
+	WSABUF bufs[5];
+
+	int iCount = MakeChunkPackage(pData, iLength, lpszExtensions, szLen, bufs);
+
+	return SendPackets(bufs, iCount);
+}
+
+template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::SendWSMessage(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
+{
+	ASSERT(lpszMask);
+
+	WSABUF szBuffer[2];
+	BYTE szHeader[HTTP_MAX_WS_HEADER_LEN];
+
+	unique_ptr<BYTE[]> szData = make_unique<BYTE[]>(iLength);
+	memcpy(szData.get(), pData, iLength);
+
+	if(!::MakeWSPacket(bFinal, iReserved, iOperationCode, lpszMask, szData.get(), iLength, ullBodyLen, szHeader, szBuffer))
+		return FALSE;
+
+	return SendPackets(szBuffer, 2);
+}
+
+template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::StartHttp()
+{
+	if(IsHttpAutoStart())
+	{
+		::SetLastError(ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	if(!IsConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	CReentrantCriSecLock locallock(m_csHttp);
+
+	if(!IsConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(m_objHttp.IsValid())
+	{
+		::SetLastError(ERROR_ALREADY_INITIALIZED);
+		return FALSE;
+	}
+
+	DoStartHttp();
+
+	if(!IsSecure())
+		FireHandShake();
+	else
+	{
+#ifdef _SSL_SUPPORT
+		if(IsSSLAutoHandShake())
+			StartSSLHandShakeNoCheck();
+#endif
+	}
+
+	return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
+{
+	CleanupRequestResult();
+
+	if(!__super::Start(lpszRemoteAddress, usPort, TRUE, lpszBindAddress, usLocalPort))
+		return FALSE;
+
+	BOOL isOK = WaitForEvent(m_dwConnectTimeout);
+
+	if(!isOK || m_enProgress != HSRP_DONE)
+	{
+		int ec = m_enProgress == HSRP_WAITING ? ERROR_TIMEDOUT : ERROR_CONNREFUSED;
+
+		if(!isOK) Stop();
+
+		SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ec);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::SendRequest(LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+{
+	CleanupRequestResult();
+
+	if(!__super::SendRequest(lpszMethod, lpszPath, lpHeaders, iHeaderCount, pBody, iLength))
+		return FALSE;
+
+	BOOL isOK = WaitForEvent(m_dwRequestTimeout);
+
+	if(!isOK || m_enProgress != HSRP_DONE)
+	{
+		int ec = m_enProgress == HSRP_WAITING ? ERROR_TIMEDOUT :
+				(m_enProgress == HSRP_CLOSE ? ERROR_CONNABORTED : ERROR_INVALID_DATA);
+
+		if(!isOK) Stop();
+
+		SetLastError(SE_DATA_SEND, __FUNCTION__, ec);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::SendWSMessage(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
+{
+	CleanupRequestResult();
+
+	if(!__super::SendWSMessage(bFinal, iReserved, iOperationCode, lpszMask, pData, iLength, ullBodyLen))
+		return FALSE;
+
+	BOOL isOK = WaitForEvent(m_dwRequestTimeout);
+
+	if(!isOK || m_enProgress != HSRP_DONE)
+	{
+		int ec = m_enProgress == HSRP_WAITING ? ERROR_TIMEDOUT :
+				(m_enProgress == HSRP_CLOSE ? ERROR_CONNABORTED : ERROR_INVALID_DATA);
+
+		if(!isOK) Stop();
+
+		SetLastError(SE_DATA_SEND, __FUNCTION__, ec);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::OpenUrl(LPCSTR lpszMethod, LPCSTR lpszUrl, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength, BOOL bForceReconnect)
+{
+	BOOL		bHttps;
+	USHORT		usPort;
+	CStringA	strHost;
+	CStringA	strPath;
+
+	if(!IsHttpAutoStart())
+	{
+		SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_NOT_SUPPORTED);
+		return FALSE;
+	}
+
+	if(!::ParseUrl(lpszUrl, bHttps, strHost, usPort, strPath))
+	{
+		SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ERROR_ADDRNOTAVAIL);
+		return FALSE;
+	}
+
+	if((bHttps && default_port == HTTP_DEFAULT_PORT) || (!bHttps && default_port == HTTPS_DEFAULT_PORT))
+	{
+		SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ERROR_PROTO);
+		return FALSE;
+	}
+
+	if(HasStarted())
+	{
+		BOOL bNeedStop = bForceReconnect;
+
+		if(!bNeedStop)
+		{
+			LPCSTR lpszHost	= nullptr;
+			USHORT usPort2	= 0;
+
+			GetRemoteHost(&lpszHost, &usPort2);
+
+			if(usPort != usPort2)
+				bNeedStop = TRUE;
+			else
+			{
+				HP_SCOPE_HOST host(CA2T((LPCSTR)strHost));
+
+				if(lstricmp(host.name, CA2T(lpszHost)) != 0)
+					bNeedStop = TRUE;
+			}
+		}
+
+		if(bNeedStop) Stop();
+	}
+
+	EnServiceState state = GetState();
+
+	if(state != SS_STARTED)
+	{
+		if(state == SS_STARTING)
+		{
+			do 
+			{
+				::WaitFor(10);
+				state = GetState();
+			} while(state != SS_STARTED && state != SS_STOPPED);
+		}
+		else
+		{
+			while(state != SS_STOPPED)
+			{
+				::WaitFor(10);
+				state = GetState();
+			}
+
+			Start(CA2T(strHost), usPort, FALSE, nullptr);
+			state = GetState();
+		}
+
+		if(state == SS_STOPPED)
+			return FALSE;
+	}
+
+	if(iLength < 0 && !::IsStrEmptyA((LPCSTR)pBody))
+		return SendLocalFile((LPCSTR)pBody, lpszMethod, strPath, lpHeaders, iHeaderCount);
+
+	return SendRequest(lpszMethod, strPath, lpHeaders, iHeaderCount, pBody, iLength);
+}
+
+template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::CleanupRequestResult()
+{
+	m_pHttpObj	 = &m_objHttp;
+	m_enProgress = HSRP_WAITING;
+
+	m_szBuffer.Free();
+	m_objHttp2.Reset();
+	m_evWait.Reset();
+	
+	return TRUE;
+}
+
+template<class T, USHORT default_port> void CHttpSyncClientT<T, default_port>::SetRequestEvent(EnHttpSyncRequestProgress enProgress, BOOL bCopyHttpObj)
+{
+	if(m_enProgress != HSRP_WAITING)
+		return;
+
+	m_enProgress = enProgress;
+
+	if(bCopyHttpObj)
+	{
+		m_objHttp2.CopyData(m_objHttp);
+		m_objHttp2.CopyWSContext(m_objHttp);
+
+		m_pHttpObj = &m_objHttp2;
+	}
+
+	m_evWait.Set();
+}
+
+template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::WaitForEvent(DWORD dwWait)
+{
+	LONG lTimeout = INFINITE;
+
+	if(dwWait != 0)
+		lTimeout  = (LONG)dwWait;
+
+	return (m_evWait.Wait(lTimeout) > TIMEOUT);
+}
+
+template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::GetResponseBody(LPCBYTE* lpszBody, int* iLength)
+{
+	ASSERT(lpszBody && iLength);
+
+	*lpszBody = m_szBuffer.Ptr();
+	*iLength  = (int)m_szBuffer.Size();
+
+	return TRUE;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnHandShake(ITcpClient* pSender, CONNID dwConnID)
+{
+	EnHandleResult rs = HR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnHandShake(pSender, dwConnID);
+
+	if(rs != HR_ERROR)
+		SetRequestEvent(HSRP_DONE, FALSE);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnClose(ITcpClient* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
+{
+	EnHandleResult rs = HR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnClose(pSender, dwConnID, enOperation, iErrorCode);
+
+	SetRequestEvent(HSRP_CLOSE);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnBody(pSender, dwConnID, pData, iLength);
+
+	if(rs != HPR_ERROR)
+		m_szBuffer.Cat(pData, iLength);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnMessageComplete(IHttpClient* pSender, CONNID dwConnID)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnMessageComplete(pSender, dwConnID);
+
+	if(rs != HPR_ERROR)
+	{
+		if(GetUpgradeType() == HUT_NONE)
+			SetRequestEvent(HSRP_DONE);
+	}
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnUpgrade(IHttpClient* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnUpgrade(pSender, dwConnID, enUpgradeType);
+
+	if(rs != HPR_ERROR)
+	{
+		if(enUpgradeType == HUT_WEB_SOCKET)
+		{
+			SetRequestEvent(HSRP_DONE);
+			rs = HPR_OK;
+		}
+		else
+		{
+			SetRequestEvent(HSRP_ERROR);
+			rs = HPR_ERROR;
+		}
+	}
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnParseError(IHttpClient* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnParseError(pSender, dwConnID, iErrorCode, lpszErrorDesc);
+
+	SetRequestEvent(HSRP_ERROR);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnWSMessageBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+{
+	EnHandleResult rs = HR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnWSMessageBody(pSender, dwConnID, pData, iLength);
+
+	if(rs != HR_ERROR)
+		m_szBuffer.Cat(pData, iLength);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnWSMessageComplete(IHttpClient* pSender, CONNID dwConnID)
+{
+	EnHandleResult rs = HR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnWSMessageComplete(pSender, dwConnID);
+
+	if(rs != HR_ERROR)
+		SetRequestEvent(HSRP_DONE);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnPrepareConnect(ITcpClient* pSender, CONNID dwConnID, SOCKET socket)
+{
+	EnHandleResult rs = HR_OK;
+
+	if(m_pListener2 != nullptr)
+		return m_pListener2->OnPrepareConnect(pSender, dwConnID, socket);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnConnect(ITcpClient* pSender, CONNID dwConnID)
+{
+	EnHandleResult rs = HR_OK;
+
+	if(m_pListener2 != nullptr)
+		return m_pListener2->OnConnect(pSender, dwConnID);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnSend(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+{
+	EnHandleResult rs = HR_OK;
+
+	if(m_pListener2 != nullptr)
+		return m_pListener2->OnSend(pSender, dwConnID, pData, iLength);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+{
+	EnHandleResult rs = HR_OK;
+
+	if(m_pListener2 != nullptr)
+		return m_pListener2->OnReceive(pSender, dwConnID, pData, iLength);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnMessageBegin(IHttpClient* pSender, CONNID dwConnID)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnMessageBegin(pSender, dwConnID);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnStatusLine(IHttpClient* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnStatusLine(pSender, dwConnID, usStatusCode, lpszDesc);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnHeader(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnHeader(pSender, dwConnID, lpszName, lpszValue);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnHeadersComplete(IHttpClient* pSender, CONNID dwConnID)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnHeadersComplete(pSender, dwConnID);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnChunkHeader(IHttpClient* pSender, CONNID dwConnID, int iLength)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnChunkHeader(pSender, dwConnID, iLength);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnChunkComplete(IHttpClient* pSender, CONNID dwConnID)
+{
+	EnHttpParseResult rs = HPR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnChunkComplete(pSender, dwConnID);
+
+	return rs;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnWSMessageHeader(IHttpClient* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
+{
+	EnHandleResult rs = HR_OK;
+
+	if(m_pListener2 != nullptr)
+		rs = m_pListener2->OnWSMessageHeader(pSender, dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);
+
+	return rs;
+}
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+template class CHttpClientT<IHttpRequester, CTcpClient, HTTP_DEFAULT_PORT>;
+template class CHttpSyncClientT<CTcpClient, HTTP_DEFAULT_PORT>;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLClient.h"
+
+template class CHttpClientT<IHttpRequester, CSSLClient, HTTPS_DEFAULT_PORT>;
+template class CHttpSyncClientT<CSSLClient, HTTPS_DEFAULT_PORT>;
+
+#endif
+
+#endif

+ 397 - 0
HttpClient.h

@@ -0,0 +1,397 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpClient.h"
+#include "HttpHelper.h"
+
+#ifdef _HTTP_SUPPORT
+
+template<class R, class T, USHORT default_port> class CHttpClientT : public R, public T
+{
+	using __super = T;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+	using __super::SendPackets;
+	using __super::HasStarted;
+	using __super::GetRemoteHost;
+
+	using __super::IsSecure;
+	using __super::IsConnected;
+	using __super::FireHandShake;
+
+#ifdef _SSL_SUPPORT
+	using __super::IsSSLAutoHandShake;
+	using __super::StartSSLHandShakeNoCheck;
+#endif
+
+protected:
+	using __super::SetLastError;
+
+	using THttpObj	= THttpObjT<CHttpClientT, IHttpClient>;
+	friend typename	CHttpClientT::THttpObj;
+
+public:
+	virtual BOOL SendRequest(LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0);
+	virtual BOOL SendLocalFile(LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0);
+	virtual BOOL SendChunkData(const BYTE* pData = nullptr, int iLength = 0, LPCSTR lpszExtensions = nullptr);
+
+	virtual BOOL SendPost(LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+		{return SendRequest(HTTP_METHOD_POST, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
+	virtual BOOL SendPut(LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+		{return SendRequest(HTTP_METHOD_PUT, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
+	virtual BOOL SendPatch(LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
+		{return SendRequest(HTTP_METHOD_PATCH, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
+	virtual BOOL SendGet(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(HTTP_METHOD_GET, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendDelete(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(HTTP_METHOD_DELETE, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendHead(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(HTTP_METHOD_HEAD, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendTrace(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(HTTP_METHOD_TRACE, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendOptions(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(HTTP_METHOD_OPTIONS, lpszPath, lpHeaders, iHeaderCount);}
+	virtual BOOL SendConnect(LPCSTR lpszHost, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
+		{return SendRequest(HTTP_METHOD_CONNECT, lpszHost, lpHeaders, iHeaderCount);}
+
+	virtual BOOL SendWSMessage(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0);
+
+	virtual BOOL StartHttp();
+
+public:
+	virtual void SetUseCookie(BOOL bUseCookie)					{ENSURE_HAS_STOPPED(); m_pCookieMgr		= bUseCookie ? &g_CookieMgr : nullptr;}
+	virtual void SetHttpAutoStart(BOOL bAutoStart)				{ENSURE_HAS_STOPPED(); m_bHttpAutoStart	= bAutoStart;}
+	virtual void SetLocalVersion(EnHttpVersion enLocalVersion)	{ENSURE_HAS_STOPPED(); m_enLocalVersion	= enLocalVersion;}
+
+	virtual BOOL IsUseCookie()									{return m_pCookieMgr != nullptr;}
+	virtual BOOL IsHttpAutoStart()								{return m_bHttpAutoStart;}
+	virtual EnHttpVersion GetLocalVersion()						{return m_enLocalVersion;}
+
+	virtual BOOL IsUpgrade()
+		{return m_objHttp.IsUpgrade();}
+	virtual BOOL IsKeepAlive()
+		{return m_objHttp.IsKeepAlive();}
+	virtual USHORT GetVersion()
+		{return m_objHttp.GetVersion();}
+	virtual ULONGLONG GetContentLength()
+		{return m_objHttp.GetContentLength();}
+	virtual LPCSTR GetContentType()
+		{return m_objHttp.GetContentType();}
+	virtual LPCSTR GetContentEncoding()
+		{return m_objHttp.GetContentEncoding();}
+	virtual LPCSTR GetTransferEncoding()
+		{return m_objHttp.GetTransferEncoding();}
+	virtual EnHttpUpgradeType GetUpgradeType()
+		{return m_objHttp.GetUpgradeType();}
+	virtual USHORT GetParseErrorCode(LPCSTR* lpszErrorDesc = nullptr)
+		{return m_objHttp.GetParseErrorCode(lpszErrorDesc);}
+
+	virtual BOOL GetHeader(LPCSTR lpszName, LPCSTR* lpszValue)
+		{return m_objHttp.GetHeader(lpszName, lpszValue);}
+	virtual BOOL GetHeaders(LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
+		{return m_objHttp.GetHeaders(lpszName, lpszValue, dwCount);}
+	virtual BOOL GetAllHeaders(THeader lpHeaders[], DWORD& dwCount)
+		{return m_objHttp.GetAllHeaders(lpHeaders, dwCount);}
+	virtual BOOL GetAllHeaderNames(LPCSTR lpszName[], DWORD& dwCount)
+		{return m_objHttp.GetAllHeaderNames(lpszName, dwCount);}
+
+	virtual BOOL GetCookie(LPCSTR lpszName, LPCSTR* lpszValue)
+		{return m_objHttp.GetCookie(lpszName, lpszValue);}
+	virtual BOOL GetAllCookies(TCookie lpCookies[], DWORD& dwCount)
+		{return m_objHttp.GetAllCookies(lpCookies, dwCount);}
+
+	virtual USHORT GetStatusCode()
+		{return m_objHttp.GetStatusCode();}
+
+	virtual BOOL GetWSMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
+		{return m_objHttp.GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);}
+
+private:
+	virtual BOOL CheckParams();
+
+	void DoStartHttp()
+		{m_objHttp.SetValid(TRUE);}
+
+	virtual EnHandleResult FireConnect()
+		{return m_bHttpAutoStart ? __super::FireConnect() : __super::DoFireConnect(this);}
+
+	virtual EnHandleResult DoFireConnect(ITcpClient* pSender)
+	{
+		ASSERT(pSender == this);
+
+		DoStartHttp();
+
+		EnHandleResult result = __super::DoFireConnect(this);
+
+		if(result == HR_ERROR)
+			m_objHttp.SetValid(FALSE);
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
+		{ASSERT(pSender == this); return m_objHttp.IsValid() ? m_objHttp.Execute(pData, iLength) : __super::DoFireReceive(pSender, pData, iLength);}
+
+	EnHandleResult DoFireSuperReceive(IHttpClient* pSender, const BYTE* pData, int iLength)
+		{ASSERT(pSender == (IHttpClient*)this); return __super::DoFireReceive(pSender, pData, iLength);}
+
+	virtual EnHandleResult DoFireClose(ITcpClient* pSender, EnSocketOperation enOperation, int iErrorCode)
+	{
+		ASSERT(pSender == (IHttpClient*)this);
+
+		m_objHttp.CheckBodyIdentityEof();
+
+		return __super::DoFireClose(pSender, enOperation, iErrorCode);
+	}
+
+	virtual void Reset()
+	{
+		m_objHttp.Reset();
+
+		__super::Reset();
+	}
+
+	EnHttpParseResult FireMessageBegin(IHttpClient* pSender)
+		{return m_pListener->OnMessageBegin(pSender, pSender->GetConnectionID());}
+	EnHttpParseResult FireRequestLine(IHttpClient* pSender, LPCSTR lpszMethod, LPCSTR lpszUrl)
+		{return m_pListener->OnRequestLine(pSender, pSender->GetConnectionID(), lpszMethod, lpszUrl);}
+	EnHttpParseResult FireStatusLine(IHttpClient* pSender, USHORT usStatusCode, LPCSTR lpszDesc)
+		{return m_pListener->OnStatusLine(pSender, pSender->GetConnectionID(), usStatusCode, lpszDesc);}
+	EnHttpParseResult FireHeader(IHttpClient* pSender, LPCSTR lpszName, LPCSTR lpszValue)
+		{return m_pListener->OnHeader(pSender, pSender->GetConnectionID(), lpszName, lpszValue);}
+	EnHttpParseResult FireHeadersComplete(IHttpClient* pSender)
+		{return m_pListener->OnHeadersComplete(pSender, pSender->GetConnectionID());}
+	EnHttpParseResult FireBody(IHttpClient* pSender, const BYTE* pData, int iLength)
+		{return m_pListener->OnBody(pSender, pSender->GetConnectionID(), pData, iLength);}
+	EnHttpParseResult FireChunkHeader(IHttpClient* pSender, int iLength)
+		{return m_pListener->OnChunkHeader(pSender, pSender->GetConnectionID(), iLength);}
+	EnHttpParseResult FireChunkComplete(IHttpClient* pSender)
+		{return m_pListener->OnChunkComplete(pSender, pSender->GetConnectionID());}
+	EnHttpParseResult FireMessageComplete(IHttpClient* pSender)
+		{return m_pListener->OnMessageComplete(pSender, pSender->GetConnectionID());}
+	EnHttpParseResult FireUpgrade(IHttpClient* pSender, EnHttpUpgradeType enUpgradeType)
+		{return m_pListener->OnUpgrade(pSender, pSender->GetConnectionID(), enUpgradeType);}
+	EnHttpParseResult FireParseError(IHttpClient* pSender, int iErrorCode, LPCSTR lpszErrorDesc)
+		{return m_pListener->OnParseError(pSender, pSender->GetConnectionID(), iErrorCode, lpszErrorDesc);}
+
+	EnHandleResult FireWSMessageHeader(IHttpClient* pSender, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
+		{return m_pListener->OnWSMessageHeader(pSender, pSender->GetConnectionID(), bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
+	EnHandleResult FireWSMessageBody(IHttpClient* pSender, const BYTE* pData, int iLength)
+		{return m_pListener->OnWSMessageBody(pSender, pSender->GetConnectionID(), pData, iLength);}
+	EnHandleResult FireWSMessageComplete(IHttpClient* pSender)
+		{return m_pListener->OnWSMessageComplete(pSender, pSender->GetConnectionID());}
+
+	CCookieMgr* GetCookieMgr()						{return m_pCookieMgr;}
+	LPCSTR GetRemoteDomain(IHttpClient* pSender)	{LPCSTR lpszDomain; GetRemoteHost(&lpszDomain); return lpszDomain;}
+
+public:
+	CHttpClientT(IHttpClientListener* pListener)
+	: T					(pListener)
+	, m_pListener		(pListener)
+	, m_pCookieMgr		(&g_CookieMgr)
+	, m_bHttpAutoStart	(TRUE)
+	, m_enLocalVersion	(DEFAULT_HTTP_VERSION)
+	, m_objHttp			(FALSE, this, (IHttpClient*)this)
+	{
+
+	}
+
+	virtual ~CHttpClientT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	BOOL					m_bHttpAutoStart;
+
+	IHttpClientListener*	m_pListener;
+	CCookieMgr*				m_pCookieMgr;
+	EnHttpVersion			m_enLocalVersion;
+
+	CReentrantCriSec		m_csHttp;
+
+protected:
+	THttpObj				m_objHttp;
+};
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+template<class T, USHORT default_port> class CHttpSyncClientT : public CHttpClientT<IHttpSyncRequester, T, default_port>, private CHttpClientListener
+{
+	using __super = CHttpClientT<IHttpSyncRequester, T, default_port>;
+	using __super::m_objHttp;
+	using __super::SetLastError;
+
+	using typename __super::THttpObj;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+	using __super::HasStarted;
+	using __super::GetRemoteHost;
+	using __super::SendLocalFile;
+	using __super::IsHttpAutoStart;
+
+public:
+	virtual BOOL Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect = TRUE, LPCTSTR lpszBindAddress = nullptr, USHORT usLocalPort = 0);
+public:
+	virtual BOOL SendRequest(LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0);
+	virtual BOOL SendWSMessage(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4] = nullptr, const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0);
+
+public:
+	virtual BOOL IsUpgrade()
+		{return m_pHttpObj->IsUpgrade();}
+	virtual BOOL IsKeepAlive()
+		{return m_pHttpObj->IsKeepAlive();}
+	virtual USHORT GetVersion()
+		{return m_pHttpObj->GetVersion();}
+	virtual ULONGLONG GetContentLength()
+		{return m_pHttpObj->GetContentLength();}
+	virtual LPCSTR GetContentType()
+		{return m_pHttpObj->GetContentType();}
+	virtual LPCSTR GetContentEncoding()
+		{return m_pHttpObj->GetContentEncoding();}
+	virtual LPCSTR GetTransferEncoding()
+		{return m_pHttpObj->GetTransferEncoding();}
+	virtual EnHttpUpgradeType GetUpgradeType()
+		{return m_pHttpObj->GetUpgradeType();}
+	virtual USHORT GetParseErrorCode(LPCSTR* lpszErrorDesc = nullptr)
+		{return m_pHttpObj->GetParseErrorCode(lpszErrorDesc);}
+
+	virtual BOOL GetHeader(LPCSTR lpszName, LPCSTR* lpszValue)
+		{return m_pHttpObj->GetHeader(lpszName, lpszValue);}
+	virtual BOOL GetHeaders(LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
+		{return m_pHttpObj->GetHeaders(lpszName, lpszValue, dwCount);}
+	virtual BOOL GetAllHeaders(THeader lpHeaders[], DWORD& dwCount)
+		{return m_pHttpObj->GetAllHeaders(lpHeaders, dwCount);}
+	virtual BOOL GetAllHeaderNames(LPCSTR lpszName[], DWORD& dwCount)
+		{return m_pHttpObj->GetAllHeaderNames(lpszName, dwCount);}
+
+	virtual BOOL GetCookie(LPCSTR lpszName, LPCSTR* lpszValue)
+		{return m_pHttpObj->GetCookie(lpszName, lpszValue);}
+	virtual BOOL GetAllCookies(TCookie lpCookies[], DWORD& dwCount)
+		{return m_pHttpObj->GetAllCookies(lpCookies, dwCount);}
+
+	virtual USHORT GetStatusCode()
+		{return m_pHttpObj->GetStatusCode();}
+
+	virtual BOOL GetWSMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
+		{return m_pHttpObj->GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);}
+
+public:
+	virtual BOOL OpenUrl(LPCSTR lpszMethod, LPCSTR lpszUrl, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0, BOOL bForceReconnect = FALSE);
+	virtual BOOL CleanupRequestResult();
+
+public:
+	virtual BOOL GetResponseBody	(LPCBYTE* lpszBody, int* iLength);
+
+	virtual void SetConnectTimeout	(DWORD dwConnectTimeout)	{ENSURE_HAS_STOPPED(); m_dwConnectTimeout = dwConnectTimeout;}
+	virtual void SetRequestTimeout	(DWORD dwRequestTimeout)	{ENSURE_HAS_STOPPED(); m_dwRequestTimeout = dwRequestTimeout;}
+
+	virtual DWORD GetConnectTimeout	()	{return m_dwConnectTimeout;}
+	virtual DWORD GetRequestTimeout	()	{return m_dwRequestTimeout;}
+
+private:
+	virtual EnHandleResult OnHandShake(ITcpClient* pSender, CONNID dwConnID);
+	virtual EnHandleResult OnClose(ITcpClient* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode);
+
+	virtual EnHttpParseResult OnBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
+	virtual EnHttpParseResult OnMessageComplete(IHttpClient* pSender, CONNID dwConnID);
+	virtual EnHttpParseResult OnUpgrade(IHttpClient* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType);
+	virtual EnHttpParseResult OnParseError(IHttpClient* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc);
+
+	virtual EnHandleResult OnWSMessageBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
+	virtual EnHandleResult OnWSMessageComplete(IHttpClient* pSender, CONNID dwConnID);
+
+	virtual EnHandleResult OnPrepareConnect(ITcpClient* pSender, CONNID dwConnID, SOCKET socket);
+	virtual EnHandleResult OnConnect(ITcpClient* pSender, CONNID dwConnID);
+	virtual EnHandleResult OnSend(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
+	virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
+
+	virtual EnHttpParseResult OnMessageBegin(IHttpClient* pSender, CONNID dwConnID);
+	virtual EnHttpParseResult OnStatusLine(IHttpClient* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc);
+	virtual EnHttpParseResult OnHeader(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue);
+	virtual EnHttpParseResult OnHeadersComplete(IHttpClient* pSender, CONNID dwConnID);
+	virtual EnHttpParseResult OnChunkHeader(IHttpClient* pSender, CONNID dwConnID, int iLength);
+	virtual EnHttpParseResult OnChunkComplete(IHttpClient* pSender, CONNID dwConnID);
+
+	virtual EnHandleResult OnWSMessageHeader(IHttpClient* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen);
+
+
+private:
+	void SetRequestEvent(EnHttpSyncRequestProgress enProgress, BOOL bCopyHttpObj = TRUE);
+	BOOL WaitForEvent(DWORD dwWait);
+
+public:
+	CHttpSyncClientT(IHttpClientListener* pListener = nullptr)
+	: __super				(this)
+	, m_enProgress			(HSRP_DONE)
+	, m_objHttp2			(FALSE, this, (IHttpClient*)this)
+	, m_pHttpObj			(nullptr)
+	, m_pListener2			(pListener)
+	, m_dwConnectTimeout	(DEFAULT_HTTP_SYNC_CONNECT_TIMEOUT)
+	, m_dwRequestTimeout	(DEFAULT_HTTP_SYNC_REQUEST_TIMEOUT)
+	{
+
+	}
+
+	virtual ~CHttpSyncClientT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	DWORD		m_dwConnectTimeout;
+	DWORD		m_dwRequestTimeout;
+
+	CEvt		m_evWait;
+	THttpObj	m_objHttp2;
+
+	THttpObj*				m_pHttpObj;
+	IHttpClientListener*	m_pListener2;
+
+	EnHttpSyncRequestProgress		m_enProgress;
+	CBufferPtrT<BYTE, 16 * 1024>	m_szBuffer;
+};
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+typedef CHttpClientT<IHttpRequester, CTcpClient, HTTP_DEFAULT_PORT>		CHttpClient;
+typedef CHttpSyncClientT<CTcpClient, HTTP_DEFAULT_PORT>					CHttpSyncClient;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLClient.h"
+
+typedef CHttpClientT<IHttpRequester, CSSLClient, HTTPS_DEFAULT_PORT>	CHttpsClient;
+typedef CHttpSyncClientT<CSSLClient, HTTPS_DEFAULT_PORT>				CHttpsSyncClient;
+
+#endif
+
+#endif

+ 927 - 0
HttpCookie.cpp

@@ -0,0 +1,927 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "HttpCookie.h"
+
+#ifdef _HTTP_SUPPORT
+
+static const char* s_short_week[]	= {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+static const char* s_short_month[]	= {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+CCookieMgr g_CookieMgr;
+
+CCookie* CCookie::FromString(const CStringA& strCookie, LPCSTR lpszDefaultDomain, LPCSTR lpszDefaultPath)
+{
+	CStringA strName;
+	CStringA strValue;
+	CStringA strDomain;
+	CStringA strPath;
+
+	int iMaxAge			  = -1;
+	BOOL bHttpOnly		  = FALSE;
+	BOOL bSecure		  = FALSE;
+	EnSameSite enSameSite = SS_UNKNOWN;
+
+	int i		= 0;
+	int iStart	= 0;
+
+	while(TRUE)
+	{
+		CStringA strField = strCookie.Tokenize(COOKIE_FIELD_SEP, iStart);
+		strField.Trim();
+
+		if(i == 0)
+		{
+			ParseFieldKV(strField, strName, strValue, COOKIE_KV_SEP_CHAR);
+
+			if(strName.IsEmpty())
+				return nullptr;
+		}
+		else
+		{
+			if(strField.IsEmpty())
+				break;
+
+			CStringA strKey;
+			CStringA strVal;
+
+			ParseFieldKV(strField, strKey, strVal, COOKIE_KV_SEP_CHAR);
+
+			if(strKey.CompareNoCase(COOKIE_DOMAIN) == 0)
+				strDomain = strVal;
+			else if(strKey.CompareNoCase(COOKIE_PATH) == 0)
+				strPath = strVal;
+			else if(strKey.CompareNoCase(COOKIE_MAX_AGE) == 0 && !strVal.IsEmpty())
+				iMaxAge = atoi(strVal);
+			else if(strKey.CompareNoCase(COOKIE_EXPIRES) == 0 && !strVal.IsEmpty() && iMaxAge == -1)
+			{
+				__time64_t tmExpires = -1;
+
+				if(!ParseExpires(strVal, tmExpires))
+					return nullptr;
+
+				iMaxAge = ExpiresToMaxAge(tmExpires);
+			}
+			else if(strKey.CompareNoCase(COOKIE_HTTPONLY) == 0)
+				bHttpOnly = TRUE;
+			else if(strKey.CompareNoCase(COOKIE_SECURE) == 0)
+				bSecure = TRUE;
+			else if(strKey.CompareNoCase(COOKIE_SAMESITE) == 0)
+			{
+				if(strVal.IsEmpty() || strVal.CompareNoCase(COOKIE_SAMESITE_LAX) == 0)
+					enSameSite = SS_LAX;
+				else if(strVal.CompareNoCase(COOKIE_SAMESITE_STRICT) == 0)
+					enSameSite = SS_STRICT;
+				else if(strVal.CompareNoCase(COOKIE_SAMESITE_NONE) == 0)
+					enSameSite = SS_NONE;
+			}
+		}
+
+		++i;
+	}
+
+	if(!AdjustDomain(strDomain, lpszDefaultDomain) || !AdjustPath(strPath, lpszDefaultPath))
+		return nullptr;
+
+	CCookie* pCookie = new CCookie(strName, strValue, strDomain, strPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
+	ASSERT(pCookie->IsValid());
+
+	return pCookie;
+}
+
+CStringA CCookie::ToString(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, EnSameSite enSameSite)
+{
+	CCookie cookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
+	return cookie.ToString();
+}
+
+BOOL CCookie::ToString(char lpszBuff[], int& iBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, EnSameSite enSameSite)
+{
+	BOOL isOK	 = FALSE;
+	CStringA str = ToString(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
+	int iLength	 = str.GetLength() + 1;
+
+	if(lpszBuff && iBuffLen >= iLength)
+	{
+		memcpy(lpszBuff, (LPCSTR)str, iLength * sizeof(char));
+		isOK = TRUE;
+	}
+
+	iBuffLen = iLength;
+
+	return isOK;
+}
+
+CStringA CCookie::ToString()
+{
+	ASSERT(!name.IsEmpty());
+
+	CStringA strCookie;
+
+	strCookie.AppendFormat("%s=%s", (LPCSTR)name, (LPCSTR)value);
+
+	if(!domain.IsEmpty())
+		strCookie.AppendFormat("; %s=%s", COOKIE_DOMAIN, (LPCSTR)domain);
+	if(!path.IsEmpty())
+		strCookie.AppendFormat("; %s=%s", COOKIE_PATH, (LPCSTR)path);
+	if(expires >= 0)
+		strCookie.AppendFormat("; %s=%s", COOKIE_EXPIRES, (LPCSTR)MakeExpiresStr(expires));
+	if(httpOnly)
+		strCookie.AppendFormat("; %s", COOKIE_HTTPONLY);
+	if(secure)
+		strCookie.AppendFormat("; %s", COOKIE_SECURE);
+	if(sameSite != SS_UNKNOWN)
+		strCookie.AppendFormat("; %s=%s", COOKIE_SAMESITE, sameSite == SS_LAX ? COOKIE_SAMESITE_LAX : (sameSite == SS_STRICT ? COOKIE_SAMESITE_STRICT : COOKIE_SAMESITE_NONE));
+
+	return strCookie;
+}
+
+BOOL CCookie::Match(LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
+{
+	int iLen  = (int)strlen(lpszDomain);
+	int iDiff = iLen - domain.GetLength();
+
+	if(iDiff < 0 || stricmp(lpszDomain + iDiff, domain) != 0)
+		return FALSE;
+	if(iDiff > 0 && *(lpszDomain + iDiff - 1) != COOKIE_DOMAIN_SEP_CHAR)
+		return FALSE;
+	if(strncmp(lpszPath, path, path.GetLength()) != 0)
+		return FALSE;
+
+	return (bHttp || !httpOnly) && (bSecure || !secure);
+}
+
+BOOL CCookie::IsSameDomain(LPCSTR lpszDomain)
+{
+	int iLen  = (int)strlen(lpszDomain);
+	int iDiff = iLen - domain.GetLength();
+
+	LPCSTR lpszLong, lpszShort;
+
+	if(iDiff < 0)
+	{
+		lpszLong  = (LPCSTR)domain + iDiff;
+		lpszShort = lpszDomain;
+	}
+	else
+	{
+		lpszLong  = lpszDomain + iDiff;
+		lpszShort = (LPCSTR)domain;
+	}
+
+	if(stricmp(lpszLong, lpszShort) != 0)
+		return FALSE;
+	if(iDiff != 0 && *(lpszLong - 1) != COOKIE_DOMAIN_SEP_CHAR)
+		return FALSE;
+
+	return TRUE;
+}
+
+void CCookie::ParseFieldKV(const CStringA& strField, CStringA& strKey, CStringA& strVal, char chSep)
+{
+	int i = strField.Find(chSep);
+
+	if(i < 0)
+		strKey = strField;
+	else
+	{
+		strKey = strField.Left(i);
+		strVal = strField.Mid(i + 1);
+
+		strVal.Trim();
+	}
+
+	strKey.Trim();
+}
+
+BOOL CCookie::ParseExpires(LPCSTR lpszExpires, __time64_t& tmExpires)
+{
+	int iLength = (int)strlen(lpszExpires);
+
+	if(iLength == 0 || iLength > 50)
+		return FALSE;
+
+	char szMonth[10];
+	char szZone[10];
+
+	tm t = {0};
+
+	if(sscanf(	lpszExpires, "%*[^, ]%*[, ]%2d%*[-/ ]%8[^-/ ]%*[-/ ]%4d %2d:%2d:%2d %8s", 
+				&t.tm_mday, szMonth, &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec, szZone) != 7)
+		return FALSE;
+
+	if(t.tm_year < 70)
+		t.tm_year += 100;
+	else if (t.tm_year > 100)
+		t.tm_year -= 1900;
+
+	int i	 = 0;
+	int size = _countof(s_short_month);
+
+	for(; i < size; i++)
+	{
+		if(strnicmp(szMonth, s_short_month[i], 3) == 0)
+			break;
+	}
+
+	if(i == size)
+		return FALSE;
+
+	t.tm_mon = i;
+
+	CStringA strZone = szZone;
+
+	int iZone	= 0;
+	int iMix	= 0;
+	int iPos	= strZone.Find('+');
+
+	if(iPos >= 0)
+		iMix = 1;
+	else
+	{
+		iPos = strZone.Find('-');
+
+		if(iPos >= 0)
+			iMix = -1;
+	}
+
+	if(iPos >= 0)
+	{
+		strZone = strZone.Mid(iPos + 1);
+		strZone.Remove(':');
+
+		int val = atoi(strZone);
+
+		if(val > 0)
+		{
+			int minutes	= val % 100;
+			int hours	= val / 100;
+
+			iZone = iMix * (minutes * 60 + hours * 3600);
+		}
+	}
+
+	tmExpires = GetUTCTime(t, iZone);
+
+	return tmExpires >= 0;
+}
+
+BOOL CCookie::AdjustDomain(CStringA& strDomain, LPCSTR lpszDefaultDomain)
+{
+	if(strDomain.IsEmpty() && lpszDefaultDomain)
+		strDomain = lpszDefaultDomain;
+
+	strDomain.TrimLeft(COOKIE_DOMAIN_SEP_CHAR).MakeLower();
+
+	return !strDomain.IsEmpty();
+}
+
+BOOL CCookie::AdjustPath(CStringA& strPath, LPCSTR lpszDefaultPath)
+{
+	if(strPath.IsEmpty() && lpszDefaultPath)
+		strPath = lpszDefaultPath;
+
+	int iLength = strPath.GetLength();
+
+	if(iLength == 0)
+		return FALSE;
+
+	if(strPath.GetAt(iLength - 1) != COOKIE_PATH_SEP_CHAR)
+	{
+		int iPos = strPath.ReverseFind(COOKIE_PATH_SEP_CHAR);
+
+		if(iPos >= 0)
+			strPath = strPath.Left(iPos + 1);
+		else
+			strPath.Empty();
+	}
+
+	if(!strPath.IsEmpty() && strPath.GetAt(0) != COOKIE_PATH_SEP_CHAR)
+		strPath.Insert(0, COOKIE_PATH_SEP_CHAR);
+
+	return !strPath.IsEmpty();
+}
+
+__time64_t CCookie::GetUTCTime(tm& t, int iSecondOffsetTZ)
+{
+	__time64_t v = _mkgmtime64(&t);
+	if(v >= 0) v -= iSecondOffsetTZ;
+
+	return v;
+}
+
+CStringA CCookie::MakeExpiresStr(__time64_t tmExpires)
+{
+	ASSERT( tmExpires >= 0);
+
+	if(tmExpires < 1) tmExpires = 1;
+
+	tm t;
+	VERIFY(_gmtime64(&t, &tmExpires) != nullptr);
+
+	CStringA str;
+	str.Format("%s, %02d-%s-%04d %02d:%02d:%02d GMT", 
+				s_short_week[t.tm_wday], t.tm_mday, s_short_month[t.tm_mon], t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec);
+
+	return str;
+}
+
+BOOL CCookie::MakeExpiresStr(char lpszBuff[], int& iBuffLen, __time64_t tmExpires)
+{
+	BOOL isOK	 = FALSE;
+	CStringA str = MakeExpiresStr(tmExpires);
+	int iLength	 = str.GetLength() + 1;
+
+	if(lpszBuff && iBuffLen >= iLength)
+	{
+		memcpy(lpszBuff, (LPCSTR)str, iLength * sizeof(char));
+		isOK = TRUE;
+	}
+
+	iBuffLen = iLength;
+
+	return isOK;
+}
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+BOOL CCookieMgr::LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists)
+{
+	BOOL isOK	= FALSE;
+	FILE* pFile = nullptr;
+
+	if((pFile = fopen(lpszFile, "r")) == nullptr)
+		goto _ERROR_END;
+
+	{
+		CStringA strDomain;
+		CStringA strPath;
+		CCookie cookie;
+
+		char szBuffer[8192];
+		int iBufferSize			= _countof(szBuffer);
+		__time64_t tmCurrent	= _time64(nullptr);
+		CCookieSet* pCookieSet	= nullptr;
+
+		CWriteLock locallock(m_cs);
+
+		if(!bKeepExists)
+			ClearDomainCookiesNoLock();
+
+		while(fgets(szBuffer, iBufferSize, pFile) != nullptr)
+		{
+			char c = szBuffer[0];
+
+			if(c == '\n' || c == '\r')
+				continue;
+			else if(c != '\t')
+			{
+				if(!LoadDomainAndPath(szBuffer, strDomain, strPath))
+					goto _ERROR_END;
+
+				pCookieSet	= GetCookieSetNoLock(strDomain, strPath);
+			}
+			else
+			{
+				if(!LoadCookie(szBuffer, strDomain, strPath, cookie))
+					goto _ERROR_END;
+
+				if(cookie.expires <= tmCurrent)
+					continue;
+
+				if(pCookieSet)
+				{
+					if(bKeepExists)
+					{
+						CCookieSetCI it = pCookieSet->find(cookie);
+						if(it != pCookieSet->end())
+							continue;
+					}
+
+					pCookieSet->emplace(move(cookie));
+				}
+				else
+				{
+					SetCookieNoLock(cookie, FALSE);
+					pCookieSet = GetCookieSetNoLock(strDomain, strPath);
+				}
+			}
+		}
+
+		if(!feof(pFile))
+			goto _ERROR_END;
+	}
+
+	isOK = TRUE;
+
+_ERROR_END:
+
+	if(pFile) fclose(pFile);
+
+	return isOK;
+}
+
+BOOL CCookieMgr::SaveToFile(LPCSTR lpszFile, BOOL bKeepExists)
+{
+	if(bKeepExists)
+	{
+		if(!LoadFromFile(lpszFile, TRUE) && !IS_ERROR(ERROR_FILE_NOT_FOUND))
+			return FALSE;
+	}
+
+	BOOL isOK	= FALSE;
+	FILE* pFile = nullptr;
+
+	if((pFile = fopen(lpszFile, "w")) == nullptr)
+		goto _ERROR_END;
+
+	{
+		__time64_t tmCurrent = _time64(nullptr);
+
+		CReadLock locallock(m_cs);
+
+		for(CCookieDomainMapCI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
+		{
+			const CStringA& strDomain	= it->first;
+			const CCookiePathMap& paths	= it->second;
+
+			for(CCookiePathMapCI it2 = paths.begin(), end2 = paths.end(); it2 != end2; ++it2)
+			{
+				const CStringA& strPath		= it2->first;
+				const CCookieSet& cookies	= it2->second;
+
+				if(fprintf(pFile, "%s %s\n", (LPCSTR)strDomain, (LPCSTR)strPath) < 0)
+					goto _ERROR_END;
+
+				for(CCookieSetCI it3 = cookies.begin(), end3 = cookies.end(); it3 != end3; ++it3)
+				{
+					const CCookie& cookie = *it3;
+
+					if(cookie.expires <= tmCurrent)
+						continue;
+
+					LPCSTR lpszValue = (LPCSTR)cookie.value;
+
+					if(lpszValue[0] == 0)
+						lpszValue = " ";
+
+					if(fprintf(pFile, "\t%s;%s;%lld;%d;%d;%d\n", (LPCSTR)cookie.name, lpszValue, cookie.expires, cookie.httpOnly, cookie.secure, cookie.sameSite) < 0)
+						goto _ERROR_END;
+				}
+			}
+		}
+	}
+
+	isOK = TRUE;
+
+_ERROR_END:
+
+	if(pFile) fclose(pFile);
+
+	return isOK;
+}
+
+BOOL CCookieMgr::ClearCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
+{
+	CStringA strDomain;
+	CStringA strPath;
+
+	if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, TRUE))
+		return FALSE;
+
+	CWriteLock locallock(m_cs);
+
+	ClearDomainCookiesNoLock(lpszDomain, lpszPath);
+
+	return TRUE;
+}
+
+BOOL CCookieMgr::RemoveExpiredCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
+{
+	CStringA strDomain;
+	CStringA strPath;
+
+	if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, TRUE))
+		return FALSE;
+
+	CWriteLock locallock(m_cs);
+
+	RemoveExpiredCookiesNoLock(lpszDomain, lpszPath);
+
+	return TRUE;
+}
+
+BOOL CCookieMgr::GetCookies(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
+{
+	ASSERT(lpszDomain && lpszPath);
+
+	CStringA strDomain;
+	CStringA strPath;
+
+	if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, FALSE))
+		return FALSE;
+
+	list<LPCSTR> lsDomains(1, lpszDomain);
+	list<CStringA> lsPaths(1, lpszPath);
+
+	char c;
+	LPCSTR lpszTemp = lpszDomain;
+
+	while((c = *(++lpszTemp)) != 0)
+	{
+		if(c == COOKIE_DOMAIN_SEP_CHAR)
+		{
+			if((c = *(++lpszTemp)) != 0)
+				lsDomains.push_back(lpszTemp);
+			else
+				break;
+		}
+	}
+
+	lpszTemp = lpszPath + strlen(lpszPath) - 1;
+
+	while(--lpszTemp >= lpszPath)
+	{
+		if((c = *lpszTemp) == COOKIE_PATH_SEP_CHAR)
+		{
+			*(LPSTR)(lpszTemp + 1) = 0;
+			lsPaths.push_back(lpszPath);
+		}
+	}
+
+	CReadLock locallock(m_cs);
+
+	for(list<LPCSTR>::const_iterator it = lsDomains.begin(), end = lsDomains.end(); it != end; ++it)
+	{
+		for(list<CStringA>::const_iterator it2 = lsPaths.begin(), end2 = lsPaths.end(); it2 != end2; ++it2)
+			MatchCookiesNoLock(cookies, *it, *it2, bHttp, bSecure);
+	}
+
+	return TRUE;
+}
+
+BOOL CCookieMgr::SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, CCookie::EnSameSite enSameSite, BOOL bOnlyUpdateValueIfExists)
+{
+	CCookie cookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
+
+	return SetCookie(cookie, bOnlyUpdateValueIfExists);
+}
+
+BOOL CCookieMgr::SetCookie(const CStringA& strCookie, BOOL bOnlyUpdateValueIfExists)
+{
+	unique_ptr<CCookie> pCookie(CCookie::FromString(strCookie));
+	if(!pCookie) return FALSE;
+
+	return SetCookie(*pCookie, bOnlyUpdateValueIfExists);
+}
+
+BOOL CCookieMgr::SetCookie(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists)
+{
+	if(!cookie.IsValid()) return FALSE;
+
+	CWriteLock locallock(m_cs);
+
+	return SetCookieNoLock(cookie, bOnlyUpdateValueIfExists);
+}
+
+BOOL CCookieMgr::DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
+{
+	CCookie cookie(lpszName, nullptr, lpszDomain, lpszPath);
+
+	return DeleteCookie(cookie);
+}
+
+BOOL CCookieMgr::DeleteCookie(const CCookie& cookie)
+{
+	if(!cookie.IsValid()) return FALSE;
+
+	CWriteLock locallock(m_cs);
+
+	return DeleteCookieNoLock(cookie);
+}
+
+void CCookieMgr::ClearDomainCookiesNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
+{
+	if(!lpszDomain && !lpszPath)
+		m_cookies.clear();
+	else if(!lpszPath)
+		m_cookies.erase(lpszDomain);
+	else
+	{
+		if(!lpszDomain)
+		{
+			for(CCookieDomainMapI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
+				ClearPathCookiesNoLock(it->second, lpszPath);
+		}
+		else
+		{
+			CCookieDomainMapI it = m_cookies.find(lpszDomain);
+			if(it != m_cookies.end())
+				ClearPathCookiesNoLock(it->second, lpszPath);
+		}
+	}
+}
+
+void CCookieMgr::ClearPathCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath)
+{
+	if(!lpszPath)
+		paths.clear();
+	else
+	{
+		CCookiePathMapI it = paths.find(lpszPath);
+		if(it != paths.end())
+			paths.erase(it->first);
+	}
+}
+
+void CCookieMgr::RemoveExpiredCookiesNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
+{
+	if(!lpszDomain)
+	{
+		for(CCookieDomainMapI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
+			RemoveDomainExpiredCookiesNoLock(it->second, lpszPath);
+	}
+	else
+	{
+		CCookieDomainMapI it = m_cookies.find(lpszDomain);
+		if(it != m_cookies.end())
+			RemoveDomainExpiredCookiesNoLock(it->second, lpszPath);
+	}
+}
+
+void CCookieMgr::RemoveDomainExpiredCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath)
+{
+	if(!lpszPath)
+	{
+		for(CCookiePathMapI it = paths.begin(), end = paths.end(); it != end; ++it)
+			RemovePathExpiredCookiesNoLock(it->second);
+	}
+	else
+	{
+		CCookiePathMapI it = paths.find(lpszPath);
+		if(it != paths.end())
+			RemovePathExpiredCookiesNoLock(it->second);
+	}
+}
+
+void CCookieMgr::RemovePathExpiredCookiesNoLock(CCookieSet& cookies)
+{
+	CCookiePtrSet ptrs;
+
+	for(CCookieSetI it = cookies.begin(), end = cookies.end(); it != end; ++it)
+	{
+		if(it->IsExpired())
+			ptrs.emplace(&*it);
+	}
+
+	if(!ptrs.empty())
+	{
+		for(CCookiePtrSetI it = ptrs.begin(), end = ptrs.end(); it != end; ++it)
+			cookies.erase(**it);
+	}
+}
+
+const CCookie* CCookieMgr::GetCookieNoLock(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
+{
+	const CCookie cookie(lpszName, nullptr, lpszDomain, lpszPath);
+	return GetCookieNoLock(cookie);
+}
+
+const CCookie* CCookieMgr::GetCookieNoLock(const CCookie& cookie)
+{
+	const CCookie* pCookie = nullptr;
+
+	CCookieDomainMapCI it = m_cookies.find(cookie.domain);
+	if(it != m_cookies.end())
+	{
+		CCookiePathMapCI it2 = it->second.find(cookie.path);
+		if(it2 != it->second.end())
+		{
+			CCookieSetCI it3 = it2->second.find(cookie);
+			if(it3 != it2->second.end())
+				pCookie = &*it3;
+		}
+	}
+
+	return pCookie;
+}
+
+void CCookieMgr::MatchCookiesNoLock(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
+{
+	CCookieDomainMapCI it = m_cookies.find(lpszDomain);
+	if(it != m_cookies.end())
+	{
+		CCookiePathMapCI it2 = it->second.find(lpszPath);
+		if(it2 != it->second.end())
+		{
+			for(CCookieSetCI it3 = it2->second.begin(), end3 = it2->second.end(); it3 != end3; ++it3)
+			{
+				const CCookie& cookie = *it3;
+
+				if(!cookie.IsExpired() && (bHttp || !cookie.httpOnly) && (bSecure || !cookie.secure))
+					cookies.emplace(cookie);
+			}
+		}
+	}
+}
+
+BOOL CCookieMgr::SetCookieNoLock(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists)
+{
+	if(cookie.IsExpired())
+		return DeleteCookieNoLock(cookie);
+
+	CCookieDomainMapI it = m_cookies.find(cookie.domain);
+
+	if(it == m_cookies.end())
+		it = m_cookies.emplace(CCookieDomainMap::value_type(cookie.domain, CCookiePathMap())).first;
+
+	CCookiePathMapI it2 = it->second.find(cookie.path);
+
+	if(it2 == it->second.end())
+		it2 = it->second.emplace(CCookiePathMap::value_type(cookie.path, CCookieSet())).first;
+
+	CCookieSet& cookies	= it2->second;
+	CCookieSetI it3		= cookies.find(cookie);
+
+	if(it3 != cookies.end())
+	{
+		if(bOnlyUpdateValueIfExists && !it3->IsExpired() && cookie.IsTransient())
+		{
+			((CCookie*)&*it3)->value = cookie.value;
+			return TRUE;
+		}
+		
+		cookies.erase(*it3);
+	}
+
+	return cookies.emplace(cookie).second;
+}
+
+BOOL CCookieMgr::DeleteCookieNoLock(const CCookie& cookie)
+{
+	BOOL isOK = FALSE;
+
+	CCookieDomainMapI it = m_cookies.find(cookie.domain);
+	if(it != m_cookies.end())
+	{
+		CCookiePathMapI it2 = it->second.find(cookie.path);
+		if(it2 != it->second.end())
+		{
+			CCookieSetI it3 = it2->second.find(cookie);
+			if(it3 != it2->second.end())
+			{
+				it2->second.erase(*it3);
+				isOK = TRUE;
+			}
+		}
+	}
+
+	return isOK;
+}
+
+CCookieSet* CCookieMgr::GetCookieSetNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
+{
+	CCookieSet* pCookieSet	= nullptr;
+	CCookieDomainMapI it	= m_cookies.find(lpszDomain);
+
+	if(it != m_cookies.end())
+	{
+		CCookiePathMapI it2 = it->second.find(lpszPath);
+		if(it2 != it->second.end())
+			pCookieSet = &(it2->second);
+	}
+
+	return pCookieSet;
+}
+
+BOOL CCookieMgr::LoadDomainAndPath(LPSTR lpszBuff, CStringA& strDomain, CStringA& strPath)
+{
+	int i = 0;
+	char* lpszCtx = nullptr;
+
+	for(; i < 2; i++)
+	{
+		char* lpszToken = strtok_r(lpszBuff, " \n\r", &lpszCtx);
+
+		if(!lpszToken)
+		{
+			::SetLastError(ERROR_BAD_FORMAT);
+			return FALSE;
+		}
+
+		if(i == 0)
+		{
+			strDomain = lpszToken;
+			lpszBuff  = nullptr;
+		}
+		else
+			strPath = lpszToken;
+	}
+
+	if(!CCookie::AdjustDomain(strDomain))
+		return FALSE;
+	if(!CCookie::AdjustPath(strPath))
+		return FALSE;
+
+	return TRUE;
+}
+
+BOOL CCookieMgr::LoadCookie(LPSTR lpszBuff, LPCSTR lpszDomain, LPCSTR lpszPath, CCookie& cookie)
+{
+	cookie.domain	= lpszDomain;
+	cookie.path		= lpszPath;
+
+	int i			= 0;
+	char* lpszCtx	= nullptr;
+
+	for(; i < 6; i++)
+	{
+		char* lpszToken = strtok_r(lpszBuff, "\t;\n\r", &lpszCtx);
+
+		if(!lpszToken)
+		{
+			::SetLastError(ERROR_BAD_FORMAT);
+			return FALSE;
+		}
+
+		if(i == 0)
+		{
+			cookie.name		= lpszToken;
+			lpszBuff		= nullptr;
+		}
+		else if(i == 1)
+			cookie.value	= lpszToken;
+		else if(i == 2)
+			cookie.expires	= atoll(lpszToken);
+		else if(i == 3)
+			cookie.httpOnly	= (BOOL)atoi(lpszToken);
+		else if(i == 4)
+			cookie.secure	= (BOOL)atoi(lpszToken);
+		else if(i == 5)
+			cookie.sameSite	= (CCookie::EnSameSite)atoi(lpszToken);
+	}
+
+	cookie.name.Trim();
+	cookie.value.Trim();
+
+	if(cookie.name.IsEmpty() || cookie.expires <= 0 /*|| cookie.httpOnly < 0 || cookie.secure < 0*/ || cookie.sameSite < CCookie::SS_UNKNOWN || cookie.sameSite > CCookie::SS_NONE)
+	{
+		::SetLastError(ERROR_INVALID_DATA);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CCookieMgr::AdjustDomainAndPath(LPCSTR& lpszDomain, LPCSTR& lpszPath, CStringA& strDomain, CStringA& strPath, BOOL bKeepNull)
+{
+	if(!bKeepNull || lpszDomain)
+	{
+		strDomain = lpszDomain;
+
+		if(!CCookie::AdjustDomain(strDomain))
+			return FALSE;
+
+		lpszDomain = (LPCSTR)strDomain;
+	}
+
+	if(!bKeepNull || lpszPath)
+	{
+		strPath = lpszPath;
+
+		if(!CCookie::AdjustPath(strPath))
+			return FALSE;
+
+		lpszPath = (LPCSTR)strPath;
+	}
+
+	return TRUE;
+}
+
+CCookieMgr::CCookieMgr(BOOL bEnableThirdPartyCookie)
+: m_bEnableThirdPartyCookie(bEnableThirdPartyCookie)
+{
+
+}
+
+#endif

+ 194 - 0
HttpCookie.h

@@ -0,0 +1,194 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "hpsocket/HPTypeDef.h"
+#include "common/GeneralHelper.h"
+
+#ifdef _HTTP_SUPPORT
+
+#define COOKIE_DOMAIN			"domain"
+#define COOKIE_PATH				"path"
+#define COOKIE_EXPIRES			"expires"
+#define COOKIE_MAX_AGE			"Max-Age"
+#define COOKIE_HTTPONLY			"HttpOnly"
+#define COOKIE_SECURE			"secure"
+#define COOKIE_SAMESITE			"SameSite"
+#define COOKIE_SAMESITE_STRICT	"Strict"
+#define COOKIE_SAMESITE_LAX		"Lax"
+#define COOKIE_SAMESITE_NONE	"None"
+#define COOKIE_DEFAULT_PATH		"/"
+#define COOKIE_FIELD_SEP		";"
+#define COOKIE_DOMAIN_SEP_CHAR	'.'
+#define COOKIE_PATH_SEP_CHAR	'/'
+#define COOKIE_KV_SEP_CHAR		'='
+
+class CCookie
+{
+public:
+	enum EnSameSite
+	{
+		SS_UNKNOWN	= 0,
+		SS_STRICT	= 1,
+		SS_LAX		= 2,
+		SS_NONE		= 3
+	};
+
+	CStringA name;
+	CStringA value;
+	CStringA domain;
+	CStringA path;
+	__time64_t expires;
+	BOOL httpOnly;
+	BOOL secure;
+	EnSameSite sameSite;
+
+	CCookie(LPCSTR lpszName = nullptr, LPCSTR lpszValue = nullptr, LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr, int iMaxAge = -1, BOOL bHttpOnly = FALSE, BOOL bSecure = FALSE, EnSameSite enSameSite = SS_UNKNOWN)
+	: name		(lpszName)
+	, value		(lpszValue)
+	, domain	(lpszDomain)
+	, path		(lpszPath)
+	, expires	(MaxAgeToExpires(iMaxAge))
+	, httpOnly	(bHttpOnly)
+	, secure	(bSecure)
+	, sameSite	(enSameSite)
+	{
+		AdjustDomain(domain);
+		AdjustPath(path);
+	}
+
+	static __time64_t CurrentUTCTime()					{return _time64(nullptr);}
+	static __time64_t MaxAgeToExpires(int iMaxAge)		{return iMaxAge > 0 ? _time64(nullptr) + iMaxAge : (iMaxAge < 0 ? -1 : 0);}
+	static int ExpiresToMaxAge(__time64_t tmExpires)	{if(tmExpires < 0) return -1; __time64_t tmDiff = tmExpires - _time64(nullptr); return (tmDiff > 0 ? (int)tmDiff : 0);}
+
+	static __time64_t GetUTCTime(tm& t, int iSecondOffsetTZ);
+	static void ParseFieldKV(const CStringA& strField, CStringA& strKey, CStringA& strVal, char chSep);
+	static BOOL ParseExpires(LPCSTR lpszExpires, __time64_t& tmExpires);
+	static BOOL AdjustDomain(CStringA& strDomain, LPCSTR lpszDefaultDomain = nullptr);
+	static BOOL AdjustPath(CStringA& strPath, LPCSTR lpszDefaultPath = nullptr);
+	static CStringA MakeExpiresStr(__time64_t tmExpires);
+	static BOOL MakeExpiresStr(char lpszBuff[], int& iBuffLen, __time64_t tmExpires);
+	static CCookie* FromString(const CStringA& strCookie, LPCSTR lpszDefaultDomain = nullptr, LPCSTR lpszDefaultPath = nullptr);
+	static CStringA ToString(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge = -1, BOOL bHttpOnly = FALSE, BOOL bSecure = FALSE, EnSameSite enSameSite = SS_UNKNOWN);
+	static BOOL ToString(char lpszBuff[], int& iBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge = -1, BOOL bHttpOnly = FALSE, BOOL bSecure = FALSE, EnSameSite enSameSite = SS_UNKNOWN);
+
+	CStringA ToString();
+	BOOL Match(LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure);
+	BOOL IsSameDomain(LPCSTR lpszDomain);
+
+	BOOL IsTransient()	const	{return expires < 0;}
+	BOOL IsExpired()	const	{return !IsTransient() && expires <= _time64(nullptr);}
+	BOOL IsValid()		const	{return !name.IsEmpty() && !domain.IsEmpty() && !path.IsEmpty();}
+};
+
+struct ccookie_hash_func
+{
+	struct hash
+	{
+		size_t operator() (const CCookie& c) const
+		{
+			return hash_value((LPCSTR)(c.name));
+		}
+	};
+
+	struct equal_to
+	{
+		bool operator () (const CCookie& cA, const CCookie& cB) const
+		{
+			return (cA.name == cB.name);
+		}
+	};
+
+};
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+typedef unordered_set<const CCookie*>									CCookiePtrSet;
+typedef CCookiePtrSet::const_iterator									CCookiePtrSetCI;
+typedef CCookiePtrSet::iterator											CCookiePtrSetI;
+
+typedef unordered_set<CCookie,
+		ccookie_hash_func::hash, ccookie_hash_func::equal_to>			CCookieSet;
+typedef CCookieSet::const_iterator										CCookieSetCI;
+typedef CCookieSet::iterator											CCookieSetI;
+
+typedef unordered_map<CStringA, CCookieSet,
+		cstringa_hash_func::hash, cstringa_hash_func::equal_to>			CCookiePathMap;
+typedef CCookiePathMap::const_iterator									CCookiePathMapCI;
+typedef CCookiePathMap::iterator										CCookiePathMapI;
+
+typedef unordered_map<CStringA, CCookiePathMap,
+		cstringa_nc_hash_func::hash, cstringa_nc_hash_func::equal_to>	CCookieDomainMap;
+typedef CCookieDomainMap::const_iterator								CCookieDomainMapCI;
+typedef CCookieDomainMap::iterator										CCookieDomainMapI;
+
+class CCookieMgr
+{
+public:
+	BOOL LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists = TRUE);
+	BOOL SaveToFile(LPCSTR lpszFile, BOOL bKeepExists = TRUE);
+
+	BOOL ClearCookies(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr);
+	BOOL RemoveExpiredCookies(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr);
+
+	BOOL GetCookies(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure);
+	BOOL SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge = -1, BOOL bHttpOnly = FALSE, BOOL bSecure = FALSE, CCookie::EnSameSite enSameSite = CCookie::SS_UNKNOWN, BOOL bOnlyUpdateValueIfExists = TRUE);
+	BOOL SetCookie(const CStringA& strCookie, BOOL bOnlyUpdateValueIfExists = TRUE);
+	BOOL SetCookie(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists = TRUE);
+	BOOL DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName);
+	BOOL DeleteCookie(const CCookie& cookie);
+
+private:
+	void ClearDomainCookiesNoLock(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr);
+	void ClearPathCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath = nullptr);
+	void RemoveExpiredCookiesNoLock(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr);
+	void RemoveDomainExpiredCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath = nullptr);
+	void RemovePathExpiredCookiesNoLock(CCookieSet& cookies);
+	const CCookie* GetCookieNoLock(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName);
+	const CCookie* GetCookieNoLock(const CCookie& cookie);
+	void MatchCookiesNoLock(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp = TRUE, BOOL bSecure = FALSE);
+	BOOL SetCookieNoLock(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists = TRUE);
+	BOOL DeleteCookieNoLock(const CCookie& cookie);
+	CCookieSet* GetCookieSetNoLock(LPCSTR lpszDomain, LPCSTR lpszPath);
+
+private:
+	static BOOL LoadDomainAndPath(LPSTR lpszBuff, CStringA& strDomain, CStringA& strPath);
+	static BOOL LoadCookie(LPSTR lpszBuff, LPCSTR lpszDomain, LPCSTR lpszPath, CCookie& cookie);
+	static BOOL AdjustDomainAndPath(LPCSTR& lpszDomain, LPCSTR& lpszPath, CStringA& strDomain, CStringA& strPath, BOOL bKeepNull = TRUE);
+
+public:
+	CCookieMgr(BOOL bEnableThirdPartyCookie = TRUE);
+
+	void SetEnableThirdPartyCookie	(BOOL bEnableThirdPartyCookie = TRUE)	{m_bEnableThirdPartyCookie = bEnableThirdPartyCookie;}
+	BOOL IsEnableThirdPartyCookie	()										{return m_bEnableThirdPartyCookie;}
+
+private:
+	CSimpleRWLock		m_cs;
+	CCookieDomainMap	m_cookies;
+	BOOL				m_bEnableThirdPartyCookie;
+};
+
+extern CCookieMgr g_CookieMgr;
+
+#endif

+ 469 - 0
HttpHelper.cpp

@@ -0,0 +1,469 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "HttpHelper.h"
+
+#ifdef _HTTP_SUPPORT
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+CStringA& GetHttpVersionStr(EnHttpVersion enVersion, CStringA& strResult)
+{
+	strResult.Format("HTTP/%d.%d", LOBYTE(enVersion), HIBYTE(enVersion));
+	return strResult;
+}
+
+CStringA& AdjustRequestPath(BOOL bConnect, LPCSTR lpszPath, CStringA& strPath)
+{
+	strPath = lpszPath;
+
+	if(strPath.IsEmpty() || (!bConnect && strPath.GetAt(0) != HTTP_PATH_SEPARATOR_CHAR))
+		strPath.Insert(0, HTTP_PATH_SEPARATOR_CHAR);
+
+	return strPath;
+}
+
+LPCSTR GetHttpDefaultStatusCodeDesc(EnHttpStatusCode enCode)
+{
+	switch(enCode)
+	{
+	case HSC_CONTINUE									: return "Continue";
+	case HSC_SWITCHING_PROTOCOLS						: return "Switching Protocols";
+	case HSC_PROCESSING									: return "Processing";
+	case HSC_EARLY_HINTS								: return "Early Hints";
+	case HSC_RESPONSE_IS_STALE							: return "Response Is Stale";
+	case HSC_REVALIDATION_FAILED						: return "Revalidation Failed";
+	case HSC_DISCONNECTED_OPERATION						: return "Disconnected Operation";
+	case HSC_HEURISTIC_EXPIRATION						: return "Heuristic Expiration";
+	case HSC_MISCELLANEOUS_WARNING						: return "Miscellaneous Warning";
+
+	case HSC_OK											: return "OK";
+	case HSC_CREATED									: return "Created";
+	case HSC_ACCEPTED									: return "Accepted";
+	case HSC_NON_AUTHORITATIVE_INFORMATION				: return "Non-Authoritative Information";
+	case HSC_NO_CONTENT									: return "No Content";
+	case HSC_RESET_CONTENT								: return "Reset Content";
+	case HSC_PARTIAL_CONTENT							: return "Partial Content";
+	case HSC_MULTI_STATUS								: return "Multi-Status";
+	case HSC_ALREADY_REPORTED							: return "Already Reported";
+	case HSC_TRANSFORMATION_APPLIED						: return "Transformation Applied";
+	case HSC_IM_USED									: return "IM Used";
+	case HSC_MISCELLANEOUS_PERSISTENT_WARNING			: return "Miscellaneous Persistent Warning";
+
+	case HSC_MULTIPLE_CHOICES							: return "Multiple Choices";
+	case HSC_MOVED_PERMANENTLY							: return "Moved Permanently";
+	case HSC_MOVED_TEMPORARILY							: return "Move temporarily";
+	case HSC_SEE_OTHER									: return "See Other";
+	case HSC_NOT_MODIFIED								: return "Not Modified";
+	case HSC_USE_PROXY									: return "Use Proxy";
+	case HSC_SWITCH_PROXY								: return "Switch Proxy";
+	case HSC_TEMPORARY_REDIRECT							: return "Temporary Redirect";
+	case HSC_PERMANENT_REDIRECT							: return "Permanent Redirect";
+
+	case HSC_BAD_REQUEST								: return "Bad Request";
+	case HSC_UNAUTHORIZED								: return "Unauthorized";
+	case HSC_PAYMENT_REQUIRED							: return "Payment Required";
+	case HSC_FORBIDDEN									: return "Forbidden";
+	case HSC_NOT_FOUND									: return "Not Found";
+	case HSC_METHOD_NOT_ALLOWED							: return "Method Not Allowed";
+	case HSC_NOT_ACCEPTABLE								: return "Not Acceptable";
+	case HSC_PROXY_AUTHENTICATION_REQUIRED				: return "Proxy Authentication Required";
+	case HSC_REQUEST_TIMEOUT							: return "Request Timeout";
+	case HSC_CONFLICT									: return "Conflict";
+	case HSC_GONE										: return "Gone";
+	case HSC_LENGTH_REQUIRED							: return "Length Required";
+	case HSC_PRECONDITION_FAILED						: return "Precondition Failed";
+	case HSC_REQUEST_ENTITY_TOO_LARGE					: return "Request Entity Too Large";
+	case HSC_REQUEST_URI_TOO_LONG						: return "Request-URI Too Long";
+	case HSC_UNSUPPORTED_MEDIA_TYPE						: return "Unsupported Media Type";
+	case HSC_REQUESTED_RANGE_NOT_SATISFIABLE			: return "Requested Range Not Satisfiable";
+	case HSC_EXPECTATION_FAILED							: return "Expectation Failed";
+	case HSC_IM_A_TEAPOT								: return "I'm a teapot";
+	case HSC_PAGE_EXPIRED								: return "Page Expired";
+	case HSC_ENHANCE_YOUR_CALM							: return "Enhance Your Calm";
+	case HSC_MISDIRECTED_REQUEST						: return "Misdirected Request";
+	case HSC_UNPROCESSABLE_ENTITY						: return "Unprocessable Entity";
+	case HSC_LOCKED										: return "Locked";
+	case HSC_FAILED_DEPENDENCY							: return "Failed Dependency";
+	case HSC_UNORDERED_COLLECTION						: return "Unordered Collection";
+	case HSC_UPGRADE_REQUIRED							: return "Upgrade Required";
+	case HSC_PRECONDITION_REQUIRED						: return "Precondition Required";
+	case HSC_TOO_MANY_REQUESTS							: return "Too Many Requests";
+	case HSC_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL	: return "Request Header Fields Too Large Unofficial";
+	case HSC_REQUEST_HEADER_FIELDS_TOO_LARGE			: return "Request Header Fields Too Large";
+	case HSC_LOGIN_TIMEOUT								: return "Login Timeout";
+	case HSC_NO_RESPONSE								: return "No Response";
+	case HSC_RETRY_WITH									: return "Retry With";
+	case HSC_BLOCKED_BY_PARENTAL_CONTROL				: return "Blocked By Parental Control";
+	case HSC_UNAVAILABLE_FOR_LEGAL_REASONS				: return "Unavailable For Legal Reasons";
+	case HSC_CLIENT_CLOSED_LOAD_BALANCED_REQUEST		: return "Client Closed Load Balance Request";
+	case HSC_INVALID_X_FORWARDED_FOR					: return "Invalid X-Forwarded-For";
+	case HSC_REQUEST_HEADER_TOO_LARGE					: return "Request Header Too Large";
+	case HSC_SSL_CERTIFICATE_ERROR						: return "SSL Certificate Error";
+	case HSC_SSL_CERTIFICATE_REQUIRED					: return "SSL Certificate Required";
+	case HSC_HTTP_REQUEST_SENT_TO_HTTPS_PORT			: return "HTTP Request Sent To HTTPS Port";
+	case HSC_INVALID_TOKEN								: return "Invalid Token";
+	case HSC_CLIENT_CLOSED_REQUEST						: return "Client Closed Request";
+
+	case HSC_INTERNAL_SERVER_ERROR						: return "Internal Server Error";
+	case HSC_NOT_IMPLEMENTED							: return "Not Implemented";
+	case HSC_BAD_GATEWAY								: return "Bad Gateway";
+	case HSC_SERVICE_UNAVAILABLE						: return "Service Unavailable";
+	case HSC_GATEWAY_TIMEOUT							: return "Gateway Timeout";
+	case HSC_HTTP_VERSION_NOT_SUPPORTED					: return "HTTP Version Not Supported";
+	case HSC_VARIANT_ALSO_NEGOTIATES					: return "Variant Also Negotiates";
+	case HSC_INSUFFICIENT_STORAGE						: return "Insufficient Storage";
+	case HSC_LOOP_DETECTED								: return "Loop Detected";
+	case HSC_BANDWIDTH_LIMIT_EXCEEDED					: return "Bandwidth Limit Exceeded";
+	case HSC_NOT_EXTENDED								: return "Not Extended";
+	case HSC_NETWORK_AUTHENTICATION_REQUIRED			: return "Network Authentication Required";
+	case HSC_WEB_SERVER_UNKNOWN_ERROR					: return "Web Server Unknown Error";
+	case HSC_WEB_SERVER_IS_DOWN							: return "Web Server Is Down";
+	case HSC_CONNECTION_TIMEOUT							: return "Connection Timeout";
+	case HSC_ORIGIN_IS_UNREACHABLE						: return "Origin Is Unreachable";
+	case HSC_TIMEOUT_OCCURED							: return "Timeout Occured";
+	case HSC_SSL_HANDSHAKE_FAILED						: return "SSL Handshake Failed";
+	case HSC_INVALID_SSL_CERTIFICATE					: return "Invalid SSL Certificate";
+	case HSC_RAILGUN_ERROR								: return "Railgun Error";
+	case HSC_SITE_IS_OVERLOADED							: return "Site Is Overloaded";
+	case HSC_SITE_IS_FROZEN								: return "Site Is Frozen";
+	case HSC_IDENTITY_PROVIDER_AUTHENTICATION_ERROR		: return "Identity Provider Authentication Error";
+	case HSC_NETWORK_READ_TIMEOUT						: return "Network Read Timeout";
+	case HSC_NETWORK_CONNECT_TIMEOUT					: return "Network Connect Timeout";
+
+	case HSC_UNPARSEABLE_RESPONSE_HEADERS				: return "Unparseable Response Headers";
+
+	default												: return "***";
+	}
+}
+
+static inline CStringA& AppendHeader(LPCSTR lpszName, LPCSTR lpszValue, CStringA& strValue)
+{
+	strValue.Append(lpszName);
+	strValue.Append(HTTP_HEADER_SEPARATOR);
+	strValue.Append(lpszValue);
+	strValue.Append(HTTP_CRLF);
+
+	return strValue;
+}
+
+void MakeRequestLine(LPCSTR lpszMethod, LPCSTR lpszPath, EnHttpVersion enVersion, CStringA& strValue)
+{
+	ASSERT(lpszMethod);
+
+	strValue.Format("%s %s HTTP/%d.%d%s", (LPCSTR)(CStringA(lpszMethod).MakeUpper()), lpszPath, LOBYTE(enVersion), HIBYTE(enVersion), HTTP_CRLF);
+}
+
+void MakeStatusLine(EnHttpVersion enVersion, USHORT usStatusCode, LPCSTR lpszDesc, CStringA& strValue)
+{
+	if(!lpszDesc) lpszDesc = ::GetHttpDefaultStatusCodeDesc((EnHttpStatusCode)usStatusCode);
+	strValue.Format("HTTP/%d.%d %d %s%s", LOBYTE(enVersion), HIBYTE(enVersion), usStatusCode, lpszDesc, HTTP_CRLF);
+}
+
+void MakeHeaderLines(const THeader lpHeaders[], int iHeaderCount, const TCookieMap* pCookies, int iBodyLength, BOOL bRequest, int iConnFlag, LPCSTR lpszDefaultHost, USHORT usPort, CStringA& strValue)
+{
+	unordered_set<LPCSTR, str_nc_hash_func::hash, str_nc_hash_func::equal_to> szHeaderNames;
+
+	if(iHeaderCount > 0)
+	{
+		ASSERT(lpHeaders);
+
+		for(int i = 0; i < iHeaderCount; i++)
+		{
+			const THeader& header = lpHeaders[i];
+
+			ASSERT(!::IsStrEmptyA(header.name));
+
+			if(!::IsStrEmptyA(header.name))
+			{
+				szHeaderNames.emplace(header.name);
+				AppendHeader(header.name, header.value, strValue);
+			}
+		}
+	}
+
+	if(	(!bRequest || iBodyLength > 0)											&&
+		(szHeaderNames.empty()													||	
+		(szHeaderNames.find(HTTP_HEADER_CONTENT_LENGTH) == szHeaderNames.end()	&&
+		szHeaderNames.find(HTTP_HEADER_TRANSFER_ENCODING) == szHeaderNames.end())))
+	{
+		char szBodyLength[16];
+		itoa(iBodyLength, szBodyLength, 10);
+
+		AppendHeader(HTTP_HEADER_CONTENT_LENGTH, szBodyLength, strValue);
+	}
+
+	if(	(iConnFlag == 0 || iConnFlag == 1)										&&
+		(szHeaderNames.empty()													||	
+		szHeaderNames.find(HTTP_HEADER_CONNECTION) == szHeaderNames.end()		))
+	{
+		LPCSTR lpszValue = iConnFlag == 0 ? HTTP_CONNECTION_CLOSE_VALUE : HTTP_CONNECTION_KEEPALIVE_VALUE;
+		AppendHeader(HTTP_HEADER_CONNECTION, lpszValue, strValue);
+	}
+
+	if(	bRequest && !::IsStrEmptyA(lpszDefaultHost)						&&
+		(szHeaderNames.empty()											||	
+		(szHeaderNames.find(HTTP_HEADER_HOST) == szHeaderNames.end())	))
+	{
+		CStringA strHost(lpszDefaultHost);
+		if(usPort != 0) strHost.AppendFormat(":%u", usPort);
+
+		AppendHeader(HTTP_HEADER_HOST, strHost, strValue);
+	}
+
+	szHeaderNames.clear();
+
+	if(pCookies != nullptr)
+	{
+		DWORD dwSize = (DWORD)pCookies->size();
+
+		if(dwSize > 0)
+		{
+			strValue.Append(HTTP_HEADER_COOKIE);
+			strValue.Append(HTTP_HEADER_SEPARATOR);
+
+			DWORD dwIndex = 0;
+
+			for(TCookieMapCI it = pCookies->begin(), end = pCookies->end(); it != end; ++it, ++dwIndex)
+			{
+				strValue.Append(it->first);
+				strValue.AppendChar(COOKIE_KV_SEP_CHAR);
+				strValue.Append(it->second);
+
+				if(dwIndex < dwSize - 1)
+					strValue.Append(HTTP_COOKIE_SEPARATOR);
+			}
+
+			strValue.Append(HTTP_CRLF);
+		}
+	}
+
+	strValue.Append(HTTP_CRLF);
+}
+
+void MakeHttpPacket(const CStringA& strHeader, const BYTE* pBody, int iLength, WSABUF szBuffer[2])
+{
+	ASSERT(pBody != nullptr || iLength == 0);
+
+	szBuffer[0].buf = (LPBYTE)(LPCSTR)strHeader;
+	szBuffer[0].len = strHeader.GetLength();
+	szBuffer[1].buf = (LPBYTE)pBody;
+	szBuffer[1].len = iLength;
+}
+
+int MakeChunkPackage(const BYTE* pData, int iLength, LPCSTR lpszExtensions, char szLen[12], WSABUF bufs[5])
+{
+	ASSERT(iLength == 0 || pData != nullptr);
+
+	int i = 0;
+
+	if(::IsStrEmptyA(lpszExtensions))
+	{
+		sprintf(szLen, "%x" HTTP_CRLF, iLength);
+
+		bufs[i].buf = (LPBYTE)szLen;
+		bufs[i].len = (int)strlen(szLen);
+		++i;
+	}
+	else
+	{
+		LPCSTR lpszSep = lpszExtensions[0] == ';' ? " " : " ;";
+
+		sprintf(szLen, "%x%s", iLength, lpszSep);
+
+		bufs[i].buf = (LPBYTE)szLen;
+		bufs[i].len = (int)strlen(szLen);
+		++i;
+
+		bufs[i].buf = (LPBYTE)lpszExtensions;
+		bufs[i].len = (int)strlen(lpszExtensions);
+		++i;
+
+		bufs[i].buf = (LPBYTE)HTTP_CRLF;
+		bufs[i].len = 2;
+		++i;
+	}
+
+	if(iLength > 0)
+	{
+		bufs[i].buf = (LPBYTE)pData;
+		bufs[i].len = iLength;
+		++i;
+	}
+
+	bufs[i].buf = (LPBYTE)HTTP_CRLF;
+	bufs[i].len = 2;
+	++i;
+
+	return i;
+}
+
+BOOL MakeWSPacket(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], BYTE* pData, int iLength, ULONGLONG ullBodyLen, BYTE szHeader[HTTP_MAX_WS_HEADER_LEN], WSABUF szBuffer[2])
+{
+	ULONGLONG ullLength = (ULONGLONG)iLength;
+
+	ASSERT(pData != nullptr	|| iLength == 0);
+	ASSERT(ullBodyLen == 0	|| ullBodyLen >= ullLength);
+
+	if(ullBodyLen == 0)
+		ullBodyLen = ullLength;
+	else if(ullBodyLen < ullLength)
+	{
+		::SetLastError(ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	TBaseWSHeader bh(szHeader, TRUE);
+
+	int iHeaderLen = HTTP_MIN_WS_HEADER_LEN;
+
+	bh.set_fin(bFinal);
+	bh.set_rsv(iReserved);
+	bh.set_code(iOperationCode);
+	bh.set_mask(lpszMask ? TRUE : FALSE);
+
+	if(ullBodyLen < 126)
+		bh.set_len((BYTE)ullBodyLen);
+	else if(ullBodyLen <= 0xFFFF)
+	{
+		bh.set_len(126);
+		bh.set_extlen((USHORT)ullBodyLen);
+
+		iHeaderLen += 2;
+	}
+	else
+	{
+		bh.set_len(127);
+		*(ULONGLONG*)(szHeader + HTTP_MIN_WS_HEADER_LEN) = ::HToN64(ullBodyLen);
+
+		iHeaderLen += 8;
+	}
+
+	if(lpszMask)
+	{
+		memcpy(szHeader + iHeaderLen, lpszMask, 4);
+
+		for(int i = 0; i < iLength; i++)
+			pData[i] = pData[i] ^ lpszMask[i & 0x03];
+
+		iHeaderLen += 4;
+	}
+
+	szBuffer[0].buf = szHeader;
+	szBuffer[0].len = iHeaderLen;
+	szBuffer[1].buf = (LPBYTE)pData;
+	szBuffer[1].len = iLength;
+
+	return TRUE;
+}
+
+BOOL ParseUrl(const CStringA& strUrl, BOOL& bHttps, CStringA& strHost, USHORT& usPort, CStringA& strPath)
+{
+	int iSchemaLength = (int)strlen(HTTP_SCHEMA);
+
+	if(strnicmp(strUrl, HTTP_SCHEMA, iSchemaLength) == 0)
+		bHttps = FALSE;
+	else
+	{
+		iSchemaLength = (int)strlen(HTTPS_SCHEMA);
+
+		if(strnicmp(strUrl, HTTPS_SCHEMA, iSchemaLength) == 0)
+			bHttps = TRUE;
+		else
+			return FALSE;
+	}
+
+	CStringA strFullHost;
+	int i = strUrl.Find(HTTP_PATH_SEPARATOR_CHAR, iSchemaLength);
+
+	if(i > 0)
+	{
+		strFullHost	= strUrl.Mid(iSchemaLength, i - iSchemaLength);
+		strPath		= strUrl.Mid(i);
+	}
+	else
+	{
+		strFullHost	= strUrl.Mid(iSchemaLength);
+		strPath		= HTTP_PATH_SEPARATOR;
+	}
+
+	if(strFullHost.IsEmpty())
+		return FALSE;
+
+	CStringA strPort;
+
+	char c = strFullHost.GetAt(0);
+
+	if(!::isalnum(c))
+	{
+		if(c != IPV6_ADDR_BEGIN_CHAR)
+			return FALSE;
+		else
+		{
+			i = strFullHost.ReverseFind(IPV6_ADDR_END_CHAR);
+
+			if(i < 0)
+				return FALSE;
+			else
+			{
+				if(strFullHost.GetLength() > i + 1)
+				{
+					if(strFullHost.GetAt(i + 1) != PORT_SEPARATOR_CHAR)
+						return FALSE;
+
+					strPort = strFullHost.Mid(i + 2);
+
+					if(strPort.IsEmpty())
+						return FALSE;
+				}
+				
+				strHost = strFullHost.Mid(1, i - 1);
+			}
+		}
+	}
+	else
+	{
+		i = strFullHost.Find(PORT_SEPARATOR_CHAR);
+
+		if(i < 0)
+			strHost	= strFullHost;
+		else
+		{
+			strPort = strFullHost.Mid(i + 1);
+
+			if(strPort.IsEmpty())
+				return FALSE;
+
+			strHost = strFullHost.Mid(0, i);
+		}
+	}
+
+	if(strPort.IsEmpty())
+		usPort = bHttps ? HTTPS_DEFAULT_PORT : HTTP_DEFAULT_PORT;
+	else
+		usPort = (USHORT)::atoi(strPort);
+
+	return TRUE;
+}
+
+#endif

+ 1385 - 0
HttpHelper.h

@@ -0,0 +1,1385 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "SocketHelper.h"
+#include "HttpCookie.h"
+
+#ifdef _HTTP_SUPPORT
+
+#include "./common/http/llhttp.h"
+#include "./common/http/llhttp_url.h"
+
+typedef llhttp_t			http_parser;
+typedef llhttp_settings_t	http_parser_settings;
+
+#define LLHTTP_MAX_ERROR_NUM				23
+#define LLHTTP_MAX_METHOD_NUM				45
+
+
+/************************************************************************
+名称:HTTP 全局常量
+描述:声明 HTTP 组件的公共全局常量
+************************************************************************/
+
+#define HTTP_DEFAULT_PORT					80
+#define HTTPS_DEFAULT_PORT					443
+
+#define HTTP_SCHEMA							"http://"
+#define HTTPS_SCHEMA						"https://"
+
+#define HTTP_CRLF							"\r\n"
+#define HTTP_PATH_SEPARATOR_CHAR			'/'
+#define HTTP_PATH_SEPARATOR					"/"
+#define HTTP_HEADER_SEPARATOR				": "
+#define HTTP_COOKIE_SEPARATOR				"; "
+#define HTTP_1_0_STR						"HTTP/1.0"
+#define HTTP_1_1_STR						"HTTP/1.1"
+
+#define HTTP_HEADER_HOST					"Host"
+#define HTTP_HEADER_COOKIE					"Cookie"
+#define HTTP_HEADER_SET_COOKIE				"Set-Cookie"
+#define HTTP_HEADER_CONTENT_TYPE			"Content-Type"
+#define HTTP_HEADER_CONTENT_LENGTH			"Content-Length"
+#define HTTP_HEADER_CONTENT_ENCODING		"Content-Encoding"
+#define HTTP_HEADER_TRANSFER_ENCODING		"Transfer-Encoding"
+#define HTTP_HEADER_CONNECTION				"Connection"
+#define HTTP_HEADER_UPGRADE					"Upgrade"
+#define HTTP_HEADER_VALUE_WEB_SOCKET		"WebSocket"
+
+#define HTTP_CONNECTION_CLOSE_VALUE			"close"
+#define HTTP_CONNECTION_KEEPALIVE_VALUE		"keep-alive"
+
+#define HTTP_METHOD_POST					"POST"
+#define HTTP_METHOD_PUT						"PUT"
+#define HTTP_METHOD_PATCH					"PATCH"
+#define HTTP_METHOD_GET						"GET"
+#define HTTP_METHOD_DELETE					"DELETE"
+#define HTTP_METHOD_HEAD					"HEAD"
+#define HTTP_METHOD_TRACE					"TRACE"
+#define HTTP_METHOD_OPTIONS					"OPTIONS"
+#define HTTP_METHOD_CONNECT					"CONNECT"
+
+#define HTTP_MIN_WS_HEADER_LEN				2
+#define HTTP_MAX_WS_HEADER_LEN				14
+
+#define MIN_HTTP_RELEASE_CHECK_INTERVAL		((DWORD)1000)
+#define MIN_HTTP_RELEASE_DELAY				100
+#define MAX_HTTP_RELEASE_DELAY				(60 * 1000)
+#define DEFAULT_HTTP_RELEASE_DELAY			(5 * 1000)
+#define DEFAULT_HTTP_VERSION				HV_1_1
+
+#define DEFAULT_HTTP_SYNC_CONNECT_TIMEOUT	5000
+#define DEFAULT_HTTP_SYNC_REQUEST_TIMEOUT	10000
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+enum EnHttpSyncRequestProgress
+{
+	HSRP_DONE,
+	HSRP_WAITING,
+	HSRP_ERROR,
+	HSRP_CLOSE
+};
+
+struct TDyingConnection
+{
+	CONNID			connID;
+	DWORD			killTime;
+
+	TDyingConnection(CONNID id, DWORD kt = 0)
+	: connID		(id)
+	, killTime		(kt == 0 ? ::TimeGetTime() : kt)
+	{
+
+	}
+
+	static TDyingConnection* Construct(CONNID id, DWORD kt = 0)	{return new TDyingConnection(id, kt);}
+	static void Destruct(TDyingConnection* pObj)				{if(pObj) delete pObj;}
+
+};
+
+typedef unordered_multimap<CStringA, CStringA,
+		cstringa_nc_hash_func::hash, cstringa_nc_hash_func::equal_to>	THeaderMap;
+typedef THeaderMap::const_iterator										THeaderMapCI;
+typedef THeaderMap::iterator											THeaderMapI;
+
+typedef unordered_map<CStringA, CStringA,
+		cstringa_hash_func::hash, cstringa_hash_func::equal_to>			TCookieMap;
+typedef TCookieMap::const_iterator										TCookieMapCI;
+typedef TCookieMap::iterator											TCookieMapI;
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+struct TBaseWSHeader
+{
+public:
+	BOOL fin()
+	{
+		return (data >> 7) & 0x1;
+	}
+
+	void set_fin(BOOL v)
+	{
+		data |= ((v ? 1 : 0) << 7);
+	}
+
+	BYTE rsv()
+	{
+		return (data >> 4) & 0x7;
+	}
+
+	void set_rsv(BYTE v)
+	{
+		data |= ((v & 0x7) << 4);
+	}
+
+	BYTE code()
+	{
+		return data & 0xF;
+	}
+
+	void set_code(BYTE v)
+	{
+		data |= (v & 0xF);
+	}
+
+	BOOL mask()
+	{
+		return (data >> 15) & 0x1;
+	}
+
+	void set_mask(BOOL v)
+	{
+		data |= ((v ? 1 : 0) << 15);
+	}
+
+	BYTE len()
+	{
+		return (data >> 8) & 0x7F;
+	}
+
+	void set_len(BYTE v)
+	{
+		data |= ((v & 0x7F) << 8);
+	}
+
+	USHORT extlen()
+	{
+		return ntohs((USHORT)(data >> 16));
+	}
+
+	void set_extlen(USHORT v)
+	{
+		data |= (htons(v) << 16);
+	}
+
+	TBaseWSHeader(const BYTE* p, BOOL bZero = FALSE)
+	: data(*(UINT*)p)
+	{
+		if(bZero) data = 0;
+	}
+
+private:
+	UINT& data;
+};
+
+template<class T> struct TWSContext
+{
+public:
+	EnHandleResult Parse(const BYTE* pData, int iLength)
+	{
+		ASSERT(pData != nullptr && iLength > 0);
+
+		EnHandleResult hr	= HR_OK;
+		BYTE* pTemp			= (BYTE*)pData;
+		int iRemain			= iLength;
+		int iMin			= 0;
+
+		while(iRemain > 0)
+		{
+			if(m_bHeader)
+			{
+				iMin = MIN(m_iHeaderRemain, iRemain);
+				memcpy(m_szHeader + m_iHeaderLen - m_iHeaderRemain, pTemp, iMin);
+
+				m_iHeaderRemain	-= iMin;
+
+				if(m_iHeaderRemain == 0)
+				{
+					TBaseWSHeader bh(m_szHeader);
+
+					int iLen			= bh.len();
+					int iExtLen			= iLen < 126 ? 0 : (iLen == 126 ? 2 : 8);
+					int iMaskLen		= bh.mask() ? 4 : 0;
+					int iRealHeaderLen	= HTTP_MIN_WS_HEADER_LEN + iExtLen + iMaskLen;
+
+					if(m_iHeaderLen < iRealHeaderLen)
+					{
+						m_iHeaderRemain	= iRealHeaderLen - m_iHeaderLen;
+						m_iHeaderLen	= iRealHeaderLen;
+					}
+					else
+					{
+						if((m_pHttpObj->IsRequest() && iMaskLen == 0) || (!m_pHttpObj->IsRequest() && iMaskLen > 0))
+						{
+							::SetLastError(ERROR_INVALID_DATA);
+							hr = HR_ERROR;
+
+							break;
+						}
+
+						m_ullBodyLen	= iExtLen == 0 ? iLen : (iExtLen == 2 ? bh.extlen() : NToH64(*(ULONGLONG*)(m_szHeader + HTTP_MIN_WS_HEADER_LEN)));
+						m_ullBodyRemain	= m_ullBodyLen;
+						m_lpszMask		= iMaskLen > 0 ? m_szHeader + HTTP_MIN_WS_HEADER_LEN + iExtLen : nullptr;
+
+						hr = m_pHttpObj->on_ws_message_header(bh.fin(), bh.rsv(), bh.code(), m_lpszMask, m_ullBodyLen);
+
+						if(hr == HR_ERROR)
+							break;
+
+						if(m_ullBodyLen > 0)
+							m_bHeader = FALSE;
+						else
+						{
+							hr = CompleteMessage();
+
+							if(hr == HR_ERROR)
+								break;
+						}
+					}
+				}
+			}
+			else
+			{
+				iMin = (int)MIN(m_ullBodyRemain, (ULONGLONG)iRemain);
+
+				if(m_lpszMask)
+				{
+					int iFactor = (m_ullBodyLen - m_ullBodyRemain) & 0x03;
+
+					for(int i = 0; i < iMin; i++)
+						pTemp[i] = pTemp[i] ^ m_lpszMask[(i + iFactor) & 0x03];
+				}
+
+				m_ullBodyRemain	-= iMin;
+
+				hr = m_pHttpObj->on_ws_message_body(pTemp, iMin);
+
+				if(hr == HR_ERROR)
+					break;
+
+				if(m_ullBodyRemain == 0)
+				{
+					hr = CompleteMessage();
+
+					if(hr == HR_ERROR)
+						break;
+				}
+			}
+
+			pTemp	+= iMin;
+			iRemain	-= iMin;
+		}
+
+		return hr;
+	}
+
+	BOOL GetMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
+	{
+		TBaseWSHeader bh(m_szHeader);
+
+		if(lpbFinal)			*lpbFinal			= bh.fin();
+		if(lpiReserved)			*lpiReserved		= bh.rsv();
+		if(lpiOperationCode)	*lpiOperationCode	= bh.code();
+		if(lpszMask)			*lpszMask			= m_lpszMask;
+		if(lpullBodyLen)		*lpullBodyLen		= m_ullBodyLen;
+		if(lpullBodyRemain)		*lpullBodyRemain	= m_ullBodyRemain;
+
+		return TRUE;
+	}
+
+	BOOL CopyData(const TWSContext& src)
+	{
+		if(&src == this)
+			return FALSE;
+
+		memcpy(m_szHeader, src.m_szHeader, HTTP_MAX_WS_HEADER_LEN);
+
+		if(src.m_lpszMask)
+			m_lpszMask = m_szHeader + (src.m_lpszMask - src.m_szHeader);
+		else
+			m_lpszMask = nullptr;
+
+		m_ullBodyLen	= src.m_ullBodyLen;
+		m_ullBodyRemain	= src.m_ullBodyRemain;
+
+		return TRUE;
+	}
+
+public:
+	TWSContext(T* pHttpObj) : m_pHttpObj(pHttpObj)
+	{
+		Reset();
+	}
+
+private:
+	EnHandleResult CompleteMessage()
+	{
+		EnHandleResult hr = m_pHttpObj->on_ws_message_complete();
+
+		Reset();
+
+		return hr;
+	}
+
+	void Reset()
+	{
+		m_bHeader		= TRUE;
+		m_lpszMask		= nullptr;
+		m_iHeaderLen	= HTTP_MIN_WS_HEADER_LEN;
+		m_iHeaderRemain	= HTTP_MIN_WS_HEADER_LEN;
+		m_ullBodyLen	= 0;
+		m_ullBodyRemain	= 0;
+	}
+
+private:
+	T* m_pHttpObj;
+
+	BYTE m_szHeader[HTTP_MAX_WS_HEADER_LEN];
+	const BYTE* m_lpszMask;
+
+	BOOL m_bHeader;
+	int m_iHeaderLen;
+	int m_iHeaderRemain;
+	ULONGLONG m_ullBodyLen;
+	ULONGLONG m_ullBodyRemain;
+};
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+/* Http 上下文结构 */
+template<class T, class S> struct THttpObjT
+{
+public:
+	EnHandleResult Execute(const BYTE* pData, int iLength)
+	{
+		ASSERT(pData != nullptr && iLength > 0);
+
+		if(m_parser.upgrade)
+		{
+			if(m_enUpgrade == HUT_WEB_SOCKET)
+			{
+				if(m_pwsContext != nullptr)
+					return m_pwsContext->Parse(pData, iLength);
+			}
+			else
+			{
+				return m_pContext->DoFireSuperReceive(m_pSocket, pData, iLength);
+			}
+		}
+
+		EnHandleResult hr	= HR_OK;
+		llhttp_errno rs		= ::llhttp_execute(&m_parser, (LPCSTR)pData, iLength);
+
+		if(rs == HPE_OK)
+			ASSERT(m_parser.error_pos == nullptr);
+		else if(rs == HPE_PAUSED_UPGRADE)
+		{
+			int iPased	= (m_parser.error_pos == nullptr) ? iLength : (int)((const BYTE*)(m_parser.error_pos) - pData);
+			hr			= Upgrade(pData, iLength, iPased);
+		}
+		else
+		{
+			m_pContext->FireParseError(m_pSocket, m_parser.error, ::llhttp_get_error_reason(&m_parser));
+			hr = HR_ERROR;
+		}
+
+		return hr;
+	}
+
+	void CheckBodyIdentityEof()
+	{
+		if(!m_parser.upgrade)
+			::llhttp_finish(&m_parser);
+	}
+
+	static int on_message_begin(http_parser* p)
+	{
+		THttpObjT* pSelf = Self(p);
+
+		pSelf->ResetHeaderState(FALSE, FALSE);
+
+		return pSelf->m_pContext->FireMessageBegin(pSelf->m_pSocket);
+	}
+
+	static int on_url(http_parser* p, const char* at, size_t length)
+	{
+		Self(p)->AppendBuffer(at, length);
+
+		return HPR_OK;
+	}
+
+	static int on_url_complete(llhttp_t* p)
+	{
+		THttpObjT* pSelf		= Self(p);
+		EnHttpParseResult hpr	= pSelf->ParseUrl();
+
+		if(hpr == HPR_OK)
+			hpr = pSelf->m_pContext->FireRequestLine(pSelf->m_pSocket, ::llhttp_method_name((llhttp_method_t)p->method), pSelf->GetBuffer());
+
+		pSelf->ResetBuffer();
+
+		return hpr;
+	}
+
+	static int on_status(http_parser* p, const char* at, size_t length)
+	{
+		Self(p)->AppendBuffer(at, length);
+
+		return HPR_OK;
+	}
+
+	static int on_status_complete(llhttp_t* p)
+	{
+		THttpObjT* pSelf		= Self(p);
+		EnHttpParseResult hpr	= pSelf->m_pContext->FireStatusLine(pSelf->m_pSocket, p->status_code, pSelf->GetBuffer());
+
+		pSelf->ResetBuffer();
+
+		return hpr;
+	}
+
+	static int on_header_field(http_parser* p, const char* at, size_t length)
+	{
+		Self(p)->AppendBuffer(at, length);
+
+		return HPR_OK;
+	}
+
+	static int on_header_field_complete(llhttp_t* p)
+	{
+		THttpObjT* pSelf		= Self(p);
+		pSelf->m_strCurHeader	= pSelf->GetBuffer();
+		pSelf->ResetBuffer();
+
+		return HPR_OK;
+	}
+
+	static int on_header_value(http_parser* p, const char* at, size_t length)
+	{
+		Self(p)->AppendBuffer(at, length);
+
+		return HPR_OK;
+	}
+
+	static int on_header_value_complete(llhttp_t* p)
+	{
+		THttpObjT* pSelf = Self(p);
+
+		pSelf->m_headers.emplace(move(THeaderMap::value_type(pSelf->m_strCurHeader, pSelf->GetBuffer())));
+
+		EnHttpParseResult hpr = pSelf->m_pContext->FireHeader(pSelf->m_pSocket, pSelf->m_strCurHeader, pSelf->GetBuffer());
+
+		if(hpr != HPR_ERROR && !pSelf->GetBufferRef().IsEmpty())
+		{
+			if(pSelf->m_bRequest && pSelf->m_strCurHeader.CompareNoCase(HTTP_HEADER_COOKIE) == 0)
+				hpr = pSelf->ParseCookie();
+			else if(!pSelf->m_bRequest && pSelf->m_strCurHeader.CompareNoCase(HTTP_HEADER_SET_COOKIE) == 0)
+				hpr = pSelf->ParseSetCookie();
+		}
+
+		pSelf->ResetBuffer();
+
+		return hpr;
+	}
+
+	static int on_headers_complete(http_parser* p)
+	{
+		THttpObjT* pSelf = Self(p);
+
+		pSelf->CheckUpgrade();
+		pSelf->ResetHeaderBuffer();
+
+		EnHttpParseResult rs = pSelf->m_pContext->FireHeadersComplete(pSelf->m_pSocket);
+
+		if(!pSelf->m_bRequest && pSelf->GetMethodInt() == HTTP_HEAD && rs == HPR_OK)
+			rs = HPR_SKIP_BODY;
+
+		return rs;
+	}
+
+	static int on_body(http_parser* p, const char* at, size_t length)
+	{
+		THttpObjT* pSelf = Self(p);
+
+		return pSelf->m_pContext->FireBody(pSelf->m_pSocket, (const BYTE*)at, (int)length);
+	}
+
+	static int on_chunk_header(http_parser* p)
+	{
+		THttpObjT* pSelf = Self(p);
+
+		return pSelf->m_pContext->FireChunkHeader(pSelf->m_pSocket, (int)p->content_length);
+	}
+
+	static int on_chunk_complete(http_parser* p)
+	{
+		THttpObjT* pSelf = Self(p);
+
+		return pSelf->m_pContext->FireChunkComplete(pSelf->m_pSocket);
+	}
+
+	static int on_message_complete(http_parser* p)
+	{
+		THttpObjT* pSelf = Self(p);
+		
+		return pSelf->m_pContext->FireMessageComplete(pSelf->m_pSocket);
+	}
+
+	EnHandleResult on_ws_message_header(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
+	{
+		return m_pContext->FireWSMessageHeader(m_pSocket, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);
+	}
+
+	EnHandleResult on_ws_message_body(const BYTE* pData, int iLength)
+	{
+		return m_pContext->FireWSMessageBody(m_pSocket, pData, iLength);
+	}
+
+	EnHandleResult on_ws_message_complete()
+	{
+		return m_pContext->FireWSMessageComplete(m_pSocket);
+	}
+
+private:
+
+	EnHandleResult Upgrade(const BYTE* pData, int iLength, int iPased)
+	{
+		ASSERT(m_parser.upgrade);
+
+		if(m_pContext->FireUpgrade(m_pSocket, m_enUpgrade) != HPR_OK)
+			return HR_ERROR;
+
+		ResetHeaderState();
+
+		if(m_enUpgrade == HUT_WEB_SOCKET)
+			m_pwsContext = new TWSContext<THttpObjT<T, S>>(this);
+
+		if(iPased < iLength)
+			return Execute(pData + iPased, iLength - iPased);
+
+		return HR_OK;
+	}
+
+	void CheckUpgrade()
+	{
+		if(!m_parser.upgrade)
+			return;
+
+		if(m_bRequest && m_parser.method == HTTP_CONNECT)
+			m_enUpgrade = HUT_HTTP_TUNNEL;
+		else
+		{
+			LPCSTR lpszValue;
+			if(GetHeader(HTTP_HEADER_UPGRADE, &lpszValue) && stricmp(HTTP_HEADER_VALUE_WEB_SOCKET, lpszValue) == 0)
+				m_enUpgrade = HUT_WEB_SOCKET;
+			else
+				m_enUpgrade = HUT_UNKNOWN;
+		}
+	}
+
+	EnHttpParseResult ParseUrl()
+	{
+		http_parser_url url = {0};
+
+		BOOL isConnect	= m_parser.method == HTTP_CONNECT;
+		int rs			= ::http_parser_parse_url(m_strBuffer, m_strBuffer.GetLength(), isConnect, &url);
+
+		if(rs != HPE_OK)
+		{
+			/*
+			m_parser.error	= HPE_INVALID_URL;
+			m_parser.reason	= "Parse url fail";
+			*/
+			
+			return HPR_ERROR;
+		}
+
+		m_usUrlFieldSet		= url.field_set;
+		LPCSTR lpszBuffer	= m_strBuffer;
+
+		for(int i = 0; i < UF_MAX; i++)
+		{
+			if((url.field_set & (1 << i)) != 0)
+				m_pstrUrlFileds[i].SetString((lpszBuffer + url.field_data[i].off), url.field_data[i].len);
+		}
+
+		return HPR_OK;
+	}
+
+	EnHttpParseResult ParseCookie()
+	{
+		int i = 0;
+
+		do 
+		{
+			CStringA tk = m_strBuffer.Tokenize(COOKIE_FIELD_SEP, i);
+
+			if(i == -1)
+				break;
+
+			int j = tk.Trim().Find(COOKIE_KV_SEP_CHAR);
+
+			if(j <= 0)
+				continue;
+			/*
+			{
+				m_parser.http_errno = HPE_INVALID_HEADER_TOKEN;
+				return HPR_ERROR;
+			}
+			*/
+
+			AddCookie(tk.Left(j), tk.Mid(j + 1));
+
+		} while(TRUE);
+
+		return HPR_OK;
+	}
+
+	EnHttpParseResult ParseSetCookie()
+	{
+		CCookieMgr* pCookieMgr = m_pContext->GetCookieMgr();
+
+		if(pCookieMgr == nullptr)
+			return HPR_OK;
+
+		LPCSTR lpszDomain	= GetDomain();
+		LPCSTR lpszPath		= GetPath();
+
+		unique_ptr<CCookie> pCookie(CCookie::FromString(m_strBuffer, lpszDomain, lpszPath));
+
+		if(pCookie == nullptr)
+			return HPR_ERROR;
+
+		if(pCookie->Match(lpszDomain, lpszPath, TRUE, m_pContext->IsSecure()))
+		{
+			if(pCookie->IsExpired())
+				DeleteCookie(pCookie->name);
+			else
+				AddCookie(pCookie->name, pCookie->value);
+		}
+
+		if(pCookieMgr->IsEnableThirdPartyCookie() || pCookie->IsSameDomain(lpszDomain))
+			pCookieMgr->SetCookie(*pCookie);
+
+		return HPR_OK;
+	}
+
+public:
+	int GetCount()		const		{return 0;}
+	DWORD GetFreeTime() const		{return m_dwFreeTime;}
+	void SetFree()					{m_dwFreeTime = ::TimeGetTime();}
+
+	BOOL IsRequest()				{return m_bRequest;}
+	BOOL IsUpgrade()				{return m_parser.upgrade;}
+	BOOL IsKeepAlive()				{return ::llhttp_should_keep_alive(&m_parser);}
+	USHORT GetVersion()				{return MAKEWORD(m_parser.http_major, m_parser.http_minor);}
+	ULONGLONG GetContentLength()	{return m_parser.content_length;}
+
+	int GetMethodInt()				{return m_bRequest ? m_parser.method : m_sRequestMethod;}
+	USHORT GetUrlFieldSet()			{return m_usUrlFieldSet;}
+	USHORT GetStatusCode()			{return m_parser.status_code;}
+
+	EnHttpUpgradeType GetUpgradeType()	{return m_enUpgrade;}
+
+	THeaderMap& GetHeaderMap()		{return m_headers;}
+	TCookieMap& GetCookieMap()		{return m_cookies;}
+
+	BOOL HasReleased()				{return m_bReleased;}
+	void Release()					{m_bReleased = TRUE;}
+
+	LPCSTR GetMethod()
+	{
+		int iMethod = GetMethodInt();
+
+		if(iMethod >= 0 && iMethod <= LLHTTP_MAX_METHOD_NUM)
+			return ::llhttp_method_name((llhttp_method)iMethod);
+
+		return nullptr;
+	}
+
+	LPCSTR GetContentType()
+	{
+		LPCSTR lpszValue = nullptr;
+		GetHeader(HTTP_HEADER_CONTENT_TYPE, &lpszValue);
+
+		return lpszValue;
+	}
+
+	LPCSTR GetContentEncoding()
+	{
+		LPCSTR lpszValue = nullptr;
+		GetHeader(HTTP_HEADER_CONTENT_ENCODING, &lpszValue);
+
+		return lpszValue;
+	}
+
+	LPCSTR GetTransferEncoding()
+	{
+		LPCSTR lpszValue = nullptr;
+		GetHeader(HTTP_HEADER_TRANSFER_ENCODING, &lpszValue);
+
+		return lpszValue;
+	}
+
+	LPCSTR GetHost()
+	{
+		LPCSTR lpszValue = nullptr;
+		GetHeader(HTTP_HEADER_HOST, &lpszValue);
+
+		return lpszValue;
+	}
+
+	USHORT GetParseErrorCode(LPCSTR* lpszErrorDesc = nullptr)
+	{
+		USHORT usError = (USHORT)m_parser.error;
+
+		if(lpszErrorDesc)
+		{
+			if(usError == HPE_OK)
+				*lpszErrorDesc = ::llhttp_errno_name((llhttp_errno_t)usError);
+			else
+			{
+				*lpszErrorDesc = ::llhttp_get_error_reason(&m_parser);
+
+				if(::IsStrEmptyA(*lpszErrorDesc) && usError <= LLHTTP_MAX_ERROR_NUM)
+					*lpszErrorDesc = ::llhttp_errno_name((llhttp_errno_t)usError);
+			}
+		}
+
+		return usError;
+	}
+
+	LPCSTR GetUrlField(EnHttpUrlField enField)
+	{
+		ASSERT(m_bRequest && enField < HUF_MAX);
+
+		if(!m_bRequest || enField >= HUF_MAX)
+			return nullptr;
+
+		return m_pstrUrlFileds[enField];
+	}
+
+	LPCSTR GetPath()
+	{
+		if(m_bRequest)
+			return GetUrlField(HUF_PATH);
+		else
+			return *m_pstrRequestPath;
+	}
+
+	LPCSTR GetDomain()
+	{
+		ASSERT(!m_bRequest);
+
+		return m_pContext->GetRemoteDomain(m_pSocket);
+	}
+
+	LPCSTR GetRequestPath()
+	{
+		if(m_bRequest)
+			return nullptr;
+
+		return *m_pstrRequestPath;
+	}
+
+	void SetRequestPath(LPCSTR lpszMethod, LPCSTR lpszPath)
+	{
+		ASSERT(!m_bRequest);
+
+		if(m_bRequest)
+			return;
+
+		*m_pstrRequestPath = lpszPath;
+
+#define HTTP_METHOD_GEN(NUM, NAME, STRING) else if(stricmp(lpszMethod, #STRING) == 0) m_sRequestMethod = HTTP_##NAME;
+
+		if(::IsStrEmptyA(lpszMethod)) m_sRequestMethod = -1;
+		HTTP_METHOD_MAP(HTTP_METHOD_GEN)
+		else
+			m_sRequestMethod = -1;
+
+#undef HTTP_METHOD_GEN
+	}
+
+	BOOL GetHeader(LPCSTR lpszName, LPCSTR* lpszValue)
+	{
+		ASSERT(lpszName);
+
+		BOOL isOK		= FALSE;
+		THeaderMapCI it = m_headers.find(lpszName);
+
+		if(it != m_headers.end())
+		{
+			*lpszValue	= it->second;
+			isOK		= TRUE;
+		}
+
+		return isOK;
+	}
+
+	BOOL GetHeaders(LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
+	{
+		ASSERT(lpszName);
+
+		if(lpszValue == nullptr || dwCount == 0)
+		{
+			dwCount = (DWORD)m_headers.count(lpszName);
+			return FALSE;
+		}
+
+		pair<THeaderMapCI, THeaderMapCI> range = m_headers.equal_range(lpszName);
+
+		THeaderMapCI it	= range.first;
+		DWORD dwIndex	= 0;
+
+		while(it != range.second)
+		{
+			if(dwIndex < dwCount)
+				lpszValue[dwIndex] = it->second;
+
+			++dwIndex;
+			++it;
+		}
+
+		BOOL isOK	= (dwIndex > 0 && dwIndex <= dwCount);
+		dwCount		= dwIndex;
+
+		return isOK;
+	}
+
+	BOOL GetAllHeaders(THeader lpHeaders[], DWORD& dwCount)
+	{
+		DWORD dwSize = (DWORD)m_headers.size();
+
+		if(lpHeaders == nullptr || dwCount == 0 || dwSize == 0 || dwSize > dwCount)
+		{
+			dwCount = dwSize;
+			return FALSE;
+		}
+
+		DWORD dwIndex = 0;
+
+		for(THeaderMapCI it = m_headers.begin(), end = m_headers.end(); it != end; ++it, ++dwIndex)
+		{
+			lpHeaders[dwIndex].name  = it->first;
+			lpHeaders[dwIndex].value = it->second;
+		}
+
+		dwCount = dwSize;
+		return TRUE;
+	}
+
+	BOOL GetAllHeaderNames(LPCSTR lpszName[], DWORD& dwCount)
+	{
+		DWORD dwSize = (DWORD)m_headers.size();
+
+		if(lpszName == nullptr || dwCount == 0 || dwSize == 0 || dwSize > dwCount)
+		{
+			dwCount = dwSize;
+			return FALSE;
+		}
+
+		DWORD dwIndex = 0;
+
+		for(THeaderMapCI it = m_headers.begin(), end = m_headers.end(); it != end; ++it, ++dwIndex)
+			lpszName[dwIndex] = it->first;
+
+		dwCount = dwSize;
+		return TRUE;
+	}
+
+	BOOL AddCookie(LPCSTR lpszName, LPCSTR lpszValue, BOOL bRelpace = TRUE)
+	{
+		ASSERT(lpszName);
+
+		TCookieMapI it = m_cookies.find(lpszName);
+
+		if(it == m_cookies.end())
+			return m_cookies.emplace(move(TCookieMap::value_type(lpszName, lpszValue))).second;
+
+		BOOL isOK = FALSE;
+
+		if(bRelpace)
+		{
+			it->second	= lpszValue;
+			isOK		= TRUE;
+		}
+
+		return isOK;
+	}
+
+	BOOL DeleteCookie(LPCSTR lpszName)
+	{
+		ASSERT(lpszName);
+
+		return m_cookies.erase(lpszName) > 0;
+	}
+
+	void DeleteAllCookies()
+	{
+		m_cookies.clear();
+	}
+
+	BOOL GetCookie(LPCSTR lpszName, LPCSTR* lpszValue)
+	{
+		ASSERT(lpszName);
+
+		BOOL isOK		= FALSE;
+		TCookieMapCI it = m_cookies.find(lpszName);
+
+		if(it != m_cookies.end())
+		{
+			*lpszValue	= it->second;
+			isOK		= TRUE;
+		}
+
+		return isOK;
+	}
+
+	BOOL GetAllCookies(TCookie lpCookies[], DWORD& dwCount)
+	{
+		DWORD dwSize = (DWORD)m_cookies.size();
+
+		if(lpCookies == nullptr || dwCount == 0 || dwSize == 0 || dwSize > dwCount)
+		{
+			dwCount = dwSize;
+			return FALSE;
+		}
+
+		DWORD dwIndex = 0;
+
+		for(TCookieMapCI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it, ++dwIndex)
+		{
+			lpCookies[dwIndex].name  = it->first;
+			lpCookies[dwIndex].value = it->second;
+		}
+
+		dwCount = dwSize;
+		return TRUE;
+	}
+
+	BOOL ReloadCookies()
+	{
+		CCookieMgr* pCookieMgr = m_pContext->GetCookieMgr();
+
+		if(pCookieMgr == nullptr)
+			return TRUE;
+
+		DeleteAllCookies();
+
+		CCookieSet cookies;
+
+		if(!pCookieMgr->GetCookies(cookies, GetDomain(), GetPath(), TRUE, m_pContext->IsSecure()))
+			return FALSE;
+
+		for(CCookieSetCI it = cookies.begin(), end = cookies.end(); it != end; ++it)
+			AddCookie(it->name, it->value);
+
+		return TRUE;
+	}
+
+	BOOL GetWSMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
+	{
+		if(!m_pwsContext)
+			return FALSE;
+
+		return m_pwsContext->GetMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);
+	}
+
+public:
+	THttpObjT			(BOOL bRequest, T* pContext, S* pSocket)
+	: m_pContext		(pContext)
+	, m_pSocket			(pSocket)
+	, m_bRequest		(bRequest)
+	, m_bValid			(FALSE)
+	, m_bReleased		(FALSE)
+	, m_dwFreeTime		(0)
+	, m_usUrlFieldSet	(m_bRequest ? 0 : -1)
+	, m_pstrUrlFileds	(nullptr)
+	, m_enUpgrade		(HUT_NONE)
+	, m_pwsContext		(nullptr)
+	{
+		if(m_bRequest)
+			m_pstrUrlFileds	  = new CStringA[HUF_MAX];
+		else
+			m_pstrRequestPath = new CStringA;
+
+		ResetParser();
+	}
+
+	~THttpObjT()
+	{
+		if(m_bRequest)
+			delete[] m_pstrUrlFileds;
+		else
+			delete m_pstrRequestPath;
+
+		ReleaseWSContext();
+	}
+
+	static THttpObjT* Construct(BOOL bRequest, T* pContext, S* pSocket)
+		{return new THttpObjT(bRequest, pContext, pSocket);}
+
+	static void Destruct(THttpObjT* pHttpObj)
+		{if(pHttpObj) delete pHttpObj;}
+
+	void Reset(BOOL bValid = FALSE)
+	{
+		ResetParser();
+		ResetHeaderState();
+		ReleaseWSContext();
+
+		m_bValid	 = bValid;
+		m_bReleased  = FALSE;
+		m_enUpgrade  = HUT_NONE;
+		m_dwFreeTime = 0;
+	}
+
+	void Renew(T* pContext, S* pSocket)
+	{
+		Reset(TRUE);
+
+		m_pContext	= pContext;
+		m_pSocket	= pSocket;
+	}
+
+	void SetValid(BOOL bValid = TRUE)
+	{
+		m_bValid = bValid;
+	}
+
+	BOOL IsValid()
+	{
+		return m_bValid;
+	}
+
+	BOOL CopyData(const THttpObjT& src)
+	{
+		if(&src == this)
+			return FALSE;
+		if(m_bRequest != src.m_bRequest)
+			return FALSE;
+
+		void* p			= m_parser.data;
+		m_parser		= src.m_parser;
+		m_parser.data	= p;
+
+		m_headers = src.m_headers;
+		m_cookies = src.m_cookies;
+
+		if(m_bRequest)
+		{
+			m_usUrlFieldSet = src.m_usUrlFieldSet;
+
+			for(int i = 0;i < HUF_MAX; i++)
+				m_pstrUrlFileds[i] = src.m_pstrUrlFileds[i];
+		}
+		else
+		{
+			m_sRequestMethod	= src.m_sRequestMethod;
+			*m_pstrRequestPath	= *src.m_pstrRequestPath;
+		}
+
+		m_enUpgrade = src.m_enUpgrade;
+
+		return TRUE;
+	}
+
+	BOOL CopyWSContext(const THttpObjT& src)
+	{
+		if(&src == this)
+			return FALSE;
+		if(m_bRequest != src.m_bRequest)
+			return FALSE;
+
+		if(!src.m_pwsContext && !m_pwsContext)
+			;
+		else if(!src.m_pwsContext && m_pwsContext)
+		{
+			delete m_pwsContext;
+			m_pwsContext = nullptr;
+		}
+		else
+		{
+			if(!m_pwsContext)
+				m_pwsContext = new TWSContext<THttpObjT<T, S>>(this);
+
+			m_pwsContext->CopyData(*src.m_pwsContext);
+		}
+
+		return TRUE;
+	}
+
+private:
+
+	void ResetParser()
+	{
+		::llhttp_init(&m_parser, m_bRequest ? HTTP_REQUEST : HTTP_RESPONSE, &sm_settings);
+		m_parser.data = this;		
+	}
+
+	void ResetHeaderState(BOOL bClearCookies = TRUE, BOOL bResetRequestData = TRUE)
+	{
+		if(m_bRequest)
+		{
+			if(m_usUrlFieldSet != 0)
+			{
+				m_usUrlFieldSet = 0;
+
+				for(int i = 0; i < HUF_MAX; i++)
+					m_pstrUrlFileds[i].Empty();
+			}
+		}
+		else
+		{
+			if(bResetRequestData)
+			{
+				m_sRequestMethod = -1;
+				m_pstrRequestPath->Empty();
+			}
+		}
+
+		if(m_bRequest || bClearCookies)
+			DeleteAllCookies();
+			
+		m_headers.clear();
+		ResetHeaderBuffer();
+	}
+
+	void ResetHeaderBuffer()
+	{
+		ResetBuffer();
+		m_strCurHeader.Empty();
+	}
+
+	void ReleaseWSContext()
+	{
+		if(m_pwsContext)
+		{
+			delete m_pwsContext;
+			m_pwsContext = nullptr;
+		}
+	}
+
+	void AppendBuffer(const char* at, size_t length)	{m_strBuffer.Append(at, (int)length);}
+	void ResetBuffer()									{m_strBuffer.Empty();}
+	LPCSTR GetBuffer()									{return m_strBuffer;}
+	CStringA& GetBufferRef()							{return m_strBuffer;}
+
+	static THttpObjT* Self(http_parser* p)				{return (THttpObjT*)(p->data);}
+	static T* SelfContext(http_parser* p)				{return Self(p)->m_pContext;}
+	static S* SelfSocketObj(http_parser* p)				{return Self(p)->m_pSocket;}
+
+private:
+	BOOL		m_bValid;
+	BOOL		m_bRequest;
+	BOOL		m_bReleased;
+	T*			m_pContext;
+	S*			m_pSocket;
+	http_parser	m_parser;
+	THeaderMap	m_headers;
+	TCookieMap	m_cookies;
+	CStringA	m_strBuffer;
+	CStringA	m_strCurHeader;
+
+	union
+	{
+		USHORT		m_usUrlFieldSet;
+		short		m_sRequestMethod;
+	};
+
+	union
+	{
+		CStringA* m_pstrUrlFileds;
+		CStringA* m_pstrRequestPath;
+	};
+
+	EnHttpUpgradeType	m_enUpgrade;
+	DWORD				m_dwFreeTime;
+
+	TWSContext<THttpObjT<T, S>>* m_pwsContext;
+
+	static http_parser_settings sm_settings;
+};
+
+template<class T, class S> http_parser_settings THttpObjT<T, S>::sm_settings = 
+{
+	on_message_begin,
+	on_url,
+	on_status,
+	nullptr,	// on_method
+	nullptr,	// on_version
+	on_header_field,
+	on_header_value,
+	nullptr,	// on_chunk_extension_name
+	nullptr,	// on_chunk_extension_value
+	on_headers_complete,
+	on_body,
+	on_message_complete,
+	on_url_complete,
+	on_status_complete,
+	nullptr,	// on_method_complete
+	nullptr,	// on_version_complete
+	on_header_field_complete,
+	on_header_value_complete,
+	nullptr,	// on_chunk_extension_name_complete
+	nullptr,	// on_chunk_extension_value_complete
+	on_chunk_header,
+	on_chunk_complete,
+	nullptr		// on_reset
+};
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+template<BOOL is_request, class T, class S> class CHttpObjPoolT
+{
+	typedef THttpObjT<T, S>		THttpObj;
+	typedef CRingPool<THttpObj>	TSSLHttpObjList;
+	typedef CCASQueue<THttpObj>	TSSLHttpObjQueue;
+
+public:
+	THttpObj* PickFreeHttpObj(T* pContext, S* pSocket)
+	{
+		DWORD dwIndex;
+		THttpObj* pHttpObj = nullptr;
+
+		if(m_lsFreeHttpObj.TryLock(&pHttpObj, dwIndex))
+		{
+			if(::GetTimeGap32(pHttpObj->GetFreeTime()) >= m_dwHttpObjLockTime)
+				VERIFY(m_lsFreeHttpObj.ReleaseLock(nullptr, dwIndex));
+			else
+			{
+				VERIFY(m_lsFreeHttpObj.ReleaseLock(pHttpObj, dwIndex));
+				pHttpObj = nullptr;
+			}
+		}
+
+		if(pHttpObj)
+			pHttpObj->Renew(pContext, pSocket);
+		else
+		{
+			pHttpObj = THttpObj::Construct(is_request, pContext, pSocket);
+			ASSERT(pHttpObj);
+		}
+		
+		return pHttpObj;
+	}
+
+	void PutFreeHttpObj(THttpObj* pHttpObj)
+	{
+		pHttpObj->SetFree();
+
+#ifndef USE_EXTERNAL_GC
+		ReleaseGCHttpObj();
+#endif
+		if(!m_lsFreeHttpObj.TryPut(pHttpObj))
+			m_lsGCHttpObj.PushBack(pHttpObj);
+	}
+
+	void Prepare()
+	{
+		m_lsFreeHttpObj.Reset(m_dwHttpObjPoolSize);
+	}
+
+	void Clear()
+	{
+		m_lsFreeHttpObj.Clear();
+
+		ReleaseGCHttpObj(TRUE);
+		VERIFY(m_lsGCHttpObj.IsEmpty());
+	}
+
+	void ReleaseGCHttpObj(BOOL bForce = FALSE)
+	{
+		::ReleaseGCObj(m_lsGCHttpObj, m_dwHttpObjLockTime, bForce);
+	}
+
+public:
+	void SetHttpObjLockTime	(DWORD dwHttpObjLockTime)	{m_dwHttpObjLockTime = dwHttpObjLockTime;}
+	void SetHttpObjPoolSize	(DWORD dwHttpObjPoolSize)	{m_dwHttpObjPoolSize = dwHttpObjPoolSize;}
+	void SetHttpObjPoolHold	(DWORD dwHttpObjPoolHold)	{m_dwHttpObjPoolHold = dwHttpObjPoolHold;}
+
+	DWORD GetHttpObjLockTime()	{return m_dwHttpObjLockTime;}
+	DWORD GetHttpObjPoolSize()	{return m_dwHttpObjPoolSize;}
+	DWORD GetHttpObjPoolHold()	{return m_dwHttpObjPoolHold;}
+
+public:
+	CHttpObjPoolT(	DWORD dwPoolSize = DEFAULT_HTTPOBJ_POOL_SIZE,
+					DWORD dwPoolHold = DEFAULT_HTTPOBJ_POOL_HOLD,
+					DWORD dwLockTime = DEFAULT_HTTPOBJ_LOCK_TIME)
+	: m_dwHttpObjPoolSize(dwPoolSize)
+	, m_dwHttpObjPoolHold(dwPoolHold)
+	, m_dwHttpObjLockTime(dwLockTime)
+	{
+
+	}
+
+	~CHttpObjPoolT()	{Clear();}
+
+	DECLARE_NO_COPY_CLASS(CHttpObjPoolT)
+
+public:
+	static const DWORD DEFAULT_HTTPOBJ_LOCK_TIME;
+	static const DWORD DEFAULT_HTTPOBJ_POOL_SIZE;
+	static const DWORD DEFAULT_HTTPOBJ_POOL_HOLD;
+
+private:
+	DWORD				m_dwHttpObjLockTime;
+	DWORD				m_dwHttpObjPoolSize;
+	DWORD				m_dwHttpObjPoolHold;
+
+	TSSLHttpObjList		m_lsFreeHttpObj;
+	TSSLHttpObjQueue	m_lsGCHttpObj;
+};
+
+template<BOOL is_request, class T, class S> const DWORD CHttpObjPoolT<is_request, T, S>::DEFAULT_HTTPOBJ_LOCK_TIME	= DEFAULT_OBJECT_CACHE_LOCK_TIME;
+template<BOOL is_request, class T, class S> const DWORD CHttpObjPoolT<is_request, T, S>::DEFAULT_HTTPOBJ_POOL_SIZE	= DEFAULT_OBJECT_CACHE_POOL_SIZE;
+template<BOOL is_request, class T, class S> const DWORD CHttpObjPoolT<is_request, T, S>::DEFAULT_HTTPOBJ_POOL_HOLD	= DEFAULT_OBJECT_CACHE_POOL_HOLD;
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+extern CStringA& GetHttpVersionStr(EnHttpVersion enVersion, CStringA& strResult);
+extern CStringA& AdjustRequestPath(BOOL bConnect, LPCSTR lpszPath, CStringA& strPath);
+extern LPCSTR GetHttpDefaultStatusCodeDesc(EnHttpStatusCode enCode);
+extern void MakeRequestLine(LPCSTR lpszMethod, LPCSTR lpszPath, EnHttpVersion enVersion, CStringA& strValue);
+extern void MakeStatusLine(EnHttpVersion enVersion, USHORT usStatusCode, LPCSTR lpszDesc, CStringA& strValue);
+extern void MakeHeaderLines(const THeader lpHeaders[], int iHeaderCount, const TCookieMap* pCookies, int iBodyLength, BOOL bRequest, int iConnFlag, LPCSTR lpszDefaultHost, USHORT usPort, CStringA& strValue);
+extern void MakeHttpPacket(const CStringA& strHeader, const BYTE* pBody, int iLength, WSABUF szBuffer[2]);
+extern int MakeChunkPackage(const BYTE* pData, int iLength, LPCSTR lpszExtensions, char szLen[12], WSABUF bufs[5]);
+extern BOOL MakeWSPacket(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], BYTE* pData, int iLength, ULONGLONG ullBodyLen, BYTE szHeader[HTTP_MAX_WS_HEADER_LEN], WSABUF szBuffer[2]);
+extern BOOL ParseUrl(const CStringA& strUrl, BOOL& bHttps, CStringA& strHost, USHORT& usPort, CStringA& strPath);
+
+#endif

+ 615 - 0
HttpServer.cpp

@@ -0,0 +1,615 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "HttpServer.h"
+
+#ifdef _HTTP_SUPPORT
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::Start(LPCTSTR lpszBindAddress, USHORT usPort)
+{
+	BOOL isOK = __super::Start(lpszBindAddress, usPort);
+
+	if(isOK) VERIFY(m_thCleaner.Start(this, &CHttpServerT::CleanerThreadProc));
+
+	return isOK;
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::CheckParams()
+{
+	if	((m_enLocalVersion != HV_1_1 && m_enLocalVersion != HV_1_0)								||
+		(m_dwReleaseDelay < MIN_HTTP_RELEASE_DELAY || m_dwReleaseDelay > MAX_HTTP_RELEASE_DELAY))
+	{
+		SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	return __super::CheckParams();
+}
+
+template<class T, USHORT default_port> void CHttpServerT<T, default_port>::PrepareStart()
+{
+	__super::PrepareStart();
+
+	m_objPool.SetHttpObjLockTime(GetFreeSocketObjLockTime());
+	m_objPool.SetHttpObjPoolSize(GetFreeSocketObjPool());
+	m_objPool.SetHttpObjPoolHold(GetFreeSocketObjHold());
+
+	m_objPool.Prepare();
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::SendResponse(CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc, const THeader lpHeaders[], int iHeaderCount, const BYTE* pData, int iLength)
+{
+	WSABUF szBuffer[2];
+	CStringA strHeader;
+
+	::MakeStatusLine(m_enLocalVersion, usStatusCode, lpszDesc, strHeader);
+	::MakeHeaderLines(lpHeaders, iHeaderCount, nullptr, iLength, FALSE, IsKeepAlive(dwConnID), nullptr, 0, strHeader);
+	::MakeHttpPacket(strHeader, pData, iLength, szBuffer);
+
+	return SendPackets(dwConnID, szBuffer, 2);
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, USHORT usStatusCode, LPCSTR lpszDesc, const THeader lpHeaders[], int iHeaderCount)
+{
+	CFile file;
+	CFileMapping fmap;
+
+	HRESULT hr = ::ReadSmallFile(CA2T(lpszFileName), file, fmap);
+
+	if(FAILED(hr))
+	{
+		::SetLastError(hr);
+		return FALSE;
+	}
+
+	return SendResponse(dwConnID, usStatusCode, lpszDesc, lpHeaders, iHeaderCount, (BYTE*)fmap, (int)fmap.Size());
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::SendChunkData(CONNID dwConnID, const BYTE* pData, int iLength, LPCSTR lpszExtensions)
+{
+	char szLen[12];
+	WSABUF bufs[5];
+
+	int iCount = MakeChunkPackage(pData, iLength, lpszExtensions, szLen, bufs);
+
+	return SendPackets(dwConnID, bufs, iCount);
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::Release(CONNID dwConnID)
+{
+	if(!HasStarted())
+		return FALSE;
+
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr || pHttpObj->HasReleased())
+		return FALSE;
+
+	pHttpObj->Release();
+
+	m_lsDyingQueue.PushBack(TDyingConnection::Construct(dwConnID));
+
+	return TRUE;
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::SendWSMessage(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
+{
+	WSABUF szBuffer[2];
+	BYTE szHeader[HTTP_MAX_WS_HEADER_LEN];
+
+	if(!::MakeWSPacket(bFinal, iReserved, iOperationCode, nullptr, (BYTE*)pData, iLength, ullBodyLen, szHeader, szBuffer))
+		return FALSE;
+
+	return SendPackets(dwConnID, szBuffer, 2);
+}
+
+template<class T, USHORT default_port> UINT CHttpServerT<T, default_port>::CleanerThreadProc(PVOID pv)
+{
+	TRACE("---------------> Connection Cleaner Thread 0x%08X started <---------------", SELF_THREAD_ID);
+
+	pollfd pfd		 = {m_evCleaner.GetFD(), POLLIN};
+	DWORD dwInterval = MAX(MIN_HTTP_RELEASE_CHECK_INTERVAL, (m_dwReleaseDelay - MIN_HTTP_RELEASE_DELAY / 2));
+
+	while(HasStarted())
+	{
+		int rs = (int)::PollForSingleObject(pfd, dwInterval);
+		ASSERT(rs >= TIMEOUT);
+
+		if(rs < TIMEOUT)
+			ERROR_ABORT();
+
+		if(rs == TIMEOUT)
+			KillDyingConnection();
+		else if(rs == 1)
+		{
+			m_evCleaner.Reset();
+			goto END_DETECTOR;
+		}
+		else
+			ASSERT(FALSE);
+	}
+
+END_DETECTOR:
+
+	ReleaseDyingConnection();
+	VERIFY(!HasStarted());
+
+	TRACE("---------------> Connection Cleaner Thread 0x%08X stoped <---------------", SELF_THREAD_ID);
+
+	return 0;
+}
+
+template<class T, USHORT default_port> void CHttpServerT<T, default_port>::KillDyingConnection()
+{
+	TDyingConnection* pDyingConn		= nullptr;
+	TDyingConnection* pFirstDyingConn	= nullptr;
+	DWORD now							= ::TimeGetTime();
+
+	while(m_lsDyingQueue.UnsafePeekFront(&pDyingConn))
+	{
+		if((int)(now - pDyingConn->killTime) < (int)m_dwReleaseDelay)
+			break;
+
+		BOOL bDisconnect = TRUE;
+		BOOL bDestruct	 = TRUE;
+
+		VERIFY(m_lsDyingQueue.UnsafePopFront(&pDyingConn));
+
+		int iPending;
+		if(!GetPendingDataLength(pDyingConn->connID, iPending))
+			bDisconnect = FALSE;
+		else if(iPending > 0)
+		{
+			bDisconnect	= FALSE;
+			bDestruct	= FALSE;
+		}
+
+		if(bDisconnect)
+			Disconnect(pDyingConn->connID, TRUE);
+
+		if(bDestruct)
+		{
+			TDyingConnection::Destruct(pDyingConn);
+
+			if(pFirstDyingConn == pDyingConn)
+				pFirstDyingConn = nullptr;
+		}
+		else
+		{
+			m_lsDyingQueue.PushBack(pDyingConn);
+
+			if(pFirstDyingConn == nullptr)
+				pFirstDyingConn = pDyingConn;
+			else if(pFirstDyingConn == pDyingConn)
+				break;
+		}
+	}
+}
+
+template<class T, USHORT default_port> void CHttpServerT<T, default_port>::ReleaseDyingConnection()
+{
+	TDyingConnection* pDyingConn = nullptr;
+
+	while(m_lsDyingQueue.UnsafePopFront(&pDyingConn))
+		TDyingConnection::Destruct(pDyingConn);
+
+	VERIFY(m_lsDyingQueue.IsEmpty());
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::FireAccept(TSocketObj* pSocketObj)
+{
+	return m_bHttpAutoStart ? __super::FireAccept(pSocketObj) : __super::DoFireAccept(pSocketObj);
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireAccept(TSocketObj* pSocketObj)
+{
+	THttpObj* pHttpObj	  = DoStartHttp(pSocketObj);
+	EnHandleResult result = __super::DoFireAccept(pSocketObj);
+
+	if(result == HR_ERROR)
+	{
+		m_objPool.PutFreeHttpObj(pHttpObj);
+		SetConnectionReserved(pSocketObj, nullptr);
+	}
+
+	return result;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireHandShake(TSocketObj* pSocketObj)
+{
+	EnHandleResult result = __super::DoFireHandShake(pSocketObj);
+
+	if(result == HR_ERROR)
+	{
+		THttpObj* pHttpObj = FindHttpObj(pSocketObj);
+
+		if(pHttpObj != nullptr)
+		{
+			m_objPool.PutFreeHttpObj(pHttpObj);
+			SetConnectionReserved(pSocketObj, nullptr);
+		}
+	}
+
+	return result;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+{
+	THttpObj* pHttpObj = FindHttpObj(pSocketObj);
+
+	if(pHttpObj == nullptr)
+		return DoFireSuperReceive(pSocketObj, pData, iLength);
+	else
+	{
+		if(pHttpObj->HasReleased())
+			return HR_ERROR;
+
+		return pHttpObj->Execute(pData, iLength);
+	}
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+{
+	EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
+
+	THttpObj* pHttpObj = FindHttpObj(pSocketObj);
+
+	if(pHttpObj != nullptr)
+	{
+		m_objPool.PutFreeHttpObj(pHttpObj);
+		SetConnectionReserved(pSocketObj, nullptr);
+	}
+
+	return result;
+}
+
+template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireShutdown()
+{
+	EnHandleResult result = __super::DoFireShutdown();
+
+	m_objPool.Clear();
+	WaitForCleanerThreadEnd();
+
+	return result;
+}
+
+template<class T, USHORT default_port> void CHttpServerT<T, default_port>::ReleaseGCSocketObj(BOOL bForce)
+{
+	__super::ReleaseGCSocketObj(bForce);
+
+#ifdef USE_EXTERNAL_GC
+	m_objPool.ReleaseGCHttpObj(bForce);
+#endif
+}
+
+template<class T, USHORT default_port> void CHttpServerT<T, default_port>::WaitForCleanerThreadEnd()
+{
+	if(m_thCleaner.IsRunning())
+	{
+		m_evCleaner.Set();
+		m_thCleaner.Join();
+		m_evCleaner.Reset();
+	}
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::IsUpgrade(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->IsUpgrade();
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::IsKeepAlive(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->IsKeepAlive();
+}
+
+template<class T, USHORT default_port> USHORT CHttpServerT<T, default_port>::GetVersion(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return 0;
+
+	return pHttpObj->GetVersion();
+}
+
+template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetHost(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return nullptr;
+
+	return pHttpObj->GetHost();
+}
+
+template<class T, USHORT default_port> ULONGLONG CHttpServerT<T, default_port>::GetContentLength(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return 0;
+
+	return pHttpObj->GetContentLength();
+}
+
+template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetContentType(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return nullptr;
+
+	return pHttpObj->GetContentType();
+}
+
+template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetContentEncoding(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return nullptr;
+
+	return pHttpObj->GetContentEncoding();
+}
+
+template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetTransferEncoding(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return nullptr;
+
+	return pHttpObj->GetTransferEncoding();
+}
+
+template<class T, USHORT default_port> EnHttpUpgradeType CHttpServerT<T, default_port>::GetUpgradeType(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return HUT_NONE;
+
+	return pHttpObj->GetUpgradeType();
+}
+
+template<class T, USHORT default_port> USHORT CHttpServerT<T, default_port>::GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return 0;
+
+	return pHttpObj->GetParseErrorCode(lpszErrorDesc);
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetHeader(lpszName, lpszValue);
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetHeaders(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetHeaders(lpszName, lpszValue, dwCount);
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetAllHeaders(lpHeaders, dwCount);
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetAllHeaderNames(lpszName, dwCount);
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetCookie(lpszName, lpszValue);
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetAllCookies(lpCookies, dwCount);
+}
+
+template<class T, USHORT default_port> USHORT CHttpServerT<T, default_port>::GetUrlFieldSet(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return 0;
+
+	return pHttpObj->GetUrlFieldSet();
+}
+
+template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetUrlField(CONNID dwConnID, EnHttpUrlField enField)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return nullptr;
+
+	return pHttpObj->GetUrlField(enField);
+}
+
+template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetMethod(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return nullptr;
+
+	return pHttpObj->GetMethod();
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetWSMessageState(CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
+{
+	THttpObj* pHttpObj = FindHttpObj(dwConnID);
+
+	if(pHttpObj == nullptr)
+		return FALSE;
+
+	return pHttpObj->GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);
+}
+
+template<class T, USHORT default_port> inline typename CHttpServerT<T, default_port>::THttpObj* CHttpServerT<T, default_port>::FindHttpObj(CONNID dwConnID)
+{
+	THttpObj* pHttpObj = nullptr;
+	GetConnectionReserved(dwConnID, (PVOID*)&pHttpObj);
+
+	return pHttpObj;
+}
+
+template<class T, USHORT default_port> inline typename CHttpServerT<T, default_port>::THttpObj* CHttpServerT<T, default_port>::FindHttpObj(TSocketObj* pSocketObj)
+{
+	THttpObj* pHttpObj = nullptr;
+	GetConnectionReserved(pSocketObj, (PVOID*)&pHttpObj);
+
+	return pHttpObj;
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::StartHttp(CONNID dwConnID)
+{
+	if(IsHttpAutoStart())
+	{
+		::SetLastError(ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return StartHttp(pSocketObj);
+}
+
+template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::StartHttp(TSocketObj* pSocketObj)
+{
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	CReentrantCriSecLock locallock(pSocketObj->csSend);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	THttpObj* pHttpObj = FindHttpObj(pSocketObj);
+
+	if(pHttpObj != nullptr)
+	{
+		::SetLastError(ERROR_ALREADY_INITIALIZED);
+		return FALSE;
+	}
+
+	DoStartHttp(pSocketObj);
+
+	if(!IsSecure())
+		FireHandShake(pSocketObj);
+	else
+	{
+#ifdef _SSL_SUPPORT
+		if(IsSSLAutoHandShake())
+			StartSSLHandShake(pSocketObj);
+#endif
+	}
+
+	return TRUE;
+}
+
+template<class T, USHORT default_port> typename CHttpServerT<T, default_port>::THttpObj* CHttpServerT<T, default_port>::DoStartHttp(TSocketObj* pSocketObj)
+{
+	THttpObj* pHttpObj = m_objPool.PickFreeHttpObj(this, pSocketObj);
+	VERIFY(SetConnectionReserved(pSocketObj, pHttpObj));
+
+	return pHttpObj;
+}
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+template class CHttpServerT<CTcpServer, HTTP_DEFAULT_PORT>;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLServer.h"
+
+template class CHttpServerT<CSSLServer, HTTPS_DEFAULT_PORT>;
+
+#endif
+
+#endif

+ 223 - 0
HttpServer.h

@@ -0,0 +1,223 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpServer.h"
+#include "HttpHelper.h"
+#include "common/Thread.h"
+
+#ifdef _HTTP_SUPPORT
+
+template<class T, USHORT default_port> class CHttpServerT : public IComplexHttpResponder, public T
+{
+	using __super = T;
+	using __super::GetConnectionReserved;
+	using __super::SetConnectionReserved;
+	using __super::SetLastError;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+	using __super::SendPackets;
+	using __super::Disconnect;
+	using __super::HasStarted;
+	using __super::GetFreeSocketObjLockTime;
+	using __super::GetFreeSocketObjPool;
+	using __super::GetFreeSocketObjHold;
+	using __super::GetPendingDataLength;
+
+	using __super::IsSecure;
+	using __super::FireHandShake;
+	using __super::FindSocketObj;
+
+#ifdef _SSL_SUPPORT
+	using __super::StartSSLHandShake;
+	using __super::IsSSLAutoHandShake;
+#endif
+
+protected:
+	using CCleanThread	= CThread<CHttpServerT, VOID, UINT>;
+	using CHttpObjPool	= CHttpObjPoolT<TRUE, CHttpServerT, TSocketObj>;
+	using THttpObj		= THttpObjT<CHttpServerT, TSocketObj>;
+
+	friend typename		CHttpServerT::CCleanThread;
+	friend typename		CHttpServerT::THttpObj;
+
+public:
+
+	virtual BOOL Start(LPCTSTR lpszBindAddress, USHORT usPort);
+
+	virtual BOOL SendResponse(CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc = nullptr, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pData = nullptr, int iLength = 0);
+	virtual BOOL SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, USHORT usStatusCode = HSC_OK, LPCSTR lpszDesc = nullptr, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0);
+	virtual BOOL SendChunkData(CONNID dwConnID, const BYTE* pData = nullptr, int iLength = 0, LPCSTR lpszExtensions = nullptr);
+
+	virtual BOOL Release(CONNID dwConnID);
+
+	virtual BOOL SendWSMessage(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0);
+
+	virtual BOOL StartHttp(CONNID dwConnID);
+
+public:
+
+	virtual void SetHttpAutoStart(BOOL bAutoStart)				{ENSURE_HAS_STOPPED(); m_bHttpAutoStart = bAutoStart;}
+	virtual void SetLocalVersion(EnHttpVersion enLocalVersion)	{ENSURE_HAS_STOPPED(); m_enLocalVersion = enLocalVersion;}
+	virtual void SetReleaseDelay(DWORD dwReleaseDelay)			{ENSURE_HAS_STOPPED(); m_dwReleaseDelay = dwReleaseDelay;}
+
+	virtual BOOL IsHttpAutoStart			()	{return m_bHttpAutoStart;}
+	virtual EnHttpVersion GetLocalVersion	()	{return m_enLocalVersion;}
+	virtual DWORD GetReleaseDelay			()	{return m_dwReleaseDelay;}
+
+	virtual BOOL IsUpgrade(CONNID dwConnID);
+	virtual BOOL IsKeepAlive(CONNID dwConnID);
+	virtual USHORT GetVersion(CONNID dwConnID);
+	virtual LPCSTR GetHost(CONNID dwConnID);
+	virtual ULONGLONG GetContentLength(CONNID dwConnID);
+	virtual LPCSTR GetContentType(CONNID dwConnID);
+	virtual LPCSTR GetContentEncoding(CONNID dwConnID);
+	virtual LPCSTR GetTransferEncoding(CONNID dwConnID);
+	virtual EnHttpUpgradeType GetUpgradeType(CONNID dwConnID);
+	virtual USHORT GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc = nullptr);
+
+	virtual BOOL GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue);
+	virtual BOOL GetHeaders(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount);
+	virtual BOOL GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount);
+	virtual BOOL GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount);
+
+	virtual BOOL GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue);
+	virtual BOOL GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount);
+
+	virtual USHORT GetUrlFieldSet(CONNID dwConnID);
+	virtual LPCSTR GetUrlField(CONNID dwConnID, EnHttpUrlField enField);
+	virtual LPCSTR GetMethod(CONNID dwConnID);
+
+	virtual BOOL GetWSMessageState(CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain);
+
+private:
+	BOOL StartHttp(TSocketObj* pSocketObj);
+	THttpObj* DoStartHttp(TSocketObj* pSocketObj);
+
+private:
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual EnHandleResult FireAccept(TSocketObj* pSocketObj);
+	virtual EnHandleResult DoFireAccept(TSocketObj* pSocketObj);
+	virtual EnHandleResult DoFireHandShake(TSocketObj* pSocketObj);
+	virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength);
+	virtual EnHandleResult DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
+	virtual EnHandleResult DoFireShutdown();
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
+
+	EnHandleResult DoFireSuperReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return __super::DoFireReceive(pSocketObj, pData, iLength);}
+
+	EnHttpParseResult FireMessageBegin(TSocketObj* pSocketObj)
+		{return m_pListener->OnMessageBegin((IHttpServer*)this, pSocketObj->connID);}
+	EnHttpParseResult FireRequestLine(TSocketObj* pSocketObj, LPCSTR lpszMethod, LPCSTR lpszUrl)
+		{return m_pListener->OnRequestLine((IHttpServer*)this, pSocketObj->connID, lpszMethod, lpszUrl);}
+	EnHttpParseResult FireStatusLine(TSocketObj* pSocketObj, USHORT usStatusCode, LPCSTR lpszDesc)
+		{return m_pListener->OnStatusLine((IHttpServer*)this, pSocketObj->connID, usStatusCode, lpszDesc);}
+	EnHttpParseResult FireHeader(TSocketObj* pSocketObj, LPCSTR lpszName, LPCSTR lpszValue)
+		{return m_pListener->OnHeader((IHttpServer*)this, pSocketObj->connID, lpszName, lpszValue);}
+	EnHttpParseResult FireHeadersComplete(TSocketObj* pSocketObj)
+		{return m_pListener->OnHeadersComplete((IHttpServer*)this, pSocketObj->connID);}
+	EnHttpParseResult FireBody(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnBody((IHttpServer*)this, pSocketObj->connID, pData, iLength);}
+	EnHttpParseResult FireChunkHeader(TSocketObj* pSocketObj, int iLength)
+		{return m_pListener->OnChunkHeader((IHttpServer*)this, pSocketObj->connID, iLength);}
+	EnHttpParseResult FireChunkComplete(TSocketObj* pSocketObj)
+		{return m_pListener->OnChunkComplete((IHttpServer*)this, pSocketObj->connID);}
+	EnHttpParseResult FireMessageComplete(TSocketObj* pSocketObj)
+		{return m_pListener->OnMessageComplete((IHttpServer*)this, pSocketObj->connID);}
+	EnHttpParseResult FireUpgrade(TSocketObj* pSocketObj, EnHttpUpgradeType enUpgradeType)
+		{return m_pListener->OnUpgrade((IHttpServer*)this, pSocketObj->connID, enUpgradeType);}
+	EnHttpParseResult FireParseError(TSocketObj* pSocketObj, int iErrorCode, LPCSTR lpszErrorDesc)
+		{return m_pListener->OnParseError((IHttpServer*)this, pSocketObj->connID, iErrorCode, lpszErrorDesc);}
+
+	EnHandleResult FireWSMessageHeader(TSocketObj* pSocketObj, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
+		{return m_pListener->OnWSMessageHeader((IHttpServer*)this, pSocketObj->connID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
+	EnHandleResult FireWSMessageBody(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnWSMessageBody((IHttpServer*)this, pSocketObj->connID, pData, iLength);}
+	EnHandleResult FireWSMessageComplete(TSocketObj* pSocketObj)
+		{return m_pListener->OnWSMessageComplete((IHttpServer*)this, pSocketObj->connID);}
+
+	inline THttpObj* FindHttpObj(CONNID dwConnID);
+	inline THttpObj* FindHttpObj(TSocketObj* pSocketObj);
+
+	CCookieMgr* GetCookieMgr()						{return nullptr;}
+	LPCSTR GetRemoteDomain(TSocketObj* pSocketObj)	{return nullptr;}
+
+private:
+	void KillDyingConnection();
+	void ReleaseDyingConnection();
+
+	UINT CleanerThreadProc(PVOID pv = nullptr);
+	void WaitForCleanerThreadEnd();
+
+public:
+	CHttpServerT(IHttpServerListener* pListener)
+	: T					(pListener)
+	, m_pListener		(pListener)
+	, m_bHttpAutoStart	(TRUE)
+	, m_enLocalVersion	(DEFAULT_HTTP_VERSION)
+	, m_dwReleaseDelay	(DEFAULT_HTTP_RELEASE_DELAY)
+	{
+
+	}
+
+	virtual ~CHttpServerT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	IHttpServerListener*		m_pListener;
+
+	CEvt						m_evCleaner;
+	CCleanThread				m_thCleaner;
+
+	EnHttpVersion				m_enLocalVersion;
+	DWORD						m_dwReleaseDelay;
+
+	BOOL						m_bHttpAutoStart;
+
+	CCASQueue<TDyingConnection>	m_lsDyingQueue;
+
+	CHttpObjPool				m_objPool;
+};
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+typedef CHttpServerT<CTcpServer, HTTP_DEFAULT_PORT> CHttpServer;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLServer.h"
+
+typedef CHttpServerT<CSSLServer, HTTPS_DEFAULT_PORT> CHttpsServer;
+
+#endif
+
+#endif

+ 132 - 0
InternalDef.h

@@ -0,0 +1,132 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "hpsocket/HPTypeDef.h"
+
+/************************************************************************
+名称:全局常量
+描述:声明组件的公共全局常量
+************************************************************************/
+
+/* Socket 缓冲区最小值 */
+#define MIN_SOCKET_BUFFER_SIZE					8
+/* 小文件最大字节数 */
+#define MAX_SMALL_FILE_SIZE						0x3FFFFF
+/* 最大连接时长 */
+#define MAX_CONNECTION_PERIOD					(MAXINT / 2)
+/* 处理接收事件时最大读取次数 */
+#define MAX_CONTINUE_READS						100
+/* 处理发送事件时最大写入次数 */
+#define MAX_CONTINUE_WRITES						100
+
+/* 默认工作队列等待的最大描述符事件数量 */
+#define DEFAULT_WORKER_MAX_EVENT_COUNT			CIODispatcher::DEF_WORKER_MAX_EVENTS
+
+/* Server/Agent 最大连接数 */
+#define MAX_CONNECTION_COUNT					(5 * 1000 * 1000)
+/* Server/Agent 默认最大连接数 */
+#define DEFAULT_CONNECTION_COUNT				10000
+/* Server/Agent 默认 Socket 对象缓存锁定时间 */
+#define DEFAULT_FREE_SOCKETOBJ_LOCK_TIME		DEFAULT_OBJECT_CACHE_LOCK_TIME
+/* Server/Agent 默认 Socket 缓存池大小 */
+#define DEFAULT_FREE_SOCKETOBJ_POOL				DEFAULT_OBJECT_CACHE_POOL_SIZE
+/* Server/Agent 默认 Socket 缓存池回收阀值 */
+#define DEFAULT_FREE_SOCKETOBJ_HOLD				DEFAULT_OBJECT_CACHE_POOL_HOLD
+/* Server/Agent 默认内存块缓存池大小 */
+#define DEFAULT_FREE_BUFFEROBJ_POOL				DEFAULT_BUFFER_CACHE_POOL_SIZE
+/* Server/Agent 默认内存块缓存池回收阀值 */
+#define DEFAULT_FREE_BUFFEROBJ_HOLD				DEFAULT_BUFFER_CACHE_POOL_HOLD
+/* Client 默认内存块缓存池大小 */
+#define DEFAULT_CLIENT_FREE_BUFFER_POOL_SIZE	60
+/* Client 默认内存块缓存池回收阀值 */
+#define DEFAULT_CLIENT_FREE_BUFFER_POOL_HOLD	60
+/* Client/Agent 默认同步连接超时时间 */
+#define DEFAULT_SYNC_CONNECT_TIMEOUT			10000
+/* IPv4 默认绑定地址 */
+#define  DEFAULT_IPV4_BIND_ADDRESS				_T("0.0.0.0")
+/* IPv6 默认绑定地址 */
+#define  DEFAULT_IPV6_BIND_ADDRESS				_T("::")
+/* IPv4 广播地址 */
+#define DEFAULT_IPV4_BROAD_CAST_ADDRESS			_T("255.255.255.255")
+
+/* TCP 默认通信数据缓冲区大小 */
+#define DEFAULT_TCP_SOCKET_BUFFER_SIZE			DEFAULT_BUFFER_CACHE_CAPACITY
+/* TCP 默认心跳包间隔 */
+#define DEFALUT_TCP_KEEPALIVE_TIME				(60 * 1000)
+/* TCP 默认心跳确认包检测间隔 */
+#define DEFALUT_TCP_KEEPALIVE_INTERVAL			(20 * 1000)
+/* TCP Server 默认 Listen 队列大小 */
+#define DEFAULT_TCP_SERVER_SOCKET_LISTEN_QUEUE	SOMAXCONN
+
+/* UDP 最大数据报文最大长度 */
+#define MAXIMUM_UDP_MAX_DATAGRAM_SIZE			(16 * DEFAULT_BUFFER_CACHE_CAPACITY)
+/* UDP 默认数据报文最大长度 */
+#define DEFAULT_UDP_MAX_DATAGRAM_SIZE			1432
+/* UDP 默认 Receive 预投递数量 */
+#define DEFAULT_UDP_POST_RECEIVE_COUNT			DEFAULT_WORKER_MAX_EVENT_COUNT
+/* UDP 默认监测包尝试次数 */
+#define DEFAULT_UDP_DETECT_ATTEMPTS				3
+/* UDP 默认监测包发送间隔 */
+#define DEFAULT_UDP_DETECT_INTERVAL				(60 * 1000)
+
+/* TCP Pack 包长度位数 */
+#define TCP_PACK_LENGTH_BITS					22
+/* TCP Pack 包长度掩码 */
+#define TCP_PACK_LENGTH_MASK					0x3FFFFF
+/* TCP Pack 包最大长度硬限制 */
+#define TCP_PACK_MAX_SIZE_LIMIT					0x3FFFFF
+/* TCP Pack 包默认最大长度 */
+#define TCP_PACK_DEFAULT_MAX_SIZE				0x040000
+/* TCP Pack 包头标识值硬限制 */
+#define TCP_PACK_HEADER_FLAG_LIMIT				0x0003FF
+/* TCP Pack 包头默认标识值 */
+#define TCP_PACK_DEFAULT_HEADER_FLAG			0x000000
+
+/* 默认压缩/解压数据缓冲器长度 */
+#define DEFAULT_COMPRESS_BUFFER_SIZE			(16 * 1024)
+
+/* 垃圾回收检查间隔(毫秒) */
+#define GC_CHECK_INTERVAL						(15 * 1000)
+
+#define HOST_SEPARATOR_CHAR						'^'
+#define PORT_SEPARATOR_CHAR						':'
+#define IPV6_ADDR_BEGIN_CHAR					'['
+#define IPV6_ADDR_END_CHAR						']'
+#define IPV4_ADDR_SEPARATOR_CHAR				'.'
+#define IPV6_ADDR_SEPARATOR_CHAR				':'
+#define IPV6_ZONE_INDEX_CHAR					'%'
+
+#define CST_CONNECTING							(-1)
+#define INVALID_SOCKET							INVALID_FD
+#define SOCKET_ERROR							HAS_ERROR
+#define WSASetLastError							SetLastError
+#define WSAGetLastError							GetLastError
+#define InetPton								inet_pton
+#define InetNtop								inet_ntop
+#define closesocket								close
+
+#define ENSURE_STOP()							{if(GetState() != SS_STOPPED) {Stop();} Wait();}
+#define ENSURE_HAS_STOPPED()					{ASSERT(GetState() == SS_STOPPED); if(GetState() != SS_STOPPED) return;}
+#define WAIT_FOR_STOP_PREDICATE					[this]() {return GetState() == SS_STOPPED;}

+ 51 - 0
MiscHelper.cpp

@@ -0,0 +1,51 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "MiscHelper.h"
+
+BOOL AddPackHeader(const WSABUF * pBuffers, int iCount, unique_ptr<WSABUF[]>& buffers, DWORD dwMaxPackSize, USHORT usPackHeaderFlag, DWORD& dwHeader)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	DWORD iLength = 0;
+
+	for(int i = 0; i < iCount; i++)
+	{
+		const WSABUF& buf	= pBuffers[i];
+		buffers[i + 1]		= buf;
+		iLength			   += buf.len;
+	}
+
+	if(iLength == 0 || iLength > dwMaxPackSize)
+	{
+		::SetLastError(ERROR_BAD_LENGTH);
+		return FALSE;
+	}
+
+	dwHeader = ::HToLE32((usPackHeaderFlag << TCP_PACK_LENGTH_BITS) | iLength);
+
+	buffers[0].len = sizeof(dwHeader);
+	buffers[0].buf = (LPBYTE)&dwHeader;
+
+	return TRUE;
+}

+ 157 - 0
MiscHelper.h

@@ -0,0 +1,157 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SocketHelper.h"
+
+/* Pack Data Info */
+template<typename B = void> struct TPackInfo
+{
+	bool	header;
+	DWORD	length;
+	B*		pBuffer;
+
+	static TPackInfo* Construct(B* pbuf = nullptr, bool head = true, DWORD len = sizeof(DWORD))
+	{
+		return new TPackInfo(pbuf, head, len);
+	}
+
+	static void Destruct(TPackInfo* pPackInfo)
+	{
+		if(pPackInfo)
+			delete pPackInfo;
+	}
+
+	TPackInfo(B* pbuf = nullptr, bool head = true, DWORD len = sizeof(DWORD))
+	: header(head), length(len), pBuffer(pbuf)
+	{
+	}
+
+	void Reset()
+	{
+		header	= true;
+		length	= sizeof(DWORD);
+		pBuffer	= nullptr;
+	}
+};
+
+typedef TPackInfo<TBuffer>	TBufferPackInfo;
+
+BOOL AddPackHeader(const WSABUF * pBuffers, int iCount, unique_ptr<WSABUF[]>& buffers, DWORD dwMaxPackSize, USHORT usPackHeaderFlag, DWORD& dwHeader);
+
+template<class B> EnFetchResult FetchBuffer(B* pBuffer, BYTE* pData, int iLength)
+{
+	ASSERT(pBuffer	!= nullptr);
+	ASSERT(pData	!= nullptr && iLength > 0);
+
+	EnFetchResult result = FR_OK;
+
+	if(pBuffer->Length() >= iLength)
+		pBuffer->Fetch(pData, iLength);
+	else
+		result = FR_LENGTH_TOO_LONG;
+
+	return result;
+}
+
+template<class B> EnFetchResult PeekBuffer(B* pBuffer, BYTE* pData, int iLength)
+{
+	ASSERT(pBuffer	!= nullptr);
+	ASSERT(pData	!= nullptr && iLength > 0);
+
+	EnFetchResult result = FR_OK;
+
+	if(pBuffer->Length() >= iLength)
+		pBuffer->Peek(pData, iLength);
+	else
+		result = FR_LENGTH_TOO_LONG;
+
+	return result;
+}
+
+template<class T, class B, class S> EnHandleResult ParsePack(T* pThis, TPackInfo<B>* pInfo, B* pBuffer, S* pSocket, DWORD dwMaxPackSize, USHORT usPackHeaderFlag)
+{
+	EnHandleResult rs = HR_OK;
+
+	int required = pInfo->length;
+	int remain	 = pBuffer->Length();
+
+	while(remain >= required)
+	{
+		if(pSocket->IsPaused())
+			break;
+
+		remain -= required;
+		CBufferPtr buffer(required);
+
+		pBuffer->Fetch(buffer, (int)buffer.Size());
+
+		if(pInfo->header)
+		{
+			DWORD header = ::HToLE32(*((DWORD*)(BYTE*)buffer));
+
+			if(usPackHeaderFlag != 0)
+			{
+				USHORT flag = (USHORT)(header >> TCP_PACK_LENGTH_BITS);
+
+				if(flag != usPackHeaderFlag)
+				{
+					::SetLastError(ERROR_INVALID_DATA);
+					return HR_ERROR;
+				}
+			}
+
+			DWORD len = header & TCP_PACK_LENGTH_MASK;
+
+			if(len == 0 || len > dwMaxPackSize)
+			{
+				::SetLastError(ERROR_BAD_LENGTH);
+				return HR_ERROR;
+			}
+
+			required = len;
+		}
+		else
+		{
+			rs = pThis->DoFireSuperReceive(pSocket, (const BYTE*)buffer, (int)buffer.Size());
+
+			if(rs == HR_ERROR)
+				return rs;
+
+			required = sizeof(DWORD);
+		}
+
+		pInfo->header = !pInfo->header;
+		pInfo->length = required;
+	}
+
+	return rs;
+}
+
+template<class T, class B, class S> EnHandleResult ParsePack(T* pThis, TPackInfo<B>* pInfo, B* pBuffer, S* pSocket, DWORD dwMaxPackSize, USHORT usPackHeaderFlag, const BYTE* pData, int iLength)
+{
+	pBuffer->Cat(pData, iLength);
+
+	return ParsePack(pThis, pInfo, pBuffer, pSocket, dwMaxPackSize, usPackHeaderFlag);
+}

+ 229 - 0
SSLAgent.cpp

@@ -0,0 +1,229 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "SSLAgent.h"
+#include "SSLHelper.h"
+
+#ifdef _SSL_SUPPORT
+
+BOOL CSSLAgent::CheckParams()
+{
+	if(!m_sslCtx.IsValid())
+	{
+		SetLastError(SE_SSL_ENV_NOT_READY, __FUNCTION__, ERROR_NOT_READY);
+		return FALSE;
+	}
+
+	return __super::CheckParams();
+}
+
+void CSSLAgent::PrepareStart()
+{
+	__super::PrepareStart();
+
+	m_sslPool.SetItemCapacity	(GetSocketBufferSize());
+	m_sslPool.SetItemPoolSize	(GetFreeBufferObjPool());
+	m_sslPool.SetItemPoolHold	(GetFreeBufferObjHold());
+	m_sslPool.SetSessionLockTime(GetFreeSocketObjLockTime());
+	m_sslPool.SetSessionPoolSize(GetFreeSocketObjPool());
+	m_sslPool.SetSessionPoolHold(GetFreeSocketObjHold());
+
+	m_sslPool.Prepare();
+}
+
+void CSSLAgent::Reset()
+{
+	m_sslPool.Clear();
+	m_sslCtx.RemoveThreadLocalState();
+
+	__super::Reset();
+}
+
+void CSSLAgent::OnWorkerThreadEnd(THR_ID dwThreadID)
+{
+	m_sslCtx.RemoveThreadLocalState();
+
+	__super::OnWorkerThreadEnd(dwThreadID);
+}
+
+void CSSLAgent::ReleaseGCSocketObj(BOOL bForce)
+{
+	__super::ReleaseGCSocketObj(bForce);
+
+#ifdef USE_EXTERNAL_GC
+	m_sslPool.ReleaseGCSession(bForce);
+#endif
+}
+
+BOOL CSSLAgent::SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession != nullptr)
+	{
+		CLocalSafeCounter localcounter(*pSession);
+		return ::ProcessSend(this, pSocketObj, pSession, pBuffers, iCount);
+	}
+
+	return DoSendPackets(pSocketObj, pBuffers, iCount);
+}
+
+EnHandleResult CSSLAgent::FireConnect(TAgentSocketObj* pSocketObj)
+{
+	EnHandleResult result = DoFireConnect(pSocketObj);
+
+	if(result != HR_ERROR && m_bSSLAutoHandShake)
+		DoSSLHandShake(pSocketObj);
+
+	return result;
+}
+
+EnHandleResult CSSLAgent::FireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+{
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession != nullptr)
+	{
+		CLocalSafeCounter localcounter(*pSession);
+		return ::ProcessReceive(this, pSocketObj, pSession, pData, iLength);
+	}
+
+	return DoFireReceive(pSocketObj, pData, iLength);
+}
+
+EnHandleResult CSSLAgent::FireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+{
+	EnHandleResult result = DoFireClose(pSocketObj, enOperation, iErrorCode);
+
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession != nullptr)
+		m_sslPool.PutFreeSession(pSession);
+
+	return result;
+}
+
+BOOL CSSLAgent::StartSSLHandShake(CONNID dwConnID)
+{
+	if(IsSSLAutoHandShake())
+	{
+		::SetLastError(ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return StartSSLHandShake(pSocketObj);
+}
+
+BOOL CSSLAgent::StartSSLHandShake(TAgentSocketObj* pSocketObj)
+{
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	CReentrantCriSecLock locallock(pSocketObj->csSend);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession != nullptr)
+	{
+		::SetLastError(ERROR_ALREADY_INITIALIZED);
+		return FALSE;
+	}
+
+	DoSSLHandShake(pSocketObj);
+
+	return TRUE;
+}
+
+void CSSLAgent::DoSSLHandShake(TAgentSocketObj* pSocketObj)
+{
+	CSSLSession* pSession = m_sslPool.PickFreeSession(pSocketObj->host);
+
+	CLocalSafeCounter localcounter(*pSession);
+
+	ENSURE(SetConnectionReserved2(pSocketObj, pSession));
+	ENSURE(::ProcessHandShake(this, pSocketObj, pSession) == HR_OK);
+}
+
+BOOL CSSLAgent::GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo)
+{
+	ASSERT(lppInfo != nullptr);
+
+	*lppInfo					= nullptr;
+	TAgentSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession == nullptr || !pSession->IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return pSession->GetSessionInfo(enInfo, lppInfo);
+}
+
+#endif

+ 107 - 0
SSLAgent.h

@@ -0,0 +1,107 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpAgent.h"
+#include "SSLHelper.h"
+
+#ifdef _SSL_SUPPORT
+
+class CSSLAgent : public CTcpAgent
+{
+	using __super = CTcpAgent;
+
+public:
+	using __super::Wait;
+
+public:
+	virtual BOOL IsSecure() {return TRUE;}
+	virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
+
+	virtual BOOL SetupSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr)
+		{return m_sslCtx.Initialize(SSL_SM_CLIENT, iVerifyMode, FALSE, (LPVOID)lpszPemCertFile, (LPVOID)lpszPemKeyFile, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCertFileOrPath, nullptr);}
+
+	virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr)
+		{return m_sslCtx.Initialize(SSL_SM_CLIENT, iVerifyMode, TRUE, (LPVOID)lpszPemCert, (LPVOID)lpszPemKey, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCert, nullptr);}
+
+
+	virtual void CleanupSSLContext()
+		{m_sslCtx.Cleanup();}
+
+	virtual BOOL StartSSLHandShake(CONNID dwConnID);
+
+public:
+	virtual void SetSSLAutoHandShake(BOOL bAutoHandShake)	{ENSURE_HAS_STOPPED(); m_bSSLAutoHandShake = bAutoHandShake;}
+	virtual void SetSSLCipherList	(LPCTSTR lpszCipherList){ENSURE_HAS_STOPPED(); m_sslCtx.SetCipherList(lpszCipherList);}
+	virtual BOOL IsSSLAutoHandShake	()						{return m_bSSLAutoHandShake;}
+	virtual LPCTSTR GetSSLCipherList()						{return m_sslCtx.GetCipherList();}
+
+	virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo);
+
+protected:
+	virtual EnHandleResult FireConnect(TAgentSocketObj* pSocketObj);
+	virtual EnHandleResult FireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength);
+	virtual EnHandleResult FireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
+
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual void OnWorkerThreadEnd(THR_ID dwThreadID);
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
+
+protected:
+	virtual BOOL StartSSLHandShake(TAgentSocketObj* pSocketObj);
+
+private:
+	void DoSSLHandShake(TAgentSocketObj* pSocketObj);
+
+private:
+	friend EnHandleResult ProcessHandShake<>(CSSLAgent* pThis, TAgentSocketObj* pSocketObj, CSSLSession* pSession);
+	friend EnHandleResult ProcessReceive<>(CSSLAgent* pThis, TAgentSocketObj* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength);
+	friend BOOL ProcessSend<>(CSSLAgent* pThis, TAgentSocketObj* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount);
+
+public:
+	CSSLAgent(ITcpAgentListener* pListener)
+	: CTcpAgent(pListener)
+	, m_sslPool(m_sslCtx)
+	, m_bSSLAutoHandShake(TRUE)
+	{
+
+	}
+
+	virtual ~CSSLAgent()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	BOOL m_bSSLAutoHandShake;
+
+	CSSLContext m_sslCtx;
+	CSSLSessionPool m_sslPool;
+};
+
+#endif

+ 150 - 0
SSLClient.cpp

@@ -0,0 +1,150 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "SSLClient.h"
+#include "SSLHelper.h"
+
+#ifdef _SSL_SUPPORT
+
+BOOL CSSLClient::CheckParams()
+{
+	if(!m_sslCtx.IsValid())
+	{
+		SetLastError(SE_SSL_ENV_NOT_READY, __FUNCTION__, ERROR_NOT_READY);
+		return FALSE;
+	}
+
+	return __super::CheckParams();
+}
+
+void CSSLClient::PrepareStart()
+{
+	m_dwMainThreadID = SELF_THREAD_ID;
+
+	__super::PrepareStart();
+}
+
+void CSSLClient::Reset()
+{
+	m_sslSession.Reset();
+
+	if(m_dwMainThreadID != 0)
+	{
+		m_sslCtx.RemoveThreadLocalState(m_dwMainThreadID);
+		m_dwMainThreadID = 0;
+	}
+
+	__super::Reset();
+}
+
+void CSSLClient::OnWorkerThreadEnd(THR_ID dwThreadID)
+{
+	m_sslCtx.RemoveThreadLocalState();
+
+	__super::OnWorkerThreadEnd(dwThreadID);
+}
+
+BOOL CSSLClient::SendPackets(const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	if(m_sslSession.IsValid())
+		return ::ProcessSend(this, this, &m_sslSession, pBuffers, iCount);
+	else
+		return DoSendPackets(this, pBuffers, iCount);
+}
+
+EnHandleResult CSSLClient::FireConnect()
+{
+	EnHandleResult result = DoFireConnect(this);
+
+	if(result != HR_ERROR && m_bSSLAutoHandShake)
+		DoSSLHandShake();
+
+	return result;
+}
+
+EnHandleResult CSSLClient::FireReceive(const BYTE* pData, int iLength)
+{
+	if(m_sslSession.IsValid())
+		return ::ProcessReceive(this, this, &m_sslSession, pData, iLength);
+	else
+		return DoFireReceive(this, pData, iLength);
+}
+
+BOOL CSSLClient::StartSSLHandShake()
+{
+	if(IsSSLAutoHandShake())
+	{
+		::SetLastError(ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	return StartSSLHandShakeNoCheck();
+}
+
+BOOL CSSLClient::StartSSLHandShakeNoCheck()
+{
+	if(!IsConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	CCriSecLock locallock(m_sslSession.GetSendLock());
+
+	if(!IsConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(m_sslSession.IsValid())
+	{
+		::SetLastError(ERROR_ALREADY_INITIALIZED);
+		return FALSE;
+	}
+
+	DoSSLHandShake();
+
+	return TRUE;
+}
+
+void CSSLClient::DoSSLHandShake()
+{
+	m_sslSession.Renew(m_sslCtx, m_strHost);
+	ENSURE(::ProcessHandShake(this, this, &m_sslSession) == HR_OK);
+}
+
+BOOL CSSLClient::GetSSLSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo)
+{
+	if(!m_sslSession.IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return m_sslSession.GetSessionInfo(enInfo, lppInfo);
+}
+
+#endif

+ 105 - 0
SSLClient.h

@@ -0,0 +1,105 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpClient.h"
+#include "SSLHelper.h"
+
+#ifdef _SSL_SUPPORT
+
+class CSSLClient : public CTcpClient
+{
+	using __super = CTcpClient;
+
+public:
+	using __super::Wait;
+
+public:
+	virtual BOOL IsSecure() {return TRUE;}
+	virtual BOOL SendPackets(const WSABUF pBuffers[], int iCount);
+
+	virtual BOOL SetupSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr)
+		{return m_sslCtx.Initialize(SSL_SM_CLIENT, iVerifyMode, FALSE, (LPVOID)lpszPemCertFile, (LPVOID)lpszPemKeyFile, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCertFileOrPath, nullptr);}
+
+	virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr)
+		{return m_sslCtx.Initialize(SSL_SM_CLIENT, iVerifyMode, TRUE, (LPVOID)lpszPemCert, (LPVOID)lpszPemKey, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCert, nullptr);}
+
+	virtual void CleanupSSLContext()
+		{m_sslCtx.Cleanup();}
+
+	virtual BOOL StartSSLHandShake();
+
+public:
+	virtual void SetSSLAutoHandShake(BOOL bAutoHandShake)	{ENSURE_HAS_STOPPED(); m_bSSLAutoHandShake = bAutoHandShake;}
+	virtual void SetSSLCipherList	(LPCTSTR lpszCipherList){ENSURE_HAS_STOPPED(); m_sslCtx.SetCipherList(lpszCipherList);}
+	virtual BOOL IsSSLAutoHandShake	()						{return m_bSSLAutoHandShake;}
+	virtual LPCTSTR GetSSLCipherList()						{return m_sslCtx.GetCipherList();}
+
+	virtual BOOL GetSSLSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo);
+
+protected:
+	virtual EnHandleResult FireConnect();
+	virtual EnHandleResult FireReceive(const BYTE* pData, int iLength);
+
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual void OnWorkerThreadEnd(THR_ID dwThreadID);
+
+protected:
+	virtual BOOL StartSSLHandShakeNoCheck();
+
+private:
+	void DoSSLHandShake();
+
+private:
+	friend EnHandleResult ProcessHandShake<>(CSSLClient* pThis, CSSLClient* pSocketObj, CSSLSession* pSession);
+	friend EnHandleResult ProcessReceive<>(CSSLClient* pThis, CSSLClient* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength);
+	friend BOOL ProcessSend<>(CSSLClient* pThis, CSSLClient* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount);
+
+public:
+	CSSLClient(ITcpClientListener* pListener)
+	: CTcpClient			(pListener)
+	, m_sslSession			(m_itPool)
+	, m_dwMainThreadID		(0)
+	, m_bSSLAutoHandShake	(TRUE)
+	{
+
+	}
+
+	virtual ~CSSLClient()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	THR_ID		m_dwMainThreadID;
+	BOOL		m_bSSLAutoHandShake;
+
+	CSSLContext m_sslCtx;
+	CSSLSession m_sslSession;
+};
+
+#endif

+ 1200 - 0
SSLHelper.cpp

@@ -0,0 +1,1200 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "SSLHelper.h"
+
+#ifdef _SSL_SUPPORT
+
+#include "SocketHelper.h"
+#include "common/FileHelper.h"
+
+#include "openssl/ssl.h"
+#include "openssl/err.h"
+#include "openssl/engine.h"
+#include "openssl/x509v3.h"
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+	int CSSLInitializer::sm_iLockNum			= 0;
+	CSimpleRWLock* CSSLInitializer::sm_pcsLocks	= nullptr;
+#endif
+
+CSSLInitializer CSSLInitializer::sm_instance;
+
+const DWORD CSSLSessionPool::DEFAULT_ITEM_CAPACITY		= CItemPool::DEFAULT_ITEM_CAPACITY;
+const DWORD CSSLSessionPool::DEFAULT_ITEM_POOL_SIZE		= CItemPool::DEFAULT_POOL_SIZE;
+const DWORD CSSLSessionPool::DEFAULT_ITEM_POOL_HOLD		= CItemPool::DEFAULT_POOL_HOLD;
+const DWORD CSSLSessionPool::DEFAULT_SESSION_LOCK_TIME	= DEFAULT_OBJECT_CACHE_LOCK_TIME;
+const DWORD CSSLSessionPool::DEFAULT_SESSION_POOL_SIZE	= DEFAULT_OBJECT_CACHE_POOL_SIZE;
+const DWORD CSSLSessionPool::DEFAULT_SESSION_POOL_HOLD	= DEFAULT_OBJECT_CACHE_POOL_HOLD;
+
+CSSLInitializer::CSSLInitializer()
+{
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+	sm_iLockNum = CRYPTO_num_locks();
+
+	if(sm_iLockNum > 0)
+		sm_pcsLocks = new CSimpleRWLock[sm_iLockNum];
+/*
+#ifdef _DEBUG
+	CRYPTO_malloc_debug_init();
+	CRYPTO_dbg_set_options(V_CRYPTO_MDEBUG_ALL);
+	CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+#endif
+*/
+	CRYPTO_set_locking_callback			(&ssl_lock_callback);
+	CRYPTO_set_dynlock_create_callback	(&ssl_lock_dyn_create_callback);
+	CRYPTO_set_dynlock_destroy_callback	(&ssl_lock_dyn_destroy_callback);
+	CRYPTO_set_dynlock_lock_callback	(&ssl_lock_dyn_callback);
+
+	SSL_library_init();
+	SSL_load_error_strings();
+	OpenSSL_add_all_algorithms();
+#else
+	OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, nullptr);
+#endif
+}
+
+CSSLInitializer::~CSSLInitializer()
+{
+	CleanupThreadState();
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+	CONF_modules_free();
+	ENGINE_cleanup();
+	EVP_cleanup();
+	CRYPTO_cleanup_all_ex_data();
+	ERR_free_strings();
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_2
+	SSL_COMP_free_compression_methods();
+#endif
+
+	CRYPTO_set_locking_callback			(nullptr);
+	CRYPTO_set_dynlock_create_callback	(nullptr);
+	CRYPTO_set_dynlock_destroy_callback	(nullptr);
+	CRYPTO_set_dynlock_lock_callback	(nullptr);
+
+	if(sm_iLockNum > 0)
+	{
+		delete[] sm_pcsLocks;
+
+		sm_pcsLocks = nullptr;
+		sm_iLockNum = 0;
+	}
+#endif
+}
+
+void CSSLInitializer::CleanupThreadState(THR_ID dwThreadID)
+{
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+	CRYPTO_THREADID tid = {nullptr, dwThreadID};
+	
+	CRYPTO_THREADID_current(&tid);
+	ERR_remove_thread_state(&tid);
+#else
+	OPENSSL_thread_stop();
+#endif
+}
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+
+void CSSLInitializer::ssl_lock_callback(int mode, int n, const char *file, int line)
+{
+	mode & CRYPTO_LOCK
+	? (mode & CRYPTO_READ
+		? sm_pcsLocks[n].lock_shared()
+		: sm_pcsLocks[n].lock())
+	: (mode & CRYPTO_READ
+		? sm_pcsLocks[n].unlock_shared()
+		: sm_pcsLocks[n].unlock());
+}
+
+CRYPTO_dynlock_value* CSSLInitializer::ssl_lock_dyn_create_callback(const char *file, int line)
+ {
+	 return new DynamicLock;
+ }
+
+void CSSLInitializer::ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line)
+{
+	mode & CRYPTO_LOCK
+	? (mode & CRYPTO_READ
+		? l->cs.lock_shared()
+		: l->cs.lock())
+	: (mode & CRYPTO_READ
+		? l->cs.unlock_shared()
+		: l->cs.unlock());
+}
+
+void CSSLInitializer::ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line)
+{
+	delete l;
+}
+
+#endif
+
+BOOL CSSLContext::Initialize(EnSSLSessionMode enSessionMode, int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert, HP_Fn_SNI_ServerNameCallback fnServerNameCallback)
+{
+	ASSERT(!IsValid());
+
+	if(IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	m_enSessionMode	= enSessionMode;
+
+	if(AddContext(iVerifyMode, bMemory, lpPemCert, lpPemKey, lpKeyPasswod, lpCAPemCert) == 0)
+		m_sslCtx = GetContext(0);
+	else
+	{
+		EXECUTE_RESTORE_ERROR(Cleanup());
+		return FALSE;
+	}
+
+	SetServerNameCallback(fnServerNameCallback);
+
+	return TRUE;
+}
+
+int CSSLContext::AddServerContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert)
+{
+	ASSERT(IsValid());
+
+	if(!IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(m_enSessionMode != SSL_SM_SERVER)
+	{
+		::SetLastError(ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	return AddContext(iVerifyMode, bMemory, lpPemCert, lpPemKey, lpKeyPasswod, lpCAPemCert);
+}
+
+BOOL CSSLContext::BindServerName(LPCTSTR lpszServerName, int iContextIndex)
+{
+	ASSERT(lpszServerName && iContextIndex >= 0 && !::IsIPAddress(lpszServerName));
+
+	if(!lpszServerName || iContextIndex < 0 || ::IsIPAddress(lpszServerName))
+	{
+		::SetLastError(ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	int iLen		= lstrlen(lpszServerName);
+	LPCTSTR lpszSep	= ::StrChr(lpszServerName, SSL_DOMAIN_SEP_CHAR);
+
+	if(lpszSep == nullptr || lpszSep == lpszServerName || lpszSep == (lpszServerName + iLen - 1))
+	{
+		::SetLastError(ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	int iSize = (int)m_lsSslCtxs.size();
+
+	if(iSize <= iContextIndex)
+	{
+		::SetLastError(ERROR_INVALID_INDEX);
+		return FALSE;
+	}
+	
+	m_sslServerNames[lpszServerName] = iContextIndex;
+
+	return TRUE;
+}
+
+int CSSLContext::AddContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert)
+{
+	USES_CONVERSION;
+
+	int iIndex = -1;
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+	SSL_CTX* sslCtx	= SSL_CTX_new(SSLv23_method());
+#else
+	SSL_CTX* sslCtx	= SSL_CTX_new(TLS_method());
+#endif
+
+	SSL_CTX_set_quiet_shutdown(sslCtx, 1);
+	SSL_CTX_set_verify(sslCtx, iVerifyMode, nullptr);
+
+	if(!SSL_CTX_set_cipher_list(sslCtx, T2CA(m_strCipherList)))
+		::SetLastError(ERROR_EMPTY);
+	else
+	{
+		if(m_enSessionMode == SSL_SM_SERVER)
+		{
+			static volatile ULONG s_session_id_context = 0;
+			ULONG session_id_context = ::InterlockedIncrement(&s_session_id_context);
+
+			SSL_CTX_set_session_id_context(sslCtx, (BYTE*)&session_id_context, sizeof(session_id_context));
+		}
+
+		if(LoadCertAndKey(sslCtx, iVerifyMode, bMemory, lpPemCert, lpPemKey, lpKeyPasswod, lpCAPemCert))
+		{
+			iIndex = (int)m_lsSslCtxs.size();
+			m_lsSslCtxs.push_back(sslCtx);
+		}
+	}
+
+	if(iIndex < 0)
+		EXECUTE_RESTORE_ERROR(SSL_CTX_free(sslCtx));
+	
+	return iIndex;
+}
+
+BOOL CSSLContext::LoadCertAndKey(SSL_CTX* sslCtx, int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert)
+{
+	if(bMemory)
+		return LoadCertAndKeyByMemory(sslCtx, iVerifyMode, (LPCSTR)lpPemCert, (LPCSTR)lpPemKey, (LPCSTR)lpKeyPasswod, (LPCSTR)lpCAPemCert);
+	else
+		return LoadCertAndKeyByFile(sslCtx, iVerifyMode, (LPCTSTR)lpPemCert, (LPCTSTR)lpPemKey, (LPCTSTR)lpKeyPasswod, (LPCTSTR)lpCAPemCert);
+}
+
+BOOL CSSLContext::LoadCertAndKeyByFile(SSL_CTX* sslCtx, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath)
+{
+	USES_CONVERSION;
+
+	if(::IsStrNotEmpty(lpszCAPemCertFileOrPath))
+	{
+		LPCTSTR lpszCAPemCertFile = nullptr;
+		LPCTSTR lpszCAPemCertPath = nullptr;
+
+		CFile fCAPemCertFile(lpszCAPemCertFileOrPath, O_RDONLY | O_CLOEXEC);
+
+		if(!fCAPemCertFile.IsExist())
+		{
+			::SetLastError(ERROR_FILE_NOT_FOUND);
+			return FALSE;
+		}
+
+		if(fCAPemCertFile.IsFile())
+			lpszCAPemCertFile = lpszCAPemCertFileOrPath;
+		else if(fCAPemCertFile.IsDirectory())
+			lpszCAPemCertPath = lpszCAPemCertFileOrPath;
+		else
+		{
+			::SetLastError(ERROR_BAD_FILE_TYPE);
+			return FALSE;
+		}
+
+		if(!SSL_CTX_load_verify_locations(sslCtx, T2CA(lpszCAPemCertFile), T2CA(lpszCAPemCertPath)))
+		{
+			::SetLastError(ERROR_INVALID_DATA);
+			return FALSE;
+		}
+
+		if(!SSL_CTX_set_default_verify_paths(sslCtx))
+		{
+			::SetLastError(ERROR_FUNCTION_FAILED);
+			return FALSE;
+		}
+
+		if(m_enSessionMode == SSL_SM_SERVER && (iVerifyMode & SSL_VM_PEER) && lpszCAPemCertFile != nullptr)
+		{
+			STACK_OF(X509_NAME)* caCertNames = SSL_load_client_CA_file(T2CA(lpszCAPemCertFile));
+
+			if(caCertNames == nullptr)
+			{
+				::SetLastError(ERROR_EMPTY);
+				return FALSE;
+			}
+
+			SSL_CTX_set_client_CA_list(sslCtx, caCertNames);
+		}
+	}
+
+	if(::IsStrNotEmpty(lpszPemCertFile))
+	{
+		CFile fPemCertFile(lpszPemCertFile, O_RDONLY | O_CLOEXEC);
+
+		if(!fPemCertFile.IsFile())
+		{
+			::SetLastError(ERROR_FILE_NOT_FOUND);
+			return FALSE;
+		}
+
+		if(::IsStrEmpty(lpszPemKeyFile))
+		{
+			::SetLastError(ERROR_INVALID_PARAMETER);
+			return FALSE;
+		}
+
+		CFile fPemKeyFile(lpszPemKeyFile, O_RDONLY | O_CLOEXEC);
+
+		if(!fPemKeyFile.IsFile())
+		{
+			::SetLastError(ERROR_FILE_NOT_FOUND);
+			return FALSE;
+		}
+		
+		if(::IsStrNotEmpty(lpszKeyPassword))
+			SSL_CTX_set_default_passwd_cb_userdata(sslCtx, (void*)T2CA(lpszKeyPassword));
+
+		if(!SSL_CTX_use_PrivateKey_file(sslCtx, T2CA(lpszPemKeyFile), SSL_FILETYPE_PEM))
+		{
+			::SetLastError(ERROR_INVALID_PASSWORD);
+			return FALSE;
+		}
+
+		if(!SSL_CTX_use_certificate_chain_file(sslCtx, T2CA(lpszPemCertFile)))
+		{
+			::SetLastError(ERROR_INVALID_DATA);
+			return FALSE;
+		}
+
+		if(!SSL_CTX_check_private_key(sslCtx))
+		{
+			::SetLastError(ERROR_INVALID_ACCESS);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CSSLContext::LoadCertAndKeyByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert)
+{
+	if(!LoadCAPemCertByMemory(sslCtx, iVerifyMode, lpszCAPemCert))
+		return FALSE;
+	if(!LoadPemCertAndKeyByMemory(sslCtx, lpszPemCert, lpszPemKey, lpszKeyPassword))
+		return FALSE;
+
+	return TRUE;
+}
+
+BOOL CSSLContext::LoadCAPemCertByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszCAPemCert)
+{
+	if(::IsStrEmptyA(lpszCAPemCert))
+		return TRUE;
+
+	if(!AddCAPemCertToStoreByMemory(sslCtx, lpszCAPemCert))
+		return FALSE;
+
+	if(!SSL_CTX_set_default_verify_paths(sslCtx))
+	{
+		::SetLastError(ERROR_FUNCTION_FAILED);
+		return FALSE;
+	}
+
+	if(m_enSessionMode == SSL_SM_SERVER && (iVerifyMode & SSL_VM_PEER))
+	{
+		if(!SetClientCAListByMemory(sslCtx, lpszCAPemCert))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CSSLContext::LoadPemCertAndKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword)
+{
+	if(::IsStrEmptyA(lpszPemCert))
+		return TRUE;
+
+	if(::IsStrEmptyA(lpszPemKey))
+	{
+		::SetLastError(ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	if(::IsStrNotEmptyA(lpszKeyPassword))
+		SSL_CTX_set_default_passwd_cb_userdata(sslCtx, (void*)(lpszKeyPassword));
+
+	if(!SetPrivateKeyByMemory(sslCtx, lpszPemKey))
+		return FALSE;
+
+	if(!SetCertChainByMemory(sslCtx, lpszPemCert))
+		return FALSE;
+
+	if(!SSL_CTX_check_private_key(sslCtx))
+	{
+		::SetLastError(ERROR_INVALID_ACCESS);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CSSLContext::AddCAPemCertToStoreByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert)
+{
+	BOOL isOK			= FALSE;
+	int iCount			= 0;
+	BIO* pBIO			= BIO_new_mem_buf(lpszPemCert, -1);
+	X509_STORE* pStore	= SSL_CTX_get_cert_store(sslCtx);
+	STACK_OF(X509_INFO) * pStack = nullptr;
+
+	if(pBIO == nullptr)
+	{
+		::SetLastError(ERROR_CREATE_FAILED);
+		goto _END;
+	}
+
+	if(pStore == nullptr)
+	{
+		::SetLastError(ERROR_NOT_FOUND);
+		goto _END;
+	}
+
+	pStack = PEM_X509_INFO_read_bio(pBIO, nullptr, nullptr, nullptr);
+
+	if(pStack == nullptr)
+	{
+		::SetLastError(ERROR_NO_DATA);
+		goto _END;
+	}
+
+	for(int i = 0; i < sk_X509_INFO_num(pStack); i++)
+	{
+		X509_INFO* pInfo = sk_X509_INFO_value(pStack, i);
+
+		if(pInfo->x509)
+		{
+			if(!X509_STORE_add_cert(pStore, pInfo->x509))
+			{
+				::SetLastError(ERROR_INVALID_DATA);
+				goto _END;
+			}
+
+			++iCount;
+		}
+
+		if(pInfo->crl)
+		{
+			if(!X509_STORE_add_crl(pStore, pInfo->crl))
+			{
+				::SetLastError(ERROR_INVALID_DATA);
+				goto _END;
+			}
+
+			++iCount;
+		}
+	}
+
+	if(iCount > 0)
+		isOK = TRUE;
+	else
+		::SetLastError(ERROR_EMPTY);
+
+_END:
+
+	if(pStack != nullptr)
+		sk_X509_INFO_pop_free(pStack, X509_INFO_free);
+
+	if(pBIO != nullptr)
+		BIO_free(pBIO);
+
+	return isOK;
+}
+
+BOOL CSSLContext::SetClientCAListByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert)
+{
+	BOOL isOK						= FALSE;
+	X509* pX509						= nullptr;
+	X509_NAME* pName				= nullptr;
+	STACK_OF(X509_NAME)* pStack		= nullptr;
+	BIO* pBIO						= BIO_new_mem_buf(lpszPemCert, -1);
+	OPENSSL_LHASH* pNameHash		= (OPENSSL_LHASH*)OPENSSL_LH_new((OPENSSL_LH_HASHFUNC)FN_X509_NAME_HASH, (OPENSSL_LH_COMPFUNC)X509_NAME_cmp);
+
+	if(pBIO == nullptr || pNameHash == nullptr)
+	{
+		::SetLastError(ERROR_CREATE_FAILED);
+		goto _ERR;
+	}
+
+	while(TRUE)
+	{
+		if(PEM_read_bio_X509(pBIO, &pX509, nullptr, nullptr) == nullptr)
+			break;
+
+		if(pStack == nullptr)
+		{
+			pStack = sk_X509_NAME_new_null();
+
+			if(pStack == nullptr)
+			{
+				::SetLastError(ERROR_CREATE_FAILED);
+				goto _ERR;
+			}
+		}
+
+		if((pName = X509_get_subject_name(pX509)) == nullptr)
+		{
+			::SetLastError(ERROR_NO_DATA);
+			goto _ERR;
+		}
+
+		if((pName = X509_NAME_dup(pName)) == nullptr)
+		{
+			::SetLastError(ERROR_CREATE_FAILED);
+			goto _ERR;
+		}
+
+		if(OPENSSL_LH_retrieve(pNameHash, pName) != nullptr)
+		{
+			X509_NAME_free(pName);
+			pName = nullptr;
+		}
+		else
+		{
+			OPENSSL_LH_insert(pNameHash, pName);
+
+			if(!sk_X509_NAME_push(pStack, pName))
+			{
+				::SetLastError(ERROR_WRITE_FAULT);
+				goto _ERR;
+			}
+		}
+	}
+
+	if(pStack == nullptr)
+	{
+		::SetLastError(ERROR_EMPTY);
+		goto _ERR;
+	}
+
+	SSL_CTX_set_client_CA_list(sslCtx, pStack);
+
+	isOK = TRUE;
+	goto _END;
+
+_ERR:
+
+	if(pName != nullptr)
+		X509_NAME_free(pName);
+	if(pStack != nullptr)
+	{
+		sk_X509_NAME_pop_free(pStack, X509_NAME_free);
+		pStack = nullptr;
+	}
+
+_END:
+
+	if(pX509 != nullptr)
+		X509_free(pX509);
+
+	if(pNameHash != nullptr)
+		OPENSSL_LH_free(pNameHash);
+
+	if(pBIO != nullptr)
+		BIO_free(pBIO);
+
+	if(pStack != nullptr)
+		ERR_clear_error();
+
+	return isOK;
+}
+
+BOOL CSSLContext::SetPrivateKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemKey)
+{
+	BOOL isOK		= FALSE;
+	BIO* pBIO		= BIO_new_mem_buf(lpszPemKey, -1);
+	EVP_PKEY* pKey	= nullptr;
+
+	if(pBIO == nullptr)
+	{
+		::SetLastError(ERROR_CREATE_FAILED);
+		goto _END;
+	}
+
+	pKey = PEM_read_bio_PrivateKey(pBIO, nullptr, SSL_CTX_get_default_passwd_cb(sslCtx), SSL_CTX_get_default_passwd_cb_userdata(sslCtx));
+
+	if(pKey == nullptr)
+	{
+		::SetLastError(ERROR_INVALID_PASSWORD);
+		goto _END;
+	}
+
+	if(!SSL_CTX_use_PrivateKey(sslCtx, pKey))
+	{
+		::SetLastError(ERROR_INVALID_DATA);
+		goto _END;
+	}
+
+	isOK = TRUE;
+
+_END:
+
+	if(pKey != nullptr)
+		EVP_PKEY_free(pKey);
+
+	if(pBIO != nullptr)
+		BIO_free(pBIO);
+
+	return isOK;
+}
+
+BOOL CSSLContext::SetCertChainByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert)
+{
+	BOOL isOK	= FALSE;
+	ULONG err	= 0;
+	BIO* pBIO	= BIO_new_mem_buf(lpszPemCert, -1);
+	X509* pX509	= nullptr;
+
+	pem_password_cb* cb	= SSL_CTX_get_default_passwd_cb(sslCtx);
+	LPVOID userdata		= SSL_CTX_get_default_passwd_cb_userdata(sslCtx);
+
+	if(pBIO == nullptr)
+	{
+		::SetLastError(ERROR_CREATE_FAILED);
+		goto _END;
+	}
+
+	pX509 = PEM_read_bio_X509_AUX(pBIO, nullptr, cb, userdata);
+
+	if(pX509 == nullptr)
+	{
+		::SetLastError(ERROR_NO_DATA);
+		goto _END;
+	}
+
+	if(!SSL_CTX_use_certificate(sslCtx, pX509) || (ERR_peek_error() != 0))
+	{
+		::SetLastError(ERROR_INVALID_DATA);
+		goto _END;
+	}
+
+	if(!SSL_CTX_clear_chain_certs(sslCtx))
+	{
+		::SetLastError(ERROR_FUNCTION_FAILED);
+		goto _END;
+	}
+
+	X509* pCA;
+	while((pCA = PEM_read_bio_X509(pBIO, nullptr, cb, userdata)) != nullptr)
+	{
+		if(!SSL_CTX_add0_chain_cert(sslCtx, pCA))
+		{
+			X509_free(pCA);
+
+			::SetLastError(ERROR_FUNCTION_FAILED);
+			goto _END;
+		}
+	}
+
+	err = ERR_peek_last_error();
+
+	if(ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)
+		ERR_clear_error();
+	else
+	{
+		::SetLastError(ERROR_FUNCTION_FAILED);
+		goto _END;
+	}
+
+	isOK = TRUE;
+
+_END:
+
+	if(pX509 != nullptr)
+		X509_free(pX509);
+
+	if(pBIO != nullptr)
+		BIO_free(pBIO);
+
+	return isOK;
+}
+
+void CSSLContext::Cleanup()
+{
+	if(IsValid())
+	{
+		int iCount = (int)m_lsSslCtxs.size();
+
+		for(int i = 0; i < iCount; i++)
+			SSL_CTX_free(m_lsSslCtxs[i]);
+
+		m_lsSslCtxs.clear();
+		m_sslServerNames.clear();
+
+		m_sslCtx = nullptr;
+	}
+
+	m_fnServerNameCallback = nullptr;
+
+	RemoveThreadLocalState();
+}
+
+void CSSLContext::SetServerNameCallback(Fn_SNI_ServerNameCallback fn)
+{
+	if(m_enSessionMode != SSL_SM_SERVER)
+		return;
+
+	if(fn == nullptr)
+		m_fnServerNameCallback = DefaultServerNameCallback;
+	else
+		m_fnServerNameCallback = fn;
+
+	VERIFY(SSL_CTX_set_tlsext_servername_callback(m_sslCtx, InternalServerNameCallback));
+	VERIFY(SSL_CTX_set_tlsext_servername_arg(m_sslCtx, this));
+}
+
+int CSSLContext::InternalServerNameCallback(SSL* ssl, int* ad, void* arg)
+{
+	USES_CONVERSION;
+
+	CSSLContext* pThis = (CSSLContext*)arg;
+	ASSERT(pThis->m_fnServerNameCallback != nullptr);
+
+	const char* lpszServerName = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+
+	if(lpszServerName == nullptr)
+		return SSL_TLSEXT_ERR_NOACK;
+
+	int iIndex = pThis->m_fnServerNameCallback(A2CT(lpszServerName), pThis);
+
+	if(iIndex == 0)
+		return SSL_TLSEXT_ERR_OK;
+
+	if(iIndex < 0)
+	{
+		::SetLastError(ERROR_INVALID_NAME);
+		return SSL_TLSEXT_ERR_ALERT_FATAL;
+	}
+
+	SSL_CTX* sslCtx = pThis->GetContext(iIndex);
+
+	if(sslCtx == nullptr)
+	{
+		::SetLastError(ERROR_INVALID_INDEX);
+		return SSL_TLSEXT_ERR_ALERT_FATAL;
+	}
+
+	SSL_set_SSL_CTX(ssl, sslCtx);
+
+	return SSL_TLSEXT_ERR_OK;
+}
+
+int __HP_CALL CSSLContext::DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext)
+{
+	CSSLContext* pThis = (CSSLContext*)pContext;
+
+	if(pThis->m_sslServerNames.empty())
+		return 0;
+
+	LPCTSTR lpszTmp = lpszServerName;
+	LPCTSTR lpszSep = ::StrChr(lpszTmp, SSL_DOMAIN_SEP_CHAR);
+
+	while(lpszSep != nullptr)
+	{
+		CServerNameMap::const_iterator it = pThis->m_sslServerNames.find(lpszTmp);
+
+		if(it != pThis->m_sslServerNames.end())
+			return it->second;
+
+		lpszTmp = (lpszSep + 1);
+		lpszSep = ::StrChr(lpszTmp, SSL_DOMAIN_SEP_CHAR);
+	}
+
+	return 0;
+}
+
+SSL_CTX* CSSLContext::GetContext(int i) const
+{
+	SSL_CTX* sslCtx = nullptr;
+
+	if(i >= 0 && i < (int)m_lsSslCtxs.size())
+		sslCtx = m_lsSslCtxs[i];
+
+	return sslCtx;
+}
+
+BOOL CSSLSession::WriteRecvChannel(const BYTE* pData, int iLength)
+{
+	ASSERT(pData && iLength > 0);
+
+	BOOL isOK = TRUE;
+	int bytes = BIO_write(m_bioRecv, pData, iLength);
+
+	if(bytes > 0)
+		ASSERT(bytes == iLength);
+	else if(!BIO_should_retry(m_bioRecv))
+	{
+		::SetLastError(ERROR_INVALID_DATA);
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+BOOL CSSLSession::ReadRecvChannel()
+{
+	BOOL isOK = TRUE;
+	int bytes = SSL_read(m_ssl, m_bufRecv.buf, m_pitRecv->Capacity());
+
+	if(bytes > 0)
+		m_bufRecv.len = bytes;
+	else if(!IsFatalError(bytes))
+		m_bufRecv.len = 0;
+	else
+		isOK = FALSE;
+
+	if(isOK && m_enStatus == SSL_HSS_PROC && SSL_is_init_finished(m_ssl))
+		m_enStatus = SSL_HSS_SUCC;
+
+	return isOK;
+}
+
+BOOL CSSLSession::WriteSendChannel(const BYTE* pData, int iLength)
+{
+	ASSERT(IsReady());
+	ASSERT(pData && iLength > 0);
+
+	BOOL isOK = TRUE;
+	int bytes = SSL_write(m_ssl, pData, iLength);
+
+	if(bytes > 0)
+		ASSERT(bytes == iLength);
+	else if(IsFatalError(bytes))
+		isOK = FALSE;
+
+	return isOK;
+}
+
+BOOL CSSLSession::WriteSendChannel(const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	BOOL isOK = TRUE;
+
+	for(int i = 0; i < iCount; i++)
+	{
+		const WSABUF& buffer = pBuffers[i];
+
+		if(buffer.len > 0)
+		{
+			if(!WriteSendChannel((const BYTE*)buffer.buf, buffer.len))
+			{
+				isOK = FALSE;
+				break;
+			}
+		}
+	}
+
+	return isOK;
+}
+
+BOOL CSSLSession::ReadSendChannel()
+{
+	if(BIO_pending(m_bioSend) == 0)
+	{
+		m_bufSend.len = 0;
+		return TRUE;
+	}
+
+	BOOL isOK = TRUE;
+	int bytes = BIO_read(m_bioSend, m_bufSend.buf, m_pitSend->Capacity());
+
+	if(bytes > 0)
+		m_bufSend.len = bytes;
+	else if(BIO_should_retry(m_bioSend))
+		m_bufSend.len = 0;
+	else
+	{
+		::SetLastError(ERROR_INVALID_DATA);
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+CSSLSession* CSSLSession::Renew(const CSSLContext& sslCtx, LPCSTR lpszHostName)
+{
+	ASSERT(!IsValid());
+
+	ResetCount();
+
+	m_ssl		= SSL_new(sslCtx.GetDefaultContext());
+	m_bioSend	= BIO_new(BIO_s_mem());
+	m_bioRecv	= BIO_new(BIO_s_mem());
+
+	SSL_set_bio(m_ssl, m_bioRecv, m_bioSend);
+
+	if(sslCtx.GetSessionMode() == SSL_SM_SERVER)
+		SSL_accept(m_ssl);
+	else
+	{
+		USES_CONVERSION;
+
+		if(lpszHostName && lpszHostName[0] != 0 && !::IsIPAddress(A2CT(lpszHostName)))
+			SSL_set_tlsext_host_name(m_ssl, lpszHostName);
+
+		SSL_connect(m_ssl);
+	}
+
+	m_pitSend		= m_itPool.PickFreeItem();
+	m_pitRecv		= m_itPool.PickFreeItem();
+	m_bufSend.buf	= m_pitSend->Ptr();
+	m_bufRecv.buf	= m_pitRecv->Ptr();
+	m_enStatus		= SSL_HSS_PROC;
+
+	return this;
+}
+
+BOOL CSSLSession::Reset()
+{
+	BOOL isOK = FALSE;
+
+	if(IsValid())
+	{
+		CCriSecLock locallock(m_csSend);
+
+		if(IsValid())
+		{
+			m_enStatus = SSL_HSS_INIT;
+
+			SSL_shutdown(m_ssl);
+			SSL_free(m_ssl);
+
+			m_itPool.PutFreeItem(m_pitSend);
+			m_itPool.PutFreeItem(m_pitRecv);
+
+			m_pitSend	= nullptr;
+			m_pitRecv	= nullptr;
+			m_ssl		= nullptr;
+			m_bioSend	= nullptr;
+			m_bioRecv	= nullptr;
+			m_dwFreeTime= ::TimeGetTime();
+
+			isOK = TRUE;
+		}
+	}
+
+	ERR_clear_error();
+
+	return isOK;
+}
+
+inline BOOL CSSLSession::IsFatalError(int iBytes)
+{
+	int iErrorCode = SSL_get_error(m_ssl, iBytes);
+
+	if(	iErrorCode == SSL_ERROR_NONE			||
+		iErrorCode == SSL_ERROR_WANT_READ		||
+		iErrorCode == SSL_ERROR_WANT_WRITE		||
+		iErrorCode == SSL_ERROR_WANT_CONNECT	||
+		iErrorCode == SSL_ERROR_WANT_ACCEPT		)
+		return FALSE;
+
+#ifdef _DEBUG
+	char szBuffer[512];
+#endif
+
+	int i		= 0;
+	ULONG iCode	= iErrorCode;
+
+	for(; iCode != SSL_ERROR_NONE; i++)
+	{
+#ifdef _DEBUG
+		ERR_error_string_n(iCode, szBuffer, sizeof(szBuffer));
+		TRACE("	> SSL Error: %ld - %s\n", iCode, szBuffer);
+#endif
+
+		iCode = ERR_get_error();
+	}
+
+	if(iErrorCode == SSL_ERROR_SYSCALL && i == 1)
+		return FALSE;
+
+	::SetLastError(ERROR_INVALID_DATA);
+
+	return TRUE;
+}
+
+BOOL CSSLSession::GetSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo)
+{
+	ASSERT(lppInfo != nullptr);
+
+	*lppInfo = nullptr;
+
+	if(enInfo < SSL_SSI_MIN || enInfo > SSL_SSI_MAX)
+	{
+		::SetLastError(ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+	if(!IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	SSL_CTX* pContext = SSL_get_SSL_CTX(m_ssl);
+
+	switch(enInfo)
+	{
+	case SSL_SSI_CTX:
+		{
+			*lppInfo = (LPVOID)(pContext);
+		}
+		break;
+	case SSL_SSI_CTX_METHOD:
+		{
+			if(pContext != nullptr)
+				*lppInfo = (LPVOID)(SSL_CTX_get_ssl_method(pContext));
+		}
+		break;
+	case SSL_SSI_CTX_CIPHERS:
+		{
+			if(pContext != nullptr)
+				*lppInfo = (LPVOID)(SSL_CTX_get_ciphers(pContext));
+		}
+		break;
+	case SSL_SSI_CTX_CERT_STORE:
+		{
+			if(pContext != nullptr)
+				*lppInfo = (LPVOID)(SSL_CTX_get_cert_store(pContext));
+		}
+		break;
+	case SSL_SSI_SERVER_NAME_TYPE:
+		{
+			*lppInfo = (LPVOID)(UINT_PTR)(SSL_get_servername_type(m_ssl));
+		}
+		break;
+	case SSL_SSI_SERVER_NAME:
+		{
+			int type = SSL_get_servername_type(m_ssl);
+
+			if(type != -1)
+				*lppInfo = (LPVOID)(SSL_get_servername(m_ssl, type));
+		}
+		break;
+	case SSL_SSI_VERSION:
+		{
+			*lppInfo = (LPVOID)(SSL_get_version(m_ssl));
+		}
+		break;
+	case SSL_SSI_METHOD:
+		{
+			*lppInfo = (LPVOID)(SSL_get_ssl_method(m_ssl));
+		}
+		break;
+	case SSL_SSI_CERT:
+		{
+			*lppInfo = (LPVOID)(SSL_get_certificate(m_ssl));
+		}
+		break;
+	case SSL_SSI_PKEY:
+		{
+			*lppInfo = (LPVOID)(SSL_get_privatekey(m_ssl));
+		}
+		break;
+	case SSL_SSI_CURRENT_CIPHER:
+		{
+			*lppInfo = (LPVOID)(SSL_get_current_cipher(m_ssl));
+		}
+		break;
+	case SSL_SSI_CIPHERS:
+		{
+			*lppInfo = (LPVOID)(SSL_get_ciphers(m_ssl));
+		}
+		break;
+	case SSL_SSI_CLIENT_CIPHERS:
+		{
+			*lppInfo = (LPVOID)(SSL_get_client_ciphers(m_ssl));
+		}
+		break;
+	case SSL_SSI_PEER_CERT:
+		{
+			X509* pCert = SSL_get_peer_certificate(m_ssl);
+
+			if(pCert != nullptr)
+			{
+				*lppInfo = (LPVOID*)pCert;
+				X509_free(pCert);
+			}
+		}
+		break;
+	case SSL_SSI_PEER_CERT_CHAIN:
+		{
+			*lppInfo = (LPVOID)(SSL_get_peer_cert_chain(m_ssl));
+		}
+		break;
+	case SSL_SSI_VERIFIED_CHAIN:
+		{
+			*lppInfo = (LPVOID)(SSL_get0_verified_chain(m_ssl));
+		}
+		break;
+	}
+
+	return TRUE;
+}
+
+CSSLSession* CSSLSessionPool::PickFreeSession(LPCSTR lpszHostName)
+{
+	DWORD dwIndex;
+	CSSLSession* pSession = nullptr;
+
+	if(m_lsFreeSession.TryLock(&pSession, dwIndex))
+	{
+		if(::GetTimeGap32(pSession->GetFreeTime()) >= m_dwSessionLockTime)
+			VERIFY(m_lsFreeSession.ReleaseLock(nullptr, dwIndex));
+		else
+		{
+			VERIFY(m_lsFreeSession.ReleaseLock(pSession, dwIndex));
+			pSession = nullptr;
+		}
+	}
+
+	if(!pSession) pSession = CSSLSession::Construct(m_itPool);
+
+	ASSERT(pSession);
+	return pSession->Renew(m_sslCtx, lpszHostName);
+}
+
+void CSSLSessionPool::PutFreeSession(CSSLSession* pSession)
+{
+	if(pSession->Reset())
+	{
+#ifndef USE_EXTERNAL_GC
+		ReleaseGCSession();
+#endif
+		if(!m_lsFreeSession.TryPut(pSession))
+			m_lsGCSession.PushBack(pSession);
+	}
+}
+
+void CSSLSessionPool::Prepare()
+{
+	m_itPool.Prepare();
+	m_lsFreeSession.Reset(m_dwSessionPoolSize);
+}
+
+void CSSLSessionPool::Clear()
+{
+	m_lsFreeSession.Clear();
+
+	ReleaseGCSession(TRUE);
+	VERIFY(m_lsGCSession.IsEmpty());
+
+	m_itPool.Clear();
+}
+
+void CSSLSessionPool::ReleaseGCSession(BOOL bForce)
+{
+	::ReleaseGCObj(m_lsGCSession, m_dwSessionLockTime, bForce);
+}
+
+#endif

+ 496 - 0
SSLHelper.h

@@ -0,0 +1,496 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "hpsocket/HPTypeDef.h"
+#include "common/BufferPool.h"
+
+#ifdef _SSL_SUPPORT
+
+#include "openssl/ssl.h"
+
+#define OPENSSL_VERSION_1_0_2	0x10002000L
+#define OPENSSL_VERSION_1_1_0	0x10100000L
+#define OPENSSL_VERSION_3_0_0	0x30000000L
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+	#define DEFAULT_CIPHER_LIST	_T("DEFAULT:!aNULL:!eNULL:!SSLv2")
+#else
+	#define DEFAULT_CIPHER_LIST	_T("DEFAULT:!aNULL:!eNULL:!SSLv2:!SSLv3")
+#endif
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_0
+	#define FN_X509_NAME_HASH	X509_NAME_hash
+#else
+	inline unsigned long FN_X509_NAME_HASH(const X509_NAME *x) {return X509_NAME_hash_ex(x, nullptr, nullptr, nullptr);}
+#endif
+
+/************************************************************************
+名称:SSL 全局常量
+描述:声明 SSL 组件的公共全局常量
+************************************************************************/
+
+#define SSL_DOMAIN_SEP_CHAR		'.'
+
+ /************************************************************************
+名称:SSL 握手状态
+描述:标识当前连接的 SSL 握手状态
+************************************************************************/
+enum EnSSLHandShakeStatus
+{
+	SSL_HSS_INIT	= 0,	// 初始状态
+	SSL_HSS_PROC	= 1,	// 正在握手
+	SSL_HSS_SUCC	= 2,	// 握手成功
+};
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+
+/* SSL CRYPTO DYNLOCK 结构 */
+typedef struct CRYPTO_dynlock_value
+{
+	CSimpleRWLock cs;
+} DynamicLock;
+
+#endif
+
+class CSSLInitializer
+{
+public:
+
+	static void CleanupThreadState(THR_ID dwThreadID = 0);
+
+private:
+
+	CSSLInitializer();
+	~CSSLInitializer();
+
+private:
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+	static void ssl_lock_callback(int mode, int n, const char *file, int line);
+	static CRYPTO_dynlock_value* ssl_lock_dyn_create_callback(const char *file, int line);
+	static void ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line);
+	static void ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line);
+#endif
+
+private:
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
+	static int				sm_iLockNum;
+	static CSimpleRWLock*	sm_pcsLocks;
+#endif
+
+	static CSSLInitializer sm_instance;
+
+};
+
+/************************************************************************
+名称:SSL Context
+描述:初始化和清理 SSL 运行环境
+************************************************************************/
+class CSSLContext
+{
+	typedef unordered_map<CString, int, cstring_nc_hash_func::hash, cstring_nc_hash_func::equal_to> CServerNameMap;
+
+public:
+
+	/*
+	* 名称:初始化 SSL 环境参数
+	* 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败
+	*		
+	* 参数:		enSessionMode			-- SSL 工作模式(参考 EnSSLSessionMode)
+	*			iVerifyMode				-- SSL 验证模式(参考 EnSSLVerifyMode)
+	*			lpPemCert				-- 证书文件(客户端可选)
+	*			lpPemKey				-- 私钥文件(客户端可选)
+	*			lpKeyPasswod			-- 私钥密码(没有密码则为空)
+	*			lpCAPemCert				-- CA 证书文件或目录(单向验证或客户端可选)
+	*			fnServerNameCallback	-- SNI 回调函数指针(可选,只用于服务端,如果为 nullptr 则使用 SNI 默认回调函数)
+	*
+	* 返回值:	TRUE	-- 成功
+	*			FALSE	-- 失败,可通过 ::GetLastError() 获取失败原因
+	*/
+	BOOL Initialize(EnSSLSessionMode enSessionMode, int iVerifyMode = SSL_VM_NONE, BOOL bMemory = FALSE, LPVOID lpPemCert = nullptr, LPVOID lpPemKey = nullptr, LPVOID lpKeyPasswod = nullptr, LPVOID lpCAPemCert = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr);
+
+	/*
+	* 名称:增加 SNI 主机证书(只用于服务端)
+	* 描述:SSL 服务端在 Initialize() 成功后可以调用本方法增加多个 SNI 主机证书
+	*		
+	* 参数:		iVerifyMode				-- SSL 验证模式(参考 EnSSLVerifyMode)
+	*			lpPemCert				-- 证书文件
+	*			lpPemKey				-- 私钥文件
+	*			lpKeyPasswod			-- 私钥密码(没有密码则为空)
+	*			lpCAPemCert				-- CA 证书文件或目录(单向验证可选)
+	*
+	* 返回值:	正数		-- 成功,并返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机
+	*			负数		-- 失败,可通过 ::GetLastError() 获取失败原因
+	*/
+	int AddServerContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod = nullptr, LPVOID lpCAPemCert = nullptr);
+
+	/*
+	* 名称:绑定 SNI 主机域名
+	* 描述:SSL 服务端在 AddServerContext() 成功后可以调用本方法绑定主机域名到 SNI 主机证书
+	*		
+	* 参数:		lpszServerName		-- 主机域名
+	*			iContextIndex		-- SNI 主机证书对应的索引
+	*
+	* 返回值:	TRUE	-- 成功
+	*			FALSE	-- 失败,可通过 ::GetLastError() 获取失败原因
+	*/
+	virtual BOOL BindServerName(LPCTSTR lpszServerName, int iContextIndex);
+
+	/*
+	* 名称:清理 SSL 运行环境
+	* 描述:清理 SSL 运行环境,回收 SSL 相关内存
+	*		1、CSSLContext 的析构函数会自动调用本方法
+	*		2、当要重新设置 SSL 环境参数时,需要先调用本方法清理原先的环境参数
+	*		
+	* 参数:	无
+	* 
+	* 返回值:无
+	*/
+	void Cleanup();
+
+	/* 获取 SSL 运行环境 SSL_CTX 对象 */
+	SSL_CTX* GetContext				(int i) const;
+	/* 获取 SSL 运行环境默认 SSL_CTX 对象 */
+	SSL_CTX* GetDefaultContext		()		const	{return m_sslCtx;}
+	/* 获取 SSL 运行环境的配置模式,配置模式参考:EnSSLSessionMode */
+	EnSSLSessionMode GetSessionMode	()		const	{return m_enSessionMode;}
+	/* 检查 SSL 运行环境是否初始化完成 */
+	BOOL IsValid					()		const	{return m_sslCtx != nullptr;}
+
+	/* 设置 SSL 加密算法列表 */
+	void SetCipherList(LPCTSTR lpszCipherList)		{m_strCipherList = lpszCipherList;}
+	/* 获取 SSL 加密算法列表 */
+	LPCTSTR GetCipherList()							{return m_strCipherList;}
+
+public:
+	
+	/*
+	* 名称:清理线程局部环境 SSL 资源
+	* 描述:任何一个操作 SSL 的线程,在通信结束时都需要清理线程局部环境 SSL 资源
+	*		1、主线程和 HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不必手工调用本方法
+	*		2、特殊情况下,当自定义线程参与 HP-Socket 通信操作并检查到 SSL 内存泄漏时,需在每次通信结束时自定义线程调用本方法
+	*		
+	* 参数:		dwThreadID	-- 线程 ID(0:当前线程)
+	* 
+	* 返回值:无
+	*/
+	static void RemoveThreadLocalState(THR_ID dwThreadID = 0)	{CSSLInitializer::CleanupThreadState(dwThreadID);}
+
+public:
+
+	CSSLContext()
+	: m_strCipherList		(DEFAULT_CIPHER_LIST)
+	, m_enSessionMode		(SSL_SM_SERVER)
+	, m_sslCtx				(nullptr)
+	, m_fnServerNameCallback(nullptr)
+	{
+
+	}
+
+	~CSSLContext() {Cleanup();}
+
+private:
+
+	void SetServerNameCallback(Fn_SNI_ServerNameCallback fn);
+	int AddContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert);
+	BOOL LoadCertAndKey(SSL_CTX* sslCtx, int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert);
+	BOOL LoadCertAndKeyByFile(SSL_CTX* sslCtx, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath);
+	BOOL LoadCertAndKeyByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert);
+	BOOL LoadCAPemCertByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszCAPemCert);
+	BOOL LoadPemCertAndKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword);
+	BOOL AddCAPemCertToStoreByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert);
+	BOOL SetClientCAListByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert);
+	BOOL SetPrivateKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemKey);
+	BOOL SetCertChainByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert);
+
+private:
+
+	static int InternalServerNameCallback(SSL* ssl, int* ad, void* arg);
+
+public:
+
+	/*
+	* 名称:SNI 默认回调函数
+	* 描述:Initialize 方法中如果不指定 SNI 回调函数则使用此 SNI 默认回调函数
+	*		
+	* 参数:		lpszServerName	-- 请求域名
+	*			pContext		-- SSL Context 对象
+	* 
+	* 返回值:SNI 主机证书对应的索引
+	*/
+	static int __HP_CALL DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext);
+
+private:
+
+	CString				m_strCipherList;
+	EnSSLSessionMode	m_enSessionMode;
+	CServerNameMap		m_sslServerNames;
+	vector<SSL_CTX*>	m_lsSslCtxs;
+	SSL_CTX*			m_sslCtx;
+
+	Fn_SNI_ServerNameCallback m_fnServerNameCallback;
+};
+
+class CSSLSession : public CSafeCounter
+{
+public:
+
+	BOOL WriteRecvChannel(const BYTE* pData, int iLength);
+	BOOL ReadRecvChannel();
+
+	BOOL WriteSendChannel(const BYTE* pData, int iLength);
+	BOOL WriteSendChannel(const WSABUF pBuffers[], int iCount);
+	BOOL ReadSendChannel();
+
+	const WSABUF& GetRecvBuffer()	const	{return m_bufRecv;}
+	const WSABUF& GetSendBuffer()	const	{return m_bufSend;}
+
+	CSSLSession*			Renew(const CSSLContext& sslCtx, LPCSTR lpszHostName = nullptr);
+	BOOL					Reset();
+	BOOL					IsValid()		const	{return GetStatus() != SSL_HSS_INIT;}
+	BOOL					IsHandShaking()	const	{return GetStatus() == SSL_HSS_PROC;}
+	BOOL					IsReady()		const	{return GetStatus() == SSL_HSS_SUCC;}
+	EnSSLHandShakeStatus	GetStatus()		const	{return m_enStatus;}
+	DWORD					GetFreeTime()	const	{return m_dwFreeTime;}
+	CCriSec&				GetSendLock()			{return m_csSend;}
+	BOOL					GetSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo);
+
+private:
+
+	BOOL IsFatalError(int iBytes);
+
+public:
+
+	CSSLSession(CItemPool& itPool)
+	: m_enStatus(SSL_HSS_INIT)
+	, m_itPool	(itPool)
+	, m_ssl		(nullptr)
+	, m_bioSend	(nullptr)
+	, m_bioRecv	(nullptr)
+	, m_pitSend	(nullptr)
+	, m_pitRecv	(nullptr)
+	{
+
+	}
+
+	~CSSLSession()
+	{
+		Reset();
+	}
+
+	static CSSLSession* Construct(CItemPool& itPool)
+		{return new CSSLSession(itPool);}
+
+	static void Destruct(CSSLSession* pSession)
+		{if(pSession) delete pSession;}
+
+private:
+	CItemPool&				m_itPool;
+	CCriSec					m_csSend;
+
+	DWORD					m_dwFreeTime;
+	EnSSLHandShakeStatus	m_enStatus;
+
+	SSL* m_ssl;
+	BIO* m_bioSend;
+	BIO* m_bioRecv;
+
+	TItem*		m_pitSend;
+	TItem*		m_pitRecv;
+	WSABUF		m_bufSend;
+	WSABUF		m_bufRecv;
+};
+
+class CSSLSessionPool
+{
+	typedef CRingPool<CSSLSession>	TSSLSessionList;
+	typedef CCASQueue<CSSLSession>	TSSLSessionQueue;
+
+public:
+	CSSLSession*	PickFreeSession	(LPCSTR lpszHostName = nullptr);
+	void			PutFreeSession	(CSSLSession* pSession);
+
+	void			Prepare			();
+	void			Clear			();
+
+	void ReleaseGCSession	(BOOL bForce = FALSE);
+
+public:
+	void SetItemCapacity	(DWORD dwItemCapacity)		{m_itPool.SetItemCapacity(dwItemCapacity);}
+	void SetItemPoolSize	(DWORD dwItemPoolSize)		{m_itPool.SetPoolSize(dwItemPoolSize);}
+	void SetItemPoolHold	(DWORD dwItemPoolHold)		{m_itPool.SetPoolHold(dwItemPoolHold);}
+
+	void SetSessionLockTime	(DWORD dwSessionLockTime)	{m_dwSessionLockTime = dwSessionLockTime;}
+	void SetSessionPoolSize	(DWORD dwSessionPoolSize)	{m_dwSessionPoolSize = dwSessionPoolSize;}
+	void SetSessionPoolHold	(DWORD dwSessionPoolHold)	{m_dwSessionPoolHold = dwSessionPoolHold;}
+
+	DWORD GetItemCapacity	()	{return m_itPool.GetItemCapacity();}
+	DWORD GetItemPoolSize	()	{return m_itPool.GetPoolSize();}
+	DWORD GetItemPoolHold	()	{return m_itPool.GetPoolHold();}
+
+	DWORD GetSessionLockTime()	{return m_dwSessionLockTime;}
+	DWORD GetSessionPoolSize()	{return m_dwSessionPoolSize;}
+	DWORD GetSessionPoolHold()	{return m_dwSessionPoolHold;}
+
+public:
+	CSSLSessionPool(const CSSLContext& sslCtx,
+					DWORD dwPoolSize = DEFAULT_SESSION_POOL_SIZE,
+					DWORD dwPoolHold = DEFAULT_SESSION_POOL_HOLD,
+					DWORD dwLockTime = DEFAULT_SESSION_LOCK_TIME)
+	: m_sslCtx(sslCtx)
+	, m_dwSessionPoolSize(dwPoolSize)
+	, m_dwSessionPoolHold(dwPoolHold)
+	, m_dwSessionLockTime(dwLockTime)
+	{
+
+	}
+
+	~CSSLSessionPool()	{Clear();}
+
+	DECLARE_NO_COPY_CLASS(CSSLSessionPool)
+
+public:
+	static const DWORD DEFAULT_ITEM_CAPACITY;
+	static const DWORD DEFAULT_ITEM_POOL_SIZE;
+	static const DWORD DEFAULT_ITEM_POOL_HOLD;
+	static const DWORD DEFAULT_SESSION_LOCK_TIME;
+	static const DWORD DEFAULT_SESSION_POOL_SIZE;
+	static const DWORD DEFAULT_SESSION_POOL_HOLD;
+
+private:
+	CItemPool			m_itPool;
+	const CSSLContext&	m_sslCtx;
+
+	DWORD				m_dwSessionLockTime;
+	DWORD				m_dwSessionPoolSize;
+	DWORD				m_dwSessionPoolHold;
+
+	TSSLSessionList		m_lsFreeSession;
+	TSSLSessionQueue	m_lsGCSession;
+};
+
+template<class T, class S> EnHandleResult ProcessHandShake(T* pThis, S* pSocketObj, CSSLSession* pSession)
+{
+	EnHandleResult result = HR_OK;
+
+	CCriSecLock locallock(pSession->GetSendLock());
+
+	while(TRUE)
+	{
+		VERIFY(pSession->ReadSendChannel());
+		const WSABUF& buffer = pSession->GetSendBuffer();
+
+		if(buffer.len == 0)
+			break;
+
+		if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
+		{
+			result = HR_ERROR;
+			break;
+		}
+	}
+
+	return result;
+}
+
+template<class T, class S> EnHandleResult ProcessReceive(T* pThis, S* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength)
+{
+	if(!pSession->WriteRecvChannel(pData, iLength))
+		return HR_ERROR;
+
+	EnHandleResult result			= HR_OK;
+	EnSSLHandShakeStatus enStatus	= pSession->GetStatus();
+
+	while(TRUE)
+	{
+		if(!pSession->ReadRecvChannel())
+			return HR_ERROR;
+
+		if(enStatus == SSL_HSS_PROC && pSession->IsReady())
+		{
+			result = ProcessHandShake(pThis, pSocketObj, pSession);
+
+			if(result == HR_ERROR)
+				break;
+
+			enStatus = SSL_HSS_SUCC;
+			result	 = pThis->DoFireHandShake(pSocketObj);
+
+			if(result == HR_ERROR)
+				break;
+		}
+
+		const WSABUF& buffer = pSession->GetRecvBuffer();
+
+		if(buffer.len == 0)
+			break;
+
+		result = pThis->DoFireReceive(pSocketObj, (const BYTE*)buffer.buf, buffer.len);
+
+		if(result == HR_ERROR)
+			break;
+	}
+
+	if(result != HR_ERROR && pSession->IsHandShaking())
+		result = ::ProcessHandShake(pThis, pSocketObj, pSession);
+
+	return result;
+}
+
+template<class T, class S> BOOL ProcessSend(T* pThis, S* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount)
+{
+	if(pSession == nullptr || !pSession->IsReady())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	CCriSecLock locallock(pSession->GetSendLock());
+
+	if(!pSession->IsReady())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	VERIFY(pSession->WriteSendChannel(pBuffers, iCount));
+
+	while(TRUE)
+	{
+		VERIFY(pSession->ReadSendChannel());
+		const WSABUF& buffer = pSession->GetSendBuffer();
+
+		if(buffer.len == 0)
+			break;
+
+		if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+#endif

+ 229 - 0
SSLServer.cpp

@@ -0,0 +1,229 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "SSLServer.h"
+#include "SSLHelper.h"
+
+#ifdef _SSL_SUPPORT
+
+BOOL CSSLServer::CheckParams()
+{
+	if(!m_sslCtx.IsValid())
+	{
+		SetLastError(SE_SSL_ENV_NOT_READY, __FUNCTION__, ERROR_NOT_READY);
+		return FALSE;
+	}
+
+	return __super::CheckParams();
+}
+
+void CSSLServer::PrepareStart()
+{
+	__super::PrepareStart();
+
+	m_sslPool.SetItemCapacity	(GetSocketBufferSize());
+	m_sslPool.SetItemPoolSize	(GetFreeBufferObjPool());
+	m_sslPool.SetItemPoolHold	(GetFreeBufferObjHold());
+	m_sslPool.SetSessionLockTime(GetFreeSocketObjLockTime());
+	m_sslPool.SetSessionPoolSize(GetFreeSocketObjPool());
+	m_sslPool.SetSessionPoolHold(GetFreeSocketObjHold());
+
+	m_sslPool.Prepare();
+}
+
+void CSSLServer::Reset()
+{
+	m_sslPool.Clear();
+	m_sslCtx.RemoveThreadLocalState();
+
+	__super::Reset();
+}
+
+void CSSLServer::OnWorkerThreadEnd(THR_ID dwThreadID)
+{
+	m_sslCtx.RemoveThreadLocalState();
+
+	__super::OnWorkerThreadEnd(dwThreadID);
+}
+
+void CSSLServer::ReleaseGCSocketObj(BOOL bForce)
+{
+	__super::ReleaseGCSocketObj(bForce);
+
+#ifdef USE_EXTERNAL_GC
+	m_sslPool.ReleaseGCSession(bForce);
+#endif
+}
+
+BOOL CSSLServer::SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession != nullptr)
+	{
+		CLocalSafeCounter localcounter(*pSession);
+		return ::ProcessSend(this, pSocketObj, pSession, pBuffers, iCount);
+	}
+
+	return DoSendPackets(pSocketObj, pBuffers, iCount);
+}
+
+EnHandleResult CSSLServer::FireAccept(TSocketObj* pSocketObj)
+{
+	EnHandleResult result = DoFireAccept(pSocketObj);
+
+	if(result != HR_ERROR && m_bSSLAutoHandShake)
+		DoSSLHandShake(pSocketObj);
+
+	return result;
+}
+
+EnHandleResult CSSLServer::FireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+{
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession != nullptr)
+	{
+		CLocalSafeCounter localcounter(*pSession);
+		return ::ProcessReceive(this, pSocketObj, pSession, pData, iLength);
+	}
+
+	return DoFireReceive(pSocketObj, pData, iLength);
+}
+
+EnHandleResult CSSLServer::FireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+{
+	EnHandleResult result = DoFireClose(pSocketObj, enOperation, iErrorCode);
+
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession != nullptr)
+		m_sslPool.PutFreeSession(pSession);
+
+	return result;
+}
+
+BOOL CSSLServer::StartSSLHandShake(CONNID dwConnID)
+{
+	if(IsSSLAutoHandShake())
+	{
+		::SetLastError(ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return StartSSLHandShake(pSocketObj);
+}
+
+BOOL CSSLServer::StartSSLHandShake(TSocketObj* pSocketObj)
+{
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	CReentrantCriSecLock locallock(pSocketObj->csSend);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession != nullptr)
+	{
+		::SetLastError(ERROR_ALREADY_INITIALIZED);
+		return FALSE;
+	}
+
+	DoSSLHandShake(pSocketObj);
+
+	return TRUE;
+}
+
+void CSSLServer::DoSSLHandShake(TSocketObj* pSocketObj)
+{
+	CSSLSession* pSession = m_sslPool.PickFreeSession();
+
+	CLocalSafeCounter localcounter(*pSession);
+
+	ENSURE(SetConnectionReserved2(pSocketObj, pSession));
+	ENSURE(::ProcessHandShake(this, pSocketObj, pSession) == HR_OK);
+}
+
+BOOL CSSLServer::GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo)
+{
+	ASSERT(lppInfo != nullptr);
+
+	*lppInfo				= nullptr;
+	TSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	CSSLSession* pSession = nullptr;
+	GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession == nullptr || !pSession->IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return pSession->GetSessionInfo(enInfo, lppInfo);
+}
+
+#endif

+ 115 - 0
SSLServer.h

@@ -0,0 +1,115 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpServer.h"
+#include "SSLHelper.h"
+
+#ifdef _SSL_SUPPORT
+
+class CSSLServer : public CTcpServer
+{
+	using __super = CTcpServer;
+	 
+public:
+	using __super::Wait;
+
+public:
+	virtual BOOL IsSecure() {return TRUE;}
+	virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
+
+	virtual BOOL SetupSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr)
+		{return m_sslCtx.Initialize(SSL_SM_SERVER, iVerifyMode, FALSE, (LPVOID)lpszPemCertFile, (LPVOID)lpszPemKeyFile, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCertFileOrPath, fnServerNameCallback);}
+
+	virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr)
+		{return m_sslCtx.Initialize(SSL_SM_SERVER, iVerifyMode, TRUE, (LPVOID)lpszPemCert, (LPVOID)lpszPemKey, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCert, fnServerNameCallback);}
+
+	virtual int AddSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr)
+		{return m_sslCtx.AddServerContext(iVerifyMode, FALSE, (LPVOID)lpszPemCertFile, (LPVOID)lpszPemKeyFile, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCertFileOrPath);}
+
+	virtual int AddSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr)
+		{return m_sslCtx.AddServerContext(iVerifyMode, TRUE, (LPVOID)lpszPemCert, (LPVOID)lpszPemKey, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCert);}
+
+	virtual BOOL BindSSLServerName(LPCTSTR lpszServerName, int iContextIndex)
+		{return m_sslCtx.BindServerName(lpszServerName, iContextIndex);}
+
+	virtual void CleanupSSLContext()
+		{m_sslCtx.Cleanup();}
+
+	virtual BOOL StartSSLHandShake(CONNID dwConnID);
+
+public:
+	virtual void SetSSLAutoHandShake(BOOL bAutoHandShake)	{ENSURE_HAS_STOPPED(); m_bSSLAutoHandShake = bAutoHandShake;}
+	virtual void SetSSLCipherList	(LPCTSTR lpszCipherList){ENSURE_HAS_STOPPED(); m_sslCtx.SetCipherList(lpszCipherList);}
+	virtual BOOL IsSSLAutoHandShake	()						{return m_bSSLAutoHandShake;}
+	virtual LPCTSTR GetSSLCipherList()						{return m_sslCtx.GetCipherList();}
+
+	virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo);
+
+protected:
+	virtual EnHandleResult FireAccept(TSocketObj* pSocketObj);
+	virtual EnHandleResult FireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength);
+	virtual EnHandleResult FireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
+
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual void OnWorkerThreadEnd(THR_ID dwThreadID);
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
+
+protected:
+	virtual BOOL StartSSLHandShake(TSocketObj* pSocketObj);
+
+private:
+	void DoSSLHandShake(TSocketObj* pSocketObj);
+
+private:
+	friend EnHandleResult ProcessHandShake<>(CSSLServer* pThis, TSocketObj* pSocketObj, CSSLSession* pSession);
+	friend EnHandleResult ProcessReceive<>(CSSLServer* pThis, TSocketObj* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength);
+	friend BOOL ProcessSend<>(CSSLServer* pThis, TSocketObj* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount);
+
+public:
+	CSSLServer(ITcpServerListener* pListener)
+	: CTcpServer(pListener)
+	, m_sslPool(m_sslCtx)
+	, m_bSSLAutoHandShake(TRUE)
+	{
+
+	}
+
+	virtual ~CSSLServer()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	BOOL m_bSSLAutoHandShake;
+
+	CSSLContext m_sslCtx;
+	CSSLSessionPool m_sslPool;
+};
+
+#endif

+ 1669 - 0
SocketHelper.cpp

@@ -0,0 +1,1669 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "SocketHelper.h"
+
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#ifdef _ICONV_SUPPORT
+#include <iconv.h>
+#endif
+
+#ifndef SO_REUSEPORT
+	#define SO_REUSEPORT	15
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static const BYTE s_szUdpCloseNotify[] = {0xBE, 0xB6, 0x1F, 0xEB, 0xDA, 0x52, 0x46, 0xBA, 0x92, 0x33, 0x59, 0xDB, 0xBF, 0xE6, 0xC8, 0xE4};
+static const int s_iUdpCloseNotifySize = ARRAY_SIZE(s_szUdpCloseNotify);
+
+const hp_addr hp_addr::ANY_ADDR4(AF_INET, TRUE);
+const hp_addr hp_addr::ANY_ADDR6(AF_INET6, TRUE);
+
+BOOL SetCurrentWorkerThreadName()
+{
+	return SetWorkerThreadDefaultName(0);
+}
+
+BOOL SetWorkerThreadDefaultName(THR_ID tid)
+{
+	static volatile UINT _s_uiSeq = MAXUINT;
+
+	return ::SetSequenceThreadName(tid, DEFAULT_WORKER_THREAD_PREFIX, _s_uiSeq);
+}
+
+LPCTSTR GetSocketErrorDesc(EnSocketError enCode)
+{
+	switch(enCode)
+	{
+	case SE_OK:						return _T("SUCCESS");
+	case SE_ILLEGAL_STATE:			return _T("Illegal State");
+	case SE_INVALID_PARAM:			return _T("Invalid Parameter");
+	case SE_SOCKET_CREATE:			return _T("Create SOCKET Fail");
+	case SE_SOCKET_BIND:			return _T("Bind SOCKET Fail");
+	case SE_SOCKET_PREPARE:			return _T("Prepare SOCKET Fail");
+	case SE_SOCKET_LISTEN:			return _T("Listen SOCKET Fail");
+	case SE_CP_CREATE:				return _T("Create IOCP Fail");
+	case SE_WORKER_THREAD_CREATE:	return _T("Create Worker Thread Fail");
+	case SE_DETECT_THREAD_CREATE:	return _T("Create Detector Thread Fail");
+	case SE_SOCKE_ATTACH_TO_CP:		return _T("Attach SOCKET to IOCP Fail");
+	case SE_CONNECT_SERVER:			return _T("Connect to Server Fail");
+	case SE_NETWORK:				return _T("Network Error");
+	case SE_DATA_PROC:				return _T("Process Data Error");
+	case SE_DATA_SEND:				return _T("Send Data Fail");
+	case SE_GC_START:				return _T("Start GC Fail");
+
+	case SE_SSL_ENV_NOT_READY:		return _T("SSL environment not ready");
+
+	default: ASSERT(FALSE);			return _T("UNKNOWN ERROR");
+	}
+}
+
+ADDRESS_FAMILY DetermineAddrFamily(LPCTSTR lpszAddress)
+{
+	if (!lpszAddress || lpszAddress[0] == 0)
+		return AF_UNSPEC;
+
+	if(::StrChr(lpszAddress, IPV6_ADDR_SEPARATOR_CHAR))
+		return AF_INET6;
+
+	TCHAR c;
+	int arr[4];
+
+	if(stscanf(lpszAddress, _T("%d.%d.%d.%d%c"), &arr[0], &arr[1], &arr[2], &arr[3], &c) != 4)
+		return AF_UNSPEC;
+
+	for(int i = 0; i < 4; i++)
+	{
+		if(arr[i] < 0 || arr[i] > 255)
+			return AF_UNSPEC;
+	}
+
+	return AF_INET;
+}
+
+BOOL GetInAddr(LPCTSTR lpszAddress, HP_ADDR& addr)
+{
+	addr.family = DetermineAddrFamily(lpszAddress);
+
+	if (addr.family == AF_UNSPEC)
+		return FALSE;
+
+	return (::InetPton(addr.family, lpszAddress, addr.Addr()) == TRUE);
+}
+
+BOOL GetSockAddr(LPCTSTR lpszAddress, USHORT usPort, HP_SOCKADDR& addr)
+{
+	if(addr.family != AF_INET && addr.family != AF_INET6)
+	{
+		::WSASetLastError(ERROR_ADDRNOTAVAIL);
+		return FALSE;
+	}
+
+	if(addr.family == AF_INET6 && StrChr(lpszAddress, IPV6_ZONE_INDEX_CHAR))
+		return GetSockAddrByHostNameDirectly(lpszAddress, usPort, addr);
+
+	addr.ZeroAddr();
+
+	int rs = ::InetPton(addr.family, lpszAddress, addr.SinAddr());
+
+	if(rs != 1)
+	{
+		if(rs == 0) ::WSASetLastError(ERROR_INVALID_PARAMETER);
+
+		return FALSE;
+	}
+
+	if(usPort != 0)
+		addr.SetPort(usPort);
+
+	return TRUE;
+}
+
+BOOL IsIPAddress(LPCTSTR lpszAddress, EnIPAddrType* penType)
+{
+	HP_ADDR addr;
+
+	BOOL isOK = GetInAddr(lpszAddress, addr);
+
+	if(isOK && penType)
+		*penType = addr.IsIPv4() ? IPT_IPV4 : IPT_IPV6;
+
+	return isOK;
+}
+
+BOOL GetIPAddress(LPCTSTR lpszHost, LPTSTR lpszIP, int& iIPLen, EnIPAddrType& enType)
+{
+	HP_SOCKADDR addr;
+
+	if(!GetSockAddrByHostName(lpszHost, 0, addr))
+		return FALSE;
+
+	enType = addr.IsIPv4() ? IPT_IPV4 : IPT_IPV6;
+
+	USHORT usPort;
+	ADDRESS_FAMILY usFamily;
+	return sockaddr_IN_2_A(addr, usFamily, lpszIP, iIPLen, usPort);
+}
+
+BOOL GetSockAddrByHostName(LPCTSTR lpszHost, USHORT usPort, HP_SOCKADDR& addr)
+{
+	addr.family = DetermineAddrFamily(lpszHost);
+
+	if(addr.family != AF_UNSPEC)
+		return GetSockAddr(lpszHost, usPort, addr);
+
+	return GetSockAddrByHostNameDirectly(lpszHost, usPort, addr);
+}
+
+BOOL GetSockAddrByHostNameDirectly(LPCTSTR lpszHost, USHORT usPort, HP_SOCKADDR& addr)
+{
+	addr.ZeroAddr();
+
+	addrinfo* pInfo	= nullptr;
+	addrinfo hints	= {0};
+
+#if defined(__ANDROID__)
+	hints.ai_flags		= 0;
+#else
+	hints.ai_flags		= (AI_V4MAPPED | AI_ADDRCONFIG);
+#endif
+	hints.ai_family		= addr.family;
+	hints.ai_socktype	= SOCK_STREAM;
+
+	int rs = ::getaddrinfo(CT2A(lpszHost), nullptr, &hints, &pInfo);
+
+	if(!IS_NO_ERROR(rs))
+	{
+		::WSASetLastError(ERROR_HOSTUNREACH);
+		return FALSE;
+	}
+
+	BOOL isOK = FALSE;
+
+	for(addrinfo* pCur = pInfo; pCur != nullptr; pCur = pCur->ai_next)
+	{
+		if(pCur->ai_family == AF_INET || pCur->ai_family == AF_INET6)
+		{
+			memcpy(addr.Addr(), pCur->ai_addr, pCur->ai_addrlen);
+			isOK = TRUE;
+
+			break;
+		}
+	}
+
+	EXECUTE_RESTORE_ERROR(::freeaddrinfo(pInfo));
+
+	if(isOK)
+		addr.SetPort(usPort);
+	else
+		::WSASetLastError(ERROR_HOSTUNREACH);
+
+	return isOK;
+}
+
+BOOL EnumHostIPAddresses(LPCTSTR lpszHost, EnIPAddrType enType, LPTIPAddr** lpppIPAddr, int& iIPAddrCount)
+{
+	*lpppIPAddr	 = nullptr;
+	iIPAddrCount = 0;
+
+	ADDRESS_FAMILY usFamily =	(enType		== IPT_ALL				?
+								AF_UNSPEC	: (enType == IPT_IPV4	?
+								AF_INET		: (enType == IPT_IPV6	?
+								AF_INET6	: 0xFF)));
+
+	if(usFamily == 0xFF)
+	{
+		::WSASetLastError(ERROR_AFNOSUPPORT);
+		return FALSE;
+	}
+
+	vector<HP_PSOCKADDR> vt;
+
+	ADDRESS_FAMILY usFamily2 = DetermineAddrFamily(lpszHost);
+
+	if(usFamily2 != AF_UNSPEC)
+	{
+		if(usFamily != AF_UNSPEC && usFamily != usFamily2)
+		{
+			::WSASetLastError(ERROR_HOSTUNREACH);
+			return FALSE;
+		}
+
+		HP_SOCKADDR addr(usFamily2);
+
+		if(!GetSockAddr(lpszHost, 0, addr))
+			return FALSE;
+
+		vt.emplace_back(&addr);
+
+		return RetrieveSockAddrIPAddresses(vt, lpppIPAddr, iIPAddrCount);
+	}
+
+	addrinfo* pInfo	= nullptr;
+	addrinfo hints	= {0};
+
+#if defined(__ANDROID__)
+	hints.ai_flags		= 0;
+#else
+	hints.ai_flags		= AI_ALL;
+#endif
+	hints.ai_family		= usFamily;
+	hints.ai_socktype	= SOCK_STREAM;
+
+	int rs = ::getaddrinfo(CT2A(lpszHost), nullptr, &hints, &pInfo);
+
+	if(rs != NO_ERROR)
+	{
+		::WSASetLastError(rs);
+		return FALSE;
+	}
+
+	for(addrinfo* pCur = pInfo; pCur != nullptr; pCur = pCur->ai_next)
+	{
+		if(pCur->ai_family == AF_INET || pCur->ai_family == AF_INET6)
+			vt.emplace_back((HP_PSOCKADDR)pCur->ai_addr);
+	}
+
+	BOOL isOK = RetrieveSockAddrIPAddresses(vt, lpppIPAddr, iIPAddrCount);
+
+	::freeaddrinfo(pInfo);
+
+	if(!isOK) ::WSASetLastError(EHOSTUNREACH);
+
+	return isOK;
+}
+
+BOOL RetrieveSockAddrIPAddresses(const vector<HP_PSOCKADDR>& vt, LPTIPAddr** lpppIPAddr, int& iIPAddrCount)
+{
+	iIPAddrCount = (int)vt.size();
+
+	if(iIPAddrCount == 0) return FALSE;
+
+	HP_PSOCKADDR	pSockAddr;
+	ADDRESS_FAMILY	usFamily;
+	USHORT			usPort;
+	int				iAddrLength;
+	LPTSTR			lpszAddr;
+	LPTIPAddr		lpItem;
+
+	(*lpppIPAddr) = new LPTIPAddr[iIPAddrCount + 1];
+	(*lpppIPAddr)[iIPAddrCount] = nullptr;
+
+	for(int i = 0; i < iIPAddrCount; i++)
+	{
+		pSockAddr	= vt[i];
+		iAddrLength	= HP_SOCKADDR::AddrMinStrLength(pSockAddr->family);
+		lpszAddr	= new TCHAR[iAddrLength];
+
+		VERIFY(sockaddr_IN_2_A(*vt[i], usFamily, lpszAddr, iAddrLength, usPort));
+
+		lpItem			= new TIPAddr;
+		lpItem->type	= pSockAddr->IsIPv4() ? IPT_IPV4 : IPT_IPV6;
+		lpItem->address	= lpszAddr;
+
+		(*lpppIPAddr)[i] = lpItem;
+	}
+
+	return TRUE;
+}
+
+BOOL FreeHostIPAddresses(LPTIPAddr* lppIPAddr)
+{
+	if(!lppIPAddr) return FALSE;
+
+	LPTIPAddr p;
+	LPTIPAddr* lppCur = lppIPAddr;
+
+	while((p = *lppCur++) != nullptr)
+	{
+		delete[] p->address;
+		delete p;
+	}
+
+	delete[] lppIPAddr;
+
+	return TRUE;
+}
+
+BOOL sockaddr_IN_2_A(const HP_SOCKADDR& addr, ADDRESS_FAMILY& usFamily, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort)
+{
+	BOOL isOK = FALSE;
+
+	usFamily  = addr.family;
+	usPort	  = addr.Port();
+
+	if(::InetNtop(addr.family, addr.SinAddr(), lpszAddress, iAddressLen))
+	{
+		iAddressLen	= (int)lstrlen(lpszAddress) + 1;
+		isOK		= TRUE;
+	}
+	else
+	{
+		if(::WSAGetLastError() == ENOSPC)
+			iAddressLen = HP_SOCKADDR::AddrMinStrLength(usFamily);
+	}
+
+	return isOK;
+}
+
+BOOL sockaddr_A_2_IN(LPCTSTR lpszAddress, USHORT usPort, HP_SOCKADDR& addr)
+{
+	addr.family = DetermineAddrFamily(lpszAddress);
+	return GetSockAddr(lpszAddress, usPort, addr);
+}
+
+BOOL GetSocketAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort, BOOL bLocal)
+{
+	HP_SOCKADDR addr;
+
+	int addr_len = addr.AddrSize();
+	int result	 = bLocal ? getsockname(socket, addr.Addr(), (socklen_t*)&addr_len) : getpeername(socket, addr.Addr(), (socklen_t*)&addr_len);
+
+	if(result != NO_ERROR)
+		return FALSE;
+
+	ADDRESS_FAMILY usFamily;
+	return sockaddr_IN_2_A(addr, usFamily, lpszAddress, iAddressLen, usPort);
+}
+
+BOOL GetSocketLocalAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort)
+{
+	return GetSocketAddress(socket, lpszAddress, iAddressLen, usPort, TRUE);
+}
+
+BOOL GetSocketRemoteAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort)
+{
+	return GetSocketAddress(socket, lpszAddress, iAddressLen, usPort, FALSE);
+}
+
+BOOL SetMultiCastSocketOptions(SOCKET sock, const HP_SOCKADDR& bindAddr, const HP_SOCKADDR& castAddr, int iMCTtl, BOOL bMCLoop)
+{
+	if(castAddr.IsIPv4())
+	{
+		BYTE ttl  = (BYTE)iMCTtl;
+		BYTE loop = (BYTE)bMCLoop;
+
+		VERIFY(::SSO_SetSocketOption(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) != SOCKET_ERROR);
+		VERIFY(::SSO_SetSocketOption(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) != SOCKET_ERROR);
+
+		ip_mreq mcast;
+		::ZeroMemory(&mcast, sizeof(mcast));
+
+		mcast.imr_multiaddr = castAddr.addr4.sin_addr;
+		mcast.imr_interface = bindAddr.addr4.sin_addr;
+
+		if(::SSO_SetSocketOption(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast, sizeof(mcast)) == SOCKET_ERROR)
+			return FALSE;
+		if(::SSO_SetSocketOption(sock, IPPROTO_IP, IP_MULTICAST_IF, bindAddr.SinAddr(), sizeof(IN_ADDR)) == SOCKET_ERROR)
+			return FALSE;
+	}
+	else
+	{
+		INT ttl	  = (INT)iMCTtl;
+		UINT loop = (UINT)bMCLoop;
+
+		VERIFY(::SSO_SetSocketOption(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) != SOCKET_ERROR);
+		VERIFY(::SSO_SetSocketOption(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) != SOCKET_ERROR);
+
+		ipv6_mreq mcast;
+		::ZeroMemory(&mcast, sizeof(mcast));
+
+		mcast.ipv6mr_multiaddr = castAddr.addr6.sin6_addr;
+		mcast.ipv6mr_interface = bindAddr.addr6.sin6_scope_id;
+
+		if(::SSO_SetSocketOption(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mcast, sizeof(mcast)) == SOCKET_ERROR)
+			return FALSE;
+		if(::SSO_SetSocketOption(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (PVOID)(&bindAddr.addr6.sin6_scope_id), sizeof(UINT)) == SOCKET_ERROR)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+int WaitForSocketWrite(SOCKET sock, DWORD dwTimeout)
+{
+	timeval tv = {(__time_t)(dwTimeout / 1000), (__suseconds_t)((dwTimeout % 1000) * 1000)};
+
+	fd_set wfds, efds;
+	FD_ZERO(&wfds);
+	FD_ZERO(&efds);
+	FD_SET(sock, &wfds);
+	FD_SET(sock, &efds);
+
+	int rs = NO_EINTR_INT(select(sock + 1, nullptr, &wfds, &efds, &tv));
+
+	if(rs <= 0) return ((rs == 0) ? ERROR_TIMEOUT : ENSURE_ERROR(ERROR_CANT_WAIT));
+	
+	if(FD_ISSET(sock, &efds))
+	{
+		rs = SSO_GetError(sock);
+		return ((rs != NO_ERROR && rs != SOCKET_ERROR) ? rs : ENSURE_ERROR(ERROR_CANT_WAIT));
+	}
+
+	VERIFY(FD_ISSET(sock, &wfds));
+
+	rs = SSO_GetError(sock);
+
+	if(!IS_NO_ERROR(rs))
+		return ((rs != SOCKET_ERROR) ? rs : ENSURE_ERROR(ERROR_CANT_WAIT));
+
+	return NO_ERROR;
+}
+
+ULONGLONG NToH64(ULONGLONG value)
+{
+	return (((ULONGLONG)ntohl((UINT)((value << 32) >> 32))) << 32) | ntohl((UINT)(value >> 32));
+}
+
+ULONGLONG HToN64(ULONGLONG value)
+{
+	return (((ULONGLONG)htonl((UINT)((value << 32) >> 32))) << 32) | htonl((UINT)(value >> 32));
+}
+
+BOOL IsLittleEndian()
+{
+	static const USHORT _s_endian_test_value = 0x0102;
+	static const BOOL _s_bLE = (*((BYTE*)&_s_endian_test_value) == 0x02);
+
+	return _s_bLE;
+}
+
+USHORT HToLE16(USHORT value)
+{
+	return IsLittleEndian() ? value : ENDIAN_SWAP_16(value);
+}
+
+USHORT HToBE16(USHORT value)
+{
+	return IsLittleEndian() ? ENDIAN_SWAP_16(value) : value;
+}
+
+DWORD HToLE32(DWORD value)
+{
+	return IsLittleEndian() ? value : ENDIAN_SWAP_32(value);
+}
+
+DWORD HToBE32(DWORD value)
+{
+	return IsLittleEndian() ? ENDIAN_SWAP_32(value) : value;
+}
+
+HRESULT ReadSmallFile(LPCTSTR lpszFileName, CFile& file, CFileMapping& fmap, DWORD dwMaxFileSize)
+{
+	ASSERT(lpszFileName != nullptr);
+
+	if(file.Open(lpszFileName, O_RDONLY))
+	{
+		SIZE_T dwSize;
+		if(file.GetSize(dwSize))
+		{
+			if(dwSize > 0 && dwSize <= dwMaxFileSize)
+			{
+				if(fmap.Map(file, dwSize))
+					return NO_ERROR;
+			}
+			else if(dwSize == 0)
+				::SetLastError(ERROR_EMPTY);
+			else
+				::SetLastError(ERROR_FILE_TOO_LARGE);
+		}
+	}
+
+	HRESULT rs = ::GetLastError();
+
+	return (!IS_NO_ERROR(rs) ? rs : ERROR_UNKNOWN);
+}
+
+HRESULT MakeSmallFilePackage(LPCTSTR lpszFileName, CFile& file, CFileMapping& fmap, WSABUF szBuf[3], const LPWSABUF pHead, const LPWSABUF pTail)
+{
+	DWORD dwMaxFileSize = MAX_SMALL_FILE_SIZE - (pHead ? pHead->len : 0) - (pTail ? pTail->len : 0);
+	ASSERT(dwMaxFileSize <= MAX_SMALL_FILE_SIZE);
+
+	HRESULT hr = ReadSmallFile(lpszFileName, file, fmap, dwMaxFileSize);
+
+	if(IS_NO_ERROR(hr))
+	{
+		szBuf[1].len = (UINT)fmap.Size();
+		szBuf[1].buf = fmap;
+
+		if(pHead) memcpy(&szBuf[0], pHead, sizeof(WSABUF));
+		else	  memset(&szBuf[0], 0, sizeof(WSABUF));
+
+		if(pTail) memcpy(&szBuf[2], pTail, sizeof(WSABUF));
+		else	  memset(&szBuf[2], 0, sizeof(WSABUF));
+	}
+
+	return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+int SSO_SetSocketOption(SOCKET sock, int level, int name, LPVOID val, int len)
+{
+	return setsockopt(sock, level, name, val, (socklen_t)len);
+}
+
+int SSO_GetSocketOption(SOCKET sock, int level, int name, LPVOID val, int* len)
+{
+	return getsockopt(sock, level, name, val, (socklen_t*)len);
+}
+
+int SSO_IoctlSocket(SOCKET sock, long cmd, PVOID arg)
+{
+	return ioctl(sock, cmd, arg);
+}
+
+int SSO_NoBlock(SOCKET sock, BOOL bNoBlock)
+{
+	return fcntl_SETFL(sock, O_NONBLOCK, bNoBlock) ? NO_ERROR : SOCKET_ERROR;
+}
+
+int SSO_NoDelay(SOCKET sock, BOOL bNoDelay)
+{
+	int val = bNoDelay ? 1 : 0;
+	return setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
+}
+
+int SSO_DontLinger(SOCKET sock, BOOL bDont)
+{
+	return SSO_Linger(sock, 0, 0);
+}
+
+int SSO_Linger(SOCKET sock, int l_onoff, int l_linger)
+{
+	linger ln = {l_onoff, l_linger};
+	return setsockopt(sock, SOL_SOCKET, SO_LINGER, &ln, sizeof(linger));
+}
+
+int SSO_KeepAlive(SOCKET sock, BOOL bKeepAlive)
+{
+	int val = bKeepAlive ? 1 : 0;
+	return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(int));
+}
+
+int SSO_KeepAliveVals(SOCKET sock, BOOL bOnOff, DWORD dwIdle, DWORD dwInterval, DWORD dwCount)
+{
+	if(bOnOff)
+	{
+		dwIdle		/= 1000;
+		dwInterval	/= 1000;
+
+		if(dwIdle == 0 || dwInterval == 0 || dwCount == 0)
+		{
+			::WSASetLastError(ERROR_INVALID_PARAMETER);
+			return SOCKET_ERROR;
+		}
+	}
+
+	BOOL isOK = IS_NO_ERROR(SSO_KeepAlive(sock, bOnOff));
+
+	if(isOK && bOnOff)
+	{
+		isOK &= IS_NO_ERROR(setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &dwIdle, sizeof(DWORD)));
+		isOK &= IS_NO_ERROR(setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &dwInterval, sizeof(DWORD)));
+		isOK &= IS_NO_ERROR(setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &dwCount, sizeof(DWORD)));
+	}
+
+	return isOK ? NO_ERROR : SOCKET_ERROR;
+}
+
+int SSO_ReuseAddress(SOCKET sock, EnReuseAddressPolicy opt)
+{
+	int iSet	= 1;
+	int iUnSet	= 0;
+	int rs		= NO_ERROR;
+
+	BOOL bReusePortSupported =
+#if defined(__linux) || defined(__linux__)
+		::IsKernelVersionAbove(2, 6, 32);
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__) || defined(__MACH__)
+		TRUE;
+#else
+		FALSE;
+#endif
+
+	if(opt == RAP_NONE)
+	{
+		rs  = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &iUnSet, sizeof(int));
+		if(bReusePortSupported)
+		rs |= setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &iUnSet, sizeof(int));
+	}
+	else if(opt == RAP_ADDR_ONLY)
+	{
+		rs  = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &iSet, sizeof(int));
+		if(bReusePortSupported)
+		rs |= setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &iUnSet, sizeof(int));
+	}
+	else if(opt == RAP_ADDR_AND_PORT)
+	{
+		rs  = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &iSet, sizeof(int));
+		if(bReusePortSupported)
+		rs |= setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &iSet, sizeof(int));
+	}
+	else
+	{
+		::SetLastError(ERROR_INVALID_PARAMETER);
+		rs = -1;
+	}
+
+	return rs;
+}
+
+int SSO_RecvBuffSize(SOCKET sock, int size)
+{
+	return setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(int));
+}
+
+int SSO_SendBuffSize(SOCKET sock, int size)
+{
+	return setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(int));
+}
+
+int SSO_RecvTimeOut(SOCKET sock, int ms)
+{
+	timeval tv;
+	::MillisecondToTimeval(ms, tv);
+	
+	return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval));
+}
+
+int SSO_SendTimeOut(SOCKET sock, int ms)
+{
+	timeval tv;
+	::MillisecondToTimeval(ms, tv);
+
+	return setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval));
+}
+
+int SSO_GetError(SOCKET sock)
+{
+	int e;
+	socklen_t len = sizeof(e);
+
+	if(IS_NO_ERROR(getsockopt(sock, SOL_SOCKET, SO_ERROR, &e, &len)))
+		return e;
+
+	return SOCKET_ERROR;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+CONNID GenerateConnectionID()
+{
+	static volatile CONNID s_dwConnID = 0;
+
+	CONNID dwConnID	= ::InterlockedIncrement(&s_dwConnID);
+	
+	if(dwConnID == 0)
+		dwConnID = ::InterlockedIncrement(&s_dwConnID);
+
+	return dwConnID;
+}
+
+int IsUdpCloseNotify(const BYTE* pData, int iLength)
+{
+	return (iLength == s_iUdpCloseNotifySize								&&
+			memcmp(pData, s_szUdpCloseNotify, s_iUdpCloseNotifySize) == 0)	;
+}
+
+int SendUdpCloseNotify(SOCKET sock)
+{
+	return (int)send(sock, (LPCSTR)s_szUdpCloseNotify, s_iUdpCloseNotifySize, 0);
+}
+
+int SendUdpCloseNotify(SOCKET sock, const HP_SOCKADDR& remoteAddr)
+{
+	return (int)sendto(sock, (LPCSTR)s_szUdpCloseNotify, s_iUdpCloseNotifySize, 0, remoteAddr.Addr(), remoteAddr.AddrSize());
+}
+
+int ManualCloseSocket(SOCKET sock, int iShutdownFlag, BOOL bGraceful)
+{
+	if(!bGraceful)
+		SSO_Linger(sock, 1, 0);
+
+	if(iShutdownFlag != 0xFF)
+		shutdown(sock, iShutdownFlag);
+
+	return closesocket(sock);
+}
+
+DWORD GuessBase64EncodeBound(DWORD dwSrcLen)
+{
+	return 4 * ((dwSrcLen + 2) / 3);
+}
+
+DWORD GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	if(dwSrcLen < 2)
+		return 0;
+
+	if(lpszSrc[dwSrcLen - 2] == '=')
+		dwSrcLen -= 2;
+	else if(lpszSrc[dwSrcLen - 1] == '=')
+			--dwSrcLen;
+
+	DWORD dwMod = dwSrcLen % 4;
+	DWORD dwAdd = dwMod == 2 ? 1 : (dwMod == 3 ? 2 : 0);
+
+	return 3 * (dwSrcLen / 4) + dwAdd;
+}
+
+int Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	static const BYTE CODES[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+	DWORD dwRealLen = GuessBase64EncodeBound(dwSrcLen);
+
+	if(lpszDest == nullptr || dwDestLen < dwRealLen)
+	{
+		dwDestLen = dwRealLen;
+		return -5;
+	}
+
+	BYTE* p		= lpszDest;
+	DWORD leven	= 3 * (dwSrcLen / 3);
+	DWORD i		= 0;
+
+	for (; i < leven; i += 3)
+	{
+		*p++ = CODES[lpszSrc[0] >> 2];
+		*p++ = CODES[((lpszSrc[0] & 3) << 4) + (lpszSrc[1] >> 4)];
+		*p++ = CODES[((lpszSrc[1] & 0xf) << 2) + (lpszSrc[2] >> 6)];
+		*p++ = CODES[lpszSrc[2] & 0x3f];
+
+		lpszSrc += 3;
+	}
+
+	if(i < dwSrcLen)
+	{
+		BYTE a = lpszSrc[0];
+		BYTE b = (i + 1 < dwSrcLen) ? lpszSrc[1] : 0;
+
+		*p++ = CODES[a >> 2];
+		*p++ = CODES[((a & 3) << 4) + (b >> 4)];
+		*p++ = (i + 1 < dwSrcLen) ? CODES[((b & 0xf) << 2)] : '=';
+		*p++ = '=';
+	}  
+
+	ASSERT(dwRealLen == (DWORD)(p - lpszDest));
+
+	if(dwDestLen > dwRealLen)
+	{
+		*p			= 0;
+		dwDestLen	= dwRealLen;
+	}
+
+	return 0;  
+}
+
+int Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	static const BYTE MAP[256]	=
+	{ 
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255,
+		255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
+		 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
+		255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
+		  7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,
+		 19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
+		255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
+		 37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
+		 49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255
+	};
+
+	DWORD dwRealLen = GuessBase64DecodeBound(lpszSrc, dwSrcLen);
+
+	if(lpszDest == nullptr || dwDestLen < dwRealLen)
+	{
+		dwDestLen = dwRealLen;
+		return -5;
+	}
+
+	BYTE c;
+	int g = 3;
+	DWORD i, x, y, z;
+
+	for(i = x = y = z = 0; i < dwSrcLen || x != 0;)
+	{
+		c = i < dwSrcLen ? MAP[lpszSrc[i++]] : 254;
+
+		if(c == 255) {dwDestLen = 0; return -3;}
+		else if(c == 254) {c = 0; g--;}
+		else if(c == 253) continue;
+
+		z = (z << 6) | c;
+
+		if(++x == 4)
+		{
+			lpszDest[y++] = (BYTE)((z >> 16) & 255);
+			if (g > 1) lpszDest[y++] = (BYTE)((z >> 8) & 255);
+			if (g > 2) lpszDest[y++] = (BYTE)(z & 255);
+
+			x = z = 0;
+		}
+	}
+
+	BOOL isOK = (y == dwRealLen);
+
+	if(!isOK)
+		dwDestLen = 0;
+	else
+	{
+		if(dwDestLen > dwRealLen)
+		{
+			lpszDest[dwRealLen]	= 0;
+			dwDestLen			= dwRealLen;
+		}
+	}
+
+	return isOK ? 0 : -3;
+}
+
+DWORD GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	DWORD dwAdd = 0;
+
+	for(DWORD i = 0; i < dwSrcLen; i++)
+	{
+		BYTE c	= lpszSrc[i];
+
+		if(!(isalnum(c) || c == ' ' || c == '.' || c == '-' || c == '_' || c == '*'))
+			dwAdd += 2;
+	}
+
+	return dwSrcLen + dwAdd;
+}
+
+DWORD GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	DWORD dwPercent = 0;
+
+	for(DWORD i = 0; i < dwSrcLen; i++)
+	{
+		if(lpszSrc[i] == '%')
+		{
+			++dwPercent;
+			i += 2;
+		}
+	}
+
+	DWORD dwSub = dwPercent * 2;
+
+	if(dwSrcLen < dwSub)
+		return 0;
+
+	return dwSrcLen - dwSub;
+}
+
+int UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	BYTE c;
+	DWORD j = 0;
+
+	if(lpszDest == nullptr || dwDestLen == 0)
+		goto ERROR_DEST_LEN;
+
+	for(DWORD i = 0; i < dwSrcLen; i++)
+	{
+		if(j >= dwDestLen)
+			goto ERROR_DEST_LEN;
+
+		c = lpszSrc[i];
+
+		if (isalnum(c) || c == '.' || c == '-' || c == '_' || c == '*')
+			lpszDest[j++] = c;
+		else if(c == ' ')
+			lpszDest[j++] = '+';
+		else
+		{
+			if(j + 3 >= dwDestLen)
+				goto ERROR_DEST_LEN;
+
+			lpszDest[j++] = '%';
+			HEX_VALUE_TO_DOUBLE_CHAR(lpszDest + j, c);
+			j += 2;
+			
+		}
+	}
+
+	if(dwDestLen > j)
+	{
+		lpszDest[j]	= 0;
+		dwDestLen	= j;
+	}
+
+	return 0;
+
+ERROR_DEST_LEN:
+	dwDestLen = GuessUrlEncodeBound(lpszSrc, dwSrcLen);
+	return -5;
+}
+
+int UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	char c;
+	DWORD j = 0;
+
+	if(lpszDest == nullptr || dwDestLen == 0)
+		goto ERROR_DEST_LEN;
+
+	for(DWORD i = 0; i < dwSrcLen; i++)
+	{
+		if(j >= dwDestLen)
+			goto ERROR_DEST_LEN;
+
+		c = lpszSrc[i];
+
+		if(c == '+')
+			lpszDest[j++] = ' ';
+		else if(c != '%')
+			lpszDest[j++] = c;
+		else
+		{
+			if(i + 2 >= dwSrcLen)
+				goto ERROR_SRC_DATA;
+
+			lpszDest[j++] = HEX_DOUBLE_CHAR_TO_VALUE(lpszSrc + i + 1);
+			i += 2;
+		}
+	}
+
+	if(dwDestLen > j)
+	{
+		lpszDest[j]	= 0;
+		dwDestLen	= j;
+	}
+
+	return 0;
+
+ERROR_SRC_DATA:
+	dwDestLen = 0;
+	return -3;
+
+ERROR_DEST_LEN:
+	dwDestLen = GuessUrlDecodeBound(lpszSrc, dwSrcLen);
+	return -5;
+}
+
+void DestroyCompressor(IHPCompressor* pCompressor)
+{
+	delete pCompressor;
+}
+
+void DestroyDecompressor(IHPDecompressor* pDecompressor)
+{
+	delete pDecompressor;
+}
+
+#ifdef _ZLIB_SUPPORT
+
+CHPZLibCompressor::CHPZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize)
+: m_fnCallback	(fnCallback)
+, m_dwBuffSize	(dwBuffSize)
+, m_bValid		(FALSE)
+{
+	ASSERT(m_fnCallback != nullptr);
+
+	::ZeroObject(m_Stream);
+
+	m_bValid = (::deflateInit2(&m_Stream, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy) == Z_OK);
+}
+CHPZLibCompressor::~CHPZLibCompressor()
+{
+	if(m_bValid) ::deflateEnd(&m_Stream);
+}
+
+BOOL CHPZLibCompressor::Reset()
+{
+	return (m_bValid = (::deflateReset(&m_Stream) == Z_OK));
+}
+
+BOOL CHPZLibCompressor::Process(const BYTE* pData, int iLength, BOOL bLast, PVOID pContext)
+{
+	return ProcessEx(pData, iLength, bLast, FALSE, pContext);
+}
+
+BOOL CHPZLibCompressor::ProcessEx(const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush, PVOID pContext)
+{
+	ASSERT(IsValid() && iLength > 0);
+
+	if(!IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	unique_ptr<BYTE[]> szBuff = make_unique<BYTE[]>(m_dwBuffSize);
+
+	m_Stream.next_in	= (z_const Bytef*)pData;
+	m_Stream.avail_in	= iLength;
+	
+	BOOL isOK	= TRUE;
+	int rs		= Z_OK;
+	int flush	= bLast ? Z_FINISH : (bFlush ? Z_SYNC_FLUSH : Z_NO_FLUSH);
+
+	while(m_Stream.avail_in > 0)
+	{
+		do
+		{
+			m_Stream.next_out  = szBuff.get();
+			m_Stream.avail_out = m_dwBuffSize;
+
+			rs = ::deflate(&m_Stream, flush);
+
+			if(rs == Z_STREAM_ERROR)
+			{
+				::SetLastError(ERROR_INVALID_DATA);
+				isOK = FALSE;
+				
+				goto ZLIB_COMPRESS_END;
+			}
+
+			int iRead = (int)(m_dwBuffSize - m_Stream.avail_out);
+
+			if(iRead == 0)
+				break;
+
+			if(!m_fnCallback(szBuff.get(), iRead, pContext))
+			{
+				::SetLastError(ERROR_CANCELLED);
+				isOK = FALSE;
+
+				goto ZLIB_COMPRESS_END;
+			}
+		} while(m_Stream.avail_out == 0);
+	}
+
+ZLIB_COMPRESS_END:
+
+	ASSERT(!isOK || (rs == Z_OK && !bLast) || (rs == Z_STREAM_END && bLast));
+
+	if(!isOK || bLast) Reset();
+
+	return isOK;
+}
+
+CHPZLibDecompressor::CHPZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits, DWORD dwBuffSize)
+: m_fnCallback	(fnCallback)
+, m_dwBuffSize	(dwBuffSize)
+, m_bValid		(FALSE)
+{
+	ASSERT(m_fnCallback != nullptr);
+
+	::ZeroObject(m_Stream);
+
+	m_bValid = (::inflateInit2(&m_Stream, iWindowBits) == Z_OK);
+}
+CHPZLibDecompressor::~CHPZLibDecompressor()
+{
+	if(m_bValid) ::inflateEnd(&m_Stream);
+}
+
+BOOL CHPZLibDecompressor::Reset()
+{
+	return (m_bValid = (::inflateReset(&m_Stream) == Z_OK));
+}
+
+BOOL CHPZLibDecompressor::Process(const BYTE* pData, int iLength, PVOID pContext)
+{
+	ASSERT(IsValid() && iLength > 0);
+
+	if(!IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	unique_ptr<BYTE[]> szBuff = make_unique<BYTE[]>(m_dwBuffSize);
+
+	m_Stream.next_in	= (z_const Bytef*)pData;
+	m_Stream.avail_in	= iLength;
+
+	BOOL isOK	= TRUE;
+	int rs		= Z_OK;
+
+	while(m_Stream.avail_in > 0)
+	{
+		do
+		{
+			m_Stream.next_out  = szBuff.get();
+			m_Stream.avail_out = m_dwBuffSize;
+
+			rs = ::inflate(&m_Stream, Z_NO_FLUSH);
+
+			if(rs != Z_OK && rs != Z_STREAM_END)
+			{
+				::SetLastError(ERROR_INVALID_DATA);
+				isOK = FALSE;
+
+				goto ZLIB_DECOMPRESS_END;
+			}
+
+			int iRead = (int)(m_dwBuffSize - m_Stream.avail_out);
+
+			if(iRead == 0)
+				break;
+
+			if(!m_fnCallback(szBuff.get(), iRead, pContext))
+			{
+				::SetLastError(ERROR_CANCELLED);
+				isOK = FALSE;
+
+				goto ZLIB_DECOMPRESS_END;
+			}
+		} while(m_Stream.avail_out == 0);
+
+		if(rs == Z_STREAM_END)
+			break;
+	}
+
+ZLIB_DECOMPRESS_END:
+
+	ASSERT(!isOK || rs == Z_OK || rs == Z_STREAM_END);
+
+	if(!isOK || rs == Z_STREAM_END) Reset();
+
+	return isOK;
+}
+
+IHPCompressor* CreateZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize)
+{
+	return new CHPZLibCompressor(fnCallback, iWindowBits, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize);
+}
+
+IHPCompressor* CreateGZipCompressor(Fn_CompressDataCallback fnCallback, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize)
+{
+	return new CHPZLibCompressor(fnCallback, MAX_WBITS + 16, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize);
+}
+
+IHPDecompressor* CreateZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits, DWORD dwBuffSize)
+{
+	return new CHPZLibDecompressor(fnCallback, iWindowBits, dwBuffSize);
+}
+
+IHPDecompressor* CreateGZipDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize)
+{
+	return new CHPZLibDecompressor(fnCallback, MAX_WBITS + 32, dwBuffSize);
+}
+
+int Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+int CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iLevel, int iMethod, int iWindowBits, int iMemLevel, int iStrategy)
+{
+	z_stream stream;
+
+	stream.next_in	 = (z_const Bytef*)lpszSrc;
+	stream.avail_in	 = dwSrcLen;
+	stream.next_out	 = lpszDest;
+	stream.avail_out = dwDestLen;
+	stream.zalloc	 = nullptr;
+	stream.zfree	 = nullptr;
+	stream.opaque	 = nullptr;
+
+	int err = ::deflateInit2(&stream, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy);
+
+	if(err != Z_OK) return err;
+
+	err = ::deflate(&stream, Z_FINISH);
+
+	if(err != Z_STREAM_END)
+	{
+		::deflateEnd(&stream);
+		return err == Z_OK ? Z_BUF_ERROR : err;
+	}
+
+	if(dwDestLen > stream.total_out)
+	{
+		lpszDest[stream.total_out]	= 0;
+		dwDestLen					= (DWORD)stream.total_out;
+	}
+
+	return ::deflateEnd(&stream);
+}
+
+int Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
+}
+
+int UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iWindowBits)
+{
+	z_stream stream;
+
+	stream.next_in	 = (z_const Bytef*)lpszSrc;
+	stream.avail_in	 = (uInt)dwSrcLen;
+	stream.next_out	 = lpszDest;
+	stream.avail_out = dwDestLen;
+	stream.zalloc	 = nullptr;
+	stream.zfree	 = nullptr;
+
+	int err = ::inflateInit2(&stream, iWindowBits);
+
+	if(err != Z_OK) return err;
+
+	err = ::inflate(&stream, Z_FINISH);
+
+	if(err != Z_STREAM_END)
+	{
+		::inflateEnd(&stream);
+		return (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) ? Z_DATA_ERROR : err;
+	}
+
+	if(dwDestLen > stream.total_out)
+	{
+		lpszDest[stream.total_out]	= 0;
+		dwDestLen					= (DWORD)stream.total_out;
+	}
+
+	return inflateEnd(&stream);
+}
+
+DWORD GuessCompressBound(DWORD dwSrcLen, BOOL bGZip)
+{
+	DWORD dwBound = (DWORD)::compressBound(dwSrcLen);
+	
+	if(bGZip) dwBound += 16;
+
+	return dwBound;
+}
+
+int GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16);
+}
+
+int GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, MAX_WBITS + 32);
+}
+
+DWORD GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen)
+{
+	if(dwSrcLen < 20 || *(USHORT*)lpszSrc != 0x8B1F)
+		return 0;
+
+	return *(DWORD*)(lpszSrc + dwSrcLen - 4);
+}
+
+#endif
+
+#ifdef _BROTLI_SUPPORT
+
+CHPBrotliCompressor::CHPBrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality, int iWindow, int iMode, DWORD dwBuffSize)
+: m_fnCallback	(fnCallback)
+, m_iQuality	(iQuality)
+, m_iWindow		(iWindow)
+, m_iMode		(iMode)
+, m_dwBuffSize	(dwBuffSize)
+, m_bValid		(FALSE)
+{
+	ASSERT(m_fnCallback != nullptr);
+
+	Reset();
+}
+
+CHPBrotliCompressor::~CHPBrotliCompressor()
+{
+	if(m_bValid) ::BrotliEncoderDestroyInstance(m_pState);
+}
+
+BOOL CHPBrotliCompressor::Reset()
+{
+	if(m_bValid) ::BrotliEncoderDestroyInstance(m_pState);
+	m_pState =   ::BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
+
+	if(m_pState != nullptr)
+	{
+		::BrotliEncoderSetParameter(m_pState, BROTLI_PARAM_QUALITY	, (UINT)m_iQuality);
+		::BrotliEncoderSetParameter(m_pState, BROTLI_PARAM_LGWIN	, (UINT)m_iWindow);
+		::BrotliEncoderSetParameter(m_pState, BROTLI_PARAM_MODE		, (UINT)m_iMode);
+
+		if (m_iWindow > BROTLI_MAX_WINDOW_BITS)
+			::BrotliEncoderSetParameter(m_pState, BROTLI_PARAM_LARGE_WINDOW, BROTLI_TRUE);
+	}
+
+	return (m_bValid = (m_pState != nullptr));
+}
+
+BOOL CHPBrotliCompressor::Process(const BYTE* pData, int iLength, BOOL bLast, PVOID pContext)
+{
+	return ProcessEx(pData, iLength, bLast, FALSE, pContext);
+}
+
+BOOL CHPBrotliCompressor::ProcessEx(const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush, PVOID pContext)
+{
+	ASSERT(IsValid() && iLength > 0);
+
+	if(!IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	unique_ptr<BYTE[]> szBuff = make_unique<BYTE[]>(m_dwBuffSize);
+
+	const BYTE* pNextInData	= pData;
+	size_t iAvlInLen		= (SIZE_T)iLength;
+	BYTE* pNextOutData		= nullptr;
+	size_t iAvlOutLen		= 0;
+
+	BOOL isOK				  = TRUE;
+	BrotliEncoderOperation op = bLast ? BROTLI_OPERATION_FINISH : (bFlush ? BROTLI_OPERATION_FLUSH : BROTLI_OPERATION_PROCESS);
+
+	while(iAvlInLen > 0)
+	{
+		do
+		{
+			pNextOutData = szBuff.get();
+			iAvlOutLen	 = m_dwBuffSize;
+
+			if(!::BrotliEncoderCompressStream(m_pState, op, &iAvlInLen, &pNextInData, &iAvlOutLen, &pNextOutData, nullptr))
+			{
+				::SetLastError(ERROR_INVALID_DATA);
+				isOK = FALSE;
+
+				goto BROTLI_COMPRESS_END;
+			}
+
+			int iRead = (int)(m_dwBuffSize - iAvlOutLen);
+
+			if(iRead == 0)
+				break;
+
+			if(!m_fnCallback(szBuff.get(), iRead, pContext))
+			{
+				::SetLastError(ERROR_CANCELLED);
+				isOK = FALSE;
+				
+				goto BROTLI_COMPRESS_END;
+			}
+		} while (iAvlOutLen == 0);
+	}
+
+BROTLI_COMPRESS_END:
+
+	if(!isOK || bLast) Reset();
+
+	return isOK;
+}
+
+CHPBrotliDecompressor::CHPBrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize)
+: m_fnCallback	(fnCallback)
+, m_dwBuffSize	(dwBuffSize)
+, m_bValid		(FALSE)
+{
+	ASSERT(m_fnCallback != nullptr);
+
+	Reset();
+}
+
+CHPBrotliDecompressor::~CHPBrotliDecompressor()
+{
+	if(m_bValid) ::BrotliDecoderDestroyInstance(m_pState);
+}
+
+BOOL CHPBrotliDecompressor::Reset()
+{
+	if(m_bValid) ::BrotliDecoderDestroyInstance(m_pState);
+	m_pState =   ::BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+
+	return (m_bValid = (m_pState != nullptr));
+}
+
+BOOL CHPBrotliDecompressor::Process(const BYTE* pData, int iLength, PVOID pContext)
+{
+	ASSERT(IsValid() && iLength > 0);
+
+	if(!IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	unique_ptr<BYTE[]> szBuff = make_unique<BYTE[]>(m_dwBuffSize);
+
+	const BYTE* pNextInData	= pData;
+	size_t iAvlInLen		= (SIZE_T)iLength;
+	BYTE* pNextOutData		= nullptr;
+	size_t iAvlOutLen		= 0;
+
+	BOOL isOK				= TRUE;
+	BrotliDecoderResult rs	= BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
+
+	do
+	{
+		do
+		{
+			pNextOutData = szBuff.get();
+			iAvlOutLen	 = m_dwBuffSize;
+
+			rs = ::BrotliDecoderDecompressStream(m_pState, &iAvlInLen, &pNextInData, &iAvlOutLen, &pNextOutData, nullptr);
+
+			if(rs == BROTLI_DECODER_RESULT_ERROR)
+			{
+				::SetLastError(ERROR_INVALID_DATA);
+				isOK = FALSE;
+
+				goto BROTLI_DECOMPRESS_END;
+			}
+
+			int iRead = (int)(m_dwBuffSize - iAvlOutLen);
+
+			if(iRead == 0)
+				break;
+
+			if(!m_fnCallback(szBuff.get(), iRead, pContext))
+			{
+				::SetLastError(ERROR_CANCELLED);
+				isOK = FALSE;
+
+				goto BROTLI_DECOMPRESS_END;
+			}
+		} while (iAvlOutLen == 0);
+
+		if(rs == BROTLI_DECODER_RESULT_SUCCESS)
+			break;
+
+	} while(rs == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
+
+BROTLI_DECOMPRESS_END:
+
+	if(!isOK || rs == BROTLI_DECODER_RESULT_SUCCESS) Reset();
+
+	return isOK;
+}
+
+IHPCompressor* CreateBrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality, int iWindow, int iMode, DWORD dwBuffSize)
+{
+	return new CHPBrotliCompressor(fnCallback, iQuality, iWindow, iMode, dwBuffSize);
+}
+
+IHPDecompressor* CreateBrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize)
+{
+	return new CHPBrotliDecompressor(fnCallback, dwBuffSize);
+}
+
+int BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	return BrotliCompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE);
+}
+
+int BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iQuality, int iWindow, int iMode)
+{
+	size_t stDestLen = (size_t)dwDestLen;
+	int rs = ::BrotliEncoderCompress(iQuality, iWindow, (BrotliEncoderMode)iMode, (size_t)dwSrcLen, lpszSrc, &stDestLen, lpszDest);
+	dwDestLen = (DWORD)stDestLen;
+
+	return (rs == 1) ? 0 : ((rs == 3) ? -5 : -3);
+}
+
+int BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
+{
+	size_t stDestLen = (size_t)dwDestLen;
+	BrotliDecoderResult rs = ::BrotliDecoderDecompress((size_t)dwSrcLen, lpszSrc, &stDestLen, lpszDest);
+	dwDestLen = (DWORD)stDestLen;
+
+	return (rs == BROTLI_DECODER_RESULT_SUCCESS) ? 0 : ((rs == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) ? -5 : -3);
+}
+
+DWORD BrotliGuessCompressBound(DWORD dwSrcLen)
+{
+	return (DWORD)::BrotliEncoderMaxCompressedSize((size_t)dwSrcLen);
+}
+
+#endif
+
+#ifdef _ICONV_SUPPORT
+
+BOOL CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int& iOutBufLen)
+{
+	ASSERT(lpszInBuf != nullptr);
+
+	SIZE_T nInBufLeft	= iInBufLen;
+	SIZE_T nOutBufLeft	= iOutBufLen;
+	int iOutBufSize		= iOutBufLen;
+	iOutBufLen			= 0;
+
+	if(lpszInBuf == nullptr)
+	{
+		SetLastError(ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	iconv_t ic = iconv_open(lpszToCharset, lpszFromCharset);
+
+	if(IS_INVALID_PVOID(ic))
+		return FALSE;
+
+	SIZE_T rs	= iconv(ic, (LPSTR*)&lpszInBuf, &nInBufLeft, &lpszOutBuf, &nOutBufLeft);
+	iOutBufLen	= iOutBufSize - (int)nOutBufLeft;
+
+	EXECUTE_RESTORE_ERROR(iconv_close(ic));
+
+	return !IS_HAS_ERROR(rs);
+}
+
+BOOL GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength)
+{
+	int iInBufLen	= (int)((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? strlen(szSrc) + 1 : 0));
+	int iOutBufLen	= (int)(iDestLength * sizeof(WCHAR));
+
+	BOOL isOK	= CharsetConvert(CHARSET_GBK, SYSTEM_CHARSET_UNICODE, szSrc, iInBufLen, (char*)szDest, iOutBufLen);
+	iDestLength	= (int)(iOutBufLen / sizeof(WCHAR));
+
+	return isOK;
+}
+
+BOOL UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength)
+{
+	int iInBufLen = (int)(((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? wcslen(szSrc) + 1 : 0)) * sizeof(WCHAR));
+
+	return CharsetConvert(SYSTEM_CHARSET_UNICODE, CHARSET_GBK, (LPCSTR)szSrc, iInBufLen, szDest, iDestLength);
+}
+
+BOOL Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength)
+{
+	int iInBufLen	= (int)((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? strlen(szSrc) + 1 : 0));
+	int iOutBufLen	= (int)(iDestLength * sizeof(WCHAR));
+
+	BOOL isOK	= CharsetConvert(CHARSET_UTF_8, SYSTEM_CHARSET_UNICODE, szSrc, iInBufLen, (char*)szDest, iOutBufLen);
+	iDestLength	= (int)(iOutBufLen / sizeof(WCHAR));
+
+	return isOK;
+}
+
+BOOL UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength)
+{
+	int iInBufLen = (int)(((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? wcslen(szSrc) + 1 : 0)) * sizeof(WCHAR));
+
+	return CharsetConvert(SYSTEM_CHARSET_UNICODE, CHARSET_UTF_8, (LPCSTR)szSrc, iInBufLen, szDest, iDestLength);
+}
+
+BOOL GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength)
+{
+	int iInBufLen = (int)((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? strlen(szSrc) + 1 : 0));
+
+	return CharsetConvert(CHARSET_GBK, CHARSET_UTF_8, szSrc, iInBufLen, szDest, iDestLength);
+}
+
+BOOL Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength)
+{
+	int iInBufLen = (int)((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? strlen(szSrc) + 1 : 0));
+
+	return CharsetConvert(CHARSET_UTF_8, CHARSET_GBK, szSrc, iInBufLen, szDest, iDestLength);
+}
+
+BOOL GbkToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength)
+{
+	return GbkToUnicodeEx(szSrc, -1, szDest, iDestLength);
+}
+
+BOOL UnicodeToGbk(const WCHAR szSrc[], char szDest[], int& iDestLength)
+{
+	return UnicodeToGbkEx(szSrc, -1, szDest, iDestLength);
+}
+
+BOOL Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength)
+{
+	return Utf8ToUnicodeEx(szSrc, -1, szDest, iDestLength);
+}
+
+BOOL UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int& iDestLength)
+{
+	return UnicodeToUtf8Ex(szSrc, -1, szDest, iDestLength);
+}
+
+BOOL GbkToUtf8(const char szSrc[], char szDest[], int& iDestLength)
+{
+	return GbkToUtf8Ex(szSrc, -1, szDest, iDestLength);
+}
+
+BOOL Utf8ToGbk(const char szSrc[], char szDest[], int& iDestLength)
+{
+	return Utf8ToGbkEx(szSrc, -1, szDest, iDestLength);
+}
+
+#endif

+ 959 - 0
SocketHelper.h

@@ -0,0 +1,959 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/HPTypeDef.h"
+#include "hpsocket/SocketInterface.h"
+#include "common/StringT.h"
+#include "common/SysHelper.h"
+#include "common/BufferPtr.h"
+#include "common/BufferPool.h"
+#include "common/RingBuffer.h"
+#include "common/FileHelper.h"
+#include "InternalDef.h"
+
+#include <netdb.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#ifdef _ZLIB_SUPPORT
+#include <zlib.h>
+#endif
+
+#ifdef _BROTLI_SUPPORT
+#include <brotli/decode.h>
+#include <brotli/encode.h>
+#endif
+
+using ADDRESS_FAMILY	= sa_family_t;
+using IN_ADDR			= in_addr;
+using IN6_ADDR			= in6_addr;
+using SOCKADDR			= sockaddr;
+using SOCKADDR_IN		= sockaddr_in;
+using SOCKADDR_IN6		= sockaddr_in6;
+
+typedef struct hp_addr
+{
+	ADDRESS_FAMILY family;
+
+	union
+	{
+		ULONG_PTR	addr;
+		IN_ADDR		addr4;
+		IN6_ADDR	addr6;
+	};
+
+	static const hp_addr ANY_ADDR4;
+	static const hp_addr ANY_ADDR6;
+
+	inline int AddrSize() const
+	{
+		return AddrSize(family);
+	}
+
+	inline static int AddrSize(ADDRESS_FAMILY f)
+	{
+		if(f == AF_INET)
+			return sizeof(IN_ADDR);
+
+		return sizeof(IN6_ADDR);
+	}
+
+	inline static const hp_addr& AnyAddr(ADDRESS_FAMILY f)
+	{
+		if(f == AF_INET)
+			return ANY_ADDR4;
+
+		return ANY_ADDR6;
+	}
+
+	inline const ULONG_PTR* Addr()	const	{return &addr;}
+	inline ULONG_PTR* Addr()				{return &addr;}
+
+	inline BOOL IsIPv4()			const	{return family == AF_INET;}
+	inline BOOL IsIPv6()			const	{return family == AF_INET6;}
+	inline BOOL IsSpecified()		const	{return IsIPv4() || IsIPv6();}
+	inline void ZeroAddr()					{::ZeroMemory(&addr6, sizeof(addr6));}
+	inline void Reset()						{::ZeroMemory(this, sizeof(*this));}
+
+	inline hp_addr& Copy(hp_addr& other) const
+	{
+		if(this != &other)
+			memcpy(&other, this, offsetof(hp_addr, addr) + AddrSize());
+
+		return other;
+	}
+
+	hp_addr(ADDRESS_FAMILY f = AF_UNSPEC, BOOL bZeroAddr = FALSE)
+	{
+		family = f;
+
+		if(bZeroAddr) ZeroAddr();
+	}
+
+} HP_ADDR, *HP_PADDR;
+
+typedef struct hp_sockaddr
+{
+	union
+	{
+		ADDRESS_FAMILY	family;
+		SOCKADDR		addr;
+		SOCKADDR_IN		addr4;
+		SOCKADDR_IN6	addr6;
+	};
+
+	inline int AddrSize() const
+	{
+		return AddrSize(family);
+	}
+
+	inline static int AddrSize(ADDRESS_FAMILY f)
+	{
+		if(f == AF_INET)
+			return sizeof(SOCKADDR_IN);
+
+		return sizeof(SOCKADDR_IN6);
+	}
+
+	inline int EffectAddrSize() const
+	{
+		return EffectAddrSize(family);
+	}
+
+	inline static int EffectAddrSize(ADDRESS_FAMILY f)
+	{
+		return (f == AF_INET) ? offsetof(SOCKADDR_IN, sin_zero) : sizeof(SOCKADDR_IN6);
+	}
+
+	inline static const hp_sockaddr& AnyAddr(ADDRESS_FAMILY f)
+	{
+		static const hp_sockaddr s_any_addr4(AF_INET, TRUE);
+		static const hp_sockaddr s_any_addr6(AF_INET6, TRUE);
+
+		if(f == AF_INET)
+			return s_any_addr4;
+
+		return s_any_addr6;
+	}
+
+	inline static int AddrMinStrLength(ADDRESS_FAMILY f)
+	{
+		if(f == AF_INET)
+			return INET_ADDRSTRLEN;
+
+		return INET6_ADDRSTRLEN;
+	}
+
+	inline BOOL IsIPv4()			const	{return family == AF_INET;}
+	inline BOOL IsIPv6()			const	{return family == AF_INET6;}
+	inline BOOL IsSpecified()		const	{return IsIPv4() || IsIPv6();}
+	inline USHORT Port()			const	{return ntohs(addr4.sin_port);}
+	inline void SetPort(USHORT usPort)		{addr4.sin_port = htons(usPort);}
+	inline void* SinAddr()			const	{return IsIPv4() ? (void*)&addr4.sin_addr : (void*)&addr6.sin6_addr;}
+	inline void* SinAddr()					{return IsIPv4() ? (void*)&addr4.sin_addr : (void*)&addr6.sin6_addr;}
+
+	inline const SOCKADDR* Addr()	const	{return &addr;}
+	inline SOCKADDR* Addr()					{return &addr;}
+	inline void ZeroAddr()					{::ZeroMemory(((char*)this) + sizeof(family), sizeof(*this) - sizeof(family));}
+	inline void Reset()						{::ZeroMemory(this, sizeof(*this));}
+
+	inline hp_sockaddr& Copy(hp_sockaddr& other) const
+	{
+		if(this != &other)
+			memcpy(&other, this, AddrSize());
+
+		return other;
+	}
+
+	size_t Hash() const
+	{
+		ASSERT(IsSpecified());
+
+		size_t _Val		  = 2166136261U;
+		const int size	  = EffectAddrSize();
+		const BYTE* pAddr = (const BYTE*)Addr();
+
+		for(int i = 0; i < size; i++)
+			_Val = 16777619U * _Val ^ (size_t)pAddr[i];
+
+		return (_Val);
+	}
+
+	bool EqualTo(const hp_sockaddr& other) const
+	{
+		ASSERT(IsSpecified() && other.IsSpecified());
+
+		return EqualMemory(this, &other, EffectAddrSize());
+	}
+
+	hp_sockaddr(ADDRESS_FAMILY f = AF_UNSPEC, BOOL bZeroAddr = FALSE)
+	{
+		family = f;
+
+		if(bZeroAddr) ZeroAddr();
+	}
+
+} HP_SOCKADDR, *HP_PSOCKADDR;
+
+typedef struct hp_scope_host
+{
+	LPCTSTR addr;
+	LPCTSTR name;
+
+	BOOL bNeedFree;
+
+	hp_scope_host(LPCTSTR lpszOriginAddress)
+	{
+		ASSERT(lpszOriginAddress != nullptr);
+
+		LPCTSTR lpszFind = ::StrChr(lpszOriginAddress, HOST_SEPARATOR_CHAR);
+
+		if(lpszFind == nullptr)
+		{
+			addr		= lpszOriginAddress;
+			name		= lpszOriginAddress;
+			bNeedFree	= FALSE;
+		}
+		else
+		{
+			int i			= (int)(lpszFind - lpszOriginAddress);
+			int iSize		= (int)lstrlen(lpszOriginAddress) + 1;
+			LPTSTR lpszCopy	= new TCHAR[iSize];
+
+			::memcpy((PVOID)lpszCopy, (PVOID)lpszOriginAddress, iSize * sizeof(TCHAR));
+
+			lpszCopy[i]	= 0;
+			addr		= lpszCopy;
+			name		= lpszCopy + i + 1;
+			bNeedFree	= TRUE;
+
+			if(::IsStrEmpty(name))
+				name = addr;
+		}
+	}
+
+	~hp_scope_host()
+	{
+		if(bNeedFree)
+			delete[] addr;
+	}
+
+} HP_SCOPE_HOST, *HP_PSCOPE_HOST;
+
+struct TNodeBufferObj : public TItem
+{
+	using __super = TItem;
+
+	HP_SOCKADDR	remoteAddr;
+
+public:
+	void Reset(int first = 0, int last = 0)
+	{
+		__super::Reset(first, last);
+		remoteAddr.Reset();
+	}
+
+public:
+	static TNodeBufferObj* Construct(CPrivateHeap& heap,
+									int		capacity	= DEFAULT_ITEM_CAPACITY,
+									BYTE*	pData		= nullptr,
+									int		length		= 0)
+	{
+		return ::ConstructItemT((TNodeBufferObj*)(nullptr), heap, capacity, pData, length);
+	}
+
+	static void Destruct(TNodeBufferObj* pBufferObj)
+	{
+		::DestructItemT(pBufferObj);
+	}
+
+	TNodeBufferObj(CPrivateHeap& hp, BYTE* pHead, int cap = DEFAULT_ITEM_CAPACITY, BYTE* pData = nullptr, int length = 0)
+	: TItem(hp, pHead, cap, pData, length)
+	{
+
+	}
+
+	~TNodeBufferObj()
+	{
+	}
+
+	DECLARE_NO_COPY_CLASS(TNodeBufferObj)
+};
+
+typedef TItemPtrT<TNodeBufferObj>					TNodeBufferObjPtr;
+typedef CNodePoolT<TNodeBufferObj>					CNodeBufferObjPool;
+typedef CCASQueue<TNodeBufferObj>					CNodeRecvQueue;
+typedef TItemListExT<TNodeBufferObj, volatile int>	TNodeBufferObjList;
+
+typedef unique_ptr<CCriSec[]>						CNodeCriSecs;
+typedef unique_ptr<TNodeBufferObjList[]>			TNodeBufferObjLists;
+
+/* Server 组件和 Agent 组件内部使用的事件处理结果常量 */
+
+// 连接已关闭
+#define HR_CLOSED		0xFF
+
+/* 命令类型 */
+enum EnDispCmdType
+{
+	DISP_CMD_SEND		= 0x01,	// 发送数据
+	DISP_CMD_RECEIVE	= 0x02,	// 接收数据
+	DISP_CMD_UNPAUSE	= 0x03,	// 恢复接收数据
+	DISP_CMD_DISCONNECT	= 0x04,	// 断开连接
+	DISP_CMD_TIMEOUT	= 0x05,	// 保活超时
+};
+
+/* 关闭连接标识 */
+enum EnSocketCloseFlag
+{
+	SCF_NONE			= 0,	// 不触发事件
+	SCF_CLOSE			= 1,	// 触发 正常关闭 OnClose 事件
+	SCF_ERROR			= 2		// 触发 异常关闭 OnClose 事件
+};
+
+/* 监听 Socket 数组智能指针 */
+typedef unique_ptr<SOCKET[]>	ListenSocketsPtr;
+
+/* 数据缓冲节点 */
+typedef TItem					TBufferObj;
+/* 数据缓冲节点智能指针 */
+typedef TItemPtr				TBufferObjPtr;
+/* 数据缓冲区对象池 */
+typedef CItemPool				CBufferObjPool;
+/* 数据缓冲区链表模板 */
+typedef TItemListExV			TBufferObjList;
+
+/* 接收缓冲区数组智能指针 */
+typedef unique_ptr<CBufferPtr[]>			CReceiveBuffersPtr;
+
+/* 线程 ID - 接收缓冲区哈希表 */
+typedef unordered_map<THR_ID, CBufferPtr*>	TReceiveBufferMap;
+/* 线程 ID - 接收缓冲区哈希表迭代器 */
+typedef TReceiveBufferMap::iterator			TReceiveBufferMapI;
+/* 线程 ID - 接收缓冲区哈希表 const 迭代器 */
+typedef TReceiveBufferMap::const_iterator	TReceiveBufferMapCI;
+
+/* Socket 缓冲区基础结构 */
+struct TSocketObjBase : public CSafeCounter
+{
+	CPrivateHeap&		heap;
+	CReentrantCriSec	csSend;
+	TBufferObjList		sndBuff;
+
+
+	CONNID		connID;
+	HP_SOCKADDR	remoteAddr;
+	PVOID		extra;
+	PVOID		reserved;
+	PVOID		reserved2;
+	DWORD		activeTime;
+
+	union
+	{
+		DWORD	freeTime;
+		DWORD	connTime;
+	};
+
+	volatile BOOL valid;
+	volatile BOOL connected;
+	volatile BOOL paused;
+
+	TSocketObjBase(CPrivateHeap& hp, CBufferObjPool& bfPool) : heap(hp), sndBuff(bfPool) {}
+
+	static BOOL IsExist(TSocketObjBase* pSocketObj)
+		{return pSocketObj != nullptr;}
+
+	static BOOL IsValid(TSocketObjBase* pSocketObj)
+		{return (IsExist(pSocketObj) && pSocketObj->valid == TRUE);}
+
+	static void Invalid(TSocketObjBase* pSocketObj)
+		{ASSERT(IsExist(pSocketObj)); pSocketObj->valid = FALSE;}
+
+	static void Release(TSocketObjBase* pSocketObj)
+	{
+		ASSERT(IsExist(pSocketObj));
+		
+		pSocketObj->freeTime = ::TimeGetTime();
+		pSocketObj->sndBuff.Release();
+	}
+
+	static BOOL InvalidSocketObj(TSocketObjBase* pSocketObj)
+	{
+		BOOL bDone = FALSE;
+
+		if(TSocketObjBase::IsValid(pSocketObj))
+		{
+			pSocketObj->SetConnected(FALSE);
+
+			CReentrantCriSecLock locallock(pSocketObj->csSend);
+
+			if(TSocketObjBase::IsValid(pSocketObj))
+			{
+				TSocketObjBase::Invalid(pSocketObj);
+				bDone = TRUE;
+			}
+		}
+
+		return bDone;
+	}
+
+	DWORD GetConnTime	()	const	{return connTime;}
+	DWORD GetFreeTime	()	const	{return freeTime;}
+	DWORD GetActiveTime	()	const	{return activeTime;}
+	BOOL IsPaused		()	const	{return paused;}
+
+	int Pending			()	const	{return sndBuff.Length();}
+	BOOL IsPending		()	const	{return Pending() > 0;}
+
+	BOOL HasConnected()							{return connected == TRUE;}
+	BOOL IsConnecting()							{return connected == CST_CONNECTING;}
+	void SetConnected(BOOL bConnected = TRUE)	{connected = bConnected;}
+
+	void Reset(CONNID dwConnID)
+	{
+		ResetCount();
+
+		connID		= dwConnID;
+		connected	= FALSE;
+		valid		= TRUE;
+		paused		= FALSE;
+		extra		= nullptr;
+		reserved	= nullptr;
+		reserved2	= nullptr;
+	}
+};
+
+/* 数据缓冲区结构 */
+struct TSocketObj : public TSocketObjBase
+{
+	using __super = TSocketObjBase;
+
+	SOCKET socket;
+
+	static TSocketObj* Construct(CPrivateHeap& hp, CBufferObjPool& bfPool)
+	{
+		TSocketObj* pSocketObj = (TSocketObj*)hp.Alloc(sizeof(TSocketObj));
+		ASSERT(pSocketObj);
+
+		return new (pSocketObj) TSocketObj(hp, bfPool);
+	}
+
+	static void Destruct(TSocketObj* pSocketObj)
+	{
+		ASSERT(pSocketObj);
+
+		CPrivateHeap& heap = pSocketObj->heap;
+		pSocketObj->TSocketObj::~TSocketObj();
+		heap.Free(pSocketObj);
+	}
+	
+	TSocketObj(CPrivateHeap& hp, CBufferObjPool& bfPool)
+	: __super(hp, bfPool)
+	{
+
+	}
+
+	void Reset(CONNID dwConnID, SOCKET soClient)
+	{
+		__super::Reset(dwConnID);
+		
+		socket = soClient;
+	}
+};
+
+/* Agent 数据缓冲区结构 */
+struct TAgentSocketObj : public TSocketObj
+{
+	using __super = TSocketObj;
+
+	CStringA host;
+	
+	static TAgentSocketObj* Construct(CPrivateHeap& hp, CBufferObjPool& bfPool)
+	{
+		TAgentSocketObj* pSocketObj = (TAgentSocketObj*)hp.Alloc(sizeof(TAgentSocketObj));
+		ASSERT(pSocketObj);
+
+		return new (pSocketObj) TAgentSocketObj(hp, bfPool);
+	}
+
+	static void Destruct(TAgentSocketObj* pSocketObj)
+	{
+		ASSERT(pSocketObj);
+
+		CPrivateHeap& heap = pSocketObj->heap;
+		pSocketObj->TAgentSocketObj::~TAgentSocketObj();
+		heap.Free(pSocketObj);
+	}
+	
+	TAgentSocketObj(CPrivateHeap& hp, CBufferObjPool& bfPool)
+	: __super(hp, bfPool)
+	{
+
+	}
+
+	void Reset(CONNID dwConnID, SOCKET soClient)
+	{
+		__super::Reset(dwConnID, soClient);
+
+		host.Empty();
+	}
+
+	BOOL GetRemoteHost(LPCSTR* lpszHost, USHORT* pusPort = nullptr)
+	{
+		*lpszHost = host;
+
+		if(pusPort)
+			*pusPort = remoteAddr.Port();
+
+		return (!host.IsEmpty());
+	}
+};
+
+/* UDP 数据缓冲区结构 */
+struct TUdpSocketObj : public TSocketObjBase
+{
+	using __super = TSocketObjBase;
+
+	int		index;
+	PVOID	pHolder;
+	FD		fdTimer;
+
+	volatile DWORD	detectFails;
+
+	static TUdpSocketObj* Construct(CPrivateHeap& hp, CBufferObjPool& bfPool)
+	{
+		TUdpSocketObj* pSocketObj = (TUdpSocketObj*)hp.Alloc(sizeof(TUdpSocketObj));
+		ASSERT(pSocketObj);
+
+		return new (pSocketObj) TUdpSocketObj(hp, bfPool);
+	}
+
+	static void Destruct(TUdpSocketObj* pSocketObj)
+	{
+		ASSERT(pSocketObj);
+
+		CPrivateHeap& heap = pSocketObj->heap;
+		pSocketObj->TUdpSocketObj::~TUdpSocketObj();
+		heap.Free(pSocketObj);
+	}
+	
+	TUdpSocketObj(CPrivateHeap& hp, CBufferObjPool& bfPool)
+	: __super(hp, bfPool)
+	{
+
+	}
+
+	void Reset(CONNID dwConnID)
+	{
+		__super::Reset(dwConnID);
+
+		index		= -1;
+		detectFails	= 0;
+		fdTimer		= INVALID_FD;
+		pHolder		= nullptr;
+	}
+};
+
+/* 有效 TSocketObj 缓存 */
+typedef CRingCache2<TSocketObj, CONNID, true>		TSocketObjPtrPool;
+/* 失效 TSocketObj 缓存 */
+typedef CRingPool<TSocketObj>						TSocketObjPtrList;
+/* 失效 TSocketObj 垃圾回收结构链表 */
+typedef CCASQueue<TSocketObj>						TSocketObjPtrQueue;
+
+/* 有效 TSocketObj 缓存 */
+typedef CRingCache2<TAgentSocketObj, CONNID, true>	TAgentSocketObjPtrPool;
+/* 失效 TSocketObj 缓存 */
+typedef CRingPool<TAgentSocketObj>					TAgentSocketObjPtrList;
+/* 失效 TSocketObj 垃圾回收结构链表 */
+typedef CCASQueue<TAgentSocketObj>					TAgentSocketObjPtrQueue;
+
+/* 有效 TUdpSocketObj 缓存 */
+typedef CRingCache2<TUdpSocketObj, CONNID, true>	TUdpSocketObjPtrPool;
+/* 失效 TUdpSocketObj 缓存 */
+typedef CRingPool<TUdpSocketObj>					TUdpSocketObjPtrList;
+/* 失效 TUdpSocketObj 垃圾回收结构链表 */
+typedef CCASQueue<TUdpSocketObj>					TUdpSocketObjPtrQueue;
+
+/* HP_SOCKADDR 比较器 */
+struct hp_sockaddr_func
+{
+	struct hash
+	{
+		size_t operator() (const HP_SOCKADDR* pA) const
+		{
+			return pA->Hash();
+		}
+	};
+
+	struct equal_to
+	{
+		bool operator () (const HP_SOCKADDR* pA, const HP_SOCKADDR* pB) const
+		{
+			return pA->EqualTo(*pB);
+		}
+	};
+
+};
+
+/* 地址-连接 ID 哈希表 */
+typedef unordered_map<const HP_SOCKADDR*, CONNID, hp_sockaddr_func::hash, hp_sockaddr_func::equal_to>
+										TSockAddrMap;
+/* 地址-连接 ID 哈希表迭代器 */
+typedef TSockAddrMap::iterator			TSockAddrMapI;
+/* 地址-连接 ID 哈希表 const 迭代器 */
+typedef TSockAddrMap::const_iterator	TSockAddrMapCI;
+
+/* IClient 组件关闭上下文 */
+struct TClientCloseContext
+{
+	BOOL bFireOnClose;
+	EnSocketOperation enOperation;
+	int iErrorCode;
+	BOOL bNotify;
+
+	TClientCloseContext(BOOL bFire = TRUE, EnSocketOperation enOp = SO_CLOSE, int iCode = SE_OK, BOOL bNtf = TRUE)
+	{
+		Reset(bFire, enOp, iCode, bNtf);
+	}
+
+	void Reset(BOOL bFire = TRUE, EnSocketOperation enOp = SO_CLOSE, int iCode = SE_OK, BOOL bNtf = TRUE)
+	{
+		bFireOnClose = bFire;
+		enOperation	 = enOp;
+		iErrorCode	 = iCode;
+		bNotify		 = bNtf;
+	}
+
+};
+
+/*****************************************************************************************************/
+/******************************************** 公共帮助方法 ********************************************/
+/*****************************************************************************************************/
+
+/* 默认工作线程前缀 */
+#define DEFAULT_WORKER_THREAD_PREFIX	"hp-worker-"
+
+/* 设置当前工作线程名称 */
+BOOL SetCurrentWorkerThreadName();
+/* 设置工作线程默认名称 */
+BOOL SetWorkerThreadDefaultName(THR_ID tid);
+
+/* 获取错误描述文本 */
+LPCTSTR GetSocketErrorDesc(EnSocketError enCode);
+/* 确定地址簇 */
+ADDRESS_FAMILY DetermineAddrFamily(LPCTSTR lpszAddress);
+/* 地址字符串地址转换为 HP_ADDR */
+BOOL GetInAddr(LPCTSTR lpszAddress, HP_ADDR& addr);
+/* 地址字符串地址转换为 HP_SOCKADDR */
+BOOL GetSockAddr(LPCTSTR lpszAddress, USHORT usPort, HP_SOCKADDR& addr);
+/* 检查字符串是否符合 IP 地址格式 */
+BOOL IsIPAddress(LPCTSTR lpszAddress, EnIPAddrType* penType = nullptr);
+/* 通过主机名获取 IP 地址 */
+BOOL GetIPAddress(LPCTSTR lpszHost, LPTSTR lpszIP, int& iIPLenth, EnIPAddrType& enType);
+/* 通过主机名获取 HP_SOCKADDR */
+BOOL GetSockAddrByHostName(LPCTSTR lpszHost, USHORT usPort, HP_SOCKADDR& addr);
+/* 通过主机名获取 HP_SOCKADDR */
+BOOL GetSockAddrByHostNameDirectly(LPCTSTR lpszHost, USHORT usPort, HP_SOCKADDR &addr);
+/* 枚举主机 IP 地址 */
+BOOL EnumHostIPAddresses(LPCTSTR lpszHost, EnIPAddrType enType, LPTIPAddr** lpppIPAddr, int& iIPAddrCount);
+/* 填充 LPTIPAddr* */
+BOOL RetrieveSockAddrIPAddresses(const vector<HP_PSOCKADDR>& vt, LPTIPAddr** lpppIPAddr, int& iIPAddrCount);
+/* 释放 LPTIPAddr* */
+BOOL FreeHostIPAddresses(LPTIPAddr* lppIPAddr);
+/* 把 HP_SOCKADDR 结构转换为地址字符串 */
+BOOL sockaddr_IN_2_A(const HP_SOCKADDR& addr, ADDRESS_FAMILY& usFamily, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort);
+/* 把地址字符串转换为 HP_SOCKADDR 结构 */
+BOOL sockaddr_A_2_IN(LPCTSTR lpszAddress, USHORT usPort, HP_SOCKADDR& addr);
+/* 获取 Socket 的本地或远程地址信息 */
+BOOL GetSocketAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort, BOOL bLocal = TRUE);
+/* 获取 Socket 的本地地址信息 */
+BOOL GetSocketLocalAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort);
+/* 获取 Socket 的远程地址信息 */
+BOOL GetSocketRemoteAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort);
+/* 设置组播选项 */
+BOOL SetMultiCastSocketOptions(SOCKET sock, const HP_SOCKADDR& bindAddr, const HP_SOCKADDR& castAddr, int iMCTtl, BOOL bMCLoop);
+/* 等待连接 */
+int WaitForSocketWrite(SOCKET sock, DWORD dwTimeout);
+
+/* 64 位网络字节序转主机字节序 */
+ULONGLONG NToH64(ULONGLONG value);
+/* 64 位主机字节序转网络字节序 */
+ULONGLONG HToN64(ULONGLONG value);
+
+/* 短整型高低字节交换 */
+#define ENDIAN_SWAP_16(A)	((USHORT)((((USHORT)(A) & 0xff00) >> 8) | (((USHORT)(A) & 0x00ff) << 8)))
+/* 长整型高低字节交换 */
+#define ENDIAN_SWAP_32(A)	((((DWORD)(A) & 0xff000000) >> 24) | \
+							(((DWORD)(A) & 0x00ff0000) >>  8)  | \
+							(((DWORD)(A) & 0x0000ff00) <<  8)  | \
+							(((DWORD)(A) & 0x000000ff) << 24)	 )
+
+/* 检查是否小端字节序 */
+BOOL IsLittleEndian();
+/* 短整型主机字节序转小端字节序 */
+USHORT HToLE16(USHORT value);
+/* 短整型主机字节序转大端字节序 */
+USHORT HToBE16(USHORT value);
+/* 长整型主机字节序转小端字节序 */
+DWORD HToLE32(DWORD value);
+/* 长整型主机字节序转大端字节序 */
+DWORD HToBE32(DWORD value);
+
+HRESULT ReadSmallFile(LPCTSTR lpszFileName, CFile& file, CFileMapping& fmap, DWORD dwMaxFileSize = MAX_SMALL_FILE_SIZE);
+HRESULT MakeSmallFilePackage(LPCTSTR lpszFileName, CFile& file, CFileMapping& fmap, WSABUF szBuf[3], const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr);
+
+/************************************************************************
+名称:setsockopt() 帮助方法
+描述:简化常用的 setsockopt() 调用
+************************************************************************/
+
+int SSO_SetSocketOption		(SOCKET sock, int level, int name, LPVOID val, int len);
+int SSO_GetSocketOption		(SOCKET sock, int level, int name, LPVOID val, int* len);
+int SSO_IoctlSocket			(SOCKET sock, long cmd, PVOID arg);
+
+int SSO_NoBlock				(SOCKET sock, BOOL bNoBlock = TRUE);
+int SSO_NoDelay				(SOCKET sock, BOOL bNoDelay = TRUE);
+int SSO_DontLinger			(SOCKET sock, BOOL bDont = TRUE);
+int SSO_Linger				(SOCKET sock, int l_onoff, int l_linger);
+int SSO_KeepAlive			(SOCKET sock, BOOL bKeepAlive = TRUE);
+int SSO_KeepAliveVals		(SOCKET sock, BOOL bOnOff, DWORD dwIdle, DWORD dwInterval, DWORD dwCount = 5);
+int SSO_ReuseAddress		(SOCKET sock, EnReuseAddressPolicy opt);
+int SSO_RecvBuffSize		(SOCKET sock, int size);
+int SSO_SendBuffSize		(SOCKET sock, int size);
+int SSO_RecvTimeOut			(SOCKET sock, int ms);
+int SSO_SendTimeOut			(SOCKET sock, int ms);
+int SSO_GetError			(SOCKET sock);
+
+/* 生成 Connection ID */
+CONNID GenerateConnectionID();
+/* 检测 UDP 连接关闭通知 */
+int IsUdpCloseNotify(const BYTE* pData, int iLength);
+/* 发送 UDP 连接关闭通知 */
+int SendUdpCloseNotify(SOCKET sock);
+/* 发送 UDP 连接关闭通知 */
+int SendUdpCloseNotify(SOCKET sock, const HP_SOCKADDR& remoteAddr);
+/* 关闭 Socket */
+int ManualCloseSocket(SOCKET sock, int iShutdownFlag = 0xFF, BOOL bGraceful = TRUE);
+
+#ifdef _ICONV_SUPPORT
+
+#define CHARSET_GBK				"GBK"
+#define CHARSET_UTF_8			"UTF-8"
+#define CHARSET_UTF_16LE		"UTF-16LE"
+#define CHARSET_UTF_32LE		"UTF-32LE"
+#define CHARSET_UTF_16BE		"UTF-16BE"
+#define CHARSET_UTF_32BE		"UTF-32BE"
+
+// 系统 UNICODE 字符集
+#define SYSTEM_CHARSET_UNICODE	( (sizeof(WCHAR) == 4)										   \
+									? (IsLittleEndian() ? CHARSET_UTF_32LE : CHARSET_UTF_32BE) \
+									: (IsLittleEndian() ? CHARSET_UTF_16LE : CHARSET_UTF_16BE) )
+
+// Charset A -> Charset B
+BOOL CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int& iOutBufLen);
+
+// GBK -> UNICODE
+BOOL GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength);
+// UNICODE -> GBK
+BOOL UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength);
+// UTF8 -> UNICODE
+BOOL Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength);
+// UNICODE -> UTF8
+BOOL UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength);
+// GBK -> UTF8
+BOOL GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength);
+// UTF8 -> GBK
+BOOL Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength);
+
+// GBK -> UNICODE
+BOOL GbkToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength);
+// UNICODE -> GBK
+BOOL UnicodeToGbk(const WCHAR szSrc[], char szDest[], int& iDestLength);
+// UTF8 -> UNICODE
+BOOL Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength);
+// UNICODE -> UTF8
+BOOL UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int& iDestLength);
+// GBK -> UTF8
+BOOL GbkToUtf8(const char szSrc[], char szDest[], int& iDestLength);
+// UTF8 -> GBK
+BOOL Utf8ToGbk(const char szSrc[], char szDest[], int& iDestLength);
+
+#endif
+
+// 计算 Base64 编码后长度
+DWORD GuessBase64EncodeBound(DWORD dwSrcLen);
+// 计算 Base64 解码后长度
+DWORD GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen);
+// Base64 编码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+// Base64 解码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+
+// 计算 URL 编码后长度
+DWORD GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen);
+// 计算 URL 解码后长度
+DWORD GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen);
+// URL 编码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+// URL 解码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+
+/* 销毁压缩器对象 */
+void DestroyCompressor(IHPCompressor* pCompressor);
+/* 销毁解压器对象 */
+void DestroyDecompressor(IHPDecompressor* pDecompressor);
+
+#ifdef _ZLIB_SUPPORT
+
+/* ZLib 压缩器 */
+class CHPZLibCompressor : public IHPCompressor
+{
+public:
+	virtual BOOL Process(const BYTE* pData, int iLength, BOOL bLast, PVOID pContext = nullptr);
+	virtual BOOL ProcessEx(const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush = FALSE, PVOID pContext = nullptr);
+	virtual BOOL IsValid() {return m_bValid;}
+	virtual BOOL Reset();
+
+public:
+	CHPZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits = MAX_WBITS, int iLevel = Z_DEFAULT_COMPRESSION, int iMethod = Z_DEFLATED, int iMemLevel = MAX_MEM_LEVEL, int iStrategy = Z_DEFAULT_STRATEGY, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+	virtual ~CHPZLibCompressor();
+
+private:
+	Fn_CompressDataCallback m_fnCallback;
+	z_stream m_Stream;
+	BOOL m_bValid;
+	DWORD m_dwBuffSize;
+};
+
+/* ZLib 解压器 */
+class CHPZLibDecompressor : public IHPDecompressor
+{
+public:
+	virtual BOOL Process(const BYTE* pData, int iLength, PVOID pContext = nullptr);
+	virtual BOOL IsValid() {return m_bValid;}
+	virtual BOOL Reset();
+
+public:
+	CHPZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits = MAX_WBITS, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+	virtual ~CHPZLibDecompressor();
+
+private:
+	Fn_DecompressDataCallback m_fnCallback;
+	z_stream m_Stream;
+	BOOL m_bValid;
+	DWORD m_dwBuffSize;
+};
+
+/* 创建 ZLib 压缩器对象 */
+IHPCompressor* CreateZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits = MAX_WBITS, int iLevel = Z_DEFAULT_COMPRESSION, int iMethod = Z_DEFLATED, int iMemLevel = MAX_MEM_LEVEL, int iStrategy = Z_DEFAULT_STRATEGY, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+/* 创建 GZip 压缩器对象 */
+IHPCompressor* CreateGZipCompressor(Fn_CompressDataCallback fnCallback, int iLevel = Z_DEFAULT_COMPRESSION, int iMethod = Z_DEFLATED, int iMemLevel = MAX_MEM_LEVEL, int iStrategy = Z_DEFAULT_STRATEGY, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+/* 创建 ZLib 解压器对象 */
+IHPDecompressor* CreateZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits = MAX_WBITS, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+/* 创建 GZip 解压器对象 */
+IHPDecompressor* CreateGZipDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+
+// 普通压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+// 高级压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iLevel = Z_DEFAULT_COMPRESSION, int iMethod = Z_DEFLATED, int iWindowBits = MAX_WBITS, int iMemLevel = MAX_MEM_LEVEL, int iStrategy = Z_DEFAULT_STRATEGY);
+// 普通解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+// 高级解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iWindowBits = MAX_WBITS);
+// 推测压缩结果长度
+DWORD GuessCompressBound(DWORD dwSrcLen, BOOL bGZip = FALSE);
+
+// Gzip 压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+// Gzip 解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+// 推测 Gzip 解压结果长度(如果返回 0 或不合理值则说明输入内容并非有效的 Gzip 格式)
+DWORD GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen);
+
+#endif
+
+#ifdef _BROTLI_SUPPORT
+
+/* Brotli 压缩器 */
+class CHPBrotliCompressor : public IHPCompressor
+{
+public:
+	virtual BOOL Process(const BYTE* pData, int iLength, BOOL bLast, PVOID pContext = nullptr);
+	virtual BOOL ProcessEx(const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush = FALSE, PVOID pContext = nullptr);
+	virtual BOOL IsValid() {return m_bValid;}
+	virtual BOOL Reset();
+
+public:
+	CHPBrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality = BROTLI_DEFAULT_QUALITY, int iWindow = BROTLI_DEFAULT_WINDOW, int iMode = BROTLI_DEFAULT_MODE, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+	virtual ~CHPBrotliCompressor();
+
+private:
+	Fn_CompressDataCallback m_fnCallback;
+	BrotliEncoderState* m_pState;
+	BOOL m_bValid;
+
+	int m_iQuality;
+	int m_iWindow;
+	int m_iMode;
+	DWORD m_dwBuffSize;
+};
+
+/* Brotli 解压器 */
+class CHPBrotliDecompressor : public IHPDecompressor
+{
+public:
+	virtual BOOL Process(const BYTE* pData, int iLength, PVOID pContext = nullptr);
+	virtual BOOL IsValid() {return m_bValid;}
+	virtual BOOL Reset();
+
+public:
+	CHPBrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+	virtual ~CHPBrotliDecompressor();
+
+private:
+	Fn_DecompressDataCallback m_fnCallback;
+	BrotliDecoderState* m_pState;
+	BOOL m_bValid;
+	DWORD m_dwBuffSize;
+};
+
+/* 创建 Brotli 压缩器对象 */
+IHPCompressor* CreateBrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality = BROTLI_DEFAULT_QUALITY, int iWindow = BROTLI_DEFAULT_WINDOW, int iMode = BROTLI_DEFAULT_MODE, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+/* 创建 Brotli 解压器对象 */
+IHPDecompressor* CreateBrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
+
+// Brotli 压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+// Brotli 高级压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iQuality = BROTLI_DEFAULT_QUALITY, int iWindow = BROTLI_DEFAULT_WINDOW, int iMode = BROTLI_DEFAULT_MODE);
+// Brotli 解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
+int BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
+// Brotli 推测压缩结果长度
+DWORD BrotliGuessCompressBound(DWORD dwSrcLen);
+
+#endif

+ 770 - 0
SocketObject4C.h

@@ -0,0 +1,770 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "hpsocket/HPSocket4C.h"
+#include "hpsocket/SocketInterface.h"
+#include "common/FuncHelper.h"
+
+class C_HP_Object
+{
+public:
+	template<class T> static inline HP_Object FromFirst(T* pFirst)
+	{
+		return (HP_Object)((char*)pFirst - first);
+	}
+
+	template<class T> static inline T* ToFirst(HP_Object pObject)
+	{
+		return (T*)((char*)pObject + first);
+	}
+
+	template<size_t offset, class T> static inline HP_Object FromSecond(T* pSecond)
+	{
+		return (C_HP_Object*)((char*)pSecond - first - (offset + __DUAL_VPTR_GAP__));
+	}
+
+	template<class T> static inline T* ToSecond(HP_Object pObject)
+	{
+		return (T*)((char*)pObject + ((C_HP_Object*)pObject)->second);
+	}
+
+public:
+	C_HP_Object(int offset = 0) : second(first + (offset + __DUAL_VPTR_GAP__)) {}
+	virtual ~C_HP_Object() {}
+
+private:
+	static const size_t first = (sizeof(PVOID) + sizeof(size_t));
+	size_t second;
+};
+
+template<class T, class L, int offset = 0> class C_HP_ObjectT : private C_HP_Object, public T
+{
+public:
+	C_HP_ObjectT(L* pListener)	: C_HP_Object(offset), T(pListener)	{}
+};
+
+template<class T, class L, size_t offset = 0> class C_HP_ServerListenerT : public L
+{
+public:
+	virtual EnHandleResult OnPrepareListen(T* pSender, SOCKET soListen)
+	{
+		return	(m_fnOnPrepareListen)
+				? m_fnOnPrepareListen(C_HP_Object::FromSecond<offset>(pSender), soListen)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnAccept(T* pSender, CONNID dwConnID, UINT_PTR soClient)
+	{
+		return	(m_fnOnAccept)
+				? m_fnOnAccept(C_HP_Object::FromSecond<offset>(pSender), dwConnID, soClient)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID)
+	{
+		return	(m_fnOnHandShake)
+				? m_fnOnHandShake(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnSend(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+	{
+		return	(m_fnOnSend)
+				? m_fnOnSend(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+	{
+		ASSERT(m_fnOnReceive);
+
+		return	(m_fnOnReceive)
+				? m_fnOnReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, int iLength)
+	{
+		ASSERT(m_fnOnPullReceive);
+
+		return	(m_fnOnPullReceive)
+				? m_fnOnPullReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnClose(T* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
+	{
+		ASSERT(m_fnOnClose);
+
+		return	(m_fnOnClose)
+				? m_fnOnClose(C_HP_Object::FromSecond<offset>(pSender), dwConnID, enOperation, iErrorCode)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnShutdown(T* pSender)
+	{
+		return	(m_fnOnShutdown)
+				? m_fnOnShutdown(C_HP_Object::FromSecond<offset>(pSender))
+				: HR_IGNORE;
+	}
+
+public:
+	C_HP_ServerListenerT()
+	: m_fnOnPrepareListen	(nullptr)
+	, m_fnOnAccept			(nullptr)
+	, m_fnOnHandShake		(nullptr)
+	, m_fnOnSend			(nullptr)
+	, m_fnOnReceive			(nullptr)
+	, m_fnOnPullReceive		(nullptr)
+	, m_fnOnClose			(nullptr)
+	, m_fnOnShutdown		(nullptr)
+	{
+	}
+
+public:
+	HP_FN_Server_OnPrepareListen	m_fnOnPrepareListen	;
+	HP_FN_Server_OnAccept			m_fnOnAccept		;
+	HP_FN_Server_OnHandShake		m_fnOnHandShake		;
+	HP_FN_Server_OnSend				m_fnOnSend			;
+	HP_FN_Server_OnReceive			m_fnOnReceive		;
+	HP_FN_Server_OnPullReceive		m_fnOnPullReceive	;
+	HP_FN_Server_OnClose			m_fnOnClose			;
+	HP_FN_Server_OnShutdown			m_fnOnShutdown		;
+};
+
+template<class T, class L, size_t offset = 0> class C_HP_AgentListenerT : public L
+{
+public:
+	virtual EnHandleResult OnPrepareConnect(T* pSender, CONNID dwConnID, SOCKET socket)
+	{
+		return	(m_fnOnPrepareConnect)
+				? m_fnOnPrepareConnect(C_HP_Object::FromSecond<offset>(pSender), dwConnID, socket)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnConnect(T* pSender, CONNID dwConnID)
+	{
+		return	(m_fnOnConnect)
+				? m_fnOnConnect(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID)
+	{
+		return	(m_fnOnHandShake)
+				? m_fnOnHandShake(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnSend(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+	{
+		return	(m_fnOnSend)
+				? m_fnOnSend(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+	{
+		ASSERT(m_fnOnReceive);
+
+		return	(m_fnOnReceive)
+				? m_fnOnReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, int iLength)
+	{
+		ASSERT(m_fnOnPullReceive);
+
+		return	(m_fnOnPullReceive)
+				? m_fnOnPullReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnClose(T* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
+	{
+		ASSERT(m_fnOnClose);
+
+		return	(m_fnOnClose)
+				? m_fnOnClose(C_HP_Object::FromSecond<offset>(pSender), dwConnID, enOperation, iErrorCode)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnShutdown(T* pSender)
+	{
+		return	(m_fnOnShutdown)
+				? m_fnOnShutdown(C_HP_Object::FromSecond<offset>(pSender))
+				: HR_IGNORE;
+	}
+
+public:
+	C_HP_AgentListenerT()
+	: m_fnOnPrepareConnect	(nullptr)
+	, m_fnOnConnect			(nullptr)
+	, m_fnOnHandShake		(nullptr)
+	, m_fnOnSend			(nullptr)
+	, m_fnOnReceive			(nullptr)
+	, m_fnOnPullReceive		(nullptr)
+	, m_fnOnClose			(nullptr)
+	, m_fnOnShutdown		(nullptr)
+	{
+	}
+
+public:
+	HP_FN_Agent_OnPrepareConnect	m_fnOnPrepareConnect;
+	HP_FN_Agent_OnConnect			m_fnOnConnect		;
+	HP_FN_Agent_OnHandShake			m_fnOnHandShake		;
+	HP_FN_Agent_OnSend				m_fnOnSend			;
+	HP_FN_Agent_OnReceive			m_fnOnReceive		;
+	HP_FN_Agent_OnPullReceive		m_fnOnPullReceive	;
+	HP_FN_Agent_OnClose				m_fnOnClose			;
+	HP_FN_Agent_OnShutdown			m_fnOnShutdown		;
+};
+
+template<class T, class L, size_t offset = 0> class C_HP_ClientListenerT : public L
+{
+public:
+	virtual EnHandleResult OnPrepareConnect(T* pSender, CONNID dwConnID, SOCKET socket)
+	{
+		return	(m_fnOnPrepareConnect)
+				? m_fnOnPrepareConnect(C_HP_Object::FromSecond<offset>(pSender), dwConnID, socket)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnConnect(T* pSender, CONNID dwConnID)
+	{
+		return	(m_fnOnConnect)
+				? m_fnOnConnect(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID)
+	{
+		return	(m_fnOnHandShake)
+				? m_fnOnHandShake(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnSend(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+	{
+		return	(m_fnOnSend)
+				? m_fnOnSend(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+	{
+		ASSERT(m_fnOnReceive);
+
+		return	(m_fnOnReceive)
+				? m_fnOnReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, int iLength)
+	{
+		ASSERT(m_fnOnPullReceive);
+
+		return	(m_fnOnPullReceive)
+				? m_fnOnPullReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnClose(T* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
+	{
+		ASSERT(m_fnOnClose);
+
+		return	(m_fnOnClose)
+				? m_fnOnClose(C_HP_Object::FromSecond<offset>(pSender), dwConnID, enOperation, iErrorCode)
+				: HR_IGNORE;
+	}
+
+public:
+	C_HP_ClientListenerT()
+	: m_fnOnPrepareConnect	(nullptr)
+	, m_fnOnConnect			(nullptr)
+	, m_fnOnHandShake		(nullptr)
+	, m_fnOnSend			(nullptr)
+	, m_fnOnReceive			(nullptr)
+	, m_fnOnPullReceive		(nullptr)
+	, m_fnOnClose			(nullptr)
+	{
+	}
+
+public:
+	HP_FN_Client_OnPrepareConnect	m_fnOnPrepareConnect;
+	HP_FN_Client_OnConnect			m_fnOnConnect		;
+	HP_FN_Client_OnHandShake		m_fnOnHandShake		;
+	HP_FN_Client_OnSend				m_fnOnSend			;
+	HP_FN_Client_OnReceive			m_fnOnReceive		;
+	HP_FN_Client_OnPullReceive		m_fnOnPullReceive	;
+	HP_FN_Client_OnClose			m_fnOnClose			;
+};
+
+typedef C_HP_ServerListenerT<ITcpServer, ITcpServerListener>						C_HP_TcpServerListener;
+typedef C_HP_ServerListenerT<ITcpServer, ITcpServerListener, sizeof(IPullSocket)>	C_HP_TcpPullServerListener;
+typedef C_HP_ServerListenerT<ITcpServer, ITcpServerListener, sizeof(IPackSocket)>	C_HP_TcpPackServerListener;
+
+typedef C_HP_AgentListenerT<ITcpAgent, ITcpAgentListener>							C_HP_TcpAgentListener;
+typedef C_HP_AgentListenerT<ITcpAgent, ITcpAgentListener, sizeof(IPullSocket)>		C_HP_TcpPullAgentListener;
+typedef C_HP_AgentListenerT<ITcpAgent, ITcpAgentListener, sizeof(IPackSocket)>		C_HP_TcpPackAgentListener;
+
+typedef C_HP_ClientListenerT<ITcpClient, ITcpClientListener>						C_HP_TcpClientListener;
+typedef C_HP_ClientListenerT<ITcpClient, ITcpClientListener, sizeof(IPullClient)>	C_HP_TcpPullClientListener;
+typedef C_HP_ClientListenerT<ITcpClient, ITcpClientListener, sizeof(IPackClient)>	C_HP_TcpPackClientListener;
+
+#ifdef _UDP_SUPPORT
+
+template<class T, class L, size_t offset = 0> class C_HP_UdpNodeListenerT : public L
+{
+public:
+	virtual EnHandleResult OnPrepareListen(T* pSender, SOCKET soListen)
+	{
+		return	(m_fnOnPrepareListen)
+				? m_fnOnPrepareListen(C_HP_Object::FromSecond<offset>(pSender), soListen)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnSend(T* pSender, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength)
+	{
+		return	(m_fnOnSend)
+				? m_fnOnSend(C_HP_Object::FromSecond<offset>(pSender), lpszRemoteAddress, usRemotePort, pData, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnReceive(T* pSender, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength)
+	{
+		ASSERT(m_fnOnReceive);
+
+		return	(m_fnOnReceive)
+				? m_fnOnReceive(C_HP_Object::FromSecond<offset>(pSender), lpszRemoteAddress, usRemotePort, pData, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnError(T* pSender, EnSocketOperation enOperation, int iErrorCode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength)
+	{
+		ASSERT(m_fnOnError);
+
+		return	(m_fnOnError)
+				? m_fnOnError(C_HP_Object::FromSecond<offset>(pSender), enOperation, iErrorCode, lpszRemoteAddress, usRemotePort, pData, iLength)
+				: HR_IGNORE;
+	}
+
+	virtual EnHandleResult OnShutdown(T* pSender)
+	{
+		return	(m_fnOnShutdown)
+				? m_fnOnShutdown(C_HP_Object::FromSecond<offset>(pSender))
+				: HR_IGNORE;
+	}
+
+public:
+	C_HP_UdpNodeListenerT()
+	: m_fnOnPrepareListen	(nullptr)
+	, m_fnOnSend			(nullptr)
+	, m_fnOnReceive			(nullptr)
+	, m_fnOnError			(nullptr)
+	, m_fnOnShutdown		(nullptr)
+	{
+	}
+
+public:
+	HP_FN_UdpNode_OnPrepareListen	m_fnOnPrepareListen	;
+	HP_FN_UdpNode_OnSend			m_fnOnSend			;
+	HP_FN_UdpNode_OnReceive			m_fnOnReceive		;
+	HP_FN_UdpNode_OnError			m_fnOnError			;
+	HP_FN_UdpNode_OnShutdown		m_fnOnShutdown		;
+};
+
+typedef C_HP_ServerListenerT<IUdpServer, IUdpServerListener>						C_HP_UdpServerListener;
+typedef C_HP_ClientListenerT<IUdpClient, IUdpClientListener>						C_HP_UdpClientListener;
+typedef C_HP_ClientListenerT<IUdpCast, IUdpCastListener>							C_HP_UdpCastListener;
+typedef C_HP_UdpNodeListenerT<IUdpNode, IUdpNodeListener>							C_HP_UdpNodeListener;
+
+typedef C_HP_ServerListenerT<IUdpServer, IUdpServerListener, sizeof(IArqSocket)>	C_HP_UdpArqServerListener;
+typedef C_HP_ClientListenerT<IUdpClient, IUdpClientListener, sizeof(IArqClient)>	C_HP_UdpArqClientListener;
+
+#endif
+
+#ifdef _HTTP_SUPPORT
+
+template<class T> class C_HP_HttpListenerT : public IHttpListenerT<T>
+{
+public:
+	virtual EnHttpParseResult OnMessageBegin(T* pSender, CONNID dwConnID)
+	{
+		return	(m_fnOnMessageBegin)
+				? m_fnOnMessageBegin(C_HP_Object::FromFirst(pSender), dwConnID)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnRequestLine(T* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl)
+	{
+		return	(m_fnOnRequestLine)
+				? m_fnOnRequestLine(C_HP_Object::FromFirst(pSender), dwConnID, lpszMethod, lpszUrl)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnStatusLine(T* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
+	{
+		return	(m_fnOnStatusLine)
+				? m_fnOnStatusLine(C_HP_Object::FromFirst(pSender), dwConnID, usStatusCode, lpszDesc)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnHeader(T* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
+	{
+		return	(m_fnOnHeader)
+				? m_fnOnHeader(C_HP_Object::FromFirst(pSender), dwConnID, lpszName, lpszValue)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnHeadersComplete(T* pSender, CONNID dwConnID)
+	{
+		ASSERT(m_fnOnHeadersComplete);
+
+		return	(m_fnOnHeadersComplete)
+				? m_fnOnHeadersComplete(C_HP_Object::FromFirst(pSender), dwConnID)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnBody(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+	{
+		ASSERT(m_fnOnBody);
+
+		return	(m_fnOnBody)
+				? m_fnOnBody(C_HP_Object::FromFirst(pSender), dwConnID, pData, iLength)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnChunkHeader(T* pSender, CONNID dwConnID, int iLength)
+	{
+		return	(m_fnOnChunkHeader)
+				? m_fnOnChunkHeader(C_HP_Object::FromFirst(pSender), dwConnID, iLength)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnChunkComplete(T* pSender, CONNID dwConnID)
+	{
+		return	(m_fnOnChunkComplete)
+				? m_fnOnChunkComplete(C_HP_Object::FromFirst(pSender), dwConnID)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnMessageComplete(T* pSender, CONNID dwConnID)
+	{
+		ASSERT(m_fnOnMessageComplete);
+
+		return	(m_fnOnMessageComplete)
+				? m_fnOnMessageComplete(C_HP_Object::FromFirst(pSender), dwConnID)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnUpgrade(T* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
+	{
+		return	(m_fnOnUpgrade)
+				? m_fnOnUpgrade(C_HP_Object::FromFirst(pSender), dwConnID, enUpgradeType)
+				: HPR_OK;
+	}
+
+	virtual EnHttpParseResult OnParseError(T* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
+	{
+		ASSERT(m_fnOnParseError);
+
+		return	(m_fnOnParseError)
+				? m_fnOnParseError(C_HP_Object::FromFirst(pSender), dwConnID, iErrorCode, lpszErrorDesc)
+				: HPR_OK;
+	}
+
+	virtual EnHandleResult OnWSMessageHeader(T* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
+	{
+		return	(m_fnOnWSMessageHeader)
+				? m_fnOnWSMessageHeader(C_HP_Object::FromFirst(pSender), dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen)
+				: HR_OK;
+	}
+
+	virtual EnHandleResult OnWSMessageBody(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+	{
+		return	(m_fnOnWSMessageBody)
+				? m_fnOnWSMessageBody(C_HP_Object::FromFirst(pSender), dwConnID, pData, iLength)
+				: HR_OK;
+	}
+
+	virtual EnHandleResult OnWSMessageComplete(T* pSender, CONNID dwConnID)
+	{
+		return	(m_fnOnWSMessageComplete)
+				? m_fnOnWSMessageComplete(C_HP_Object::FromFirst(pSender), dwConnID)
+				: HR_OK;
+	}
+
+public:
+	C_HP_HttpListenerT()
+	: m_fnOnMessageBegin	(nullptr)
+	, m_fnOnRequestLine		(nullptr)
+	, m_fnOnStatusLine		(nullptr)
+	, m_fnOnHeader			(nullptr)
+	, m_fnOnHeadersComplete	(nullptr)
+	, m_fnOnBody			(nullptr)
+	, m_fnOnChunkHeader		(nullptr)
+	, m_fnOnChunkComplete	(nullptr)
+	, m_fnOnMessageComplete	(nullptr)
+	, m_fnOnUpgrade			(nullptr)
+	, m_fnOnParseError		(nullptr)
+	, m_fnOnWSMessageHeader	(nullptr)
+	, m_fnOnWSMessageBody	(nullptr)
+	, m_fnOnWSMessageComplete(nullptr)
+	{
+	}
+
+public:
+	HP_FN_Http_OnMessageBegin		m_fnOnMessageBegin		;
+	HP_FN_Http_OnRequestLine		m_fnOnRequestLine		;
+	HP_FN_Http_OnStatusLine			m_fnOnStatusLine		;
+	HP_FN_Http_OnHeader				m_fnOnHeader			;
+	HP_FN_Http_OnHeadersComplete	m_fnOnHeadersComplete	;
+	HP_FN_Http_OnBody				m_fnOnBody				;
+	HP_FN_Http_OnChunkHeader		m_fnOnChunkHeader		;
+	HP_FN_Http_OnChunkComplete		m_fnOnChunkComplete		;
+	HP_FN_Http_OnMessageComplete	m_fnOnMessageComplete	;
+	HP_FN_Http_OnUpgrade			m_fnOnUpgrade			;
+	HP_FN_Http_OnParseError			m_fnOnParseError		;
+	HP_FN_Http_OnWSMessageHeader	m_fnOnWSMessageHeader	;
+	HP_FN_Http_OnWSMessageBody		m_fnOnWSMessageBody		;
+	HP_FN_Http_OnWSMessageComplete	m_fnOnWSMessageComplete	;
+};
+
+typedef C_HP_HttpListenerT<IHttpServer>		C_HP_HttpServerBaseListener1;
+typedef C_HP_HttpListenerT<IHttpAgent>		C_HP_HttpAgentBaseListener1;
+typedef C_HP_HttpListenerT<IHttpClient>		C_HP_HttpClientBaseListener1;
+
+typedef C_HP_ServerListenerT<ITcpServer, ITcpServerListener, sizeof(IComplexHttpResponder)>		C_HP_HttpServerBaseListener2;
+typedef C_HP_AgentListenerT<ITcpAgent, ITcpAgentListener, sizeof(IComplexHttpRequester)>		C_HP_HttpAgentBaseListener2;
+typedef C_HP_ClientListenerT<ITcpClient, ITcpClientListener, sizeof(IHttpRequester)>			C_HP_HttpClientBaseListener2;
+
+class C_HP_HttpServerListener : public IHttpServerListener
+{
+public:
+	virtual EnHttpParseResult OnMessageBegin(IHttpServer* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnMessageBegin(pSender, dwConnID);}
+	virtual EnHttpParseResult OnRequestLine(IHttpServer* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl)
+		{return m_lsnHttp.OnRequestLine(pSender, dwConnID, lpszMethod, lpszUrl);}
+	virtual EnHttpParseResult OnStatusLine(IHttpServer* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
+		{return m_lsnHttp.OnStatusLine(pSender, dwConnID, usStatusCode, lpszDesc);}
+	virtual EnHttpParseResult OnHeader(IHttpServer* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
+		{return m_lsnHttp.OnHeader(pSender, dwConnID, lpszName, lpszValue);}
+	virtual EnHttpParseResult OnHeadersComplete(IHttpServer* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnHeadersComplete(pSender, dwConnID);}
+	virtual EnHttpParseResult OnBody(IHttpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnHttp.OnBody(pSender, dwConnID, pData, iLength);}
+	virtual EnHttpParseResult OnChunkHeader(IHttpServer* pSender, CONNID dwConnID, int iLength)
+		{return m_lsnHttp.OnChunkHeader(pSender, dwConnID, iLength);}
+	virtual EnHttpParseResult OnChunkComplete(IHttpServer* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnChunkComplete(pSender, dwConnID);}
+	virtual EnHttpParseResult OnMessageComplete(IHttpServer* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnMessageComplete(pSender, dwConnID);}
+	virtual EnHttpParseResult OnUpgrade(IHttpServer* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
+		{return m_lsnHttp.OnUpgrade(pSender, dwConnID, enUpgradeType);}
+	virtual EnHttpParseResult OnParseError(IHttpServer* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
+		{return m_lsnHttp.OnParseError(pSender, dwConnID, iErrorCode, lpszErrorDesc);}
+
+	virtual EnHandleResult OnWSMessageHeader(IHttpServer* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
+		{return m_lsnHttp.OnWSMessageHeader(pSender, dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
+	virtual EnHandleResult OnWSMessageBody(IHttpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnHttp.OnWSMessageBody(pSender, dwConnID, pData, iLength);}
+	virtual EnHandleResult OnWSMessageComplete(IHttpServer* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnWSMessageComplete(pSender, dwConnID);}
+
+	virtual EnHandleResult OnPrepareListen(ITcpServer* pSender, SOCKET soListen)
+		{return m_lsnServer.OnPrepareListen(pSender, soListen);}
+	virtual EnHandleResult OnAccept(ITcpServer* pSender, CONNID dwConnID, UINT_PTR soClient)
+		{return m_lsnServer.OnAccept(pSender, dwConnID, soClient);}
+	virtual EnHandleResult OnHandShake(ITcpServer* pSender, CONNID dwConnID)
+		{return m_lsnServer.OnHandShake(pSender, dwConnID);}
+	virtual EnHandleResult OnSend(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnServer.OnSend(pSender, dwConnID, pData, iLength);}
+	virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnServer.OnReceive(pSender, dwConnID, pData, iLength);}
+	virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, int iLength)
+		{return m_lsnServer.OnReceive(pSender, dwConnID, iLength);}
+	virtual EnHandleResult OnClose(ITcpServer* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
+		{return m_lsnServer.OnClose(pSender, dwConnID, enOperation, iErrorCode);}
+	virtual EnHandleResult OnShutdown(ITcpServer* pSender)
+		{return m_lsnServer.OnShutdown(pSender);}
+
+public:
+	C_HP_HttpServerBaseListener1 m_lsnHttp;
+	C_HP_HttpServerBaseListener2 m_lsnServer;
+};
+
+class C_HP_HttpAgentListener : public IHttpAgentListener
+{
+public:
+	virtual EnHttpParseResult OnMessageBegin(IHttpAgent* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnMessageBegin(pSender, dwConnID);}
+	virtual EnHttpParseResult OnRequestLine(IHttpAgent* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl)
+		{return m_lsnHttp.OnRequestLine(pSender, dwConnID, lpszMethod, lpszUrl);}
+	virtual EnHttpParseResult OnStatusLine(IHttpAgent* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
+		{return m_lsnHttp.OnStatusLine(pSender, dwConnID, usStatusCode, lpszDesc);}
+	virtual EnHttpParseResult OnHeader(IHttpAgent* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
+		{return m_lsnHttp.OnHeader(pSender, dwConnID, lpszName, lpszValue);}
+	virtual EnHttpParseResult OnHeadersComplete(IHttpAgent* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnHeadersComplete(pSender, dwConnID);}
+	virtual EnHttpParseResult OnBody(IHttpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnHttp.OnBody(pSender, dwConnID, pData, iLength);}
+	virtual EnHttpParseResult OnChunkHeader(IHttpAgent* pSender, CONNID dwConnID, int iLength)
+		{return m_lsnHttp.OnChunkHeader(pSender, dwConnID, iLength);}
+	virtual EnHttpParseResult OnChunkComplete(IHttpAgent* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnChunkComplete(pSender, dwConnID);}
+	virtual EnHttpParseResult OnMessageComplete(IHttpAgent* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnMessageComplete(pSender, dwConnID);}
+	virtual EnHttpParseResult OnUpgrade(IHttpAgent* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
+		{return m_lsnHttp.OnUpgrade(pSender, dwConnID, enUpgradeType);}
+	virtual EnHttpParseResult OnParseError(IHttpAgent* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
+		{return m_lsnHttp.OnParseError(pSender, dwConnID, iErrorCode, lpszErrorDesc);}
+
+	virtual EnHandleResult OnWSMessageHeader(IHttpAgent* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
+		{return m_lsnHttp.OnWSMessageHeader(pSender, dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
+	virtual EnHandleResult OnWSMessageBody(IHttpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnHttp.OnWSMessageBody(pSender, dwConnID, pData, iLength);}
+	virtual EnHandleResult OnWSMessageComplete(IHttpAgent* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnWSMessageComplete(pSender, dwConnID);}
+
+	virtual EnHandleResult OnPrepareConnect(ITcpAgent* pSender, CONNID dwConnID, SOCKET socket)
+		{return m_lsnAgent.OnPrepareConnect(pSender, dwConnID, socket);}
+	virtual EnHandleResult OnConnect(ITcpAgent* pSender, CONNID dwConnID)
+		{return m_lsnAgent.OnConnect(pSender, dwConnID);}
+	virtual EnHandleResult OnHandShake(ITcpAgent* pSender, CONNID dwConnID)
+		{return m_lsnAgent.OnHandShake(pSender, dwConnID);}
+	virtual EnHandleResult OnSend(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnAgent.OnSend(pSender, dwConnID, pData, iLength);}
+	virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnAgent.OnReceive(pSender, dwConnID, pData, iLength);}
+	virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, int iLength)
+		{return m_lsnAgent.OnReceive(pSender, dwConnID, iLength);}
+	virtual EnHandleResult OnClose(ITcpAgent* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
+		{return m_lsnAgent.OnClose(pSender, dwConnID, enOperation, iErrorCode);}
+	virtual EnHandleResult OnShutdown(ITcpAgent* pSender)
+		{return m_lsnAgent.OnShutdown(pSender);}
+
+public:
+	C_HP_HttpAgentBaseListener1 m_lsnHttp;
+	C_HP_HttpAgentBaseListener2 m_lsnAgent;
+};
+
+class C_HP_HttpClientListener : public IHttpClientListener
+{
+public:
+	virtual EnHttpParseResult OnMessageBegin(IHttpClient* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnMessageBegin(pSender, dwConnID);}
+	virtual EnHttpParseResult OnRequestLine(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl)
+		{return m_lsnHttp.OnRequestLine(pSender, dwConnID, lpszMethod, lpszUrl);}
+	virtual EnHttpParseResult OnStatusLine(IHttpClient* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
+		{return m_lsnHttp.OnStatusLine(pSender, dwConnID, usStatusCode, lpszDesc);}
+	virtual EnHttpParseResult OnHeader(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
+		{return m_lsnHttp.OnHeader(pSender, dwConnID, lpszName, lpszValue);}
+	virtual EnHttpParseResult OnHeadersComplete(IHttpClient* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnHeadersComplete(pSender, dwConnID);}
+	virtual EnHttpParseResult OnBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnHttp.OnBody(pSender, dwConnID, pData, iLength);}
+	virtual EnHttpParseResult OnChunkHeader(IHttpClient* pSender, CONNID dwConnID, int iLength)
+		{return m_lsnHttp.OnChunkHeader(pSender, dwConnID, iLength);}
+	virtual EnHttpParseResult OnChunkComplete(IHttpClient* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnChunkComplete(pSender, dwConnID);}
+	virtual EnHttpParseResult OnMessageComplete(IHttpClient* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnMessageComplete(pSender, dwConnID);}
+	virtual EnHttpParseResult OnUpgrade(IHttpClient* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
+		{return m_lsnHttp.OnUpgrade(pSender, dwConnID, enUpgradeType);}
+	virtual EnHttpParseResult OnParseError(IHttpClient* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
+		{return m_lsnHttp.OnParseError(pSender, dwConnID, iErrorCode, lpszErrorDesc);}
+
+	virtual EnHandleResult OnWSMessageHeader(IHttpClient* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
+		{return m_lsnHttp.OnWSMessageHeader(pSender, dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
+	virtual EnHandleResult OnWSMessageBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnHttp.OnWSMessageBody(pSender, dwConnID, pData, iLength);}
+	virtual EnHandleResult OnWSMessageComplete(IHttpClient* pSender, CONNID dwConnID)
+		{return m_lsnHttp.OnWSMessageComplete(pSender, dwConnID);}
+
+	virtual EnHandleResult OnPrepareConnect(ITcpClient* pSender, CONNID dwConnID, SOCKET socket)
+		{return m_lsnClient.OnPrepareConnect(pSender, dwConnID, socket);}
+	virtual EnHandleResult OnConnect(ITcpClient* pSender, CONNID dwConnID)
+		{return m_lsnClient.OnConnect(pSender, dwConnID);}
+	virtual EnHandleResult OnHandShake(ITcpClient* pSender, CONNID dwConnID)
+		{return m_lsnClient.OnHandShake(pSender, dwConnID);}
+	virtual EnHandleResult OnSend(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnClient.OnSend(pSender, dwConnID, pData, iLength);}
+	virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
+		{return m_lsnClient.OnReceive(pSender, dwConnID, pData, iLength);}
+	virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, int iLength)
+		{return m_lsnClient.OnReceive(pSender, dwConnID, iLength);}
+	virtual EnHandleResult OnClose(ITcpClient* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
+		{return m_lsnClient.OnClose(pSender, dwConnID, enOperation, iErrorCode);}
+
+public:
+	C_HP_HttpClientBaseListener1 m_lsnHttp;
+	C_HP_HttpClientBaseListener2 m_lsnClient;
+};
+
+#endif
+
+class C_HP_ThreadPoolListener : public IHPThreadPoolListener
+{
+public:
+	virtual void OnStartup(IHPThreadPool* pThreadPool)
+	{
+		if(m_fnOnStartup)
+			m_fnOnStartup((HP_ThreadPool)pThreadPool);
+	}
+
+	virtual void OnShutdown(IHPThreadPool* pThreadPool)
+	{
+		if(m_fnOnShutdown)
+			m_fnOnShutdown((HP_ThreadPool)pThreadPool);
+	}
+
+	virtual void OnWorkerThreadStart(IHPThreadPool* pThreadPool, THR_ID dwThreadID)
+	{
+		if(m_fnOnWorkerThreadStart)
+			m_fnOnWorkerThreadStart((HP_ThreadPool)pThreadPool, dwThreadID);
+	}
+	virtual void OnWorkerThreadEnd(IHPThreadPool* pThreadPool, THR_ID dwThreadID)
+	{
+		if(m_fnOnWorkerThreadEnd)
+			m_fnOnWorkerThreadEnd((HP_ThreadPool)pThreadPool, dwThreadID);
+	}
+
+public:
+	C_HP_ThreadPoolListener()
+	: m_fnOnStartup				(nullptr)
+	, m_fnOnShutdown			(nullptr)
+	, m_fnOnWorkerThreadStart	(nullptr)
+	, m_fnOnWorkerThreadEnd		(nullptr)
+	{
+	}
+
+public:
+	HP_FN_ThreadPool_OnStartup				m_fnOnStartup;
+	HP_FN_ThreadPool_OnShutdown				m_fnOnShutdown;
+	HP_FN_ThreadPool_OnWorkerThreadStart	m_fnOnWorkerThreadStart;
+	HP_FN_ThreadPool_OnWorkerThreadEnd		m_fnOnWorkerThreadEnd;
+};

+ 1306 - 0
TcpAgent.cpp

@@ -0,0 +1,1306 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "TcpAgent.h"
+
+#include "./common/FileHelper.h"
+
+BOOL CTcpAgent::Start(LPCTSTR lpszBindAddress, BOOL bAsyncConnect)
+{
+	if(!CheckParams() || !CheckStarting())
+		return FALSE;
+
+	PrepareStart();
+
+	if(ParseBindAddress(lpszBindAddress))
+		if(CreateWorkerThreads())
+		{
+			m_bAsyncConnect	= bAsyncConnect;
+			m_enState		= SS_STARTED;
+
+			return TRUE;
+		}
+
+	EXECUTE_RESTORE_ERROR(Stop());
+
+	return FALSE;
+}
+
+void CTcpAgent::SetLastError(EnSocketError code, LPCSTR func, int ec)
+{
+	m_enLastError = code;
+	::SetLastError(ec);
+}
+
+BOOL CTcpAgent::CheckParams()
+{
+	if	((m_enSendPolicy >= SP_PACK && m_enSendPolicy <= SP_DIRECT)								&&
+		(m_enOnSendSyncPolicy >= OSSP_NONE && m_enOnSendSyncPolicy <= OSSP_RECEIVE)				&&
+		((int)m_dwSyncConnectTimeout > 0)														&&
+		((int)m_dwMaxConnectionCount > 0 && m_dwMaxConnectionCount <= MAX_CONNECTION_COUNT)		&&
+		((int)m_dwWorkerThreadCount > 0 && m_dwWorkerThreadCount <= MAX_WORKER_THREAD_COUNT)	&&
+		((int)m_dwSocketBufferSize >= MIN_SOCKET_BUFFER_SIZE)									&&
+		((int)m_dwFreeSocketObjLockTime >= 1000)												&&
+		((int)m_dwFreeSocketObjPool >= 0)														&&
+		((int)m_dwFreeBufferObjPool >= 0)														&&
+		((int)m_dwFreeSocketObjHold >= 0)														&&
+		((int)m_dwFreeBufferObjHold >= 0)														&&
+		((int)m_dwKeepAliveTime >= 1000 || m_dwKeepAliveTime == 0)								&&
+		((int)m_dwKeepAliveInterval >= 1000 || m_dwKeepAliveInterval == 0)						)
+		return TRUE;
+
+	SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+	return FALSE;
+}
+
+void CTcpAgent::PrepareStart()
+{
+	m_bfActiveSockets.Reset(m_dwMaxConnectionCount);
+	m_lsFreeSocket.Reset(m_dwFreeSocketObjPool);
+
+	m_bfObjPool.SetItemCapacity(m_dwSocketBufferSize);
+	m_bfObjPool.SetPoolSize(m_dwFreeBufferObjPool);
+	m_bfObjPool.SetPoolHold(m_dwFreeBufferObjHold);
+
+	m_bfObjPool.Prepare();
+
+	m_rcBuffers = make_unique<CBufferPtr[]>(m_dwWorkerThreadCount);
+	for_each(m_rcBuffers.get(), m_rcBuffers.get() + m_dwWorkerThreadCount, [this](CBufferPtr& buff) {buff.Malloc(m_dwSocketBufferSize);});
+}
+
+BOOL CTcpAgent::CheckStarting()
+{
+	CSpinLock locallock(m_csState);
+
+	if(m_enState == SS_STOPPED)
+		m_enState = SS_STARTING;
+	else
+	{
+		SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::CheckStoping()
+{
+	if(m_enState != SS_STOPPED)
+	{
+		CSpinLock locallock(m_csState);
+
+		if(HasStarted())
+		{
+			m_enState = SS_STOPPING;
+			return TRUE;
+		}
+	}
+
+	SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+
+	return FALSE;
+}
+
+BOOL CTcpAgent::ParseBindAddress(LPCTSTR lpszBindAddress)
+{
+	if(::IsStrEmpty(lpszBindAddress))
+		return TRUE;
+
+	HP_SOCKADDR addr;
+
+	if(::sockaddr_A_2_IN(lpszBindAddress, 0, addr))
+	{
+		SOCKET sock	= socket(addr.family, SOCK_STREAM, IPPROTO_TCP);
+
+		if(sock != INVALID_SOCKET)
+		{
+			if(::bind(sock, addr.Addr(), addr.AddrSize()) != SOCKET_ERROR)
+			{
+				addr.Copy(m_soAddr);
+				return TRUE;
+			}
+			else
+				SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
+
+			::ManualCloseSocket(sock);
+		}
+		else
+			SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+	}
+	else
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+
+	return FALSE;
+}
+
+BOOL CTcpAgent::CreateWorkerThreads()
+{
+	DWORD dwWorkerThreadCount = m_dwWorkerThreadCount
+#ifdef USE_EXTERNAL_GC
+														+ 1
+#endif
+														;
+
+	if(!m_ioDispatcher.Start(this, DEFAULT_WORKER_MAX_EVENT_COUNT, dwWorkerThreadCount))
+	{
+		SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+
+#ifdef USE_EXTERNAL_GC
+	m_fdGCTimer = m_ioDispatcher.AddTimer(m_dwWorkerThreadCount, GC_CHECK_INTERVAL, this);
+
+	if(IS_INVALID_FD(m_fdGCTimer))
+	{
+		SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+#endif
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::Stop()
+{
+	if(!CheckStoping())
+		return FALSE;
+	
+	DisconnectClientSocket();
+	WaitForClientSocketClose();
+	WaitForWorkerThreadEnd();
+	
+	ReleaseClientSocket();
+
+	FireShutdown();
+
+	ReleaseFreeSocket();
+
+	Reset();
+
+	return TRUE;
+}
+
+void CTcpAgent::DisconnectClientSocket()
+{
+	::WaitFor(100);
+
+	if(m_bfActiveSockets.Elements() == 0)
+		return;
+
+	TAgentSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+		Disconnect(*it);
+}
+
+void CTcpAgent::WaitForClientSocketClose()
+{
+	while(m_bfActiveSockets.Elements() > 0)
+		::WaitFor(50);
+}
+
+void CTcpAgent::WaitForWorkerThreadEnd()
+{
+	m_ioDispatcher.Stop();
+}
+
+void CTcpAgent::ReleaseClientSocket()
+{
+	VERIFY(m_bfActiveSockets.IsEmpty());
+	m_bfActiveSockets.Reset();
+}
+
+void CTcpAgent::ReleaseFreeSocket()
+{
+	m_lsFreeSocket.Clear();
+
+#ifdef USE_EXTERNAL_GC
+	if(IS_VALID_FD(m_fdGCTimer))
+	{
+		close(m_fdGCTimer);
+		m_fdGCTimer = INVALID_FD;
+	}
+#endif
+
+	ReleaseGCSocketObj(TRUE);
+	VERIFY(m_lsGCSocket.IsEmpty());
+}
+
+void CTcpAgent::Reset()
+{
+	m_bfObjPool.Clear();
+	m_phSocket.Reset();
+	m_soAddr.Reset();
+
+	m_rcBuffers = nullptr;
+
+	m_enState = SS_STOPPED;
+
+	m_evWait.SyncNotifyAll();
+}
+
+BOOL CTcpAgent::Connect(LPCTSTR lpszRemoteAddress, USHORT usPort, CONNID* pdwConnID, PVOID pExtra, USHORT usLocalPort, LPCTSTR lpszLocalAddress)
+{
+	ASSERT(lpszRemoteAddress && usPort != 0);
+
+	if(!HasStarted())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(!pdwConnID)
+		pdwConnID = CreateLocalObject(CONNID);
+
+	*pdwConnID = 0;
+
+	HP_SOCKADDR addr;
+	HP_SCOPE_HOST host(lpszRemoteAddress);
+	SOCKET soClient = INVALID_SOCKET;
+
+	DWORD result = CreateClientSocket(host.addr, usPort, lpszLocalAddress, usLocalPort, soClient, addr);
+
+	if(result == NO_ERROR)
+	{
+		result = PrepareConnect(*pdwConnID, soClient);
+
+		if(result == NO_ERROR)
+			result = ConnectToServer(*pdwConnID, host.name, soClient, addr, pExtra);
+	}
+
+	if(result != NO_ERROR)
+	{
+		if(soClient != INVALID_SOCKET)
+			::ManualCloseSocket(soClient);
+
+		::SetLastError(result);
+	}
+
+	return (result == NO_ERROR);
+}
+
+int CTcpAgent::CreateClientSocket(LPCTSTR lpszRemoteAddress, USHORT usPort, LPCTSTR lpszLocalAddress, USHORT usLocalPort, SOCKET& soClient, HP_SOCKADDR& addr)
+{
+	if(!::GetSockAddrByHostName(lpszRemoteAddress, usPort, addr))
+		return ERROR_ADDRNOTAVAIL;
+
+	HP_SOCKADDR* lpBindAddr = &m_soAddr;
+
+	if(::IsStrNotEmpty(lpszLocalAddress))
+	{
+		lpBindAddr = CreateLocalObject(HP_SOCKADDR);
+
+		if(!::sockaddr_A_2_IN(lpszLocalAddress, 0, *lpBindAddr))
+			return ::WSAGetLastError();
+	}
+
+	BOOL bBind = lpBindAddr->IsSpecified();
+
+	if(bBind && lpBindAddr->family != addr.family)
+		return ERROR_AFNOSUPPORT;
+
+	int result	= NO_ERROR;
+	soClient	= socket(addr.family, SOCK_STREAM, IPPROTO_TCP);
+
+	if(soClient == INVALID_SOCKET)
+		result = ::WSAGetLastError();
+	else
+	{
+		BOOL bOnOff	= (m_dwKeepAliveTime > 0 && m_dwKeepAliveInterval > 0);
+		VERIFY(IS_NO_ERROR(::SSO_KeepAliveVals(soClient, bOnOff, m_dwKeepAliveTime, m_dwKeepAliveInterval)));
+		VERIFY(IS_NO_ERROR(::SSO_ReuseAddress(soClient, m_enReusePolicy)));
+		VERIFY(IS_NO_ERROR(::SSO_NoDelay(soClient, m_bNoDelay)));
+
+		if(bBind && usLocalPort == 0)
+		{
+			if(::bind(soClient, lpBindAddr->Addr(), lpBindAddr->AddrSize()) == SOCKET_ERROR)
+				result = ::WSAGetLastError();
+		}
+		else if(usLocalPort != 0)
+		{
+			HP_SOCKADDR bindAddr = bBind ? *lpBindAddr : HP_SOCKADDR::AnyAddr(addr.family);
+
+			bindAddr.SetPort(usLocalPort);
+
+			if(::bind(soClient, bindAddr.Addr(), bindAddr.AddrSize()) == SOCKET_ERROR)
+				result = ::WSAGetLastError();
+		}
+	}
+
+	return result;
+}
+
+int CTcpAgent::PrepareConnect(CONNID& dwConnID, SOCKET soClient)
+{
+	if(!m_bfActiveSockets.AcquireLock(dwConnID))
+		return ERROR_CONNECTION_COUNT_LIMIT;
+
+	if(TRIGGER(FirePrepareConnect(dwConnID, soClient)) == HR_ERROR)
+	{
+		VERIFY(m_bfActiveSockets.ReleaseLock(dwConnID, nullptr));
+		return ENSURE_ERROR_CANCELLED;
+	}
+
+	return NO_ERROR;
+}
+
+int CTcpAgent::ConnectToServer(CONNID dwConnID, LPCTSTR lpszRemoteHostName, SOCKET& soClient, const HP_SOCKADDR& addr, PVOID pExtra)
+{
+	TAgentSocketObj* pSocketObj = GetFreeSocketObj(dwConnID, soClient);
+	AddClientSocketObj(dwConnID, pSocketObj, addr, lpszRemoteHostName, pExtra);
+
+	int result = HAS_ERROR;
+
+	VERIFY(::fcntl_SETFL(pSocketObj->socket, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
+
+	int rc = ::connect(pSocketObj->socket, addr.Addr(), addr.AddrSize());
+
+	if(IS_NO_ERROR(rc) || IS_IO_PENDING_ERROR())
+	{
+		if(m_bAsyncConnect)
+		{
+			if(m_ioDispatcher.AddFD(pSocketObj->socket, EPOLLOUT, pSocketObj))
+				result = NO_ERROR;
+		}
+		else
+		{
+			if(IS_HAS_ERROR(result))
+				result = ::WaitForSocketWrite(pSocketObj->socket, m_dwSyncConnectTimeout);
+
+			if(IS_NO_ERROR(result))
+			{
+				pSocketObj->SetConnected();
+
+				if(TRIGGER(FireConnect(pSocketObj)) == HR_ERROR)
+					result = ENSURE_ERROR_CANCELLED;
+				else
+				{
+					UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN);
+
+					if(!m_ioDispatcher.AddFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj))
+						result = HAS_ERROR;
+				}
+			}
+		}
+	}
+
+	if(result == HAS_ERROR)
+		result = ::WSAGetLastError();
+	if(result != NO_ERROR)
+	{
+		AddFreeSocketObj(pSocketObj, SCF_NONE);
+		soClient = INVALID_SOCKET;
+	}
+
+	return result;
+}
+
+TAgentSocketObj* CTcpAgent::GetFreeSocketObj(CONNID dwConnID, SOCKET soClient)
+{
+	DWORD dwIndex;
+	TAgentSocketObj* pSocketObj = nullptr;
+
+	if(m_lsFreeSocket.TryLock(&pSocketObj, dwIndex))
+	{
+		if(::GetTimeGap32(pSocketObj->freeTime) >= m_dwFreeSocketObjLockTime)
+			VERIFY(m_lsFreeSocket.ReleaseLock(nullptr, dwIndex));
+		else
+		{
+			VERIFY(m_lsFreeSocket.ReleaseLock(pSocketObj, dwIndex));
+			pSocketObj = nullptr;
+		}
+	}
+
+	if(!pSocketObj) pSocketObj = CreateSocketObj();
+	pSocketObj->Reset(dwConnID, soClient);
+
+	return pSocketObj;
+}
+
+TAgentSocketObj* CTcpAgent::CreateSocketObj()
+{
+	return TAgentSocketObj::Construct(m_phSocket, m_bfObjPool);
+}
+
+void CTcpAgent::DeleteSocketObj(TAgentSocketObj* pSocketObj)
+{
+	TAgentSocketObj::Destruct(pSocketObj);
+}
+
+void CTcpAgent::AddFreeSocketObj(TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode)
+{
+	if(!InvalidSocketObj(pSocketObj))
+		return;
+
+	CloseClientSocketObj(pSocketObj, enFlag, enOperation, iErrorCode);
+
+	m_bfActiveSockets.Remove(pSocketObj->connID);
+	TAgentSocketObj::Release(pSocketObj);
+
+#ifndef USE_EXTERNAL_GC
+	ReleaseGCSocketObj();
+#endif
+
+	if(!m_lsFreeSocket.TryPut(pSocketObj))
+		m_lsGCSocket.PushBack(pSocketObj);
+}
+
+void CTcpAgent::ReleaseGCSocketObj(BOOL bForce)
+{
+	::ReleaseGCObj(m_lsGCSocket, m_dwFreeSocketObjLockTime, bForce);
+}
+
+BOOL CTcpAgent::InvalidSocketObj(TAgentSocketObj* pSocketObj)
+{
+	return TAgentSocketObj::InvalidSocketObj(pSocketObj);
+}
+
+void CTcpAgent::AddClientSocketObj(CONNID dwConnID, TAgentSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr, LPCTSTR lpszRemoteHostName, PVOID pExtra)
+{
+	ASSERT(FindSocketObj(dwConnID) == nullptr);
+
+	pSocketObj->connTime	= ::TimeGetTime();
+	pSocketObj->activeTime	= pSocketObj->connTime;
+	pSocketObj->host		= lpszRemoteHostName;
+	pSocketObj->extra		= pExtra;
+
+	pSocketObj->SetConnected(CST_CONNECTING);
+	remoteAddr.Copy(pSocketObj->remoteAddr);
+
+	VERIFY(m_bfActiveSockets.ReleaseLock(dwConnID, pSocketObj));
+}
+
+TAgentSocketObj* CTcpAgent::FindSocketObj(CONNID dwConnID)
+{
+	TAgentSocketObj* pSocketObj = nullptr;
+
+	if(m_bfActiveSockets.Get(dwConnID, &pSocketObj) != TAgentSocketObjPtrPool::GR_VALID)
+		pSocketObj = nullptr;
+
+	return pSocketObj;
+}
+
+void CTcpAgent::CloseClientSocketObj(TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode, int iShutdownFlag)
+{
+	ASSERT(TAgentSocketObj::IsExist(pSocketObj));
+
+	if(enFlag == SCF_CLOSE)
+		FireClose(pSocketObj, SO_CLOSE, SE_OK);
+	else if(enFlag == SCF_ERROR)
+		FireClose(pSocketObj, enOperation, iErrorCode);
+
+	SOCKET socket = pSocketObj->socket;
+	pSocketObj->socket = INVALID_SOCKET;
+
+	::ManualCloseSocket(socket, iShutdownFlag);
+}
+
+BOOL CTcpAgent::GetLocalAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return ::GetSocketLocalAddress(pSocketObj->socket, lpszAddress, iAddressLen, usPort);
+}
+
+BOOL CTcpAgent::GetRemoteAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsExist(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	ADDRESS_FAMILY usFamily;
+	return ::sockaddr_IN_2_A(pSocketObj->remoteAddr, usFamily, lpszAddress, iAddressLen, usPort);
+}
+
+BOOL CTcpAgent::GetRemoteHost(CONNID dwConnID, TCHAR lpszHost[], int& iHostLen, USHORT& usPort)
+{
+	ASSERT(lpszHost != nullptr && iHostLen > 0);
+
+	BOOL isOK					= FALSE;
+	TAgentSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		int iLen = pSocketObj->host.GetLength() + 1;
+
+		if(iHostLen >= iLen)
+		{
+			memcpy(lpszHost, CA2CT((LPCSTR)pSocketObj->host), iLen * sizeof(TCHAR));
+			usPort = pSocketObj->remoteAddr.Port();
+
+			isOK = TRUE;
+		}
+
+		iHostLen = iLen;
+	}
+
+	return isOK;
+}
+
+BOOL CTcpAgent::GetRemoteHost(CONNID dwConnID, LPCSTR* lpszHost, USHORT* pusPort)
+{
+	*lpszHost					= nullptr;
+	TAgentSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*lpszHost = pSocketObj->host;
+
+		if(pusPort)
+			*pusPort = pSocketObj->remoteAddr.Port();
+	}
+
+	return (*lpszHost != nullptr && (*lpszHost)[0] != 0);
+}
+
+BOOL CTcpAgent::SetConnectionExtra(CONNID dwConnID, PVOID pExtra)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return SetConnectionExtra(pSocketObj, pExtra);
+}
+
+BOOL CTcpAgent::SetConnectionExtra(TAgentSocketObj* pSocketObj, PVOID pExtra)
+{
+	if(!TAgentSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		pSocketObj->extra = pExtra;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpAgent::GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return GetConnectionExtra(pSocketObj, ppExtra);
+}
+
+BOOL CTcpAgent::GetConnectionExtra(TAgentSocketObj* pSocketObj, PVOID* ppExtra)
+{
+	ASSERT(ppExtra != nullptr);
+
+	if(!TAgentSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*ppExtra = pSocketObj->extra;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpAgent::SetConnectionReserved(CONNID dwConnID, PVOID pReserved)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return SetConnectionReserved(pSocketObj, pReserved);
+}
+
+BOOL CTcpAgent::SetConnectionReserved(TAgentSocketObj* pSocketObj, PVOID pReserved)
+{
+	if(!TAgentSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		pSocketObj->reserved = pReserved;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpAgent::GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return GetConnectionReserved(pSocketObj, ppReserved);
+}
+
+BOOL CTcpAgent::GetConnectionReserved(TAgentSocketObj* pSocketObj, PVOID* ppReserved)
+{
+	ASSERT(ppReserved != nullptr);
+
+	if(!TAgentSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*ppReserved = pSocketObj->reserved;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpAgent::SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return SetConnectionReserved2(pSocketObj, pReserved2);
+}
+
+BOOL CTcpAgent::SetConnectionReserved2(TAgentSocketObj* pSocketObj, PVOID pReserved2)
+{
+	if(!TAgentSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		pSocketObj->reserved2 = pReserved2;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpAgent::GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return GetConnectionReserved2(pSocketObj, ppReserved2);
+}
+
+BOOL CTcpAgent::GetConnectionReserved2(TAgentSocketObj* pSocketObj, PVOID* ppReserved2)
+{
+	ASSERT(ppReserved2 != nullptr);
+
+	if(!TAgentSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*ppReserved2 = pSocketObj->reserved2;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpAgent::IsPauseReceive(CONNID dwConnID, BOOL& bPaused)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		bPaused = pSocketObj->paused;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpAgent::IsConnected(CONNID dwConnID)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return pSocketObj->HasConnected();
+}
+
+BOOL CTcpAgent::GetPendingDataLength(CONNID dwConnID, int& iPending)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		iPending = pSocketObj->Pending();
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+DWORD CTcpAgent::GetConnectionCount()
+{
+	return m_bfActiveSockets.Elements();
+}
+
+BOOL CTcpAgent::GetAllConnectionIDs(CONNID pIDs[], DWORD& dwCount)
+{
+	return m_bfActiveSockets.GetAllElementIndexes(pIDs, dwCount);
+}
+
+BOOL CTcpAgent::GetConnectPeriod(CONNID dwConnID, DWORD& dwPeriod)
+{
+	BOOL isOK					= TRUE;
+	TAgentSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(TAgentSocketObj::IsValid(pSocketObj))
+		dwPeriod = ::GetTimeGap32(pSocketObj->connTime);
+	else
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+BOOL CTcpAgent::GetSilencePeriod(CONNID dwConnID, DWORD& dwPeriod)
+{
+	if(!m_bMarkSilence)
+		return FALSE;
+
+	BOOL isOK					= TRUE;
+	TAgentSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(TAgentSocketObj::IsValid(pSocketObj))
+		dwPeriod = ::GetTimeGap32(pSocketObj->activeTime);
+	else
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+BOOL CTcpAgent::Disconnect(CONNID dwConnID, BOOL bForce)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_DISCONNECT, dwConnID, bForce);
+}
+
+BOOL CTcpAgent::DisconnectLongConnections(DWORD dwPeriod, BOOL bForce)
+{
+	if(dwPeriod > MAX_CONNECTION_PERIOD)
+		return FALSE;
+	if(m_bfActiveSockets.Elements() == 0)
+		return TRUE;
+
+	DWORD now = ::TimeGetTime();
+
+	TAgentSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+	{
+		CONNID connID				= *it;
+		TAgentSocketObj* pSocketObj	= FindSocketObj(connID);
+
+		if(TAgentSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->connTime) >= (int)dwPeriod)
+			Disconnect(connID, bForce);
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce)
+{
+	if(!m_bMarkSilence)
+		return FALSE;
+	if(dwPeriod > MAX_CONNECTION_PERIOD)
+		return FALSE;
+	if(m_bfActiveSockets.Elements() == 0)
+		return TRUE;
+
+	DWORD now = ::TimeGetTime();
+
+	TAgentSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+	{
+		CONNID connID				= *it;
+		TAgentSocketObj* pSocketObj	= FindSocketObj(connID);
+
+		if(TAgentSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->activeTime) >= (int)dwPeriod)
+			Disconnect(connID, bForce);
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::PauseReceive(CONNID dwConnID, BOOL bPause)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(pSocketObj->paused == bPause)
+		return TRUE;
+
+	pSocketObj->paused = bPause;
+
+	if(!bPause)
+		return m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_UNPAUSE, pSocketObj->connID);
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	if(pv == this)
+	{
+		ReleaseGCSocketObj(FALSE);
+		::ReadTimer(m_fdGCTimer);
+
+		return FALSE;
+	}
+
+	TAgentSocketObj* pSocketObj = (TAgentSocketObj*)(pv);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+		return FALSE;
+
+	if(events & _EPOLL_ALL_ERROR_EVENTS)
+		pSocketObj->SetConnected(FALSE);
+
+	pSocketObj->Increment();
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		pSocketObj->Decrement();
+		return FALSE;
+	}
+
+	if(pSocketObj->IsConnecting())
+	{
+		HandleConnect(pContext, pSocketObj, events);
+
+		pSocketObj->Decrement();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+VOID CTcpAgent::OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)
+{
+	TAgentSocketObj* pSocketObj = (TAgentSocketObj*)(pv);
+
+	if(TAgentSocketObj::IsValid(pSocketObj))
+	{
+		ASSERT(rs && !(events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)));
+
+		UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN);
+		m_ioDispatcher.ModFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj);
+	}
+
+	pSocketObj->Decrement();
+}
+
+VOID CTcpAgent::OnCommand(const TDispContext* pContext, TDispCommand* pCmd)
+{
+	switch(pCmd->type)
+	{
+	case DISP_CMD_SEND:
+		HandleCmdSend(pContext, (CONNID)(pCmd->wParam));
+		break;
+	case DISP_CMD_UNPAUSE:
+		HandleCmdUnpause(pContext, (CONNID)(pCmd->wParam));
+		break;
+	case DISP_CMD_DISCONNECT:
+		HandleCmdDisconnect(pContext, (CONNID)(pCmd->wParam), (BOOL)pCmd->lParam);
+		break;
+	}
+}
+
+VOID CTcpAgent::HandleCmdSend(const TDispContext* pContext, CONNID dwConnID)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(TAgentSocketObj::IsValid(pSocketObj) && pSocketObj->IsPending())
+		m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLOUT);
+}
+
+VOID CTcpAgent::HandleCmdUnpause(const TDispContext* pContext, CONNID dwConnID)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+		return;
+
+	if(BeforeUnpause(pSocketObj))
+		m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLIN);
+	else
+		AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
+}
+
+VOID CTcpAgent::HandleCmdDisconnect(const TDispContext* pContext, CONNID dwConnID, BOOL bForce)
+{
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(TAgentSocketObj::IsValid(pSocketObj))
+		m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLHUP);
+}
+
+BOOL CTcpAgent::OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleReceive(pContext, (TAgentSocketObj*)pv, RETRIVE_EVENT_FLAG_H(events));
+}
+
+BOOL CTcpAgent::OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleSend(pContext, (TAgentSocketObj*)pv, RETRIVE_EVENT_FLAG_H(events));
+}
+
+BOOL CTcpAgent::OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleClose(pContext, (TAgentSocketObj*)pv, SCF_CLOSE, events);
+}
+
+BOOL CTcpAgent::OnError(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleClose(pContext, (TAgentSocketObj*)pv, SCF_ERROR, events);
+}
+
+VOID CTcpAgent::OnDispatchThreadStart(THR_ID tid)
+{
+	OnWorkerThreadStart(tid);
+}
+
+VOID CTcpAgent::OnDispatchThreadEnd(THR_ID tid)
+{
+	OnWorkerThreadEnd(tid);
+}
+
+BOOL CTcpAgent::HandleClose(const TDispContext* pContext, TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag, UINT events)
+{
+	EnSocketOperation enOperation = SO_CLOSE;
+
+	if(events & _EPOLL_HUNGUP_EVENTS)
+		enOperation = SO_CLOSE;
+	else if(events & EPOLLIN)
+		enOperation = SO_RECEIVE;
+	else if(events & EPOLLOUT)
+		enOperation = SO_SEND;
+
+	int iErrorCode = 0;
+
+	if(enFlag == SCF_ERROR)
+		iErrorCode = ::SSO_GetError(pSocketObj->socket);
+
+	AddFreeSocketObj(pSocketObj, enFlag, enOperation, iErrorCode);
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::HandleConnect(const TDispContext* pContext, TAgentSocketObj* pSocketObj, UINT events)
+{
+	int code = ::SSO_GetError(pSocketObj->socket);
+
+	if(!IS_NO_ERROR(code) || (events & _EPOLL_ERROR_EVENTS))
+	{
+		AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_CONNECT, code);
+		return FALSE;
+	}
+
+	if((events & (_EPOLL_HUNGUP_EVENTS | _EPOLL_READ_EVENTS)) || !(events & EPOLLOUT))
+	{
+		AddFreeSocketObj(pSocketObj, SCF_CLOSE, SO_CONNECT, SE_OK);
+		return FALSE;
+	}
+
+	pSocketObj->SetConnected();
+
+	if(TRIGGER(FireConnect(pSocketObj)) == HR_ERROR)
+	{
+		AddFreeSocketObj(pSocketObj, SCF_NONE);
+		return FALSE;
+	}
+
+	UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN);
+	
+	if(!m_ioDispatcher.ModFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj))
+	{
+		AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_CONNECT, ::WSAGetLastError());
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::HandleReceive(const TDispContext* pContext, TAgentSocketObj* pSocketObj, int flag)
+{
+	ASSERT(TAgentSocketObj::IsValid(pSocketObj));
+
+	if(m_bMarkSilence) pSocketObj->activeTime = ::TimeGetTime();
+
+	CBufferPtr& buffer = m_rcBuffers[pContext->GetIndex()];
+
+	int reads = flag ? -1 : MAX_CONTINUE_READS;
+
+	for(int i = 0; i < reads || reads < 0; i++)
+	{
+		if(pSocketObj->paused)
+			break;
+
+		int rc = (int)read(pSocketObj->socket, buffer.Ptr(), buffer.Size());
+
+		if(rc > 0)
+		{
+			if(TRIGGER(FireReceive(pSocketObj, buffer.Ptr(), rc)) == HR_ERROR)
+			{
+				TRACE("<C-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", pSocketObj->connID);
+
+				AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
+				return FALSE;
+			}
+		}
+		else if(rc == 0)
+		{
+			AddFreeSocketObj(pSocketObj, SCF_CLOSE, SO_RECEIVE, SE_OK);
+			return FALSE;
+		}
+		else
+		{
+			ASSERT(rc == SOCKET_ERROR);
+
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+				break;
+
+			AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, code);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::HandleSend(const TDispContext* pContext, TAgentSocketObj* pSocketObj, int flag)
+{
+	ASSERT(TAgentSocketObj::IsValid(pSocketObj));
+
+	if(!pSocketObj->IsPending())
+		return TRUE;
+
+	BOOL bBlocked	= FALSE;
+	int writes		= flag ? -1 : MAX_CONTINUE_WRITES;
+
+	TBufferObjList& sndBuff = pSocketObj->sndBuff;
+	TItemPtr itPtr(sndBuff);
+
+	for(int i = 0; i < writes || writes < 0; i++)
+	{
+		{
+			CReentrantCriSecLock locallock(pSocketObj->csSend);
+			itPtr = sndBuff.PopFront();
+		}
+
+		if(!itPtr.IsValid())
+			break;
+
+		ASSERT(!itPtr->IsEmpty());
+
+		if(!SendItem(pSocketObj, itPtr, bBlocked))
+			return FALSE;
+
+		if(bBlocked)
+		{
+			ASSERT(!itPtr->IsEmpty());
+
+			CReentrantCriSecLock locallock(pSocketObj->csSend);
+			sndBuff.PushFront(itPtr.Detach());
+
+			break;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::SendItem(TAgentSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked)
+{
+	while(!pItem->IsEmpty())
+	{
+		int rc = (int)write(pSocketObj->socket, pItem->Ptr(), pItem->Size());
+
+		if(rc > 0)
+		{
+			if(TRIGGER(FireSend(pSocketObj, pItem->Ptr(), rc)) == HR_ERROR)
+			{
+				TRACE("<C-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", pSocketObj->connID);
+				ASSERT(FALSE);
+			}
+
+			pItem->Reduce(rc);
+		}
+		else if(rc == SOCKET_ERROR)
+		{
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+			{
+				bBlocked = TRUE;
+				break;
+			}
+			else
+			{
+				AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_SEND, code);
+				return FALSE;
+			}
+		}
+		else
+			ASSERT(FALSE);
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpAgent::Send(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	ASSERT(pBuffer && iLength > 0);
+
+	if(iOffset != 0) pBuffer += iOffset;
+
+	WSABUF buffer;
+	buffer.len = iLength;
+	buffer.buf = (BYTE*)pBuffer;
+
+	return SendPackets(dwConnID, &buffer, 1);
+}
+
+BOOL CTcpAgent::DoSendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TAgentSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return DoSendPackets(pSocketObj, pBuffers, iCount);
+}
+
+BOOL CTcpAgent::DoSendPackets(TAgentSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pSocketObj && pBuffers && iCount > 0);
+
+	int result = NO_ERROR;
+
+	if(!pSocketObj->HasConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(pBuffers && iCount > 0)
+	{
+		CLocalSafeCounter localcounter(*pSocketObj);
+		CReentrantCriSecLock locallock(pSocketObj->csSend);
+
+		if(TAgentSocketObj::IsValid(pSocketObj))
+			result = SendInternal(pSocketObj, pBuffers, iCount);
+		else
+			result = ERROR_OBJECT_NOT_FOUND;
+	}
+	else
+		result = ERROR_INVALID_PARAMETER;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+int CTcpAgent::SendInternal(TAgentSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount)
+{
+	BOOL bPending = pSocketObj->IsPending();
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			pSocketObj->sndBuff.Cat(pBuffer, iBufLen);
+			ASSERT(pSocketObj->sndBuff.Length() > 0);
+		}
+	}
+
+	if(!bPending && pSocketObj->IsPending())
+	{
+		if(!m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_SEND, pSocketObj->connID))
+			return ::GetLastError();
+	}
+
+	return NO_ERROR;
+}
+
+BOOL CTcpAgent::SendSmallFile(CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail)
+{
+	CFile file;
+	CFileMapping fmap;
+	WSABUF szBuf[3];
+
+	HRESULT hr = ::MakeSmallFilePackage(lpszFileName, file, fmap, szBuf, pHead, pTail);
+
+	if(FAILED(hr))
+	{
+		::SetLastError(hr);
+		return FALSE;
+	}
+
+	return SendPackets(dwConnID, szBuf, 3);
+}

+ 306 - 0
TcpAgent.h

@@ -0,0 +1,306 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "SocketHelper.h"
+#include "./common/GeneralHelper.h"
+#include "./common/IODispatcher.h"
+
+class CTcpAgent : public ITcpAgent, private CIOHandler
+{
+public:
+	virtual BOOL Start	(LPCTSTR lpszBindAddress = nullptr, BOOL bAsyncConnect = TRUE);
+	virtual BOOL Stop	();
+	virtual BOOL Connect(LPCTSTR lpszRemoteAddress, USHORT usPort, CONNID* pdwConnID = nullptr, PVOID pExtra = nullptr, USHORT usLocalPort = 0, LPCTSTR lpszLocalAddress = nullptr);
+	virtual BOOL Send	(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset = 0);
+	virtual BOOL SendSmallFile	(CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr);
+	virtual BOOL SendPackets	(CONNID dwConnID, const WSABUF pBuffers[], int iCount)	{return DoSendPackets(dwConnID, pBuffers, iCount);}
+	virtual BOOL PauseReceive	(CONNID dwConnID, BOOL bPause = TRUE);
+	virtual BOOL Wait			(DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
+	virtual BOOL			HasStarted					()	{return m_enState == SS_STARTED || m_enState == SS_STARTING;}
+	virtual EnServiceState	GetState					()	{return m_enState;}
+	virtual BOOL			Disconnect					(CONNID dwConnID, BOOL bForce = TRUE);
+	virtual BOOL			DisconnectLongConnections	(DWORD dwPeriod, BOOL bForce = TRUE);
+	virtual BOOL			DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce = TRUE);
+	virtual BOOL			GetLocalAddress				(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL			GetRemoteAddress			(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL			GetRemoteHost				(CONNID dwConnID, TCHAR lpszHost[], int& iHostLen, USHORT& usPort);
+
+	virtual BOOL IsConnected			(CONNID dwConnID);
+	virtual BOOL IsPauseReceive			(CONNID dwConnID, BOOL& bPaused);
+	virtual BOOL GetPendingDataLength	(CONNID dwConnID, int& iPending);
+	virtual DWORD GetConnectionCount	();
+	virtual BOOL GetAllConnectionIDs	(CONNID pIDs[], DWORD& dwCount);
+	virtual BOOL GetConnectPeriod		(CONNID dwConnID, DWORD& dwPeriod);
+	virtual BOOL GetSilencePeriod		(CONNID dwConnID, DWORD& dwPeriod);
+	virtual EnSocketError GetLastError	()	{return m_enLastError;}
+	virtual LPCTSTR GetLastErrorDesc	()	{return ::GetSocketErrorDesc(m_enLastError);}
+
+#ifdef _SSL_SUPPORT
+	virtual BOOL SetupSSLContext	(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr)	{return FALSE;}
+	virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr)					{return FALSE;}
+	virtual void CleanupSSLContext	()						{}
+
+	virtual BOOL StartSSLHandShake	(CONNID dwConnID)		{return FALSE;}
+	virtual void SetSSLAutoHandShake(BOOL bAutoHandShake)	{}
+	virtual BOOL IsSSLAutoHandShake	()						{return FALSE;}
+	virtual void SetSSLCipherList	(LPCTSTR lpszCipherList){}
+	virtual LPCTSTR GetSSLCipherList()						{return nullptr;}
+	virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo)	{return FALSE;}
+
+protected:
+	virtual BOOL StartSSLHandShake	(TAgentSocketObj* pSocketObj)	{return FALSE;}
+#endif
+
+private:
+	virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)			override;
+	virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)	override;
+	virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd)					override;
+	virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)				override;
+	virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)				override;
+	virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)					override;
+	virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events)					override;
+	virtual VOID OnDispatchThreadStart(THR_ID tid)												override;
+	virtual VOID OnDispatchThreadEnd(THR_ID tid)												override;
+
+public:
+	virtual BOOL IsSecure				() {return FALSE;}
+
+	virtual BOOL SetConnectionExtra(CONNID dwConnID, PVOID pExtra);
+	virtual BOOL GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra);
+
+	virtual void SetReuseAddressPolicy		(EnReuseAddressPolicy enReusePolicy)	{ENSURE_HAS_STOPPED(); m_enReusePolicy		= enReusePolicy;}
+	virtual void SetSendPolicy				(EnSendPolicy enSendPolicy)				{ENSURE_HAS_STOPPED(); ASSERT(m_enSendPolicy == enSendPolicy);}
+	virtual void SetOnSendSyncPolicy		(EnOnSendSyncPolicy enOnSendSyncPolicy)	{ENSURE_HAS_STOPPED(); ASSERT(m_enOnSendSyncPolicy == enOnSendSyncPolicy);}
+	virtual void SetSyncConnectTimeout		(DWORD dwSyncConnectTimeout)	{ENSURE_HAS_STOPPED(); m_dwSyncConnectTimeout		= dwSyncConnectTimeout;}
+	virtual void SetMaxConnectionCount		(DWORD dwMaxConnectionCount)	{ENSURE_HAS_STOPPED(); m_dwMaxConnectionCount		= dwMaxConnectionCount;}
+	virtual void SetWorkerThreadCount		(DWORD dwWorkerThreadCount)		{ENSURE_HAS_STOPPED(); m_dwWorkerThreadCount		= dwWorkerThreadCount;}
+	virtual void SetSocketBufferSize		(DWORD dwSocketBufferSize)		{ENSURE_HAS_STOPPED(); m_dwSocketBufferSize			= dwSocketBufferSize;}
+	virtual void SetFreeSocketObjLockTime	(DWORD dwFreeSocketObjLockTime)	{ENSURE_HAS_STOPPED(); m_dwFreeSocketObjLockTime	= dwFreeSocketObjLockTime;}
+	virtual void SetFreeSocketObjPool		(DWORD dwFreeSocketObjPool)		{ENSURE_HAS_STOPPED(); m_dwFreeSocketObjPool		= dwFreeSocketObjPool;}
+	virtual void SetFreeBufferObjPool		(DWORD dwFreeBufferObjPool)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferObjPool		= dwFreeBufferObjPool;}
+	virtual void SetFreeSocketObjHold		(DWORD dwFreeSocketObjHold)		{ENSURE_HAS_STOPPED(); m_dwFreeSocketObjHold		= dwFreeSocketObjHold;}
+	virtual void SetFreeBufferObjHold		(DWORD dwFreeBufferObjHold)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferObjHold		= dwFreeBufferObjHold;}
+	virtual void SetKeepAliveTime			(DWORD dwKeepAliveTime)			{ENSURE_HAS_STOPPED(); m_dwKeepAliveTime			= dwKeepAliveTime;}
+	virtual void SetKeepAliveInterval		(DWORD dwKeepAliveInterval)		{ENSURE_HAS_STOPPED(); m_dwKeepAliveInterval		= dwKeepAliveInterval;}
+	virtual void SetMarkSilence				(BOOL bMarkSilence)				{ENSURE_HAS_STOPPED(); m_bMarkSilence				= bMarkSilence;}
+	virtual void SetNoDelay					(BOOL bNoDelay)					{ENSURE_HAS_STOPPED(); m_bNoDelay					= bNoDelay;}
+
+	virtual EnReuseAddressPolicy GetReuseAddressPolicy	()	{return m_enReusePolicy;}
+	virtual EnSendPolicy GetSendPolicy					()	{return m_enSendPolicy;}
+	virtual EnOnSendSyncPolicy GetOnSendSyncPolicy		()	{return m_enOnSendSyncPolicy;}
+	virtual DWORD GetSyncConnectTimeout		()	{return m_dwSyncConnectTimeout;}
+	virtual DWORD GetMaxConnectionCount		()	{return m_dwMaxConnectionCount;}
+	virtual DWORD GetWorkerThreadCount		()	{return m_dwWorkerThreadCount;}
+	virtual DWORD GetSocketBufferSize		()	{return m_dwSocketBufferSize;}
+	virtual DWORD GetFreeSocketObjLockTime	()	{return m_dwFreeSocketObjLockTime;}
+	virtual DWORD GetFreeSocketObjPool		()	{return m_dwFreeSocketObjPool;}
+	virtual DWORD GetFreeBufferObjPool		()	{return m_dwFreeBufferObjPool;}
+	virtual DWORD GetFreeSocketObjHold		()	{return m_dwFreeSocketObjHold;}
+	virtual DWORD GetFreeBufferObjHold		()	{return m_dwFreeBufferObjHold;}
+	virtual DWORD GetKeepAliveTime			()	{return m_dwKeepAliveTime;}
+	virtual DWORD GetKeepAliveInterval		()	{return m_dwKeepAliveInterval;}
+	virtual BOOL  IsMarkSilence				()	{return m_bMarkSilence;}
+	virtual BOOL  IsNoDelay					()	{return m_bNoDelay;}
+
+protected:
+	virtual EnHandleResult FirePrepareConnect(CONNID dwConnID, SOCKET socket)
+		{return DoFirePrepareConnect(dwConnID, socket);}
+	virtual EnHandleResult FireConnect(TAgentSocketObj* pSocketObj)
+		{
+			EnHandleResult rs		= DoFireConnect(pSocketObj);
+			if(rs != HR_ERROR) rs	= FireHandShake(pSocketObj);
+			return rs;
+		}
+	virtual EnHandleResult FireHandShake(TAgentSocketObj* pSocketObj)
+		{return DoFireHandShake(pSocketObj);}
+	virtual EnHandleResult FireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return DoFireReceive(pSocketObj, pData, iLength);}
+	virtual EnHandleResult FireReceive(TAgentSocketObj* pSocketObj, int iLength)
+		{return DoFireReceive(pSocketObj, iLength);}
+	virtual EnHandleResult FireSend(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return DoFireSend(pSocketObj, pData, iLength);}
+	virtual EnHandleResult FireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+		{return DoFireClose(pSocketObj, enOperation, iErrorCode);}
+	virtual EnHandleResult FireShutdown()
+		{return DoFireShutdown();}
+
+	virtual EnHandleResult DoFirePrepareConnect(CONNID dwConnID, SOCKET socket)
+		{return m_pListener->OnPrepareConnect(this, dwConnID, socket);}
+	virtual EnHandleResult DoFireConnect(TAgentSocketObj* pSocketObj)
+		{return m_pListener->OnConnect(this, pSocketObj->connID);}
+	virtual EnHandleResult DoFireHandShake(TAgentSocketObj* pSocketObj)
+		{return m_pListener->OnHandShake(this, pSocketObj->connID);}
+	virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnReceive(this, pSocketObj->connID, pData, iLength);}
+	virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, int iLength)
+		{return m_pListener->OnReceive(this, pSocketObj->connID, iLength);}
+	virtual EnHandleResult DoFireSend(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnSend(this, pSocketObj->connID, pData, iLength);}
+	virtual EnHandleResult DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+		{return m_pListener->OnClose(this, pSocketObj->connID, enOperation, iErrorCode);}
+	virtual EnHandleResult DoFireShutdown()
+		{return m_pListener->OnShutdown(this);}
+
+	void SetLastError(EnSocketError code, LPCSTR func, int ec);
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual BOOL BeforeUnpause(TAgentSocketObj* pSocketObj) {return TRUE;}
+
+	virtual void OnWorkerThreadStart(THR_ID tid) {}
+	virtual void OnWorkerThreadEnd(THR_ID tid) {}
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
+
+	BOOL DoSendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
+	BOOL DoSendPackets(TAgentSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
+	TAgentSocketObj* FindSocketObj(CONNID dwConnID);
+	BOOL GetRemoteHost(CONNID dwConnID, LPCSTR* lpszHost, USHORT* pusPort = nullptr);
+
+protected:
+	BOOL SetConnectionExtra(TAgentSocketObj* pSocketObj, PVOID pExtra);
+	BOOL GetConnectionExtra(TAgentSocketObj* pSocketObj, PVOID* ppExtra);
+	BOOL SetConnectionReserved(CONNID dwConnID, PVOID pReserved);
+	BOOL GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved);
+	BOOL SetConnectionReserved(TAgentSocketObj* pSocketObj, PVOID pReserved);
+	BOOL GetConnectionReserved(TAgentSocketObj* pSocketObj, PVOID* ppReserved);
+	BOOL SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2);
+	BOOL GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2);
+	BOOL SetConnectionReserved2(TAgentSocketObj* pSocketObj, PVOID pReserved2);
+	BOOL GetConnectionReserved2(TAgentSocketObj* pSocketObj, PVOID* ppReserved2);
+
+private:
+	BOOL CheckStarting();
+	BOOL CheckStoping();
+	BOOL ParseBindAddress(LPCTSTR lpszBindAddress);
+	BOOL CreateWorkerThreads();
+
+	void DisconnectClientSocket();
+	void WaitForClientSocketClose();
+	void ReleaseClientSocket();
+	void ReleaseFreeSocket();
+	void WaitForWorkerThreadEnd();
+
+	TAgentSocketObj* GetFreeSocketObj(CONNID dwConnID, SOCKET soClient);
+	TAgentSocketObj* CreateSocketObj();
+	void AddFreeSocketObj	(TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0);
+	void DeleteSocketObj	(TAgentSocketObj* pSocketObj);
+	BOOL InvalidSocketObj	(TAgentSocketObj* pSocketObj);
+	void AddClientSocketObj	(CONNID dwConnID, TAgentSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr, LPCTSTR lpszRemoteHostName, PVOID pExtra);
+	void CloseClientSocketObj(TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0, int iShutdownFlag = SHUT_WR);
+
+private:
+	int CreateClientSocket(LPCTSTR lpszRemoteAddress, USHORT usPort, LPCTSTR lpszLocalAddress, USHORT usLocalPort, SOCKET& soClient, HP_SOCKADDR& addr);
+	int PrepareConnect	(CONNID& dwConnID, SOCKET soClient);
+	int ConnectToServer	(CONNID dwConnID, LPCTSTR lpszRemoteHostName, SOCKET& soClient, const HP_SOCKADDR& addr, PVOID pExtra);
+
+	VOID HandleCmdSend		(const TDispContext* pContext, CONNID dwConnID);
+	VOID HandleCmdUnpause	(const TDispContext* pContext, CONNID dwConnID);
+	VOID HandleCmdDisconnect(const TDispContext* pContext, CONNID dwConnID, BOOL bForce);
+	BOOL HandleConnect		(const TDispContext* pContext, TAgentSocketObj* pSocketObj, UINT events);
+	BOOL HandleReceive		(const TDispContext* pContext, TAgentSocketObj* pSocketObj, int flag);
+	BOOL HandleSend			(const TDispContext* pContext, TAgentSocketObj* pSocketObj, int flag);
+	BOOL HandleClose		(const TDispContext* pContext, TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag, UINT events);
+
+	int SendInternal	(TAgentSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
+	BOOL SendItem		(TAgentSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked);
+
+public:
+	CTcpAgent(ITcpAgentListener* pListener)
+	: m_pListener				(pListener)
+	, m_enLastError				(SE_OK)
+	, m_enState					(SS_STOPPED)
+	, m_fdGCTimer				(INVALID_FD)
+	, m_bAsyncConnect			(TRUE)
+	, m_enReusePolicy			(RAP_ADDR_ONLY)
+	, m_enSendPolicy			(SP_PACK)
+	, m_enOnSendSyncPolicy		(OSSP_RECEIVE)
+	, m_dwSyncConnectTimeout	(DEFAULT_SYNC_CONNECT_TIMEOUT)
+	, m_dwMaxConnectionCount	(DEFAULT_CONNECTION_COUNT)
+	, m_dwWorkerThreadCount		(DEFAULT_WORKER_THREAD_COUNT)
+	, m_dwSocketBufferSize		(DEFAULT_TCP_SOCKET_BUFFER_SIZE)
+	, m_dwFreeSocketObjLockTime	(DEFAULT_FREE_SOCKETOBJ_LOCK_TIME)
+	, m_dwFreeSocketObjPool		(DEFAULT_FREE_SOCKETOBJ_POOL)
+	, m_dwFreeBufferObjPool		(DEFAULT_FREE_BUFFEROBJ_POOL)
+	, m_dwFreeSocketObjHold		(DEFAULT_FREE_SOCKETOBJ_HOLD)
+	, m_dwFreeBufferObjHold		(DEFAULT_FREE_BUFFEROBJ_HOLD)
+	, m_dwKeepAliveTime			(DEFALUT_TCP_KEEPALIVE_TIME)
+	, m_dwKeepAliveInterval		(DEFALUT_TCP_KEEPALIVE_INTERVAL)
+	, m_bMarkSilence			(TRUE)
+	, m_bNoDelay				(FALSE)
+	, m_soAddr					(AF_UNSPEC, TRUE)
+	{
+		ASSERT(m_pListener);
+	}
+
+	virtual ~CTcpAgent()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	EnReuseAddressPolicy m_enReusePolicy;
+	EnSendPolicy m_enSendPolicy;
+	EnOnSendSyncPolicy m_enOnSendSyncPolicy;
+	DWORD m_dwSyncConnectTimeout;
+	DWORD m_dwMaxConnectionCount;
+	DWORD m_dwWorkerThreadCount;
+	DWORD m_dwSocketBufferSize;
+	DWORD m_dwFreeSocketObjLockTime;
+	DWORD m_dwFreeSocketObjPool;
+	DWORD m_dwFreeBufferObjPool;
+	DWORD m_dwFreeSocketObjHold;
+	DWORD m_dwFreeBufferObjHold;
+	DWORD m_dwKeepAliveTime;
+	DWORD m_dwKeepAliveInterval;
+	BOOL  m_bMarkSilence;
+	BOOL  m_bNoDelay;
+
+private:
+	CSEM					m_evWait;
+
+	ITcpAgentListener*		m_pListener;
+	BOOL					m_bAsyncConnect;
+	EnServiceState			m_enState;
+	EnSocketError			m_enLastError;
+	HP_SOCKADDR				m_soAddr;
+
+	CReceiveBuffersPtr		m_rcBuffers;
+
+	CPrivateHeap			m_phSocket;
+	CBufferObjPool			m_bfObjPool;
+
+	CSpinGuard				m_csState;
+
+	FD						m_fdGCTimer;
+
+	TAgentSocketObjPtrPool	m_bfActiveSockets;
+	
+	TAgentSocketObjPtrList	m_lsFreeSocket;
+	TAgentSocketObjPtrQueue	m_lsGCSocket;
+
+	CIODispatcher			m_ioDispatcher;
+};

+ 723 - 0
TcpClient.cpp

@@ -0,0 +1,723 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "TcpClient.h"
+
+#include "./common/FileHelper.h"
+
+BOOL CTcpClient::Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
+{
+	if(!CheckParams() || !CheckStarting())
+		return FALSE;
+
+	PrepareStart();
+	m_ccContext.Reset();
+
+	BOOL isOK = FALSE;
+
+	HP_SOCKADDR addrRemote, addrBind;
+
+	if(CreateClientSocket(lpszRemoteAddress, addrRemote, usPort, lpszBindAddress, addrBind))
+	{
+		if(BindClientSocket(addrBind, addrRemote, usLocalPort))
+		{
+			if(TRIGGER(FirePrepareConnect(m_soClient)) != HR_ERROR)
+			{
+				if(ConnectToServer(addrRemote, bAsyncConnect))
+				{
+					if(CreateWorkerThread())
+						isOK = TRUE;
+					else
+						SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ERROR_CREATE_FAILED);
+				}
+				else
+					SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
+			}
+			else
+				SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
+		}
+		else
+			SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
+	}
+	else
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+
+	if(!isOK)
+	{
+		m_ccContext.Reset(FALSE);
+		EXECUTE_RESTORE_ERROR(Stop());
+	}
+
+	return isOK;
+}
+
+BOOL CTcpClient::CheckParams()
+{
+	if	(((int)m_dwSyncConnectTimeout > 0)									&&
+		((int)m_dwSocketBufferSize > 0)										&&
+		((int)m_dwFreeBufferPoolSize >= 0)									&&
+		((int)m_dwFreeBufferPoolHold >= 0)									&&
+		((int)m_dwKeepAliveTime >= 1000 || m_dwKeepAliveTime == 0)			&&
+		((int)m_dwKeepAliveInterval >= 1000 || m_dwKeepAliveInterval == 0)	)
+		return TRUE;
+
+	SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+	return FALSE;
+}
+
+void CTcpClient::PrepareStart()
+{
+	m_itPool.SetItemCapacity(m_dwSocketBufferSize);
+	m_itPool.SetPoolSize(m_dwFreeBufferPoolSize);
+	m_itPool.SetPoolHold(m_dwFreeBufferPoolHold);
+
+	m_itPool.Prepare();
+}
+
+BOOL CTcpClient::CheckStarting()
+{
+	CSpinLock locallock(m_csState);
+
+	if(m_enState == SS_STOPPED)
+		m_enState = SS_STARTING;
+	else
+	{
+		SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpClient::CheckStoping()
+{
+	if(m_enState != SS_STOPPED)
+	{
+		CSpinLock locallock(m_csState);
+
+		if(HasStarted())
+		{
+			m_enState = SS_STOPPING;
+			return TRUE;
+		}
+	}
+
+	SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+
+	return FALSE;
+}
+
+BOOL CTcpClient::CreateClientSocket(LPCTSTR lpszRemoteAddress, HP_SOCKADDR& addrRemote, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& addrBind)
+{
+	HP_SCOPE_HOST host(lpszRemoteAddress);
+
+	if(!::GetSockAddrByHostName(host.addr, usPort, addrRemote))
+		return FALSE;
+
+	if(::IsStrNotEmpty(lpszBindAddress))
+	{
+		if(!::sockaddr_A_2_IN(lpszBindAddress, 0, addrBind))
+			return FALSE;
+
+		if(addrRemote.family != addrBind.family)
+		{
+			::WSASetLastError(ERROR_AFNOSUPPORT);
+			return FALSE;
+		}
+	}
+
+	m_soClient = socket(addrRemote.family, SOCK_STREAM, IPPROTO_TCP);
+
+	if(m_soClient == INVALID_SOCKET)
+		return FALSE;
+
+	BOOL bOnOff	= (m_dwKeepAliveTime > 0 && m_dwKeepAliveInterval > 0);
+	VERIFY(::SSO_KeepAliveVals(m_soClient, bOnOff, m_dwKeepAliveTime, m_dwKeepAliveInterval) == NO_ERROR);
+	VERIFY(::SSO_ReuseAddress(m_soClient, m_enReusePolicy) == NO_ERROR);
+	VERIFY(::SSO_NoDelay(m_soClient, m_bNoDelay) == NO_ERROR);
+
+	SetRemoteHost(host.name, usPort);
+
+	return TRUE;
+}
+
+BOOL CTcpClient::BindClientSocket(const HP_SOCKADDR& addrBind, const HP_SOCKADDR& addrRemote, USHORT usLocalPort)
+{
+	if(addrBind.IsSpecified() && usLocalPort == 0)
+	{
+		if(::bind(m_soClient, addrBind.Addr(), addrBind.AddrSize()) == SOCKET_ERROR)
+			return FALSE;
+	}
+	else if(usLocalPort != 0)
+	{
+		HP_SOCKADDR realBindAddr = addrBind.IsSpecified() ? addrBind : HP_SOCKADDR::AnyAddr(addrRemote.family);
+
+		realBindAddr.SetPort(usLocalPort);
+
+		if(::bind(m_soClient, realBindAddr.Addr(), realBindAddr.AddrSize()) == SOCKET_ERROR)
+			return FALSE;
+	}
+
+	m_dwConnID = ::GenerateConnectionID();
+
+	return TRUE;
+}
+
+BOOL CTcpClient::ConnectToServer(const HP_SOCKADDR& addrRemote, BOOL bAsyncConnect)
+{
+	BOOL isOK = FALSE;
+
+	VERIFY(::fcntl_SETFL(m_soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
+
+	int rc = ::connect(m_soClient, addrRemote.Addr(), addrRemote.AddrSize());
+
+	if(IS_NO_ERROR(rc) || IS_IO_PENDING_ERROR())
+	{
+		if(bAsyncConnect)
+		{
+			m_nEvents	= POLLOUT;
+			isOK		= TRUE;
+		}
+		else
+		{
+			if(IS_HAS_ERROR(rc))
+				rc = ::WaitForSocketWrite(m_soClient, m_dwSyncConnectTimeout);
+
+			if(!IS_NO_ERROR(rc))
+				::WSASetLastError(rc);
+			else
+			{
+				SetConnected();
+
+				if(TRIGGER(FireConnect()) == HR_ERROR)
+					::WSASetLastError(ENSURE_ERROR_CANCELLED);
+				else
+				{
+					m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);				
+					isOK	  = TRUE;
+				}
+
+			}
+		}
+	}
+
+	return isOK;
+}
+
+BOOL CTcpClient::Stop()
+{
+	if(!CheckStoping())
+		return FALSE;
+
+	WaitForWorkerThreadEnd();
+
+	SetConnected(FALSE);
+
+	if(m_ccContext.bFireOnClose)
+		FireClose(m_ccContext.enOperation, m_ccContext.iErrorCode);
+
+	if(m_soClient != INVALID_SOCKET)
+	{
+		shutdown(m_soClient, SHUT_WR);
+		closesocket(m_soClient);
+
+		m_soClient = INVALID_SOCKET;
+	}
+
+	Reset();
+
+	return TRUE;
+}
+
+void CTcpClient::Reset()
+{
+	CCriSecLock locallock(m_csSend);
+
+	m_evSend.Reset();
+	m_evRecv.Reset();
+	m_evStop.Reset();
+
+	m_lsSend.Clear();
+	m_itPool.Clear();
+	m_rcBuffer.Free();
+
+	m_strHost.Empty();
+
+	m_usPort	= 0;
+	m_nEvents	= 0;
+	m_bPaused	= FALSE;
+	m_enState	= SS_STOPPED;
+
+	m_evWait.SyncNotifyAll();
+}
+
+void CTcpClient::WaitForWorkerThreadEnd()
+{
+	if(!m_thWorker.IsRunning())
+		return;
+
+	if(m_thWorker.IsInMyThread())
+		m_thWorker.Detach();
+	else
+	{
+		m_evStop.Set();
+		m_thWorker.Join();
+	}
+}
+
+BOOL CTcpClient::CreateWorkerThread()
+{
+	return m_thWorker.Start(this, &CTcpClient::WorkerThreadProc);
+}
+
+UINT WINAPI CTcpClient::WorkerThreadProc(LPVOID pv)
+{
+	::SetCurrentWorkerThreadName();
+
+	TRACE("---------------> Client Worker Thread 0x%08X started <---------------", SELF_THREAD_ID);
+
+	OnWorkerThreadStart(SELF_THREAD_ID);
+
+	BOOL bCallStop	= TRUE;
+	pollfd pfds[]	= {	{m_soClient, m_nEvents}, 
+						{m_evSend.GetFD(), POLLIN}, 
+						{m_evRecv.GetFD(), POLLIN}, 
+						{m_evStop.GetFD(), POLLIN}	};
+	int size		= ARRAY_SIZE(pfds);
+
+	m_rcBuffer.Malloc(m_dwSocketBufferSize);
+
+	while(HasStarted())
+	{
+		int rs = (int)::PollForMultipleObjects(pfds, size);
+		ASSERT(rs > TIMEOUT);
+
+		if(rs <= 0)
+		{
+			m_ccContext.Reset(TRUE, SO_UNKNOWN, ::WSAGetLastError());
+			goto EXIT_WORKER_THREAD;
+		}
+
+		for(int i = 0; i < size; i++)
+		{
+			if((1 << i) & rs)
+			{
+				SHORT revents = pfds[i].revents;
+
+				if(i == 0)
+				{
+					if(!ProcessNetworkEvent(revents))
+						goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 1)
+				{
+					m_evSend.Reset();
+
+					if(!SendData())
+						goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 2)
+				{
+					m_evRecv.Reset();
+
+					if(!BeforeUnpause())
+						goto EXIT_WORKER_THREAD;
+
+					if(!ReadData())
+						goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 3)
+				{
+					m_evStop.Reset();
+
+					bCallStop = FALSE;
+					goto EXIT_WORKER_THREAD;
+				}
+				else
+					VERIFY(FALSE);
+			}
+		}
+
+		m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
+		pfds[0].events = m_nEvents;
+	}
+
+EXIT_WORKER_THREAD:
+
+	OnWorkerThreadEnd(SELF_THREAD_ID);
+
+	if(bCallStop && HasStarted())
+		Stop();
+
+	TRACE("---------------> Client Worker Thread 0x%08X stoped <---------------", SELF_THREAD_ID);
+
+	return 0;
+}
+
+BOOL CTcpClient::ProcessNetworkEvent(SHORT events)
+{
+	BOOL bContinue = TRUE;
+
+	if(bContinue && events & POLLERR)
+		bContinue = HandleClose(events);
+
+	if(bContinue && !IsConnected())
+		bContinue = HandleConnect(events);
+
+	if(bContinue && events & POLLIN)
+		bContinue = HandleRead(events);
+
+	if(bContinue && events & POLLOUT)
+		bContinue = HandleWrite(events);
+
+	if(bContinue && events & _POLL_HUNGUP_EVENTS)
+		bContinue = HandleClose(events);
+
+	return bContinue;
+}
+
+BOOL CTcpClient::HandleConnect(SHORT events)
+{
+	ASSERT(events & POLLOUT);
+
+	int code = ::SSO_GetError(m_soClient);
+
+	if(!IS_NO_ERROR(code) || (events & _POLL_ERROR_EVENTS))
+	{
+		m_ccContext.Reset(TRUE, SO_CONNECT, code);
+		return FALSE;
+	}
+
+	if(events & _POLL_HUNGUP_EVENTS)
+	{
+		m_ccContext.Reset(TRUE, SO_CONNECT, NO_ERROR);
+		return FALSE;
+	}
+
+	SetConnected();
+
+	if(TRIGGER(FireConnect()) == HR_ERROR)
+	{
+		m_ccContext.Reset(FALSE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpClient::HandleClose(SHORT events)
+{
+	EnSocketOperation enOperation = SO_CLOSE;
+
+	if(events & _POLL_HUNGUP_EVENTS)
+		enOperation = SO_CLOSE;
+	else if(events & POLLIN)
+		enOperation = SO_RECEIVE;
+	else if(events & POLLOUT)
+		enOperation = SO_SEND;
+
+	m_ccContext.Reset(TRUE, enOperation, ::SSO_GetError(m_soClient));
+
+	return FALSE;
+}
+
+BOOL CTcpClient::HandleRead(SHORT events)
+{
+	return ReadData();
+}
+
+BOOL CTcpClient::HandleWrite(SHORT events)
+{
+	return SendData();
+}
+
+BOOL CTcpClient::ReadData()
+{
+	while(TRUE)
+	{
+		if(m_bPaused)
+			break;
+
+		int rc = (int)read(m_soClient, (char*)(BYTE*)m_rcBuffer, m_dwSocketBufferSize);
+
+		if(rc > 0)
+		{
+			if(TRIGGER(FireReceive(m_rcBuffer, rc)) == HR_ERROR)
+			{
+				TRACE("<C-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", m_dwConnID);
+
+				m_ccContext.Reset(TRUE, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
+				return FALSE;
+			}
+		}
+		else if(rc == SOCKET_ERROR)
+		{
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+				break;
+			else
+			{
+				m_ccContext.Reset(TRUE, SO_RECEIVE, code);
+				return FALSE;
+			}
+		}
+		else if(rc == 0)
+		{
+			m_ccContext.Reset(TRUE, SO_CLOSE, SE_OK);
+			return FALSE;
+		}
+		else
+			ASSERT(FALSE);
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpClient::PauseReceive(BOOL bPause)
+{
+	if(!IsConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(m_bPaused == bPause)
+		return TRUE;
+
+	m_bPaused = bPause;
+
+	if(!bPause)
+		return m_evRecv.Set();
+	
+	return TRUE;
+}
+
+BOOL CTcpClient::SendData()
+{
+	BOOL bBlocked = FALSE;
+
+	while(m_lsSend.Length() > 0)
+	{
+		TItemPtr itPtr(m_itPool);
+
+		{
+			CCriSecLock locallock(m_csSend);
+			itPtr = m_lsSend.PopFront();
+		}
+
+		if(!itPtr.IsValid())
+			break;
+
+		ASSERT(!itPtr->IsEmpty());
+
+		if(!DoSendData(itPtr, bBlocked))
+			return FALSE;
+
+		if(bBlocked)
+		{
+			ASSERT(!itPtr->IsEmpty());
+
+			CCriSecLock locallock(m_csSend);
+			m_lsSend.PushFront(itPtr.Detach());
+			break;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpClient::DoSendData(TItem* pItem, BOOL& bBlocked)
+{
+	while(!pItem->IsEmpty())
+	{
+		int rc = (int)write(m_soClient, (char*)pItem->Ptr(), pItem->Size());
+
+		if(rc > 0)
+		{
+			if(TRIGGER(FireSend(pItem->Ptr(), rc)) == HR_ERROR)
+			{
+				TRACE("<C-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", m_dwConnID);
+				ASSERT(FALSE);
+			}
+
+			pItem->Reduce(rc);
+		}
+		else if(rc == SOCKET_ERROR)
+		{
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+			{
+				bBlocked = TRUE;
+				break;
+			}
+			else
+			{
+				m_ccContext.Reset(TRUE, SO_SEND, code);
+				return FALSE;
+			}
+		}
+		else
+			ASSERT(FALSE);
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpClient::Send(const BYTE* pBuffer, int iLength, int iOffset)
+{
+	ASSERT(pBuffer && iLength > 0);
+
+	if(iOffset != 0) pBuffer += iOffset;
+
+	WSABUF buffer;
+	buffer.len = iLength;
+	buffer.buf = (BYTE*)pBuffer;
+
+	return SendPackets(&buffer, 1);
+}
+
+BOOL CTcpClient::DoSendPackets(const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	int result = NO_ERROR;
+
+	if(pBuffers && iCount > 0)
+	{
+		if(IsConnected())
+		{
+			CCriSecLock locallock(m_csSend);
+
+			if(IsConnected())
+				result = SendInternal(pBuffers, iCount);
+			else
+				result = ERROR_INVALID_STATE;
+		}
+		else
+			result = ERROR_INVALID_STATE;
+	}
+	else
+		result = ERROR_INVALID_PARAMETER;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+int CTcpClient::SendInternal(const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(m_lsSend.Length() >= 0);
+
+	int iPending = m_lsSend.Length();
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			m_lsSend.Cat(pBuffer, iBufLen);
+			ASSERT(m_lsSend.Length() > 0);
+		}
+	}
+
+	if(iPending == 0 && m_lsSend.Length() > 0) m_evSend.Set();
+
+	return NO_ERROR;
+}
+
+BOOL CTcpClient::SendSmallFile(LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail)
+{
+	CFile file;
+	CFileMapping fmap;
+	WSABUF szBuf[3];
+
+	HRESULT hr = ::MakeSmallFilePackage(lpszFileName, file, fmap, szBuf, pHead, pTail);
+
+	if(FAILED(hr))
+	{
+		::SetLastError(hr);
+		return FALSE;
+	}
+
+	return SendPackets(szBuf, 3);
+}
+
+void CTcpClient::SetLastError(EnSocketError code, LPCSTR func, int ec)
+{
+	TRACE("%s --> Error: %d, EC: %d", func, code, ec);
+
+	m_enLastError = code;
+	::SetLastError(ec);
+}
+
+BOOL CTcpClient::GetLocalAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	return ::GetSocketLocalAddress(m_soClient, lpszAddress, iAddressLen, usPort);
+}
+
+void CTcpClient::SetRemoteHost(LPCTSTR lpszHost, USHORT usPort)
+{
+	m_strHost = lpszHost;
+	m_usPort  = usPort;
+}
+
+BOOL CTcpClient::GetRemoteHost(TCHAR lpszHost[], int& iHostLen, USHORT& usPort)
+{
+	BOOL isOK = FALSE;
+
+	if(m_strHost.IsEmpty())
+		return isOK;
+
+	int iLen = m_strHost.GetLength() + 1;
+
+	if(iHostLen >= iLen)
+	{
+		memcpy(lpszHost, CA2CT(m_strHost), iLen * sizeof(TCHAR));
+		usPort = m_usPort;
+
+		isOK = TRUE;
+	}
+
+	iHostLen = iLen;
+
+	return isOK;
+}
+
+BOOL CTcpClient::GetRemoteHost(LPCSTR* lpszHost, USHORT* pusPort)
+{
+	*lpszHost = m_strHost;
+
+	if(pusPort != nullptr)
+		*pusPort = m_usPort;
+
+	return !m_strHost.IsEmpty();
+}

+ 248 - 0
TcpClient.h

@@ -0,0 +1,248 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "SocketHelper.h"
+#include "./common/GeneralHelper.h"
+
+class CTcpClient : public ITcpClient
+{
+public:
+	virtual BOOL Start	(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect = TRUE, LPCTSTR lpszBindAddress = nullptr, USHORT usLocalPort = 0);
+	virtual BOOL Stop	();
+	virtual BOOL Send	(const BYTE* pBuffer, int iLength, int iOffset = 0);
+	virtual BOOL SendSmallFile	(LPCTSTR lpszFileName, const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr);
+	virtual BOOL SendPackets	(const WSABUF pBuffers[], int iCount)	{return DoSendPackets(pBuffers, iCount);}
+	virtual BOOL PauseReceive	(BOOL bPause = TRUE);
+	virtual BOOL Wait			(DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
+	virtual BOOL			HasStarted			()	{return m_enState == SS_STARTED || m_enState == SS_STARTING;}
+	virtual EnServiceState	GetState			()	{return m_enState;}
+	virtual CONNID			GetConnectionID		()	{return m_dwConnID;}
+	virtual EnSocketError	GetLastError		()	{return m_enLastError;}
+	virtual LPCTSTR			GetLastErrorDesc	()	{return ::GetSocketErrorDesc(m_enLastError);}
+
+	virtual BOOL GetLocalAddress		(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL GetRemoteHost			(TCHAR lpszHost[], int& iHostLen, USHORT& usPort);
+	virtual BOOL GetPendingDataLength	(int& iPending) {iPending = m_lsSend.Length(); return HasStarted();}
+	virtual BOOL IsPauseReceive			(BOOL& bPaused) {bPaused = m_bPaused; return HasStarted();}
+	virtual BOOL IsConnected			()				{return m_bConnected;}
+
+#ifdef _SSL_SUPPORT
+	virtual BOOL SetupSSLContext	(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr)	{return FALSE;}
+	virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr)					{return FALSE;}
+	virtual void CleanupSSLContext	()						{}
+
+	virtual BOOL StartSSLHandShake	()						{return FALSE;}
+	virtual void SetSSLAutoHandShake(BOOL bAutoHandShake)	{}
+	virtual BOOL IsSSLAutoHandShake	()						{return FALSE;}
+	virtual void SetSSLCipherList	(LPCTSTR lpszCipherList){}
+	virtual LPCTSTR GetSSLCipherList()						{return nullptr;}
+	virtual BOOL GetSSLSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo)	{return FALSE;}
+
+protected:
+	virtual BOOL StartSSLHandShakeNoCheck()					{return FALSE;}
+#endif
+
+public:
+	virtual BOOL IsSecure				() {return FALSE;}
+
+	virtual void SetReuseAddressPolicy	(EnReuseAddressPolicy enReusePolicy){ENSURE_HAS_STOPPED(); m_enReusePolicy			= enReusePolicy;}
+	virtual void SetSyncConnectTimeout	(DWORD dwSyncConnectTimeout)		{ENSURE_HAS_STOPPED(); m_dwSyncConnectTimeout	= dwSyncConnectTimeout;}
+	virtual void SetSocketBufferSize	(DWORD dwSocketBufferSize)			{ENSURE_HAS_STOPPED(); m_dwSocketBufferSize		= dwSocketBufferSize;}
+	virtual void SetKeepAliveTime		(DWORD dwKeepAliveTime)				{ENSURE_HAS_STOPPED(); m_dwKeepAliveTime		= dwKeepAliveTime;}
+	virtual void SetKeepAliveInterval	(DWORD dwKeepAliveInterval)			{ENSURE_HAS_STOPPED(); m_dwKeepAliveInterval	= dwKeepAliveInterval;}
+	virtual void SetFreeBufferPoolSize	(DWORD dwFreeBufferPoolSize)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolSize	= dwFreeBufferPoolSize;}
+	virtual void SetFreeBufferPoolHold	(DWORD dwFreeBufferPoolHold)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolHold	= dwFreeBufferPoolHold;}
+	virtual void SetNoDelay				(BOOL bNoDelay)						{ENSURE_HAS_STOPPED(); m_bNoDelay				= bNoDelay;}
+	virtual void SetExtra				(PVOID pExtra)						{m_pExtra										= pExtra;}						
+
+	virtual EnReuseAddressPolicy GetReuseAddressPolicy	()	{return m_enReusePolicy;}
+	virtual DWORD GetSyncConnectTimeout	()	{return m_dwSyncConnectTimeout;}
+	virtual DWORD GetSocketBufferSize	()	{return m_dwSocketBufferSize;}
+	virtual DWORD GetKeepAliveTime		()	{return m_dwKeepAliveTime;}
+	virtual DWORD GetKeepAliveInterval	()	{return m_dwKeepAliveInterval;}
+	virtual DWORD GetFreeBufferPoolSize	()	{return m_dwFreeBufferPoolSize;}
+	virtual DWORD GetFreeBufferPoolHold	()	{return m_dwFreeBufferPoolHold;}
+	virtual BOOL  IsNoDelay				()	{return m_bNoDelay;}
+	virtual PVOID GetExtra				()	{return m_pExtra;}
+
+protected:
+	virtual EnHandleResult FirePrepareConnect(SOCKET socket)
+		{return DoFirePrepareConnect(this, socket);}
+	virtual EnHandleResult FireConnect()
+		{
+			EnHandleResult rs		= DoFireConnect(this);
+			if(rs != HR_ERROR) rs	= FireHandShake();
+			return rs;
+		}
+	virtual EnHandleResult FireHandShake()
+		{return DoFireHandShake(this);}
+	virtual EnHandleResult FireSend(const BYTE* pData, int iLength)
+		{return DoFireSend(this, pData, iLength);}
+	virtual EnHandleResult FireReceive(const BYTE* pData, int iLength)
+		{return DoFireReceive(this, pData, iLength);}
+	virtual EnHandleResult FireReceive(int iLength)
+		{return DoFireReceive(this, iLength);}
+	virtual EnHandleResult FireClose(EnSocketOperation enOperation, int iErrorCode)
+		{return DoFireClose(this, enOperation, iErrorCode);}
+
+	virtual EnHandleResult DoFirePrepareConnect(ITcpClient* pSender, SOCKET socket)
+		{return m_pListener->OnPrepareConnect(pSender, pSender->GetConnectionID(), socket);}
+	virtual EnHandleResult DoFireConnect(ITcpClient* pSender)
+		{return m_pListener->OnConnect(pSender, pSender->GetConnectionID());}
+	virtual EnHandleResult DoFireHandShake(ITcpClient* pSender)
+		{return m_pListener->OnHandShake(pSender, pSender->GetConnectionID());}
+	virtual EnHandleResult DoFireSend(ITcpClient* pSender, const BYTE* pData, int iLength)
+		{return m_pListener->OnSend(pSender, pSender->GetConnectionID(), pData, iLength);}
+	virtual EnHandleResult DoFireReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
+		{return m_pListener->OnReceive(pSender, pSender->GetConnectionID(), pData, iLength);}
+	virtual EnHandleResult DoFireReceive(ITcpClient* pSender, int iLength)
+		{return m_pListener->OnReceive(pSender, pSender->GetConnectionID(), iLength);}
+	virtual EnHandleResult DoFireClose(ITcpClient* pSender, EnSocketOperation enOperation, int iErrorCode)
+		{return m_pListener->OnClose(pSender, pSender->GetConnectionID(), enOperation, iErrorCode);}
+
+	void SetLastError(EnSocketError code, LPCSTR func, int ec);
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual BOOL BeforeUnpause() {return TRUE;}
+
+	virtual void OnWorkerThreadStart(THR_ID tid) {}
+	virtual void OnWorkerThreadEnd(THR_ID tid) {}
+
+	BOOL DoSendPackets(const WSABUF pBuffers[], int iCount);
+
+	static BOOL DoSendPackets(CTcpClient* pClient, const WSABUF pBuffers[], int iCount)
+		{return pClient->DoSendPackets(pBuffers, iCount);}
+
+protected:
+	BOOL IsPaused		()					{return m_bPaused;}
+	void SetReserved	(PVOID pReserved)	{m_pReserved = pReserved;}						
+	PVOID GetReserved	()					{return m_pReserved;}
+	BOOL GetRemoteHost	(LPCSTR* lpszHost, USHORT* pusPort = nullptr);
+
+private:
+	void SetRemoteHost	(LPCTSTR lpszHost, USHORT usPort);
+	void SetConnected	(BOOL bConnected = TRUE) {m_bConnected = bConnected; if(bConnected) m_enState = SS_STARTED;}
+
+	BOOL CheckStarting();
+	BOOL CheckStoping();
+	BOOL CreateClientSocket(LPCTSTR lpszRemoteAddress, HP_SOCKADDR& addrRemote, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& addrBind);
+	BOOL BindClientSocket(const HP_SOCKADDR& addrBind, const HP_SOCKADDR& addrRemote, USHORT usLocalPort);
+	BOOL ConnectToServer(const HP_SOCKADDR& addrRemote, BOOL bAsyncConnect);
+	BOOL CreateWorkerThread();
+	BOOL ProcessNetworkEvent(SHORT events);
+	BOOL ReadData();
+	BOOL SendData();
+	BOOL DoSendData(TItem* pItem, BOOL& bBlocked);
+	int SendInternal(const WSABUF pBuffers[], int iCount);
+	void WaitForWorkerThreadEnd();
+
+	BOOL HandleConnect	(SHORT events);
+	BOOL HandleClose	(SHORT events);
+	BOOL HandleRead		(SHORT events);
+	BOOL HandleWrite	(SHORT events);
+
+	UINT WINAPI WorkerThreadProc(LPVOID pv);
+
+public:
+	CTcpClient(ITcpClientListener* pListener)
+	: m_pListener			(pListener)
+	, m_lsSend				(m_itPool)
+	, m_soClient			(INVALID_SOCKET)
+	, m_nEvents				(0)
+	, m_dwConnID			(0)
+	, m_usPort				(0)
+	, m_bPaused				(FALSE)
+	, m_bConnected			(FALSE)
+	, m_enLastError			(SE_OK)
+	, m_enState				(SS_STOPPED)
+	, m_bNoDelay			(FALSE)
+	, m_pExtra				(nullptr)
+	, m_pReserved			(nullptr)
+	, m_enReusePolicy		(RAP_ADDR_ONLY)
+	, m_dwSyncConnectTimeout(DEFAULT_SYNC_CONNECT_TIMEOUT)
+	, m_dwSocketBufferSize	(DEFAULT_TCP_SOCKET_BUFFER_SIZE)
+	, m_dwFreeBufferPoolSize(DEFAULT_CLIENT_FREE_BUFFER_POOL_SIZE)
+	, m_dwFreeBufferPoolHold(DEFAULT_CLIENT_FREE_BUFFER_POOL_HOLD)
+	, m_dwKeepAliveTime		(DEFALUT_TCP_KEEPALIVE_TIME)
+	, m_dwKeepAliveInterval	(DEFALUT_TCP_KEEPALIVE_INTERVAL)
+	{
+		ASSERT(m_pListener);
+	}
+
+	virtual ~CTcpClient()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	CSEM				m_evWait;
+
+	ITcpClientListener*	m_pListener;
+	TClientCloseContext m_ccContext;
+
+	SOCKET				m_soClient;
+	SHORT				m_nEvents;
+	CONNID				m_dwConnID;
+
+	EnReuseAddressPolicy m_enReusePolicy;
+	DWORD				m_dwSyncConnectTimeout;
+	DWORD				m_dwSocketBufferSize;
+	DWORD				m_dwFreeBufferPoolSize;
+	DWORD				m_dwFreeBufferPoolHold;
+	DWORD				m_dwKeepAliveTime;
+	DWORD				m_dwKeepAliveInterval;
+	BOOL				m_bNoDelay;
+
+	EnSocketError		m_enLastError;
+	volatile BOOL		m_bConnected;
+	volatile EnServiceState	m_enState;
+
+	PVOID				m_pExtra;
+	PVOID				m_pReserved;
+
+	CBufferPtr			m_rcBuffer;
+
+protected:
+	CStringA			m_strHost;
+	USHORT				m_usPort;
+
+	CItemPool			m_itPool;
+
+private:
+	CSpinGuard			m_csState;
+
+	CCriSec				m_csSend;
+	TItemListExV		m_lsSend;
+
+	CEvt				m_evSend;
+	CEvt				m_evRecv;
+	CEvt				m_evStop;
+
+	volatile BOOL		m_bPaused;
+
+	CThread<CTcpClient, VOID, UINT> m_thWorker;
+};

+ 24 - 0
TcpPackAgent.cpp

@@ -0,0 +1,24 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "TcpPackAgent.h"

+ 221 - 0
TcpPackAgent.h

@@ -0,0 +1,221 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpAgent.h"
+#include "MiscHelper.h"
+
+template<class T> class CTcpPackAgentT : public IPackSocket, public T
+{
+	using __super = T;
+	using __super::SetConnectionReserved;
+	using __super::GetConnectionReserved;
+	using __super::GetMaxConnectionCount;
+	using __super::GetSocketBufferSize;
+	using __super::GetFreeBufferObjPool;
+	using __super::GetFreeBufferObjHold;
+	using __super::GetFreeSocketObjLockTime;
+	using __super::GetFreeSocketObjPool;
+	using __super::GetFreeSocketObjHold;
+	using __super::SetLastError;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+
+public:
+	virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+	{
+		int iNewCount = iCount + 1;
+		unique_ptr<WSABUF[]> buffers(new WSABUF[iNewCount]);
+
+		DWORD dwHeader;
+		if(!::AddPackHeader(pBuffers, iCount, buffers, m_dwMaxPackSize, m_usHeaderFlag, dwHeader))
+			return FALSE;
+
+		return __super::SendPackets(dwConnID, buffers.get(), iNewCount);
+	}
+
+protected:
+	virtual EnHandleResult DoFireConnect(TAgentSocketObj* pSocketObj)
+	{
+		EnHandleResult result = __super::DoFireConnect(pSocketObj);
+
+		if(result != HR_ERROR)
+		{
+			TBuffer* pBuffer = m_bfPool.PickFreeBuffer(pSocketObj->connID);
+			ENSURE(SetConnectionReserved(pSocketObj, TBufferPackInfo::Construct(pBuffer)));
+		}
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireHandShake(TAgentSocketObj* pSocketObj)
+	{
+		EnHandleResult result = __super::DoFireHandShake(pSocketObj);
+
+		if(result == HR_ERROR)
+			ReleaseConnectionExtra(pSocketObj);
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+	{
+		TBufferPackInfo* pInfo = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
+		ASSERT(pInfo);
+
+		TBuffer* pBuffer = (TBuffer*)pInfo->pBuffer;
+		ASSERT(pBuffer && pBuffer->IsValid());
+
+		return ParsePack(this, pInfo, pBuffer, pSocketObj, m_dwMaxPackSize, m_usHeaderFlag, pData, iLength);
+	}
+
+	virtual EnHandleResult DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+	{
+		EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
+
+		ReleaseConnectionExtra(pSocketObj);
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireShutdown()
+	{
+		EnHandleResult result = __super::DoFireShutdown();
+
+		m_bfPool.Clear();
+
+		return result;
+	}
+
+	virtual BOOL BeforeUnpause(TAgentSocketObj* pSocketObj)
+	{
+		if(!TAgentSocketObj::IsValid(pSocketObj))
+			return FALSE;
+
+		if(pSocketObj->IsPaused())
+			return TRUE;
+
+		TBufferPackInfo* pInfo = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
+		ASSERT(pInfo);
+
+		TBuffer* pBuffer = (TBuffer*)pInfo->pBuffer;
+		ASSERT(pBuffer && pBuffer->IsValid());
+
+		return (ParsePack(this, pInfo, pBuffer, pSocketObj, m_dwMaxPackSize, m_usHeaderFlag) != HR_ERROR);
+	}
+
+	virtual BOOL CheckParams()
+	{
+		if	((m_dwMaxPackSize > 0 && m_dwMaxPackSize <= TCP_PACK_MAX_SIZE_LIMIT)	&&
+			(m_usHeaderFlag >= 0 && m_usHeaderFlag <= TCP_PACK_HEADER_FLAG_LIMIT)	)
+			return __super::CheckParams();
+
+		SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	virtual void PrepareStart()
+	{
+		__super::PrepareStart();
+
+		m_bfPool.SetMaxCacheSize	(GetMaxConnectionCount());
+		m_bfPool.SetItemCapacity	(GetSocketBufferSize());
+		m_bfPool.SetItemPoolSize	(GetFreeBufferObjPool());
+		m_bfPool.SetItemPoolHold	(GetFreeBufferObjHold());
+		m_bfPool.SetBufferLockTime	(GetFreeSocketObjLockTime());
+		m_bfPool.SetBufferPoolSize	(GetFreeSocketObjPool());
+		m_bfPool.SetBufferPoolHold	(GetFreeSocketObjHold());
+
+		m_bfPool.Prepare();
+	}
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE)
+	{
+		__super::ReleaseGCSocketObj(bForce);
+
+#ifdef USE_EXTERNAL_GC
+		m_bfPool.ReleaseGCBuffer(bForce);
+#endif
+	}
+
+public:
+	virtual void SetMaxPackSize		(DWORD dwMaxPackSize)		{ENSURE_HAS_STOPPED(); m_dwMaxPackSize = dwMaxPackSize;}
+	virtual void SetPackHeaderFlag	(USHORT usPackHeaderFlag)	{ENSURE_HAS_STOPPED(); m_usHeaderFlag  = usPackHeaderFlag;}
+	virtual DWORD GetMaxPackSize	()							{return m_dwMaxPackSize;}
+	virtual USHORT GetPackHeaderFlag()							{return m_usHeaderFlag;}
+
+private:
+	void ReleaseConnectionExtra(TAgentSocketObj* pSocketObj)
+	{
+		TBufferPackInfo* pInfo = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
+
+		if(pInfo != nullptr)
+		{
+			m_bfPool.PutFreeBuffer(pInfo->pBuffer);
+			TBufferPackInfo::Destruct(pInfo);
+
+			ENSURE(SetConnectionReserved(pSocketObj, nullptr));
+		}
+	}
+
+	EnHandleResult DoFireSuperReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return __super::DoFireReceive(pSocketObj, pData, iLength);}
+
+	friend EnHandleResult ParsePack<>(CTcpPackAgentT* pThis, TBufferPackInfo* pInfo, TBuffer* pBuffer, TAgentSocketObj* pSocket, DWORD dwMaxPackSize, USHORT usPackHeaderFlag);
+
+public:
+	CTcpPackAgentT(ITcpAgentListener* pListener)
+	: T					(pListener)
+	, m_dwMaxPackSize	(TCP_PACK_DEFAULT_MAX_SIZE)
+	, m_usHeaderFlag	(TCP_PACK_DEFAULT_HEADER_FLAG)
+	{
+
+	}
+
+	virtual ~CTcpPackAgentT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	DWORD	m_dwMaxPackSize;
+	USHORT	m_usHeaderFlag;
+
+	CBufferPool m_bfPool;
+};
+
+typedef CTcpPackAgentT<CTcpAgent> CTcpPackAgent;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLAgent.h"
+typedef CTcpPackAgentT<CSSLAgent> CSSLPackAgent;
+
+#endif

+ 24 - 0
TcpPackClient.cpp

@@ -0,0 +1,24 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "TcpPackClient.h"

+ 126 - 0
TcpPackClient.h

@@ -0,0 +1,126 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpClient.h"
+#include "MiscHelper.h"
+
+template<class T> class CTcpPackClientT : public IPackClient, public T
+{
+	using __super = T;
+	using __super::SetLastError;
+	using __super::m_itPool;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+
+public:
+	virtual BOOL SendPackets(const WSABUF pBuffers[], int iCount)
+	{
+		int iNewCount = iCount + 1;
+		unique_ptr<WSABUF[]> buffers(new WSABUF[iNewCount]);
+
+		DWORD dwHeader;
+		if(!::AddPackHeader(pBuffers, iCount, buffers, m_dwMaxPackSize, m_usHeaderFlag, dwHeader))
+			return FALSE;
+
+		return __super::SendPackets(buffers.get(), iNewCount);
+	}
+
+protected:
+	virtual EnHandleResult DoFireReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
+	{
+		return ParsePack(this, &m_pkInfo, &m_lsBuffer, (CTcpPackClientT*)pSender, m_dwMaxPackSize, m_usHeaderFlag, pData, iLength);
+	}
+
+	virtual BOOL BeforeUnpause()
+	{
+		return (ParsePack(this, &m_pkInfo, &m_lsBuffer, (CTcpPackClientT*)this, m_dwMaxPackSize, m_usHeaderFlag) != HR_ERROR);
+	}
+
+	virtual BOOL CheckParams()
+	{
+		if	((m_dwMaxPackSize > 0 && m_dwMaxPackSize <= TCP_PACK_MAX_SIZE_LIMIT)	&&
+			(m_usHeaderFlag >= 0 && m_usHeaderFlag <= TCP_PACK_HEADER_FLAG_LIMIT)	)
+			return __super::CheckParams();
+
+		SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	virtual void Reset()
+	{
+		m_lsBuffer.Clear();
+		m_pkInfo.Reset();
+
+		__super::Reset();
+	}
+
+public:
+	virtual void SetMaxPackSize		(DWORD dwMaxPackSize)		{ENSURE_HAS_STOPPED(); m_dwMaxPackSize = dwMaxPackSize;}
+	virtual void SetPackHeaderFlag	(USHORT usPackHeaderFlag)	{ENSURE_HAS_STOPPED(); m_usHeaderFlag  = usPackHeaderFlag;}
+	virtual DWORD GetMaxPackSize	()							{return m_dwMaxPackSize;}
+	virtual USHORT GetPackHeaderFlag()							{return m_usHeaderFlag;}
+
+private:
+	EnHandleResult DoFireSuperReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
+		{return __super::DoFireReceive(pSender, pData, iLength);}
+
+	friend EnHandleResult ParsePack<>	(CTcpPackClientT* pThis, TPackInfo<TItemListEx>* pInfo, TItemListEx* pBuffer, CTcpPackClientT* pSocket,
+										DWORD dwMaxPackSize, USHORT usPackHeaderFlag);
+
+public:
+	CTcpPackClientT(ITcpClientListener* pListener)
+	: T					(pListener)
+	, m_dwMaxPackSize	(TCP_PACK_DEFAULT_MAX_SIZE)
+	, m_usHeaderFlag	(TCP_PACK_DEFAULT_HEADER_FLAG)
+	, m_pkInfo			(nullptr)
+	, m_lsBuffer		(m_itPool)
+	{
+
+	}
+
+	virtual ~CTcpPackClientT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	DWORD	m_dwMaxPackSize;
+	USHORT	m_usHeaderFlag;
+
+	TPackInfo<TItemListEx>	m_pkInfo;
+	TItemListEx				m_lsBuffer;
+};
+
+typedef CTcpPackClientT<CTcpClient> CTcpPackClient;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLClient.h"
+typedef CTcpPackClientT<CSSLClient> CSSLPackClient;
+
+#endif

+ 24 - 0
TcpPackServer.cpp

@@ -0,0 +1,24 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "TcpPackServer.h"

+ 221 - 0
TcpPackServer.h

@@ -0,0 +1,221 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpServer.h"
+#include "MiscHelper.h"
+
+template<class T> class CTcpPackServerT : public IPackSocket, public T
+{
+	using __super = T;
+	using __super::SetConnectionReserved;
+	using __super::GetConnectionReserved;
+	using __super::GetMaxConnectionCount;
+	using __super::GetSocketBufferSize;
+	using __super::GetFreeBufferObjPool;
+	using __super::GetFreeBufferObjHold;
+	using __super::GetFreeSocketObjLockTime;
+	using __super::GetFreeSocketObjPool;
+	using __super::GetFreeSocketObjHold;
+	using __super::SetLastError;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+
+public:
+	virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+	{
+		int iNewCount = iCount + 1;
+		unique_ptr<WSABUF[]> buffers(new WSABUF[iNewCount]);
+
+		DWORD dwHeader;
+		if(!::AddPackHeader(pBuffers, iCount, buffers, m_dwMaxPackSize, m_usHeaderFlag, dwHeader))
+			return FALSE;
+
+		return __super::SendPackets(dwConnID, buffers.get(), iNewCount);
+	}
+
+protected:
+	virtual EnHandleResult DoFireAccept(TSocketObj* pSocketObj)
+	{
+		EnHandleResult result = __super::DoFireAccept(pSocketObj);
+
+		if(result != HR_ERROR)
+		{
+			TBuffer* pBuffer = m_bfPool.PickFreeBuffer(pSocketObj->connID);
+			ENSURE(SetConnectionReserved(pSocketObj, TBufferPackInfo::Construct(pBuffer)));
+		}
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireHandShake(TSocketObj* pSocketObj)
+	{
+		EnHandleResult result = __super::DoFireHandShake(pSocketObj);
+
+		if(result == HR_ERROR)
+			ReleaseConnectionExtra(pSocketObj);
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+	{
+		TBufferPackInfo* pInfo = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
+		ASSERT(pInfo);
+
+		TBuffer* pBuffer = (TBuffer*)pInfo->pBuffer;
+		ASSERT(pBuffer && pBuffer->IsValid());
+
+		return ParsePack(this, pInfo, pBuffer, pSocketObj, m_dwMaxPackSize, m_usHeaderFlag, pData, iLength);
+	}
+
+	virtual EnHandleResult DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+	{
+		EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
+
+		ReleaseConnectionExtra(pSocketObj);
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireShutdown()
+	{
+		EnHandleResult result = __super::DoFireShutdown();
+
+		m_bfPool.Clear();
+
+		return result;
+	}
+
+	virtual BOOL BeforeUnpause(TSocketObj* pSocketObj)
+	{
+		if(!TSocketObj::IsValid(pSocketObj))
+			return FALSE;
+
+		if(pSocketObj->IsPaused())
+			return TRUE;
+
+		TBufferPackInfo* pInfo = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
+		ASSERT(pInfo);
+
+		TBuffer* pBuffer = (TBuffer*)pInfo->pBuffer;
+		ASSERT(pBuffer && pBuffer->IsValid());
+
+		return (ParsePack(this, pInfo, pBuffer, pSocketObj, m_dwMaxPackSize, m_usHeaderFlag) != HR_ERROR);
+	}
+
+	virtual BOOL CheckParams()
+	{
+		if	((m_dwMaxPackSize > 0 && m_dwMaxPackSize <= TCP_PACK_MAX_SIZE_LIMIT)	&&
+			(m_usHeaderFlag >= 0 && m_usHeaderFlag <= TCP_PACK_HEADER_FLAG_LIMIT)	)
+			return __super::CheckParams();
+
+		SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+		return FALSE;
+	}
+
+	virtual void PrepareStart()
+	{
+		__super::PrepareStart();
+
+		m_bfPool.SetMaxCacheSize	(GetMaxConnectionCount());
+		m_bfPool.SetItemCapacity	(GetSocketBufferSize());
+		m_bfPool.SetItemPoolSize	(GetFreeBufferObjPool());
+		m_bfPool.SetItemPoolHold	(GetFreeBufferObjHold());
+		m_bfPool.SetBufferLockTime	(GetFreeSocketObjLockTime());
+		m_bfPool.SetBufferPoolSize	(GetFreeSocketObjPool());
+		m_bfPool.SetBufferPoolHold	(GetFreeSocketObjHold());
+
+		m_bfPool.Prepare();
+	}
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE)
+	{
+		__super::ReleaseGCSocketObj(bForce);
+
+#ifdef USE_EXTERNAL_GC
+		m_bfPool.ReleaseGCBuffer(bForce);
+#endif
+	}
+
+public:
+	virtual void SetMaxPackSize		(DWORD dwMaxPackSize)		{ENSURE_HAS_STOPPED(); m_dwMaxPackSize = dwMaxPackSize;}
+	virtual void SetPackHeaderFlag	(USHORT usPackHeaderFlag)	{ENSURE_HAS_STOPPED(); m_usHeaderFlag  = usPackHeaderFlag;}
+	virtual DWORD GetMaxPackSize	()							{return m_dwMaxPackSize;}
+	virtual USHORT GetPackHeaderFlag()							{return m_usHeaderFlag;}
+
+private:
+	void ReleaseConnectionExtra(TSocketObj* pSocketObj)
+	{
+		TBufferPackInfo* pInfo = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
+
+		if(pInfo != nullptr)
+		{
+			m_bfPool.PutFreeBuffer(pInfo->pBuffer);
+			TBufferPackInfo::Destruct(pInfo);
+
+			ENSURE(SetConnectionReserved(pSocketObj, nullptr));
+		}
+	}
+
+	EnHandleResult DoFireSuperReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return __super::DoFireReceive(pSocketObj, pData, iLength);}
+
+	friend EnHandleResult ParsePack<>(CTcpPackServerT* pThis, TBufferPackInfo* pInfo, TBuffer* pBuffer, TSocketObj* pSocket, DWORD dwMaxPackSize, USHORT usPackHeaderFlag);
+
+public:
+	CTcpPackServerT(ITcpServerListener* pListener)
+	: T					(pListener)
+	, m_dwMaxPackSize	(TCP_PACK_DEFAULT_MAX_SIZE)
+	, m_usHeaderFlag	(TCP_PACK_DEFAULT_HEADER_FLAG)
+	{
+
+	}
+
+	virtual ~CTcpPackServerT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	DWORD	m_dwMaxPackSize;
+	USHORT	m_usHeaderFlag;
+
+	CBufferPool m_bfPool;
+};
+
+typedef CTcpPackServerT<CTcpServer> CTcpPackServer;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLServer.h"
+typedef CTcpPackServerT<CSSLServer> CSSLPackServer;
+
+#endif

+ 24 - 0
TcpPullAgent.cpp

@@ -0,0 +1,24 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "TcpPullAgent.h"

+ 173 - 0
TcpPullAgent.h

@@ -0,0 +1,173 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpAgent.h"
+#include "MiscHelper.h"
+
+template<class T> class CTcpPullAgentT : public IPullSocket, public T
+{
+	using __super = T;
+	using __super::SetConnectionReserved;
+	using __super::GetConnectionReserved;
+	using __super::GetMaxConnectionCount;
+	using __super::GetSocketBufferSize;
+	using __super::GetFreeBufferObjPool;
+	using __super::GetFreeBufferObjHold;
+	using __super::GetFreeSocketObjLockTime;
+	using __super::GetFreeSocketObjPool;
+	using __super::GetFreeSocketObjHold;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+
+public:
+	virtual EnFetchResult Fetch(CONNID dwConnID, BYTE* pData, int iLength)
+	{
+		TBuffer* pBuffer = m_bfPool[dwConnID];
+		return ::FetchBuffer(pBuffer, pData, iLength);
+	}
+
+	virtual EnFetchResult Peek(CONNID dwConnID, BYTE* pData, int iLength)
+	{
+		TBuffer* pBuffer = m_bfPool[dwConnID];
+		return ::PeekBuffer(pBuffer, pData, iLength);
+	}
+
+protected:
+	virtual EnHandleResult DoFireConnect(TAgentSocketObj* pSocketObj)
+	{
+		EnHandleResult result = __super::DoFireConnect(pSocketObj);
+
+		if(result != HR_ERROR)
+		{
+			TBuffer* pBuffer = m_bfPool.PutCacheBuffer(pSocketObj->connID);
+			ENSURE(SetConnectionReserved(pSocketObj, pBuffer));
+		}
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireHandShake(TAgentSocketObj* pSocketObj)
+	{
+		EnHandleResult result = __super::DoFireHandShake(pSocketObj);
+
+		if(result == HR_ERROR)
+			ReleaseConnectionExtra(pSocketObj);
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
+	{
+		TBuffer* pBuffer = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pBuffer);
+		ASSERT(pBuffer && pBuffer->IsValid());
+
+		pBuffer->Cat(pData, iLength);
+
+		return __super::DoFireReceive(pSocketObj, pBuffer->Length());
+	}
+
+	virtual EnHandleResult DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+	{
+		EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
+
+		ReleaseConnectionExtra(pSocketObj);
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireShutdown()
+	{
+		EnHandleResult result = __super::DoFireShutdown();
+
+		m_bfPool.Clear();
+
+		return result;
+	}
+
+	virtual void PrepareStart()
+	{
+		__super::PrepareStart();
+
+		m_bfPool.SetMaxCacheSize	(GetMaxConnectionCount());
+		m_bfPool.SetItemCapacity	(GetSocketBufferSize());
+		m_bfPool.SetItemPoolSize	(GetFreeBufferObjPool());
+		m_bfPool.SetItemPoolHold	(GetFreeBufferObjHold());
+		m_bfPool.SetBufferLockTime	(GetFreeSocketObjLockTime());
+		m_bfPool.SetBufferPoolSize	(GetFreeSocketObjPool());
+		m_bfPool.SetBufferPoolHold	(GetFreeSocketObjHold());
+
+		m_bfPool.Prepare();
+	}
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE)
+	{
+		__super::ReleaseGCSocketObj(bForce);
+
+#ifdef USE_EXTERNAL_GC
+		m_bfPool.ReleaseGCBuffer(bForce);
+#endif
+	}
+
+private:
+	void ReleaseConnectionExtra(TAgentSocketObj* pSocketObj)
+	{
+		TBuffer* pBuffer = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pBuffer);
+
+		if(pBuffer != nullptr)
+		{
+			m_bfPool.PutFreeBuffer(pBuffer);
+			ENSURE(SetConnectionReserved(pSocketObj, nullptr));
+		}
+	}
+
+public:
+	CTcpPullAgentT(ITcpAgentListener* pListener)
+	: T(pListener)
+	{
+
+	}
+
+	virtual ~CTcpPullAgentT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	CBufferPool m_bfPool;
+};
+
+typedef CTcpPullAgentT<CTcpAgent> CTcpPullAgent;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLAgent.h"
+typedef CTcpPullAgentT<CSSLAgent> CSSLPullAgent;
+
+#endif

+ 24 - 0
TcpPullClient.cpp

@@ -0,0 +1,24 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "TcpPullClient.h"

+ 89 - 0
TcpPullClient.h

@@ -0,0 +1,89 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpClient.h"
+#include "MiscHelper.h"
+
+template<class T> class CTcpPullClientT : public IPullClient, public T
+{
+	using __super = T;
+	using __super::m_itPool;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+
+public:
+	virtual EnFetchResult Fetch(BYTE* pData, int iLength)
+	{
+		return ::FetchBuffer(&m_lsBuffer, pData, iLength);
+	}
+
+	virtual EnFetchResult Peek(BYTE* pData, int iLength)
+	{
+		return ::PeekBuffer(&m_lsBuffer, pData, iLength);
+	}
+
+protected:
+	virtual EnHandleResult DoFireReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
+	{
+		m_lsBuffer.Cat(pData, iLength);
+
+		return __super::DoFireReceive(pSender, m_lsBuffer.Length());
+	}
+
+	virtual void Reset()
+	{
+		m_lsBuffer.Clear();
+
+		__super::Reset();
+	}
+
+public:
+	CTcpPullClientT(ITcpClientListener* pListener)
+	: T			(pListener)
+	, m_lsBuffer(m_itPool)
+	{
+
+	}
+
+	virtual ~CTcpPullClientT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	TItemListEx	m_lsBuffer;
+};
+
+typedef CTcpPullClientT<CTcpClient> CTcpPullClient;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLClient.h"
+typedef CTcpPullClientT<CSSLClient> CSSLPullClient;
+
+#endif

+ 24 - 0
TcpPullServer.cpp

@@ -0,0 +1,24 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "TcpPullServer.h"

+ 173 - 0
TcpPullServer.h

@@ -0,0 +1,173 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "TcpServer.h"
+#include "MiscHelper.h"
+
+template<class T> class CTcpPullServerT : public IPullSocket, public T
+{
+	using __super = T;
+	using __super::SetConnectionReserved;
+	using __super::GetConnectionReserved;
+	using __super::GetMaxConnectionCount;
+	using __super::GetSocketBufferSize;
+	using __super::GetFreeBufferObjPool;
+	using __super::GetFreeBufferObjHold;
+	using __super::GetFreeSocketObjLockTime;
+	using __super::GetFreeSocketObjPool;
+	using __super::GetFreeSocketObjHold;
+
+public:
+	using __super::Stop;
+	using __super::Wait;
+	using __super::GetState;
+
+public:
+	virtual EnFetchResult Fetch(CONNID dwConnID, BYTE* pData, int iLength)
+	{
+		TBuffer* pBuffer = m_bfPool[dwConnID];
+		return ::FetchBuffer(pBuffer, pData, iLength);
+	}
+
+	virtual EnFetchResult Peek(CONNID dwConnID, BYTE* pData, int iLength)
+	{
+		TBuffer* pBuffer = m_bfPool[dwConnID];
+		return ::PeekBuffer(pBuffer, pData, iLength);
+	}
+
+protected:
+	virtual EnHandleResult DoFireAccept(TSocketObj* pSocketObj)
+	{
+		EnHandleResult result = __super::DoFireAccept(pSocketObj);
+
+		if(result != HR_ERROR)
+		{
+			TBuffer* pBuffer = m_bfPool.PutCacheBuffer(pSocketObj->connID);
+			ENSURE(SetConnectionReserved(pSocketObj, pBuffer));
+		}
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireHandShake(TSocketObj* pSocketObj)
+	{
+		EnHandleResult result = __super::DoFireHandShake(pSocketObj);
+
+		if(result == HR_ERROR)
+			ReleaseConnectionExtra(pSocketObj);
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+	{
+		TBuffer* pBuffer = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pBuffer);
+		ASSERT(pBuffer && pBuffer->IsValid());
+
+		pBuffer->Cat(pData, iLength);
+
+		return __super::DoFireReceive(pSocketObj, pBuffer->Length());
+	}
+
+	virtual EnHandleResult DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+	{
+		EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
+
+		ReleaseConnectionExtra(pSocketObj);
+
+		return result;
+	}
+
+	virtual EnHandleResult DoFireShutdown()
+	{
+		EnHandleResult result = __super::DoFireShutdown();
+
+		m_bfPool.Clear();
+
+		return result;
+	}
+
+	virtual void PrepareStart()
+	{
+		__super::PrepareStart();
+
+		m_bfPool.SetMaxCacheSize	(GetMaxConnectionCount());
+		m_bfPool.SetItemCapacity	(GetSocketBufferSize());
+		m_bfPool.SetItemPoolSize	(GetFreeBufferObjPool());
+		m_bfPool.SetItemPoolHold	(GetFreeBufferObjHold());
+		m_bfPool.SetBufferLockTime	(GetFreeSocketObjLockTime());
+		m_bfPool.SetBufferPoolSize	(GetFreeSocketObjPool());
+		m_bfPool.SetBufferPoolHold	(GetFreeSocketObjHold());
+
+		m_bfPool.Prepare();
+	}
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE)
+	{
+		__super::ReleaseGCSocketObj(bForce);
+
+#ifdef USE_EXTERNAL_GC
+		m_bfPool.ReleaseGCBuffer(bForce);
+#endif
+	}
+
+private:
+	void ReleaseConnectionExtra(TSocketObj* pSocketObj)
+	{
+		TBuffer* pBuffer = nullptr;
+		GetConnectionReserved(pSocketObj, (PVOID*)&pBuffer);
+
+		if(pBuffer != nullptr)
+		{
+			m_bfPool.PutFreeBuffer(pBuffer);
+			ENSURE(SetConnectionReserved(pSocketObj, nullptr));
+		}
+	}
+
+public:
+	CTcpPullServerT(ITcpServerListener* pListener)
+	: T(pListener)
+	{
+
+	}
+
+	virtual ~CTcpPullServerT()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	CBufferPool m_bfPool;
+};
+
+typedef CTcpPullServerT<CTcpServer> CTcpPullServer;
+
+#ifdef _SSL_SUPPORT
+
+#include "SSLServer.h"
+typedef CTcpPullServerT<CSSLServer> CSSLPullServer;
+
+#endif

+ 1187 - 0
TcpServer.cpp

@@ -0,0 +1,1187 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "TcpServer.h"
+
+#include "./common/FileHelper.h"
+
+BOOL CTcpServer::Start(LPCTSTR lpszBindAddress, USHORT usPort)
+{
+	if(!CheckParams() || !CheckStarting())
+		return FALSE;
+
+	PrepareStart();
+
+	if(CreateListenSocket(lpszBindAddress, usPort))
+		if(CreateWorkerThreads())
+			if(StartAccept())
+			{
+				m_enState = SS_STARTED;
+				return TRUE;
+			}
+
+	EXECUTE_RESTORE_ERROR(Stop());
+
+	return FALSE;
+}
+
+void CTcpServer::SetLastError(EnSocketError code, LPCSTR func, int ec)
+{
+	m_enLastError = code;
+	::SetLastError(ec);
+}
+
+BOOL CTcpServer::CheckParams()
+{
+	if	((m_enSendPolicy >= SP_PACK && m_enSendPolicy <= SP_DIRECT)								&&
+		(m_enOnSendSyncPolicy >= OSSP_NONE && m_enOnSendSyncPolicy <= OSSP_RECEIVE)				&&
+		((int)m_dwMaxConnectionCount > 0 && m_dwMaxConnectionCount <= MAX_CONNECTION_COUNT)		&&
+		((int)m_dwWorkerThreadCount > 0 && m_dwWorkerThreadCount <= MAX_WORKER_THREAD_COUNT)	&&
+		((int)m_dwAcceptSocketCount > 0)														&&
+		((int)m_dwSocketBufferSize >= MIN_SOCKET_BUFFER_SIZE)									&&
+		((int)m_dwSocketListenQueue > 0)														&&
+		((int)m_dwFreeSocketObjLockTime >= 1000)												&&
+		((int)m_dwFreeSocketObjPool >= 0)														&&
+		((int)m_dwFreeBufferObjPool >= 0)														&&
+		((int)m_dwFreeSocketObjHold >= 0)														&&
+		((int)m_dwFreeBufferObjHold >= 0)														&&
+		((int)m_dwKeepAliveTime >= 1000 || m_dwKeepAliveTime == 0)								&&
+		((int)m_dwKeepAliveInterval >= 1000 || m_dwKeepAliveInterval == 0)						)
+		return TRUE;
+
+	SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+	return FALSE;
+}
+
+void CTcpServer::PrepareStart()
+{
+	m_bfActiveSockets.Reset(m_dwMaxConnectionCount);
+	m_lsFreeSocket.Reset(m_dwFreeSocketObjPool);
+
+	m_bfObjPool.SetItemCapacity(m_dwSocketBufferSize);
+	m_bfObjPool.SetPoolSize(m_dwFreeBufferObjPool);
+	m_bfObjPool.SetPoolHold(m_dwFreeBufferObjHold);
+
+	m_bfObjPool.Prepare();
+
+	m_rcBuffers = make_unique<CBufferPtr[]>(m_dwWorkerThreadCount);
+	for_each(m_rcBuffers.get(), m_rcBuffers.get() + m_dwWorkerThreadCount, [this](CBufferPtr& buff) {buff.Malloc(m_dwSocketBufferSize);});
+
+	m_soListens = make_unique<SOCKET[]>(m_dwWorkerThreadCount);
+	for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock) {sock = INVALID_FD;});
+}
+
+BOOL CTcpServer::CheckStarting()
+{
+	CSpinLock locallock(m_csState);
+
+	if(m_enState == SS_STOPPED)
+		m_enState = SS_STARTING;
+	else
+	{
+		SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::CheckStoping()
+{
+	if(m_enState != SS_STOPPED)
+	{
+		CSpinLock locallock(m_csState);
+
+		if(HasStarted())
+		{
+			m_enState = SS_STOPPING;
+			return TRUE;
+		}
+	}
+
+	SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+
+	return FALSE;
+}
+
+BOOL CTcpServer::CreateListenSocket(LPCTSTR lpszBindAddress, USHORT usPort)
+{
+	if(::IsStrEmpty(lpszBindAddress))
+		lpszBindAddress = DEFAULT_IPV4_BIND_ADDRESS;
+
+	HP_SOCKADDR addr;
+
+	if(!::sockaddr_A_2_IN(lpszBindAddress, usPort, addr))
+	{
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+
+	for(DWORD i = 0; i < m_dwWorkerThreadCount; i++)
+	{
+		m_soListens[i]  = socket(addr.family, SOCK_STREAM, IPPROTO_TCP);
+		SOCKET soListen = m_soListens[i];
+
+		if(IS_INVALID_FD(soListen))
+		{
+			SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+
+		::fcntl_SETFL(soListen, O_NOATIME | O_NONBLOCK | O_CLOEXEC);
+
+		BOOL bOnOff	= (m_dwKeepAliveTime > 0 && m_dwKeepAliveInterval > 0);
+		VERIFY(IS_NO_ERROR(::SSO_KeepAliveVals(soListen, bOnOff, m_dwKeepAliveTime, m_dwKeepAliveInterval)));
+		VERIFY(IS_NO_ERROR(::SSO_ReuseAddress(soListen, m_enReusePolicy)));
+		VERIFY(IS_NO_ERROR(::SSO_NoDelay(soListen, m_bNoDelay)));
+
+		if(IS_HAS_ERROR(::bind(soListen, addr.Addr(), addr.AddrSize())))
+		{
+			SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+
+		if(TRIGGER(FirePrepareListen(soListen)) == HR_ERROR)
+		{
+			SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
+			return FALSE;
+		}
+
+		if(IS_HAS_ERROR(::listen(soListen, m_dwSocketListenQueue)))
+		{
+			SetLastError(SE_SOCKET_LISTEN, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::CreateWorkerThreads()
+{
+	DWORD dwWorkerThreadCount = m_dwWorkerThreadCount
+#ifdef USE_EXTERNAL_GC
+														+ 1
+#endif
+														;
+
+	if(!m_ioDispatcher.Start(this, m_dwAcceptSocketCount, dwWorkerThreadCount))
+	{
+		SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::StartAccept()
+{
+	for(int i = 0; i < (int)m_dwWorkerThreadCount; i++)
+	{
+		SOCKET& soListen = m_soListens[i];
+
+		if(!m_ioDispatcher.AddFD(i, soListen, EPOLLIN | EPOLLET, TO_PVOID(&soListen)))
+		{
+			SetLastError(SE_SOCKE_ATTACH_TO_CP, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+	}
+
+#ifdef USE_EXTERNAL_GC
+	m_fdGCTimer = m_ioDispatcher.AddTimer(m_dwWorkerThreadCount, GC_CHECK_INTERVAL, this);
+
+	if(IS_INVALID_FD(m_fdGCTimer))
+	{
+		SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+#endif
+
+	return TRUE;
+}
+
+BOOL CTcpServer::Stop()
+{
+	if(!CheckStoping())
+		return FALSE;
+
+	CloseListenSocket();
+
+	DisconnectClientSocket();
+	WaitForClientSocketClose();
+	WaitForWorkerThreadEnd();
+	
+	ReleaseClientSocket();
+
+	FireShutdown();
+
+	ReleaseFreeSocket();
+
+	Reset();
+
+	return TRUE;
+}
+
+void CTcpServer::CloseListenSocket()
+{
+	if(m_soListens)
+	{
+		for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock)
+		{
+			if(sock != INVALID_FD)
+			{
+				::ManualCloseSocket(sock);
+				sock = INVALID_FD;
+			}
+		});
+
+		::WaitFor(100);
+	}
+}
+
+void CTcpServer::DisconnectClientSocket()
+{
+	if(m_bfActiveSockets.Elements() == 0)
+		return;
+
+	TSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+		Disconnect(*it);
+}
+
+void CTcpServer::WaitForClientSocketClose()
+{
+	while(m_bfActiveSockets.Elements() > 0)
+		::WaitFor(50);
+}
+
+void CTcpServer::WaitForWorkerThreadEnd()
+{
+	m_ioDispatcher.Stop();
+}
+
+void CTcpServer::ReleaseClientSocket()
+{
+	VERIFY(m_bfActiveSockets.IsEmpty());
+	m_bfActiveSockets.Reset();
+}
+
+void CTcpServer::ReleaseFreeSocket()
+{
+	m_lsFreeSocket.Clear();
+
+#ifdef USE_EXTERNAL_GC
+	if(IS_VALID_FD(m_fdGCTimer))
+	{
+		close(m_fdGCTimer);
+		m_fdGCTimer = INVALID_FD;
+	}
+#endif
+
+	ReleaseGCSocketObj(TRUE);
+	VERIFY(m_lsGCSocket.IsEmpty());
+}
+
+void CTcpServer::Reset()
+{
+	m_phSocket.Reset();
+	m_bfObjPool.Clear();
+
+	m_rcBuffers = nullptr;
+	m_soListens = nullptr;
+
+	m_enState = SS_STOPPED;
+
+	m_evWait.SyncNotifyAll();
+}
+
+TSocketObj* CTcpServer::GetFreeSocketObj(CONNID dwConnID, SOCKET soClient)
+{
+	DWORD dwIndex;
+	TSocketObj* pSocketObj = nullptr;
+
+	if(m_lsFreeSocket.TryLock(&pSocketObj, dwIndex))
+	{
+		if(::GetTimeGap32(pSocketObj->freeTime) >= m_dwFreeSocketObjLockTime)
+			VERIFY(m_lsFreeSocket.ReleaseLock(nullptr, dwIndex));
+		else
+		{
+			VERIFY(m_lsFreeSocket.ReleaseLock(pSocketObj, dwIndex));
+			pSocketObj = nullptr;
+		}
+	}
+
+	if(!pSocketObj) pSocketObj = CreateSocketObj();
+	pSocketObj->Reset(dwConnID, soClient);
+
+	return pSocketObj;
+}
+
+TSocketObj* CTcpServer::CreateSocketObj()
+{
+	return TSocketObj::Construct(m_phSocket, m_bfObjPool);
+}
+
+void CTcpServer::DeleteSocketObj(TSocketObj* pSocketObj)
+{
+	TSocketObj::Destruct(pSocketObj);
+}
+
+void CTcpServer::AddFreeSocketObj(TSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode)
+{
+	if(!InvalidSocketObj(pSocketObj))
+		return;
+
+	CloseClientSocketObj(pSocketObj, enFlag, enOperation, iErrorCode);
+
+	m_bfActiveSockets.Remove(pSocketObj->connID);
+	TSocketObj::Release(pSocketObj);
+
+#ifndef USE_EXTERNAL_GC
+	ReleaseGCSocketObj();
+#endif
+
+	if(!m_lsFreeSocket.TryPut(pSocketObj))
+		m_lsGCSocket.PushBack(pSocketObj);
+}
+
+void CTcpServer::ReleaseGCSocketObj(BOOL bForce)
+{
+	::ReleaseGCObj(m_lsGCSocket, m_dwFreeSocketObjLockTime, bForce);
+}
+
+BOOL CTcpServer::InvalidSocketObj(TSocketObj* pSocketObj)
+{
+	return TSocketObj::InvalidSocketObj(pSocketObj);
+}
+
+void CTcpServer::AddClientSocketObj(CONNID dwConnID, TSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr)
+{
+	ASSERT(FindSocketObj(dwConnID) == nullptr);
+
+	pSocketObj->connTime	= ::TimeGetTime();
+	pSocketObj->activeTime	= pSocketObj->connTime;
+
+	remoteAddr.Copy(pSocketObj->remoteAddr);
+	pSocketObj->SetConnected();
+
+	VERIFY(m_bfActiveSockets.ReleaseLock(dwConnID, pSocketObj));
+}
+
+TSocketObj* CTcpServer::FindSocketObj(CONNID dwConnID)
+{
+	TSocketObj* pSocketObj = nullptr;
+
+	if(m_bfActiveSockets.Get(dwConnID, &pSocketObj) != TSocketObjPtrPool::GR_VALID)
+		pSocketObj = nullptr;
+
+	return pSocketObj;
+}
+
+void CTcpServer::CloseClientSocketObj(TSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode, int iShutdownFlag)
+{
+	ASSERT(TSocketObj::IsExist(pSocketObj));
+
+	if(enFlag == SCF_CLOSE)
+		FireClose(pSocketObj, SO_CLOSE, SE_OK);
+	else if(enFlag == SCF_ERROR)
+		FireClose(pSocketObj, enOperation, iErrorCode);
+
+	SOCKET socket = pSocketObj->socket;
+	pSocketObj->socket = INVALID_SOCKET;
+
+	::ManualCloseSocket(socket, iShutdownFlag);
+}
+
+BOOL CTcpServer::GetListenAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	if(!HasStarted())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return ::GetSocketLocalAddress(m_soListens[0], lpszAddress, iAddressLen, usPort);
+}
+
+BOOL CTcpServer::GetLocalAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return ::GetSocketLocalAddress(pSocketObj->socket, lpszAddress, iAddressLen, usPort);
+}
+
+BOOL CTcpServer::GetRemoteAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsExist(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	ADDRESS_FAMILY usFamily;
+	return ::sockaddr_IN_2_A(pSocketObj->remoteAddr, usFamily, lpszAddress, iAddressLen, usPort);
+}
+
+BOOL CTcpServer::SetConnectionExtra(CONNID dwConnID, PVOID pExtra)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return SetConnectionExtra(pSocketObj, pExtra);
+}
+
+BOOL CTcpServer::SetConnectionExtra(TSocketObj* pSocketObj, PVOID pExtra)
+{
+	if(!TSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		pSocketObj->extra = pExtra;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpServer::GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return GetConnectionExtra(pSocketObj, ppExtra);
+}
+
+BOOL CTcpServer::GetConnectionExtra(TSocketObj* pSocketObj, PVOID* ppExtra)
+{
+	ASSERT(ppExtra != nullptr);
+
+	if(!TSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*ppExtra = pSocketObj->extra;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpServer::SetConnectionReserved(CONNID dwConnID, PVOID pReserved)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return SetConnectionReserved(pSocketObj, pReserved);
+}
+
+BOOL CTcpServer::SetConnectionReserved(TSocketObj* pSocketObj, PVOID pReserved)
+{
+	if(!TSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		pSocketObj->reserved = pReserved;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpServer::GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return GetConnectionReserved(pSocketObj, ppReserved);
+}
+
+BOOL CTcpServer::GetConnectionReserved(TSocketObj* pSocketObj, PVOID* ppReserved)
+{
+	ASSERT(ppReserved != nullptr);
+
+	if(!TSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*ppReserved = pSocketObj->reserved;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpServer::SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return SetConnectionReserved2(pSocketObj, pReserved2);
+}
+
+BOOL CTcpServer::SetConnectionReserved2(TSocketObj* pSocketObj, PVOID pReserved2)
+{
+	if(!TSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		pSocketObj->reserved2 = pReserved2;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpServer::GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return GetConnectionReserved2(pSocketObj, ppReserved2);
+}
+
+BOOL CTcpServer::GetConnectionReserved2(TSocketObj* pSocketObj, PVOID* ppReserved2)
+{
+	ASSERT(ppReserved2 != nullptr);
+
+	if(!TSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*ppReserved2 = pSocketObj->reserved2;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpServer::IsPauseReceive(CONNID dwConnID, BOOL& bPaused)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		bPaused = pSocketObj->paused;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpServer::IsConnected(CONNID dwConnID)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return pSocketObj->HasConnected();
+}
+
+BOOL CTcpServer::GetPendingDataLength(CONNID dwConnID, int& iPending)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		iPending = pSocketObj->Pending();
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+DWORD CTcpServer::GetConnectionCount()
+{
+	return m_bfActiveSockets.Elements();
+}
+
+BOOL CTcpServer::GetAllConnectionIDs(CONNID pIDs[], DWORD& dwCount)
+{
+	return m_bfActiveSockets.GetAllElementIndexes(pIDs, dwCount);
+}
+
+BOOL CTcpServer::GetConnectPeriod(CONNID dwConnID, DWORD& dwPeriod)
+{
+	BOOL isOK				= TRUE;
+	TSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(TSocketObj::IsValid(pSocketObj))
+		dwPeriod = ::GetTimeGap32(pSocketObj->connTime);
+	else
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+BOOL CTcpServer::GetSilencePeriod(CONNID dwConnID, DWORD& dwPeriod)
+{
+	if(!m_bMarkSilence)
+		return FALSE;
+
+	BOOL isOK				= TRUE;
+	TSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(TSocketObj::IsValid(pSocketObj))
+		dwPeriod = ::GetTimeGap32(pSocketObj->activeTime);
+	else
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+BOOL CTcpServer::Disconnect(CONNID dwConnID, BOOL bForce)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_DISCONNECT, dwConnID, bForce);
+}
+
+BOOL CTcpServer::DisconnectLongConnections(DWORD dwPeriod, BOOL bForce)
+{
+	if(dwPeriod > MAX_CONNECTION_PERIOD)
+		return FALSE;
+	if(m_bfActiveSockets.Elements() == 0)
+		return TRUE;
+
+	DWORD now = ::TimeGetTime();
+
+	TSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+	{
+		CONNID connID			= *it;
+		TSocketObj* pSocketObj	= FindSocketObj(connID);
+
+		if(TSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->connTime) >= (int)dwPeriod)
+			Disconnect(connID, bForce);
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce)
+{
+	if(!m_bMarkSilence)
+		return FALSE;
+	if(dwPeriod > MAX_CONNECTION_PERIOD)
+		return FALSE;
+	if(m_bfActiveSockets.Elements() == 0)
+		return TRUE;
+
+	DWORD now = ::TimeGetTime();
+
+	TSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+	{
+		CONNID connID			= *it;
+		TSocketObj* pSocketObj	= FindSocketObj(connID);
+
+		if(TSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->activeTime) >= (int)dwPeriod)
+			Disconnect(connID, bForce);
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::PauseReceive(CONNID dwConnID, BOOL bPause)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	if(pSocketObj->paused == bPause)
+		return TRUE;
+
+	pSocketObj->paused = bPause;
+
+	if(!bPause)
+		return m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_UNPAUSE, pSocketObj->connID);
+
+	return TRUE;
+}
+
+BOOL CTcpServer::OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	if(pv == &m_soListens[pContext->GetIndex()])
+	{
+		HandleAccept(pContext, events);
+		return FALSE;
+	}
+	else if(pv == this)
+	{
+		ReleaseGCSocketObj(FALSE);
+		::ReadTimer(m_fdGCTimer);
+
+		return FALSE;
+	}
+
+	TSocketObj* pSocketObj = (TSocketObj*)(pv);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+		return FALSE;
+
+	if(events & _EPOLL_ALL_ERROR_EVENTS)
+		pSocketObj->SetConnected(FALSE);
+
+	pSocketObj->Increment();
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		pSocketObj->Decrement();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+VOID CTcpServer::OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)
+{
+	TSocketObj* pSocketObj = (TSocketObj*)(pv);
+
+	if(TSocketObj::IsValid(pSocketObj))
+	{
+		ASSERT(rs && !(events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)));
+
+		UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN);
+		m_ioDispatcher.ModFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj);
+	}
+
+	pSocketObj->Decrement();
+}
+
+VOID CTcpServer::OnCommand(const TDispContext* pContext, TDispCommand* pCmd)
+{
+	switch(pCmd->type)
+	{
+	case DISP_CMD_SEND:
+		HandleCmdSend(pContext, (CONNID)(pCmd->wParam));
+		break;
+	case DISP_CMD_UNPAUSE:
+		HandleCmdUnpause(pContext, (CONNID)(pCmd->wParam));
+		break;
+	case DISP_CMD_DISCONNECT:
+		HandleCmdDisconnect(pContext, (CONNID)(pCmd->wParam), (BOOL)pCmd->lParam);
+		break;
+	}
+}
+
+VOID CTcpServer::HandleCmdSend(const TDispContext* pContext, CONNID dwConnID)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(TSocketObj::IsValid(pSocketObj) && pSocketObj->IsPending())
+		m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLOUT);
+}
+
+VOID CTcpServer::HandleCmdUnpause(const TDispContext* pContext, CONNID dwConnID)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+		return;
+
+	if(BeforeUnpause(pSocketObj))
+		m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLIN);
+	else
+		AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
+}
+
+VOID CTcpServer::HandleCmdDisconnect(const TDispContext* pContext, CONNID dwConnID, BOOL bForce)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(TSocketObj::IsValid(pSocketObj))
+		m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLHUP);
+}
+
+BOOL CTcpServer::OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleReceive(pContext, (TSocketObj*)pv, RETRIVE_EVENT_FLAG_H(events));
+}
+
+BOOL CTcpServer::OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleSend(pContext, (TSocketObj*)pv, RETRIVE_EVENT_FLAG_H(events));
+}
+
+BOOL CTcpServer::OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleClose(pContext, (TSocketObj*)pv, SCF_CLOSE, events);
+}
+
+BOOL CTcpServer::OnError(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleClose(pContext, (TSocketObj*)pv, SCF_ERROR, events);
+}
+
+VOID CTcpServer::OnDispatchThreadStart(THR_ID tid)
+{
+	OnWorkerThreadStart(tid);
+}
+
+VOID CTcpServer::OnDispatchThreadEnd(THR_ID tid)
+{
+	OnWorkerThreadEnd(tid);
+}
+
+BOOL CTcpServer::HandleClose(const TDispContext* pContext, TSocketObj* pSocketObj, EnSocketCloseFlag enFlag, UINT events)
+{
+	EnSocketOperation enOperation = SO_CLOSE;
+
+	if(events & _EPOLL_HUNGUP_EVENTS)
+		enOperation = SO_CLOSE;
+	else if(events & EPOLLIN)
+		enOperation = SO_RECEIVE;
+	else if(events & EPOLLOUT)
+		enOperation = SO_SEND;
+
+	int iErrorCode = 0;
+
+	if(enFlag == SCF_ERROR)
+		iErrorCode = ::SSO_GetError(pSocketObj->socket);
+
+	AddFreeSocketObj(pSocketObj, enFlag, enOperation, iErrorCode);
+
+	return TRUE;
+}
+
+BOOL CTcpServer::HandleAccept(const TDispContext* pContext, UINT events)
+{
+	if(events & _EPOLL_ALL_ERROR_EVENTS)
+	{
+		ASSERT(!HasStarted());
+		return FALSE;
+	}
+
+	while(TRUE)
+	{
+		HP_SOCKADDR addr;
+
+		socklen_t addrLen	= (socklen_t)addr.AddrSize();
+		SOCKET soClient		= ::accept(m_soListens[pContext->GetIndex()], addr.Addr(), &addrLen);
+
+		if(soClient == INVALID_SOCKET)
+		{
+			int code = ::WSAGetLastError();
+
+			switch(code)
+			{
+			case ERROR_WOULDBLOCK		: return TRUE;
+			case ERROR_CONNABORTED		: continue;
+			case ERROR_HANDLES_CLOSED	: return FALSE;
+			default						: ERROR_EXIT2(EXIT_CODE_SOFTWARE, code);
+			}
+		}
+
+		if(!::fcntl_SETFL(soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC))
+		{
+			::ManualCloseSocket(soClient, SHUT_RDWR);
+			continue;
+		}
+
+		CONNID dwConnID = 0;
+
+		if(!m_bfActiveSockets.AcquireLock(dwConnID))
+		{
+			::ManualCloseSocket(soClient, SHUT_RDWR);
+			continue;
+		}
+
+		TSocketObj* pSocketObj = GetFreeSocketObj(dwConnID, soClient);
+
+		AddClientSocketObj(dwConnID, pSocketObj, addr);
+
+		if(TRIGGER(FireAccept(pSocketObj)) == HR_ERROR)
+		{
+			AddFreeSocketObj(pSocketObj, SCF_NONE);
+			continue;
+		}
+
+		UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN);
+
+		if(!m_ioDispatcher.AddFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj))
+		{
+			AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_ACCEPT, ::WSAGetLastError());
+			continue;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::HandleReceive(const TDispContext* pContext, TSocketObj* pSocketObj, int flag)
+{
+	ASSERT(TSocketObj::IsValid(pSocketObj));
+
+	if(m_bMarkSilence) pSocketObj->activeTime = ::TimeGetTime();
+
+	CBufferPtr& buffer = m_rcBuffers[pContext->GetIndex()];
+
+	int reads = flag ? -1 : MAX_CONTINUE_READS;
+
+	for(int i = 0; i < reads || reads < 0; i++)
+	{
+		if(pSocketObj->paused)
+			break;
+
+		int rc = (int)read(pSocketObj->socket, buffer.Ptr(), buffer.Size());
+
+		if(rc > 0)
+		{
+			if(TRIGGER(FireReceive(pSocketObj, buffer.Ptr(), rc)) == HR_ERROR)
+			{
+				TRACE("<S-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", pSocketObj->connID);
+
+				AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
+				return FALSE;
+			}
+		}
+		else if(rc == 0)
+		{
+			AddFreeSocketObj(pSocketObj, SCF_CLOSE, SO_RECEIVE, SE_OK);
+			return FALSE;
+		}
+		else
+		{
+			ASSERT(rc == SOCKET_ERROR);
+
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+				break;
+
+			AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, code);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::HandleSend(const TDispContext* pContext, TSocketObj* pSocketObj, int flag)
+{
+	ASSERT(TSocketObj::IsValid(pSocketObj));
+
+	if(!pSocketObj->IsPending())
+		return TRUE;
+
+	BOOL bBlocked	= FALSE;
+	int writes		= flag ? -1 : MAX_CONTINUE_WRITES;
+
+	TBufferObjList& sndBuff = pSocketObj->sndBuff;
+	TItemPtr itPtr(sndBuff);
+
+	for(int i = 0; i < writes || writes < 0; i++)
+	{
+		{
+			CReentrantCriSecLock locallock(pSocketObj->csSend);
+			itPtr = sndBuff.PopFront();
+		}
+
+		if(!itPtr.IsValid())
+			break;
+
+		ASSERT(!itPtr->IsEmpty());
+
+		if(!SendItem(pSocketObj, itPtr, bBlocked))
+			return FALSE;
+
+		if(bBlocked)
+		{
+			ASSERT(!itPtr->IsEmpty());
+
+			CReentrantCriSecLock locallock(pSocketObj->csSend);
+			sndBuff.PushFront(itPtr.Detach());
+
+			break;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::SendItem(TSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked)
+{
+	while(!pItem->IsEmpty())
+	{
+		int rc = (int)write(pSocketObj->socket, pItem->Ptr(), pItem->Size());
+
+		if(rc > 0)
+		{
+			if(TRIGGER(FireSend(pSocketObj, pItem->Ptr(), rc)) == HR_ERROR)
+			{
+				TRACE("<S-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", pSocketObj->connID);
+				ASSERT(FALSE);
+			}
+
+			pItem->Reduce(rc);
+		}
+		else if(rc == SOCKET_ERROR)
+		{
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+			{
+				bBlocked = TRUE;
+				break;
+			}
+			else
+			{
+				AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_SEND, code);
+				return FALSE;
+			}
+		}
+		else
+			ASSERT(FALSE);
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::Send(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	ASSERT(pBuffer && iLength > 0);
+
+	if(iOffset != 0) pBuffer += iOffset;
+
+	WSABUF buffer;
+	buffer.len = iLength;
+	buffer.buf = (BYTE*)pBuffer;
+
+	return SendPackets(dwConnID, &buffer, 1);
+}
+
+BOOL CTcpServer::DoSendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return DoSendPackets(pSocketObj, pBuffers, iCount);
+}
+
+BOOL CTcpServer::DoSendPackets(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pSocketObj && pBuffers && iCount > 0);
+
+	int result = NO_ERROR;
+
+	if(pBuffers && iCount > 0)
+	{
+		CLocalSafeCounter localcounter(*pSocketObj);
+		CReentrantCriSecLock locallock(pSocketObj->csSend);
+
+		if(TSocketObj::IsValid(pSocketObj))
+			result = SendInternal(pSocketObj, pBuffers, iCount);
+		else
+			result = ERROR_OBJECT_NOT_FOUND;
+	}
+	else
+		result = ERROR_INVALID_PARAMETER;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+int CTcpServer::SendInternal(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount)
+{
+	BOOL bPending = pSocketObj->IsPending();
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			pSocketObj->sndBuff.Cat(pBuffer, iBufLen);
+			ASSERT(pSocketObj->sndBuff.Length() > 0);
+		}
+	}
+
+	if(!bPending && pSocketObj->IsPending())
+	{
+		if(!m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_SEND, pSocketObj->connID))
+			return ::GetLastError();
+	}
+
+	return NO_ERROR;
+}
+
+BOOL CTcpServer::SendSmallFile(CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail)
+{
+	CFile file;
+	CFileMapping fmap;
+	WSABUF szBuf[3];
+
+	HRESULT hr = ::MakeSmallFilePackage(lpszFileName, file, fmap, szBuf, pHead, pTail);
+
+	if(FAILED(hr))
+	{
+		::SetLastError(hr);
+		return FALSE;
+	}
+
+	return SendPackets(dwConnID, szBuf, 3);
+}

+ 306 - 0
TcpServer.h

@@ -0,0 +1,306 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "SocketHelper.h"
+#include "./common/GeneralHelper.h"
+#include "./common/IODispatcher.h"
+
+class CTcpServer : public ITcpServer, private CIOHandler
+{
+public:
+	virtual BOOL Start	(LPCTSTR lpszBindAddress, USHORT usPort);
+	virtual BOOL Stop	();
+	virtual BOOL Send	(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset = 0);
+	virtual BOOL SendSmallFile	(CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr);
+	virtual BOOL SendPackets	(CONNID dwConnID, const WSABUF pBuffers[], int iCount)	{return DoSendPackets(dwConnID, pBuffers, iCount);}
+	virtual BOOL PauseReceive	(CONNID dwConnID, BOOL bPause = TRUE);
+	virtual BOOL Wait			(DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
+	virtual BOOL			HasStarted					()	{return m_enState == SS_STARTED || m_enState == SS_STARTING;}
+	virtual EnServiceState	GetState					()	{return m_enState;}
+	virtual BOOL			Disconnect					(CONNID dwConnID, BOOL bForce = TRUE);
+	virtual BOOL			DisconnectLongConnections	(DWORD dwPeriod, BOOL bForce = TRUE);
+	virtual BOOL			DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce = TRUE);
+	virtual BOOL			GetListenAddress			(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL			GetLocalAddress				(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL			GetRemoteAddress			(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	
+	virtual BOOL IsConnected			(CONNID dwConnID);
+	virtual BOOL IsPauseReceive			(CONNID dwConnID, BOOL& bPaused);
+	virtual BOOL GetPendingDataLength	(CONNID dwConnID, int& iPending);
+	virtual DWORD GetConnectionCount	();
+	virtual BOOL GetAllConnectionIDs	(CONNID pIDs[], DWORD& dwCount);
+	virtual BOOL GetConnectPeriod		(CONNID dwConnID, DWORD& dwPeriod);
+	virtual BOOL GetSilencePeriod		(CONNID dwConnID, DWORD& dwPeriod);
+	virtual EnSocketError GetLastError	()	{return m_enLastError;}
+	virtual LPCTSTR	GetLastErrorDesc	()	{return ::GetSocketErrorDesc(m_enLastError);}
+
+#ifdef _SSL_SUPPORT
+	virtual BOOL SetupSSLContext	(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr)	{return FALSE;}
+	virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr)						{return FALSE;}
+	virtual int AddSSLContext		(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr)																{return FALSE;}
+	virtual int AddSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr)																					{return FALSE;}
+	virtual BOOL BindSSLServerName	(LPCTSTR lpszServerName, int iContextIndex)																																																		{return FALSE;}
+	virtual void CleanupSSLContext	()						{}
+
+	virtual BOOL StartSSLHandShake	(CONNID dwConnID)		{return FALSE;}
+	virtual void SetSSLAutoHandShake(BOOL bAutoHandShake)	{}
+	virtual BOOL IsSSLAutoHandShake	()						{return FALSE;}
+	virtual void SetSSLCipherList	(LPCTSTR lpszCipherList){}
+	virtual LPCTSTR GetSSLCipherList()						{return nullptr;}
+	virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo)	{return FALSE;}
+
+protected:
+	virtual BOOL StartSSLHandShake	(TSocketObj* pSocketObj){return FALSE;}
+#endif
+
+private:
+	virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)			override;
+	virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)	override;
+	virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd)					override;
+	virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)				override;
+	virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)				override;
+	virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)					override;
+	virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events)					override;
+	virtual VOID OnDispatchThreadStart(THR_ID tid)												override;
+	virtual VOID OnDispatchThreadEnd(THR_ID tid)												override;
+
+public:
+	virtual BOOL IsSecure					() {return FALSE;}
+
+	virtual BOOL SetConnectionExtra(CONNID dwConnID, PVOID pExtra);
+	virtual BOOL GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra);
+
+	virtual void SetReuseAddressPolicy		(EnReuseAddressPolicy enReusePolicy)	{ENSURE_HAS_STOPPED(); ASSERT(m_enReusePolicy == enReusePolicy);}
+	virtual void SetSendPolicy				(EnSendPolicy enSendPolicy)				{ENSURE_HAS_STOPPED(); ASSERT(m_enSendPolicy == enSendPolicy);}
+	virtual void SetOnSendSyncPolicy		(EnOnSendSyncPolicy enOnSendSyncPolicy)	{ENSURE_HAS_STOPPED(); ASSERT(m_enOnSendSyncPolicy == enOnSendSyncPolicy);}
+	virtual void SetMaxConnectionCount		(DWORD dwMaxConnectionCount)	{ENSURE_HAS_STOPPED(); m_dwMaxConnectionCount		= dwMaxConnectionCount;}
+	virtual void SetWorkerThreadCount		(DWORD dwWorkerThreadCount)		{ENSURE_HAS_STOPPED(); m_dwWorkerThreadCount		= dwWorkerThreadCount;}
+	virtual void SetSocketListenQueue		(DWORD dwSocketListenQueue)		{ENSURE_HAS_STOPPED(); m_dwSocketListenQueue		= dwSocketListenQueue;}
+	virtual void SetAcceptSocketCount		(DWORD dwAcceptSocketCount)		{ENSURE_HAS_STOPPED(); m_dwAcceptSocketCount		= dwAcceptSocketCount;}
+	virtual void SetSocketBufferSize		(DWORD dwSocketBufferSize)		{ENSURE_HAS_STOPPED(); m_dwSocketBufferSize			= dwSocketBufferSize;}
+	virtual void SetFreeSocketObjLockTime	(DWORD dwFreeSocketObjLockTime)	{ENSURE_HAS_STOPPED(); m_dwFreeSocketObjLockTime	= dwFreeSocketObjLockTime;}
+	virtual void SetFreeSocketObjPool		(DWORD dwFreeSocketObjPool)		{ENSURE_HAS_STOPPED(); m_dwFreeSocketObjPool		= dwFreeSocketObjPool;}
+	virtual void SetFreeBufferObjPool		(DWORD dwFreeBufferObjPool)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferObjPool		= dwFreeBufferObjPool;}
+	virtual void SetFreeSocketObjHold		(DWORD dwFreeSocketObjHold)		{ENSURE_HAS_STOPPED(); m_dwFreeSocketObjHold		= dwFreeSocketObjHold;}
+	virtual void SetFreeBufferObjHold		(DWORD dwFreeBufferObjHold)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferObjHold		= dwFreeBufferObjHold;}
+	virtual void SetKeepAliveTime			(DWORD dwKeepAliveTime)			{ENSURE_HAS_STOPPED(); m_dwKeepAliveTime			= dwKeepAliveTime;}
+	virtual void SetKeepAliveInterval		(DWORD dwKeepAliveInterval)		{ENSURE_HAS_STOPPED(); m_dwKeepAliveInterval		= dwKeepAliveInterval;}
+	virtual void SetMarkSilence				(BOOL bMarkSilence)				{ENSURE_HAS_STOPPED(); m_bMarkSilence				= bMarkSilence;}
+	virtual void SetNoDelay					(BOOL bNoDelay)					{ENSURE_HAS_STOPPED(); m_bNoDelay					= bNoDelay;}
+
+	virtual EnReuseAddressPolicy GetReuseAddressPolicy	()	{return m_enReusePolicy;}
+	virtual EnSendPolicy GetSendPolicy					()	{return m_enSendPolicy;}
+	virtual EnOnSendSyncPolicy GetOnSendSyncPolicy		()	{return m_enOnSendSyncPolicy;}
+	virtual DWORD GetMaxConnectionCount		()	{return m_dwMaxConnectionCount;}
+	virtual DWORD GetWorkerThreadCount		()	{return m_dwWorkerThreadCount;}
+	virtual DWORD GetSocketListenQueue		()	{return m_dwSocketListenQueue;}
+	virtual DWORD GetAcceptSocketCount		()	{return m_dwAcceptSocketCount;}
+	virtual DWORD GetSocketBufferSize		()	{return m_dwSocketBufferSize;}
+	virtual DWORD GetFreeSocketObjLockTime	()	{return m_dwFreeSocketObjLockTime;}
+	virtual DWORD GetFreeSocketObjPool		()	{return m_dwFreeSocketObjPool;}
+	virtual DWORD GetFreeBufferObjPool		()	{return m_dwFreeBufferObjPool;}
+	virtual DWORD GetFreeSocketObjHold		()	{return m_dwFreeSocketObjHold;}
+	virtual DWORD GetFreeBufferObjHold		()	{return m_dwFreeBufferObjHold;}
+	virtual DWORD GetKeepAliveTime			()	{return m_dwKeepAliveTime;}
+	virtual DWORD GetKeepAliveInterval		()	{return m_dwKeepAliveInterval;}
+	virtual BOOL  IsMarkSilence				()	{return m_bMarkSilence;}
+	virtual BOOL  IsNoDelay					()	{return m_bNoDelay;}
+
+protected:
+	virtual EnHandleResult FirePrepareListen(SOCKET soListen)
+		{return DoFirePrepareListen(soListen);}
+	virtual EnHandleResult FireAccept(TSocketObj* pSocketObj)
+		{
+			EnHandleResult rs		= DoFireAccept(pSocketObj);
+			if(rs != HR_ERROR) rs	= FireHandShake(pSocketObj);
+			return rs;
+		}
+	virtual EnHandleResult FireHandShake(TSocketObj* pSocketObj)
+		{return DoFireHandShake(pSocketObj);}
+	virtual EnHandleResult FireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return DoFireReceive(pSocketObj, pData, iLength);}
+	virtual EnHandleResult FireReceive(TSocketObj* pSocketObj, int iLength)
+		{return DoFireReceive(pSocketObj, iLength);}
+	virtual EnHandleResult FireSend(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return DoFireSend(pSocketObj, pData, iLength);}
+	virtual EnHandleResult FireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+		{return DoFireClose(pSocketObj, enOperation, iErrorCode);}
+	virtual EnHandleResult FireShutdown()
+		{return DoFireShutdown();}
+
+	virtual EnHandleResult DoFirePrepareListen(SOCKET soListen)
+		{return m_pListener->OnPrepareListen(this, soListen);}
+	virtual EnHandleResult DoFireAccept(TSocketObj* pSocketObj)
+		{return m_pListener->OnAccept(this, pSocketObj->connID, pSocketObj->socket);}
+	virtual EnHandleResult DoFireHandShake(TSocketObj* pSocketObj)
+		{return m_pListener->OnHandShake(this, pSocketObj->connID);}
+	virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnReceive(this, pSocketObj->connID, pData, iLength);}
+	virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, int iLength)
+		{return m_pListener->OnReceive(this, pSocketObj->connID, iLength);}
+	virtual EnHandleResult DoFireSend(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnSend(this, pSocketObj->connID, pData, iLength);}
+	virtual EnHandleResult DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+		{return m_pListener->OnClose(this, pSocketObj->connID, enOperation, iErrorCode);}
+	virtual EnHandleResult DoFireShutdown()
+		{return m_pListener->OnShutdown(this);}
+
+	void SetLastError(EnSocketError code, LPCSTR func, int ec);
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual BOOL BeforeUnpause(TSocketObj* pSocketObj) {return TRUE;}
+
+	virtual void OnWorkerThreadStart(THR_ID tid)	{}
+	virtual void OnWorkerThreadEnd(THR_ID tid)		{}
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
+
+	BOOL DoSendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
+	BOOL DoSendPackets(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
+	TSocketObj* FindSocketObj(CONNID dwConnID);
+
+protected:
+	BOOL SetConnectionExtra(TSocketObj* pSocketObj, PVOID pExtra);
+	BOOL GetConnectionExtra(TSocketObj* pSocketObj, PVOID* ppExtra);
+	BOOL SetConnectionReserved(CONNID dwConnID, PVOID pReserved);
+	BOOL GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved);
+	BOOL SetConnectionReserved(TSocketObj* pSocketObj, PVOID pReserved);
+	BOOL GetConnectionReserved(TSocketObj* pSocketObj, PVOID* ppReserved);
+	BOOL SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2);
+	BOOL GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2);
+	BOOL SetConnectionReserved2(TSocketObj* pSocketObj, PVOID pReserved2);
+	BOOL GetConnectionReserved2(TSocketObj* pSocketObj, PVOID* ppReserved2);
+
+private:
+	BOOL CheckStarting();
+	BOOL CheckStoping();
+	BOOL CreateListenSocket(LPCTSTR lpszBindAddress, USHORT usPort);
+	BOOL CreateWorkerThreads();
+	BOOL StartAccept();
+
+	void CloseListenSocket();
+	void DisconnectClientSocket();
+	void WaitForClientSocketClose();
+	void ReleaseClientSocket();
+	void ReleaseFreeSocket();
+	void WaitForWorkerThreadEnd();
+
+	TSocketObj* GetFreeSocketObj(CONNID dwConnID, SOCKET soClient);
+	TSocketObj* CreateSocketObj();
+	void AddFreeSocketObj	(TSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0);
+	void DeleteSocketObj	(TSocketObj* pSocketObj);
+	BOOL InvalidSocketObj	(TSocketObj* pSocketObj);
+	void AddClientSocketObj	(CONNID dwConnID, TSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr);
+	void CloseClientSocketObj(TSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0, int iShutdownFlag = SHUT_WR);
+
+private:
+	VOID HandleCmdSend		(const TDispContext* pContext, CONNID dwConnID);
+	VOID HandleCmdUnpause	(const TDispContext* pContext, CONNID dwConnID);
+	VOID HandleCmdDisconnect(const TDispContext* pContext, CONNID dwConnID, BOOL bForce);
+	BOOL HandleAccept		(const TDispContext* pContext, UINT events);
+	BOOL HandleReceive		(const TDispContext* pContext, TSocketObj* pSocketObj, int flag);
+	BOOL HandleSend			(const TDispContext* pContext, TSocketObj* pSocketObj, int flag);
+	BOOL HandleClose		(const TDispContext* pContext, TSocketObj* pSocketObj, EnSocketCloseFlag enFlag, UINT events);
+
+	int SendInternal	(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
+	BOOL SendItem		(TSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked);
+
+public:
+	CTcpServer(ITcpServerListener* pListener)
+	: m_pListener				(pListener)
+	, m_enLastError				(SE_OK)
+	, m_enState					(SS_STOPPED)
+	, m_fdGCTimer				(INVALID_FD)
+	, m_enReusePolicy			(RAP_ADDR_AND_PORT)
+	, m_enSendPolicy			(SP_PACK)
+	, m_enOnSendSyncPolicy		(OSSP_RECEIVE)
+	, m_dwMaxConnectionCount	(DEFAULT_CONNECTION_COUNT)
+	, m_dwWorkerThreadCount		(DEFAULT_WORKER_THREAD_COUNT)
+	, m_dwSocketListenQueue		(DEFAULT_TCP_SERVER_SOCKET_LISTEN_QUEUE)
+	, m_dwAcceptSocketCount		(DEFAULT_WORKER_MAX_EVENT_COUNT)
+	, m_dwSocketBufferSize		(DEFAULT_TCP_SOCKET_BUFFER_SIZE)
+	, m_dwFreeSocketObjLockTime	(DEFAULT_FREE_SOCKETOBJ_LOCK_TIME)
+	, m_dwFreeSocketObjPool		(DEFAULT_FREE_SOCKETOBJ_POOL)
+	, m_dwFreeBufferObjPool		(DEFAULT_FREE_BUFFEROBJ_POOL)
+	, m_dwFreeSocketObjHold		(DEFAULT_FREE_SOCKETOBJ_HOLD)
+	, m_dwFreeBufferObjHold		(DEFAULT_FREE_BUFFEROBJ_HOLD)
+	, m_dwKeepAliveTime			(DEFALUT_TCP_KEEPALIVE_TIME)
+	, m_dwKeepAliveInterval		(DEFALUT_TCP_KEEPALIVE_INTERVAL)
+	, m_bMarkSilence			(TRUE)
+	, m_bNoDelay				(FALSE)
+	{
+		ASSERT(m_pListener);
+	}
+
+	virtual ~CTcpServer()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	EnReuseAddressPolicy m_enReusePolicy;
+	EnSendPolicy m_enSendPolicy;
+	EnOnSendSyncPolicy m_enOnSendSyncPolicy;
+	DWORD m_dwMaxConnectionCount;
+	DWORD m_dwWorkerThreadCount;
+	DWORD m_dwSocketListenQueue;
+	DWORD m_dwAcceptSocketCount;
+	DWORD m_dwSocketBufferSize;
+	DWORD m_dwFreeSocketObjLockTime;
+	DWORD m_dwFreeSocketObjPool;
+	DWORD m_dwFreeBufferObjPool;
+	DWORD m_dwFreeSocketObjHold;
+	DWORD m_dwFreeBufferObjHold;
+	DWORD m_dwKeepAliveTime;
+	DWORD m_dwKeepAliveInterval;
+	BOOL  m_bMarkSilence;
+	BOOL  m_bNoDelay;
+
+private:
+	CSEM				m_evWait;
+
+	ITcpServerListener*	m_pListener;
+	ListenSocketsPtr	m_soListens;
+	EnServiceState		m_enState;
+	EnSocketError		m_enLastError;
+
+	CReceiveBuffersPtr	m_rcBuffers;
+
+	CPrivateHeap		m_phSocket;
+	CBufferObjPool		m_bfObjPool;
+
+	CSpinGuard			m_csState;
+
+	FD					m_fdGCTimer;
+
+	TSocketObjPtrPool	m_bfActiveSockets;
+
+	TSocketObjPtrList	m_lsFreeSocket;
+	TSocketObjPtrQueue	m_lsGCSocket;
+
+	CIODispatcher		m_ioDispatcher;
+};

+ 186 - 0
UdpArqClient.cpp

@@ -0,0 +1,186 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "UdpArqClient.h"
+
+#ifdef _UDP_SUPPORT
+
+BOOL CUdpArqClient::CheckParams()
+{
+	DWORD dwMaxDatagramSize = GetMaxDatagramSize();
+
+	if(m_dwMtu == 0)
+		m_arqAttr.dwMtu = dwMaxDatagramSize;
+	else
+	{
+		if(m_dwMtu > dwMaxDatagramSize)
+			return FALSE;
+
+		m_arqAttr.dwMtu = m_dwMtu;
+	}
+
+	return __super::CheckParams() && m_arqAttr.IsValid();
+}
+
+void CUdpArqClient::PrepareStart()
+{
+	__super::PrepareStart();
+}
+
+void CUdpArqClient::Reset()
+{
+	m_arqSession.Reset();
+
+	__super::Reset();
+}
+
+void CUdpArqClient::OnWorkerThreadStart(THR_ID dwThreadID)
+{
+	m_arqBuffer.Malloc(m_arqAttr.dwMaxMessageSize);
+	m_arqTimer = ::CreateTimer(m_arqAttr.dwFlushInterval);
+}
+
+void CUdpArqClient::OnWorkerThreadEnd(THR_ID dwThreadID)
+{
+	if(IS_VALID_FD(m_arqTimer))
+	{
+		close(m_arqTimer);
+		m_arqTimer = INVALID_FD;
+	}
+
+	m_arqBuffer.Free();
+}
+
+HANDLE CUdpArqClient::GetUserEvent()
+{
+	return m_arqTimer;
+}
+
+BOOL CUdpArqClient::OnUserEvent()
+{
+	::ReadTimer(m_arqTimer);
+
+	return m_arqSession.Check();
+}
+
+BOOL CUdpArqClient::Send(const BYTE* pBuffer, int iLength, int iOffset)
+{
+	ASSERT(pBuffer && iLength > 0 && iLength <= (int)m_arqAttr.dwMaxMessageSize);
+
+	int result = NO_ERROR;
+
+	if(pBuffer && iLength > 0 && iLength <= (int)m_arqAttr.dwMaxMessageSize)
+	{
+		if(IsConnected())
+		{
+			if(iOffset != 0) pBuffer += iOffset;
+			result = m_arqSession.Send(pBuffer, iLength);
+		}
+		else
+			result = ERROR_INVALID_STATE;
+	}
+	else
+		result = ERROR_INVALID_PARAMETER;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+BOOL CUdpArqClient::SendPackets(const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	if(!pBuffers || iCount <= 0)
+		return ERROR_INVALID_PARAMETER;
+	if(iCount == 1)
+		return Send((const BYTE*)pBuffers[0].buf, pBuffers[0].len);
+	if(!IsConnected())
+		return ERROR_INVALID_STATE;
+
+	int iLength	= 0;
+	int iMaxLen	= (int)m_arqAttr.dwMaxMessageSize;
+
+	for(int i = 0; i < iCount; i++)
+		iLength += pBuffers[i].len;
+
+	if(iLength <= 0 || iLength > iMaxLen)
+		return ERROR_INCORRECT_SIZE;
+
+	CBufferPtr sndBuffer(iLength);
+	sndBuffer.SetSize(0);
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			sndBuffer.Cat(pBuffer, iBufLen);
+		}
+	}
+
+	int result = m_arqSession.Send(sndBuffer.Ptr(), (int)sndBuffer.Size());
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+int CUdpArqClient::ArqOutputProc(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv)
+{
+	CUdpArqClient* pClient = (CUdpArqClient*)pv;
+
+	BOOL isOK = pClient->__super::Send((const BYTE*)pBuffer, iLength);
+
+	return isOK ? NO_ERROR : ::WSAGetLastError();
+}
+
+EnHandleResult CUdpArqClient::FireConnect()
+{
+	EnHandleResult result = DoFireConnect(this);
+
+	if(result != HR_ERROR)
+		m_arqSession.Renew(this, this, m_arqAttr);
+
+	return result;
+}
+
+EnHandleResult CUdpArqClient::FireReceive(const BYTE* pData, int iLength)
+{
+	return m_arqSession.Receive(pData, iLength, m_arqBuffer.Ptr(), (int)m_arqBuffer.Size());
+}
+
+BOOL CUdpArqClient::GetWaitingSendMessageCount(int& iCount)
+{
+	iCount = m_arqSession.GetWaitingSend();
+
+	return (iCount >= 0);
+}
+
+#endif

+ 114 - 0
UdpArqClient.h

@@ -0,0 +1,114 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "UdpClient.h"
+#include "ArqHelper.h"
+
+#ifdef _UDP_SUPPORT
+
+class CUdpArqClient : public IArqClient, public CUdpClient
+{
+	using __super		= CUdpClient;
+
+	using CArqSession	= CArqSessionT<CUdpArqClient, CUdpArqClient>;
+
+	friend typename		CUdpArqClient::CArqSession;
+
+public:
+	virtual BOOL Send		(const BYTE* pBuffer, int iLength, int iOffset = 0);
+	virtual BOOL SendPackets(const WSABUF pBuffers[], int iCount);
+
+protected:
+	virtual EnHandleResult FireConnect();
+	virtual EnHandleResult FireReceive(const BYTE* pData, int iLength);
+
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual void OnWorkerThreadStart(THR_ID dwThreadID);
+	virtual void OnWorkerThreadEnd(THR_ID dwThreadID);
+
+	virtual FD GetUserEvent();
+	virtual BOOL OnUserEvent();
+
+public:
+	virtual void SetNoDelay				(BOOL bNoDelay)				{ENSURE_HAS_STOPPED(); m_arqAttr.bNoDelay			= bNoDelay;}
+	virtual void SetTurnoffCongestCtrl	(BOOL bTurnOff)				{ENSURE_HAS_STOPPED(); m_arqAttr.bTurnoffNc			= bTurnOff;}
+	virtual void SetFlushInterval		(DWORD dwFlushInterval)		{ENSURE_HAS_STOPPED(); m_arqAttr.dwFlushInterval	= dwFlushInterval;}
+	virtual void SetResendByAcks		(DWORD dwResendByAcks)		{ENSURE_HAS_STOPPED(); m_arqAttr.dwResendByAcks		= dwResendByAcks;}
+	virtual void SetSendWndSize			(DWORD dwSendWndSize)		{ENSURE_HAS_STOPPED(); m_arqAttr.dwSendWndSize		= dwSendWndSize;}
+	virtual void SetRecvWndSize			(DWORD dwRecvWndSize)		{ENSURE_HAS_STOPPED(); m_arqAttr.dwRecvWndSize		= dwRecvWndSize;}
+	virtual void SetMinRto				(DWORD dwMinRto)			{ENSURE_HAS_STOPPED(); m_arqAttr.dwMinRto			= dwMinRto;}
+	virtual void SetFastLimit			(DWORD dwFastLimit)			{ENSURE_HAS_STOPPED(); m_arqAttr.dwFastLimit		= dwFastLimit;}
+	virtual void SetMaxTransUnit		(DWORD dwMaxTransUnit)		{ENSURE_HAS_STOPPED(); m_dwMtu						= dwMaxTransUnit;}
+	virtual void SetMaxMessageSize		(DWORD dwMaxMessageSize)	{ENSURE_HAS_STOPPED(); m_arqAttr.dwMaxMessageSize	= dwMaxMessageSize;}
+	virtual void SetHandShakeTimeout	(DWORD dwHandShakeTimeout)	{ENSURE_HAS_STOPPED(); m_arqAttr.dwHandShakeTimeout	= dwHandShakeTimeout;}
+
+	virtual BOOL IsNoDelay				()	{return m_arqAttr.bNoDelay;}
+	virtual BOOL IsTurnoffCongestCtrl	()	{return m_arqAttr.bTurnoffNc;}
+	virtual DWORD GetFlushInterval		()	{return m_arqAttr.dwFlushInterval;}
+	virtual DWORD GetResendByAcks		()	{return m_arqAttr.dwResendByAcks;}
+	virtual DWORD GetSendWndSize		()	{return m_arqAttr.dwSendWndSize;}
+	virtual DWORD GetRecvWndSize		()	{return m_arqAttr.dwRecvWndSize;}
+	virtual DWORD GetMinRto				()	{return m_arqAttr.dwMinRto;}
+	virtual DWORD GetFastLimit			()	{return m_arqAttr.dwFastLimit;}
+	virtual DWORD GetMaxTransUnit		()	{return m_dwMtu;}
+	virtual DWORD GetMaxMessageSize		()	{return m_arqAttr.dwMaxMessageSize;}
+	virtual DWORD GetHandShakeTimeout	()	{return m_arqAttr.dwHandShakeTimeout;}
+
+	virtual BOOL GetWaitingSendMessageCount	(int& iCount);
+
+public:
+	const TArqAttr& GetArqAttribute		()	{return m_arqAttr;}
+	Fn_ArqOutputProc GetArqOutputProc	()	{return ArqOutputProc;}
+
+private:
+	static int ArqOutputProc(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv);
+
+public:
+	CUdpArqClient(IUdpClientListener* pListener)
+	: CUdpClient(pListener)
+	, m_dwMtu	(0)
+	{
+
+	}
+
+	virtual ~CUdpArqClient()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	DWORD		m_dwMtu;
+	TArqAttr	m_arqAttr;
+
+	CBufferPtr	m_arqBuffer;
+	FD			m_arqTimer;
+
+	CArqSession	m_arqSession;
+};
+
+#endif

+ 253 - 0
UdpArqServer.cpp

@@ -0,0 +1,253 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "UdpArqServer.h"
+
+#ifdef _UDP_SUPPORT
+
+BOOL CUdpArqServer::CheckParams()
+{
+	DWORD dwMaxDatagramSize = GetMaxDatagramSize();
+
+	if(m_dwMtu == 0)
+		m_arqAttr.dwMtu = dwMaxDatagramSize;
+	else
+	{
+		if(m_dwMtu > dwMaxDatagramSize)
+			return FALSE;
+
+		m_arqAttr.dwMtu = m_dwMtu;
+	}
+
+	return __super::CheckParams() && m_arqAttr.IsValid();
+}
+
+void CUdpArqServer::PrepareStart()
+{
+	__super::PrepareStart();
+
+	m_ssPool.SetSessionLockTime(GetFreeSocketObjLockTime());
+	m_ssPool.SetSessionPoolSize(GetFreeSocketObjPool());
+	m_ssPool.SetSessionPoolHold(GetFreeSocketObjHold());
+
+	m_ssPool.Prepare();
+}
+
+void CUdpArqServer::Reset()
+{
+	::ClearPtrMap(m_rcBuffers);
+
+	m_ssPool.Clear();
+
+	__super::Reset();
+}
+
+void CUdpArqServer::OnWorkerThreadStart(THR_ID dwThreadID)
+{
+	{
+		CCriSecLock locallock(m_csRcBuffers);
+		m_rcBuffers[dwThreadID] = new CBufferPtr(m_arqAttr.dwMaxMessageSize);
+	}
+
+	while((DWORD)m_rcBuffers.size() < GetWorkerThreadCount())
+		::WaitFor(3);
+}
+
+void CUdpArqServer::ReleaseGCSocketObj(BOOL bForce)
+{
+	__super::ReleaseGCSocketObj(bForce);
+
+#ifdef USE_EXTERNAL_GC
+	m_ssPool.ReleaseGCSession(bForce);
+#endif
+}
+
+BOOL CUdpArqServer::Send(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	ASSERT(pBuffer && iLength > 0 && iLength <= (int)m_arqAttr.dwMaxMessageSize);
+
+	int result = NO_ERROR;
+
+	if(pBuffer && iLength > 0 && iLength <= (int)m_arqAttr.dwMaxMessageSize)
+	{
+		if(iOffset != 0) pBuffer += iOffset;
+		TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+		if(TUdpSocketObj::IsValid(pSocketObj))
+			result = SendArq(pSocketObj, pBuffer, iLength);
+		else
+			result = ERROR_OBJECT_NOT_FOUND;
+	}
+	else
+		result = ERROR_INVALID_PARAMETER;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+BOOL CUdpArqServer::SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	if(!pBuffers || iCount <= 0)
+		return ERROR_INVALID_PARAMETER;
+	if(iCount == 1)
+		return Send(dwConnID, (const BYTE*)pBuffers[0].buf, pBuffers[0].len);
+
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TUdpSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	int iLength	= 0;
+	int iMaxLen	= (int)m_arqAttr.dwMaxMessageSize;
+
+	for(int i = 0; i < iCount; i++)
+		iLength += pBuffers[i].len;
+
+	if(iLength <= 0 || iLength > iMaxLen)
+		return ERROR_INCORRECT_SIZE;
+
+	CBufferPtr sndBuffer(iLength);
+	sndBuffer.SetSize(0);
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			sndBuffer.Cat(pBuffer, iBufLen);
+		}
+	}
+
+	int result = SendArq(pSocketObj, sndBuffer.Ptr(), (int)sndBuffer.Size());
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+int CUdpArqServer::SendArq(TUdpSocketObj* pSocketObj, const BYTE* pBuffer, int iLength)
+{
+	CArqSessionEx* pSession = nullptr;
+	GetConnectionReserved(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession == nullptr)
+		return ERROR_OBJECT_NOT_FOUND;
+
+	CLocalSafeCounter localcounter(*pSession);
+
+	return pSession->Send(pBuffer, iLength);
+}
+
+int CUdpArqServer::ArqOutputProc(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv)
+{
+	TUdpSocketObj* pSocketObj = (TUdpSocketObj*)pv;
+
+	if(!TUdpSocketObj::IsValid(pSocketObj))
+		return ERROR_OBJECT_NOT_FOUND;
+
+	CUdpArqServer* pServer = (CUdpArqServer*)IUdpArqServer::FromS((IUdpServer*)pSocketObj->pHolder);
+
+	TItemPtr itPtr(pServer->m_bfObjPool, pServer->m_bfObjPool.PickFreeItem());
+	itPtr->Cat((const BYTE*)pBuffer, iLength);
+
+	return pServer->SendInternal(pSocketObj, itPtr);
+}
+
+EnHandleResult CUdpArqServer::FireAccept(TUdpSocketObj* pSocketObj)
+{
+	EnHandleResult result = DoFireAccept(pSocketObj);
+
+	if(result != HR_ERROR)
+	{
+		CArqSessionEx* pSession = m_ssPool.PickFreeSession(this, pSocketObj, m_arqAttr);
+		ENSURE(SetConnectionReserved(pSocketObj, pSession));
+	}
+
+	return result;
+}
+
+EnHandleResult CUdpArqServer::FireReceive(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
+{
+	CArqSessionEx* pSession = nullptr;
+	GetConnectionReserved(pSocketObj, (PVOID*)&pSession);
+
+	CLocalSafeCounter localcounter(*pSession);
+
+	CBufferPtr& rcBuffer = *m_rcBuffers[SELF_THREAD_ID];
+	return pSession->Receive(pData, iLength, rcBuffer.Ptr(), (int)rcBuffer.Size());
+}
+
+EnHandleResult CUdpArqServer::FireClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+{
+	EnHandleResult result = DoFireClose(pSocketObj, enOperation, iErrorCode);
+
+	CArqSessionEx* pSession = nullptr;
+	GetConnectionReserved(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession != nullptr)
+		m_ssPool.PutFreeSession(pSession);
+
+	return result;
+}
+
+BOOL CUdpArqServer::GetWaitingSendMessageCount(CONNID dwConnID, int& iCount)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TUdpSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	CArqSessionEx* pSession = nullptr;
+	GetConnectionReserved(pSocketObj, (PVOID*)&pSession);
+
+	if(pSession == nullptr)
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	{
+		CLocalSafeCounter localcounter(*pSession);
+
+		iCount = pSession->GetWaitingSend();
+	}
+
+	return (iCount >= 0);
+}
+
+#endif

+ 120 - 0
UdpArqServer.h

@@ -0,0 +1,120 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "UdpServer.h"
+#include "ArqHelper.h"
+
+#ifdef _UDP_SUPPORT
+
+#include "common/STLHelper.h"
+
+class CUdpArqServer : public IArqSocket, public CUdpServer
+{
+	using __super			= CUdpServer;
+
+	using CArqSession		= CArqSessionT<CUdpArqServer, TUdpSocketObj>;
+	using CArqSessionEx		= CArqSessionExT<CUdpArqServer, TUdpSocketObj>;
+	using CArqSessionPool	= CArqSessionPoolT<CUdpArqServer, TUdpSocketObj>;
+	using CRecvBufferMap	= unordered_map<THR_ID, CBufferPtr*>;
+
+	friend typename			CUdpArqServer::CArqSession;
+
+public:
+	virtual BOOL Send		(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset = 0);
+	virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
+
+protected:
+	virtual EnHandleResult FireAccept(TUdpSocketObj* pSocketObj);
+	virtual EnHandleResult FireReceive(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength);
+	virtual EnHandleResult FireClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
+
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+	virtual void OnWorkerThreadStart(THR_ID dwThreadID);
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
+
+public:
+	virtual void SetNoDelay				(BOOL bNoDelay)				{ENSURE_HAS_STOPPED(); m_arqAttr.bNoDelay			= bNoDelay;}
+	virtual void SetTurnoffCongestCtrl	(BOOL bTurnOff)				{ENSURE_HAS_STOPPED(); m_arqAttr.bTurnoffNc			= bTurnOff;}
+	virtual void SetFlushInterval		(DWORD dwFlushInterval)		{ENSURE_HAS_STOPPED(); m_arqAttr.dwFlushInterval	= dwFlushInterval;}
+	virtual void SetResendByAcks		(DWORD dwResendByAcks)		{ENSURE_HAS_STOPPED(); m_arqAttr.dwResendByAcks		= dwResendByAcks;}
+	virtual void SetSendWndSize			(DWORD dwSendWndSize)		{ENSURE_HAS_STOPPED(); m_arqAttr.dwSendWndSize		= dwSendWndSize;}
+	virtual void SetRecvWndSize			(DWORD dwRecvWndSize)		{ENSURE_HAS_STOPPED(); m_arqAttr.dwRecvWndSize		= dwRecvWndSize;}
+	virtual void SetMinRto				(DWORD dwMinRto)			{ENSURE_HAS_STOPPED(); m_arqAttr.dwMinRto			= dwMinRto;}
+	virtual void SetFastLimit			(DWORD dwFastLimit)			{ENSURE_HAS_STOPPED(); m_arqAttr.dwFastLimit		= dwFastLimit;}
+	virtual void SetMaxTransUnit		(DWORD dwMaxTransUnit)		{ENSURE_HAS_STOPPED(); m_dwMtu						= dwMaxTransUnit;}
+	virtual void SetMaxMessageSize		(DWORD dwMaxMessageSize)	{ENSURE_HAS_STOPPED(); m_arqAttr.dwMaxMessageSize	= dwMaxMessageSize;}
+	virtual void SetHandShakeTimeout	(DWORD dwHandShakeTimeout)	{ENSURE_HAS_STOPPED(); m_arqAttr.dwHandShakeTimeout	= dwHandShakeTimeout;}
+
+	virtual BOOL IsNoDelay				()	{return m_arqAttr.bNoDelay;}
+	virtual BOOL IsTurnoffCongestCtrl	()	{return m_arqAttr.bTurnoffNc;}
+	virtual DWORD GetFlushInterval		()	{return m_arqAttr.dwFlushInterval;}
+	virtual DWORD GetResendByAcks		()	{return m_arqAttr.dwResendByAcks;}
+	virtual DWORD GetSendWndSize		()	{return m_arqAttr.dwSendWndSize;}
+	virtual DWORD GetRecvWndSize		()	{return m_arqAttr.dwRecvWndSize;}
+	virtual DWORD GetMinRto				()	{return m_arqAttr.dwMinRto;}
+	virtual DWORD GetFastLimit			()	{return m_arqAttr.dwFastLimit;}
+	virtual DWORD GetMaxTransUnit		()	{return m_dwMtu;}
+	virtual DWORD GetMaxMessageSize		()	{return m_arqAttr.dwMaxMessageSize;}
+	virtual DWORD GetHandShakeTimeout	()	{return m_arqAttr.dwHandShakeTimeout;}
+
+	virtual BOOL GetWaitingSendMessageCount	(CONNID dwConnID, int& iCount);
+
+public:
+	const TArqAttr& GetArqAttribute		()	{return m_arqAttr;}
+	Fn_ArqOutputProc GetArqOutputProc	()	{return ArqOutputProc;}
+
+private:
+	int SendArq(TUdpSocketObj* pSocketObj, const BYTE* pBuffer, int iLength);
+
+	static int ArqOutputProc(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv);
+
+public:
+	CUdpArqServer(IUdpServerListener* pListener)
+	: CUdpServer(pListener)
+	, m_ssPool	(this)
+	, m_dwMtu	(0)
+	{
+		
+	}
+
+	virtual ~CUdpArqServer()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	DWORD			m_dwMtu;
+	TArqAttr		m_arqAttr;
+
+	CCriSec			m_csRcBuffers;
+	CRecvBufferMap	m_rcBuffers;
+
+	CArqSessionPool m_ssPool;
+};
+
+#endif

+ 701 - 0
UdpCast.cpp

@@ -0,0 +1,701 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "UdpCast.h"
+
+#ifdef _UDP_SUPPORT
+
+BOOL CUdpCast::Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
+{
+	ASSERT(usLocalPort == 0);
+
+	if(!CheckParams() || !CheckStarting())
+		return FALSE;
+
+	PrepareStart();
+	m_ccContext.Reset();
+
+	BOOL isOK = FALSE;
+	HP_SOCKADDR bindAddr(AF_UNSPEC, TRUE);
+
+	if(CreateClientSocket(lpszRemoteAddress, usPort, lpszBindAddress, bindAddr))
+	{
+		if(BindClientSocket(bindAddr))
+		{
+			if(TRIGGER(FirePrepareConnect(m_soClient)) != HR_ERROR)
+			{
+				if(ConnectToGroup(bindAddr))
+				{
+					if(CreateWorkerThread())
+					{
+							isOK = TRUE;
+					}
+					else
+						SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ERROR_CREATE_FAILED);
+				}
+				else
+					SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
+			}
+			else
+				SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
+			}
+		else
+			SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
+	}
+	else
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+
+	if(!isOK)
+	{
+		m_ccContext.Reset(FALSE);
+		EXECUTE_RESTORE_ERROR(Stop());
+	}
+
+	return isOK;
+}
+
+BOOL CUdpCast::CheckParams()
+{
+	if	(((int)m_dwMaxDatagramSize > 0 && m_dwMaxDatagramSize <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE)	&&
+		((int)m_dwFreeBufferPoolSize >= 0)														&&
+		((int)m_dwFreeBufferPoolHold >= 0)														&&
+		(m_enCastMode >= CM_MULTICAST && m_enCastMode <= CM_BROADCAST)							&&
+		(m_iMCTtl >= 0 && m_iMCTtl <= 255)														&&
+		(m_bMCLoop == TRUE || m_bMCLoop == FALSE)												)
+		return TRUE;
+
+	SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+	return FALSE;
+}
+
+void CUdpCast::PrepareStart()
+{
+	m_itPool.SetItemCapacity(m_dwMaxDatagramSize);
+	m_itPool.SetPoolSize(m_dwFreeBufferPoolSize);
+	m_itPool.SetPoolHold(m_dwFreeBufferPoolHold);
+
+	m_itPool.Prepare();
+}
+
+BOOL CUdpCast::CheckStarting()
+{
+	CSpinLock locallock(m_csState);
+
+	if(m_enState == SS_STOPPED)
+		m_enState = SS_STARTING;
+	else
+	{
+		SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpCast::CheckStoping()
+{
+	if(m_enState != SS_STOPPED)
+	{
+		CSpinLock locallock(m_csState);
+
+		if(HasStarted())
+		{
+			m_enState = SS_STOPPING;
+			return TRUE;
+		}
+	}
+
+	SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+
+	return FALSE;
+}
+
+BOOL CUdpCast::CreateClientSocket(LPCTSTR lpszRemoteAddress, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& bindAddr)
+{
+	HP_SCOPE_HOST host(lpszRemoteAddress);
+	LPCTSTR lpszRealAddress = host.addr;
+
+ 	if(m_enCastMode == CM_BROADCAST && ::IsStrEmpty(lpszRealAddress))
+		lpszRealAddress = DEFAULT_IPV4_BROAD_CAST_ADDRESS;
+
+	if(!::GetSockAddrByHostName(lpszRealAddress, usPort, m_castAddr))
+		return FALSE;
+
+	if(::IsStrEmpty(lpszBindAddress))
+	{
+		bindAddr.family = m_castAddr.family;
+		bindAddr.SetPort(usPort);
+	}
+	else
+	{
+		if(!::sockaddr_A_2_IN(lpszBindAddress, usPort, bindAddr))
+			return FALSE;
+	}
+
+	if(m_enCastMode == CM_BROADCAST && bindAddr.IsIPv6())
+	{
+		::WSASetLastError(ERROR_PFNOSUPPORT);
+		return FALSE;
+	}
+
+	if(m_castAddr.family != bindAddr.family)
+	{
+		::WSASetLastError(ERROR_AFNOSUPPORT);
+		return FALSE;
+	}
+
+	m_soClient = socket(m_castAddr.family, SOCK_DGRAM, IPPROTO_UDP);
+
+	if(m_soClient == INVALID_SOCKET)
+		return FALSE;
+
+	VERIFY(::fcntl_SETFL(m_soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
+	VERIFY(::SSO_ReuseAddress(m_soClient, m_enReusePolicy) == NO_ERROR);
+
+	SetRemoteHost(host.name, usPort);
+
+	return TRUE;
+}
+
+BOOL CUdpCast::BindClientSocket(HP_SOCKADDR& bindAddr)
+{
+	HP_SOCKADDR anyAddr = HP_SOCKADDR::AnyAddr(m_castAddr.family);
+	anyAddr.SetPort(m_castAddr.Port());
+
+	if(::bind(m_soClient, anyAddr.Addr(), anyAddr.AddrSize()) == SOCKET_ERROR)
+		return FALSE;
+
+	m_dwConnID = ::GenerateConnectionID();
+
+	return TRUE;
+}
+
+BOOL CUdpCast::ConnectToGroup(const HP_SOCKADDR& bindAddr)
+{
+	if(m_enCastMode == CM_MULTICAST)
+	{
+		if(!::SetMultiCastSocketOptions(m_soClient, bindAddr, m_castAddr, m_iMCTtl, m_bMCLoop))
+			return FALSE;
+	}
+	else
+	{
+		ASSERT(m_castAddr.IsIPv4());
+
+		UINT iSet = 1;
+		VERIFY(::SSO_SetSocketOption(m_soClient, SOL_SOCKET, SO_BROADCAST, &iSet, sizeof(UINT)) != SOCKET_ERROR);
+	}
+
+	SetConnected();
+
+	if(TRIGGER(FireConnect()) == HR_ERROR)
+	{
+		::WSASetLastError(ENSURE_ERROR_CANCELLED);
+		return FALSE;
+	}
+
+	m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
+
+	return TRUE;
+}
+
+BOOL CUdpCast::Stop()
+{
+	if(!CheckStoping())
+		return FALSE;
+
+	WaitForWorkerThreadEnd();
+
+	SetConnected(FALSE);
+
+	if(m_ccContext.bFireOnClose)
+		FireClose(m_ccContext.enOperation, m_ccContext.iErrorCode);
+
+	if(m_soClient != INVALID_SOCKET)
+	{
+		shutdown(m_soClient, SHUT_WR);
+		closesocket(m_soClient);
+
+		m_soClient = INVALID_SOCKET;
+	}
+
+	Reset();
+
+	return TRUE;
+}
+
+void CUdpCast::Reset()
+{
+	CCriSecLock locallock(m_csSend);
+
+	m_evSend.Reset();
+	m_evRecv.Reset();
+	m_evStop.Reset();
+
+	m_lsSend.Clear();
+	m_itPool.Clear();
+	m_rcBuffer.Free();
+
+	m_castAddr.Reset();
+	m_remoteAddr.Reset();
+
+	m_strHost.Empty();
+
+	m_usPort		= 0;
+	m_nEvents		= 0;
+	m_bPaused		= FALSE;
+	m_enState		= SS_STOPPED;
+
+	m_evWait.SyncNotifyAll();
+}
+
+void CUdpCast::WaitForWorkerThreadEnd()
+{
+	if(!m_thWorker.IsRunning())
+		return;
+
+	if(m_thWorker.IsInMyThread())
+		m_thWorker.Detach();
+	else
+	{
+		m_evStop.Set();
+		m_thWorker.Join();
+	}
+}
+
+BOOL CUdpCast::CreateWorkerThread()
+{
+	return m_thWorker.Start(this, &CUdpCast::WorkerThreadProc);
+}
+
+UINT WINAPI CUdpCast::WorkerThreadProc(LPVOID pv)
+{
+	::SetCurrentWorkerThreadName();
+
+	TRACE("---------------> Cast Worker Thread 0x%08X started <---------------", SELF_THREAD_ID);
+
+	OnWorkerThreadStart(SELF_THREAD_ID);
+
+	BOOL bCallStop	= TRUE;
+	pollfd pfds[]	= {	{m_soClient, m_nEvents},
+						{m_evSend.GetFD(), POLLIN},
+						{m_evRecv.GetFD(), POLLIN},
+						{m_evStop.GetFD(), POLLIN}	};
+	int size		= ARRAY_SIZE(pfds);
+
+	m_rcBuffer.Malloc(m_dwMaxDatagramSize);
+
+	while(HasStarted())
+	{
+		int rs = (int)::PollForMultipleObjects(pfds, size);
+		ASSERT(rs > TIMEOUT);
+
+		if(rs <= 0)
+		{
+			m_ccContext.Reset(TRUE, SO_UNKNOWN, ::WSAGetLastError());
+			goto EXIT_WORKER_THREAD;
+		}
+
+		for(int i = 0; i < size; i++)
+		{
+			if((1 << i) & rs)
+			{
+				SHORT revents = pfds[i].revents;
+
+				if(i == 0)
+				{
+					if(!ProcessNetworkEvent(revents))
+						goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 1)
+				{
+					m_evSend.Reset();
+
+					if(!SendData())
+						goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 2)
+				{
+					m_evRecv.Reset();
+
+					if(!ReadData())
+						goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 3)
+				{
+					m_evStop.Reset();
+
+					bCallStop = FALSE;
+					goto EXIT_WORKER_THREAD;
+				}
+				else
+					VERIFY(FALSE);
+			}
+		}
+
+		m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
+		pfds[0].events = m_nEvents;
+	}
+
+EXIT_WORKER_THREAD:
+
+	OnWorkerThreadEnd(SELF_THREAD_ID);
+
+	if(bCallStop && HasStarted())
+		Stop();
+
+	TRACE("---------------> Cast Worker Thread 0x%08X stoped <---------------", SELF_THREAD_ID);
+
+	return 0;
+}
+
+BOOL CUdpCast::ProcessNetworkEvent(SHORT events)
+{
+	ASSERT(IsConnected());
+
+	BOOL bContinue = TRUE;
+
+	if(bContinue && events & POLLERR)
+		bContinue = HandleClose(events);
+
+	if(bContinue && events & POLLIN)
+		bContinue = HandleRead(events);
+
+	if(bContinue && events & POLLOUT)
+		bContinue = HandleWrite(events);
+
+	if(bContinue && events & _POLL_HUNGUP_EVENTS)
+		bContinue = HandleClose(events);
+
+	return bContinue;
+}
+
+BOOL CUdpCast::HandleClose(SHORT events)
+{
+	EnSocketOperation enOperation = SO_CLOSE;
+
+	if(events & _POLL_HUNGUP_EVENTS)
+		enOperation = SO_CLOSE;
+	else if(events & POLLIN)
+		enOperation = SO_RECEIVE;
+	else if(events & POLLOUT)
+		enOperation = SO_SEND;
+
+	m_ccContext.Reset(TRUE, enOperation, ::SSO_GetError(m_soClient));
+
+	return FALSE;
+}
+
+BOOL CUdpCast::HandleRead(SHORT events)
+{
+	return ReadData();
+}
+
+BOOL CUdpCast::HandleWrite(SHORT events)
+{
+	return SendData();
+}
+
+BOOL CUdpCast::ReadData()
+{
+	while(TRUE)
+	{
+		if(m_bPaused)
+			break;
+
+		socklen_t addrLen = (socklen_t)m_remoteAddr.AddrSize();
+		int rc			  = (int)recvfrom(m_soClient, (char*)(BYTE*)m_rcBuffer, m_dwMaxDatagramSize, MSG_TRUNC, m_remoteAddr.Addr(), &addrLen);
+
+		if(rc >= 0)
+		{
+			if(rc > (int)m_dwMaxDatagramSize)
+			{
+				m_ccContext.Reset(TRUE, SO_RECEIVE, ERROR_BAD_LENGTH);
+				return FALSE;
+			}
+
+			if(TRIGGER(FireReceive(m_rcBuffer, rc)) == HR_ERROR)
+			{
+				TRACE("<C-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", m_dwConnID);
+
+				m_ccContext.Reset(TRUE, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
+				return FALSE;
+			}
+		}
+		else if(rc == SOCKET_ERROR)
+		{
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+				break;
+			else
+			{
+				m_ccContext.Reset(TRUE, SO_RECEIVE, code);
+				return FALSE;
+			}
+		}
+		else
+			ASSERT(FALSE);
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpCast::PauseReceive(BOOL bPause)
+{
+	if(!IsConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(m_bPaused == bPause)
+		return TRUE;
+
+	m_bPaused = bPause;
+
+	if(!bPause)
+		return m_evRecv.Set();
+
+	return TRUE;
+}
+
+BOOL CUdpCast::SendData()
+{
+	BOOL bBlocked = FALSE;
+
+	while(m_lsSend.Length() > 0)
+	{
+		TItemPtr itPtr(m_itPool);
+
+		{
+			CCriSecLock locallock(m_csSend);
+			itPtr = m_lsSend.PopFront();
+		}
+
+		if(!itPtr.IsValid())
+			break;
+
+		if(!DoSendData(itPtr, bBlocked))
+			return FALSE;
+
+		if(bBlocked)
+		{
+			CCriSecLock locallock(m_csSend);
+			m_lsSend.PushFront(itPtr.Detach());
+			break;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpCast::DoSendData(TItem* pItem, BOOL& bBlocked)
+{
+	int rc = (int)sendto(m_soClient, (char*)pItem->Ptr(), pItem->Size(), 0, m_castAddr.Addr(), m_castAddr.AddrSize());
+
+	if(rc >= 0)
+	{
+		ASSERT(rc == pItem->Size());
+
+		if(rc == 0)
+		{
+			CCriSecLock locallock(m_csSend);
+			m_lsSend.ReduceLength(1);
+		}
+
+		if(TRIGGER(FireSend(pItem->Ptr(), rc)) == HR_ERROR)
+		{
+			TRACE("<C-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", m_dwConnID);
+			ASSERT(FALSE);
+		}
+	}
+	else if(rc == SOCKET_ERROR)
+	{
+		int code = ::WSAGetLastError();
+
+		if(code == ERROR_WOULDBLOCK)
+			bBlocked = TRUE;
+		else
+		{
+			m_ccContext.Reset(TRUE, SO_SEND, code);
+			return FALSE;
+		}
+	}
+	else
+		ASSERT(FALSE);
+
+	return TRUE;
+}
+
+BOOL CUdpCast::Send(const BYTE* pBuffer, int iLength, int iOffset)
+{
+	ASSERT(pBuffer && iLength >= 0 && iLength <= (int)m_dwMaxDatagramSize);
+
+	int result = NO_ERROR;
+
+	if(pBuffer && iLength >= 0 && iLength <= (int)m_dwMaxDatagramSize)
+	{
+		if(IsConnected())
+		{
+			if(iOffset != 0) pBuffer += iOffset;
+
+			TItemPtr itPtr(m_itPool, m_itPool.PickFreeItem());
+			itPtr->Cat(pBuffer, iLength);
+
+			result = SendInternal(itPtr);
+		}
+		else
+			result = ERROR_INVALID_STATE;
+	}
+	else
+		result = ERROR_INVALID_PARAMETER;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+BOOL CUdpCast::SendPackets(const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	if(!pBuffers || iCount <= 0)
+		return ERROR_INVALID_PARAMETER;
+	if(!IsConnected())
+		return ERROR_INVALID_STATE;
+
+	int result	= NO_ERROR;
+	int iLength	= 0;
+	int iMaxLen	= (int)m_dwMaxDatagramSize;
+
+	TItemPtr itPtr(m_itPool, m_itPool.PickFreeItem());
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			iLength += iBufLen;
+
+			if(iLength <= iMaxLen)
+				itPtr->Cat(pBuffer, iBufLen);
+			else
+				break;
+		}
+	}
+
+	if(iLength >= 0 && iLength <= iMaxLen)
+		result = SendInternal(itPtr);
+	else
+		result = ERROR_INCORRECT_SIZE;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+int CUdpCast::SendInternal(TItemPtr& itPtr)
+{
+	int iPending;
+	int iBufferSize;
+
+	{
+		CCriSecLock locallock(m_csSend);
+
+		if(!IsConnected())
+			return ERROR_INVALID_STATE;
+
+		iPending	= m_lsSend.Length();
+		iBufferSize	= itPtr->Size();
+
+		m_lsSend.PushBack(itPtr.Detach());
+		if(iBufferSize == 0) m_lsSend.IncreaseLength(1);
+
+		ASSERT(m_lsSend.Length() > 0);
+	}
+
+	if(iPending == 0 && m_lsSend.Length() > 0) m_evSend.Set();
+
+	return NO_ERROR;
+}
+
+void CUdpCast::SetLastError(EnSocketError code, LPCSTR func, int ec)
+{
+	TRACE("%s --> Error: %d, EC: %d", func, code, ec);
+
+	m_enLastError = code;
+	::SetLastError(ec);
+}
+
+BOOL CUdpCast::GetLocalAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	return ::GetSocketLocalAddress(m_soClient, lpszAddress, iAddressLen, usPort);
+}
+
+void CUdpCast::SetRemoteHost(LPCTSTR lpszHost, USHORT usPort)
+{
+	m_strHost = lpszHost;
+	m_usPort  = usPort;
+}
+
+BOOL CUdpCast::GetRemoteHost(TCHAR lpszHost[], int& iHostLen, USHORT& usPort)
+{
+	BOOL isOK = FALSE;
+
+	if(m_strHost.IsEmpty())
+		return isOK;
+
+	int iLen = m_strHost.GetLength() + 1;
+
+	if(iHostLen >= iLen)
+	{
+		memcpy(lpszHost, CA2CT(m_strHost), iLen * sizeof(TCHAR));
+		usPort = m_usPort;
+
+		isOK = TRUE;
+	}
+
+	iHostLen = iLen;
+
+	return isOK;
+}
+
+BOOL CUdpCast::GetRemoteHost(LPCSTR* lpszHost, USHORT* pusPort)
+{
+	*lpszHost = m_strHost;
+
+	if(pusPort != nullptr)
+		*pusPort = m_usPort;
+
+	return !m_strHost.IsEmpty();
+}
+
+#endif

+ 219 - 0
UdpCast.h

@@ -0,0 +1,219 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "SocketHelper.h"
+#include "./common/GeneralHelper.h"
+
+#ifdef _UDP_SUPPORT
+
+class CUdpCast : public IUdpCast
+{
+public:
+	virtual BOOL Start	(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect = TRUE, LPCTSTR lpszBindAddress = nullptr, USHORT usLocalPort = 0);
+	virtual BOOL Stop	();
+	virtual BOOL Send	(const BYTE* pBuffer, int iLength, int iOffset = 0);
+	virtual BOOL SendPackets	(const WSABUF pBuffers[], int iCount);
+	virtual BOOL PauseReceive	(BOOL bPause = TRUE);
+	virtual BOOL Wait			(DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
+	virtual BOOL			HasStarted			()	{return m_enState == SS_STARTED || m_enState == SS_STARTING;}
+	virtual EnServiceState	GetState			()	{return m_enState;}
+	virtual CONNID			GetConnectionID		()	{return m_dwConnID;}
+	virtual EnSocketError	GetLastError		()	{return m_enLastError;}
+	virtual LPCTSTR			GetLastErrorDesc	()	{return ::GetSocketErrorDesc(m_enLastError);}
+
+	virtual BOOL GetLocalAddress		(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL GetRemoteHost			(TCHAR lpszHost[], int& iHostLen, USHORT& usPort);
+	virtual BOOL GetPendingDataLength	(int& iPending) {iPending = m_lsSend.Length(); return HasStarted();}
+	virtual BOOL IsPauseReceive			(BOOL& bPaused) {bPaused = m_bPaused; return HasStarted();}
+	virtual BOOL IsConnected			()				{return m_bConnected;}
+
+public:
+	virtual BOOL IsSecure				() {return FALSE;}
+
+	virtual void SetReuseAddressPolicy	(EnReuseAddressPolicy enReusePolicy){ENSURE_HAS_STOPPED(); m_enReusePolicy			= enReusePolicy;}
+	virtual void SetMaxDatagramSize		(DWORD dwMaxDatagramSize)			{ENSURE_HAS_STOPPED(); m_dwMaxDatagramSize		= dwMaxDatagramSize;}
+	virtual void SetFreeBufferPoolSize	(DWORD dwFreeBufferPoolSize)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolSize	= dwFreeBufferPoolSize;}
+	virtual void SetFreeBufferPoolHold	(DWORD dwFreeBufferPoolHold)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolHold	= dwFreeBufferPoolHold;}
+	virtual void SetCastMode			(EnCastMode enCastMode)				{ENSURE_HAS_STOPPED(); m_enCastMode				= enCastMode;}
+	virtual void SetMultiCastTtl		(int iMCTtl)						{ENSURE_HAS_STOPPED(); m_iMCTtl					= iMCTtl;}
+	virtual void SetMultiCastLoop		(BOOL bMCLoop)						{ENSURE_HAS_STOPPED(); m_bMCLoop				= bMCLoop;}
+	virtual void SetExtra				(PVOID pExtra)						{m_pExtra										= pExtra;}						
+
+	virtual EnReuseAddressPolicy GetReuseAddressPolicy	()	{return m_enReusePolicy;}
+	virtual DWORD GetMaxDatagramSize	()	{return m_dwMaxDatagramSize;}
+	virtual DWORD GetFreeBufferPoolSize	()	{return m_dwFreeBufferPoolSize;}
+	virtual DWORD GetFreeBufferPoolHold	()	{return m_dwFreeBufferPoolHold;}
+	virtual EnCastMode GetCastMode		()	{return m_enCastMode;}
+	virtual int GetMultiCastTtl			()	{return m_iMCTtl;}
+	virtual BOOL IsMultiCastLoop		()	{return m_bMCLoop;}
+	virtual PVOID GetExtra				()	{return m_pExtra;}
+
+	virtual BOOL GetRemoteAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+	{
+		ADDRESS_FAMILY usFamily;
+		return ::sockaddr_IN_2_A(m_remoteAddr, usFamily, lpszAddress, iAddressLen, usPort);
+	}
+
+protected:
+	virtual EnHandleResult FirePrepareConnect(SOCKET socket)
+		{return m_pListener->OnPrepareConnect(this, m_dwConnID, socket);}
+	virtual EnHandleResult FireConnect()
+		{
+			EnHandleResult rs		= m_pListener->OnConnect(this, m_dwConnID);
+			if(rs != HR_ERROR) rs	= FireHandShake();
+			return rs;
+		}
+	virtual EnHandleResult FireHandShake()
+		{return m_pListener->OnHandShake(this, m_dwConnID);}
+	virtual EnHandleResult FireSend(const BYTE* pData, int iLength)
+		{return m_pListener->OnSend(this, m_dwConnID, pData, iLength);}
+	virtual EnHandleResult FireReceive(const BYTE* pData, int iLength)
+		{return m_pListener->OnReceive(this, m_dwConnID, pData, iLength);}
+	virtual EnHandleResult FireReceive(int iLength)
+		{return m_pListener->OnReceive(this, m_dwConnID, iLength);}
+	virtual EnHandleResult FireClose(EnSocketOperation enOperation, int iErrorCode)
+		{return m_pListener->OnClose(this, m_dwConnID, enOperation, iErrorCode);}
+
+	void SetLastError(EnSocketError code, LPCSTR func, int ec);
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual void OnWorkerThreadStart(THR_ID tid) {}
+	virtual void OnWorkerThreadEnd(THR_ID tid) {}
+
+protected:
+	void SetReserved	(PVOID pReserved)	{m_pReserved = pReserved;}						
+	PVOID GetReserved	()					{return m_pReserved;}
+	BOOL GetRemoteHost	(LPCSTR* lpszHost, USHORT* pusPort = nullptr);
+
+private:
+	void SetRemoteHost	(LPCTSTR lpszHost, USHORT usPort);
+	void SetConnected	(BOOL bConnected = TRUE) {m_bConnected = bConnected; if(bConnected) m_enState = SS_STARTED;}
+
+	BOOL CheckStarting();
+	BOOL CheckStoping();
+	BOOL CreateClientSocket(LPCTSTR lpszRemoteAddress, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& bindAddr);
+	BOOL BindClientSocket(HP_SOCKADDR& bindAddr);
+	BOOL ConnectToGroup(const HP_SOCKADDR& bindAddr);
+	BOOL CreateWorkerThread();
+	BOOL ProcessNetworkEvent(SHORT events);
+	BOOL ReadData();
+	BOOL SendData();
+	BOOL DoSendData(TItem* pItem, BOOL& bBlocked);
+	int SendInternal(TItemPtr& itPtr);
+	void WaitForWorkerThreadEnd();
+
+	BOOL HandleClose(SHORT events);
+	BOOL HandleRead(SHORT events);
+	BOOL HandleWrite(SHORT events);
+
+	UINT WINAPI WorkerThreadProc(LPVOID pv);
+
+public:
+	CUdpCast(IUdpCastListener* pListener)
+	: m_pListener			(pListener)
+	, m_lsSend				(m_itPool)
+	, m_soClient			(INVALID_SOCKET)
+	, m_nEvents				(0)
+	, m_dwConnID			(0)
+	, m_usPort				(0)
+	, m_bPaused				(FALSE)
+	, m_bConnected			(FALSE)
+	, m_enLastError			(SE_OK)
+	, m_enState				(SS_STOPPED)
+	, m_pExtra				(nullptr)
+	, m_pReserved			(nullptr)
+	, m_enReusePolicy		(RAP_ADDR_ONLY)
+	, m_dwMaxDatagramSize	(DEFAULT_UDP_MAX_DATAGRAM_SIZE)
+	, m_dwFreeBufferPoolSize(DEFAULT_CLIENT_FREE_BUFFER_POOL_SIZE)
+	, m_dwFreeBufferPoolHold(DEFAULT_CLIENT_FREE_BUFFER_POOL_HOLD)
+	, m_iMCTtl				(1)
+	, m_bMCLoop				(FALSE)
+	, m_enCastMode			(CM_MULTICAST)
+	, m_castAddr			(AF_UNSPEC, TRUE)
+	, m_remoteAddr			(AF_UNSPEC, TRUE)
+	{
+		ASSERT(m_pListener);
+	}
+
+	virtual ~CUdpCast()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	CSEM				m_evWait;
+
+	IUdpCastListener*	m_pListener;
+	TClientCloseContext m_ccContext;
+
+	SOCKET				m_soClient;
+	SHORT				m_nEvents;
+	CONNID				m_dwConnID;
+
+	EnReuseAddressPolicy m_enReusePolicy;
+	DWORD				m_dwMaxDatagramSize;
+	DWORD				m_dwFreeBufferPoolSize;
+	DWORD				m_dwFreeBufferPoolHold;
+
+	int					m_iMCTtl;
+	BOOL				m_bMCLoop;
+	EnCastMode			m_enCastMode;
+
+	EnSocketError		m_enLastError;
+	volatile BOOL		m_bConnected;
+	volatile EnServiceState	m_enState;
+
+	PVOID				m_pExtra;
+	PVOID				m_pReserved;
+
+	HP_SOCKADDR			m_castAddr;
+	HP_SOCKADDR			m_remoteAddr;
+
+	CBufferPtr			m_rcBuffer;
+
+protected:
+	CStringA			m_strHost;
+	USHORT				m_usPort;
+
+	CItemPool			m_itPool;
+
+private:
+	CSpinGuard			m_csState;
+
+	CCriSec				m_csSend;
+	TItemListExV		m_lsSend;
+
+	CEvt				m_evSend;
+	CEvt				m_evRecv;
+	CEvt				m_evStop;
+
+	volatile BOOL		m_bPaused;
+
+	CThread<CUdpCast, VOID, UINT> m_thWorker;
+};
+
+#endif

+ 830 - 0
UdpClient.cpp

@@ -0,0 +1,830 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "UdpClient.h"
+
+#ifdef _UDP_SUPPORT
+
+BOOL CUdpClient::Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
+{
+	if(!CheckParams() || !CheckStarting())
+		return FALSE;
+
+	PrepareStart();
+	m_ccContext.Reset();
+
+	BOOL isOK = FALSE;
+	HP_SOCKADDR addrRemote, addrBind;
+
+	if(CreateClientSocket(lpszRemoteAddress, addrRemote, usPort, lpszBindAddress, addrBind))
+	{
+		if(BindClientSocket(addrBind, addrRemote, usLocalPort))
+		{
+			if(TRIGGER(FirePrepareConnect(m_soClient)) != HR_ERROR)
+			{
+				if(ConnectToServer(addrRemote, bAsyncConnect))
+				{
+					if(CreateWorkerThread())
+						isOK = TRUE;
+					else
+						SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ERROR_CREATE_FAILED);
+				}
+				else
+					SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
+			}
+			else
+				SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
+		}
+		else
+			SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
+	}
+	else
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+
+	if(!isOK)
+	{
+		m_ccContext.Reset(FALSE);
+		EXECUTE_RESTORE_ERROR(Stop());
+	}
+
+	return isOK;
+}
+
+BOOL CUdpClient::CheckParams()
+{
+	if	(((int)m_dwMaxDatagramSize > 0 && m_dwMaxDatagramSize <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE)	&&
+		((int)m_dwFreeBufferPoolSize >= 0)														&&
+		((int)m_dwFreeBufferPoolHold >= 0)														&&
+		((int)m_dwDetectAttempts >= 0)															&&
+		((int)m_dwDetectInterval >= 1000 || m_dwDetectInterval == 0)							)
+		return TRUE;
+
+	SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+	return FALSE;
+}
+
+void CUdpClient::PrepareStart()
+{
+	m_itPool.SetItemCapacity(m_dwMaxDatagramSize);
+	m_itPool.SetPoolSize(m_dwFreeBufferPoolSize);
+	m_itPool.SetPoolHold(m_dwFreeBufferPoolHold);
+
+	m_itPool.Prepare();
+}
+
+BOOL CUdpClient::CheckStarting()
+{
+	CSpinLock locallock(m_csState);
+
+	if(m_enState == SS_STOPPED)
+		m_enState = SS_STARTING;
+	else
+	{
+		SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpClient::CheckStoping()
+{
+	if(m_enState != SS_STOPPED)
+	{
+		CSpinLock locallock(m_csState);
+
+		if(HasStarted())
+		{
+			m_enState = SS_STOPPING;
+			return TRUE;
+		}
+	}
+
+	SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+
+	return FALSE;
+}
+
+BOOL CUdpClient::CreateClientSocket(LPCTSTR lpszRemoteAddress, HP_SOCKADDR& addrRemote, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& addrBind)
+{
+	HP_SCOPE_HOST host(lpszRemoteAddress);
+
+	if(!::GetSockAddrByHostName(host.addr, usPort, addrRemote))
+		return FALSE;
+
+	if(::IsStrNotEmpty(lpszBindAddress))
+	{
+		if(!::sockaddr_A_2_IN(lpszBindAddress, 0, addrBind))
+			return FALSE;
+
+		if(addrRemote.family != addrBind.family)
+		{
+			::WSASetLastError(ERROR_AFNOSUPPORT);
+			return FALSE;
+		}
+	}
+
+	m_soClient = socket(addrRemote.family, SOCK_DGRAM, IPPROTO_UDP);
+
+	if(m_soClient == INVALID_SOCKET)
+		return FALSE;
+
+	VERIFY(::SSO_ReuseAddress(m_soClient, m_enReusePolicy) == NO_ERROR);
+
+	SetRemoteHost(host.name, usPort);
+
+	return TRUE;
+}
+
+BOOL CUdpClient::BindClientSocket(const HP_SOCKADDR& addrBind, const HP_SOCKADDR& addrRemote, USHORT usLocalPort)
+{
+	if(addrBind.IsSpecified() && usLocalPort == 0)
+	{
+		if(::bind(m_soClient, addrBind.Addr(), addrBind.AddrSize()) == SOCKET_ERROR)
+			return FALSE;
+	}
+	else if(usLocalPort != 0)
+	{
+		HP_SOCKADDR realBindAddr = addrBind.IsSpecified() ? addrBind : HP_SOCKADDR::AnyAddr(addrRemote.family);
+
+		realBindAddr.SetPort(usLocalPort);
+
+		if(::bind(m_soClient, realBindAddr.Addr(), realBindAddr.AddrSize()) == SOCKET_ERROR)
+			return FALSE;
+	}
+
+	m_dwConnID = ::GenerateConnectionID();
+
+	return TRUE;
+}
+
+BOOL CUdpClient::ConnectToServer(const HP_SOCKADDR& addrRemote, BOOL bAsyncConnect)
+{
+	BOOL isOK = FALSE;
+
+	if(bAsyncConnect)
+	{
+		VERIFY(::fcntl_SETFL(m_soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
+
+		int rc = ::connect(m_soClient, addrRemote.Addr(), addrRemote.AddrSize());
+
+		if(IS_NO_ERROR(rc) || IS_IO_PENDING_ERROR())
+		{
+			m_nEvents	= POLLOUT;
+			isOK		= TRUE;
+		}
+	}
+	else
+	{
+		if(::connect(m_soClient, addrRemote.Addr(), addrRemote.AddrSize()) != SOCKET_ERROR)
+		{
+			VERIFY(::fcntl_SETFL(m_soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
+
+			SetConnected();
+
+			if(TRIGGER(FireConnect()) == HR_ERROR)
+				::WSASetLastError(ENSURE_ERROR_CANCELLED);
+			else
+			{
+				VERIFY(DetectConnection());
+
+				m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
+				isOK	  = TRUE;
+			}
+		}
+	}
+
+	return isOK;
+}
+
+BOOL CUdpClient::Stop()
+{
+	if(!CheckStoping())
+		return FALSE;
+
+	WaitForWorkerThreadEnd();
+
+	CheckConnected();
+
+	if(m_ccContext.bFireOnClose)
+		FireClose(m_ccContext.enOperation, m_ccContext.iErrorCode);
+
+	if(m_soClient != INVALID_SOCKET)
+	{
+		shutdown(m_soClient, SHUT_WR);
+		closesocket(m_soClient);
+
+		m_soClient = INVALID_SOCKET;
+	}
+
+	Reset();
+
+	return TRUE;
+}
+
+void CUdpClient::Reset()
+{
+	CCriSecLock locallock(m_csSend);
+
+	m_evSend.Reset();
+	m_evRecv.Reset();
+	m_evStop.Reset();
+
+	m_lsSend.Clear();
+	m_itPool.Clear();
+	m_rcBuffer.Free();
+
+	m_strHost.Empty();
+
+	m_usPort		= 0;
+	m_nEvents		= 0;
+	m_dwDetectFails	= 0;
+	m_bPaused		= FALSE;
+	m_enState		= SS_STOPPED;
+
+	m_evWait.SyncNotifyAll();
+}
+
+void CUdpClient::WaitForWorkerThreadEnd()
+{
+	if(!m_thWorker.IsRunning())
+		return;
+
+	if(m_thWorker.IsInMyThread())
+		m_thWorker.Detach();
+	else
+	{
+		m_evStop.Set();
+		m_thWorker.Join();
+	}
+}
+
+void CUdpClient::CheckConnected()
+{
+	if(!IsConnected())
+		return;
+
+	if(m_ccContext.bNotify)
+		::SendUdpCloseNotify(m_soClient);
+
+	SetConnected(FALSE);
+}
+
+BOOL CUdpClient::CreateWorkerThread()
+{
+	return m_thWorker.Start(this, &CUdpClient::WorkerThreadProc);
+}
+
+UINT WINAPI CUdpClient::WorkerThreadProc(LPVOID pv)
+{
+	::SetCurrentWorkerThreadName();
+
+	TRACE("---------------> Client Worker Thread 0x%08X started <---------------", SELF_THREAD_ID);
+
+	OnWorkerThreadStart(SELF_THREAD_ID);
+
+	BOOL bCallStop	= TRUE;
+	DWORD dwSize	= 4;
+	DWORD dwIndex	= 0;
+	BOOL bDetect	= IsNeedDetect();
+	FD fdUserEvt	= GetUserEvent();
+
+	if(bDetect) ++dwSize;
+	if(IS_VALID_FD(fdUserEvt)) ++dwSize;
+
+	pollfd* pfds	= CreateLocalObjects(pollfd, dwSize);
+
+	pfds[dwIndex++] = {m_soClient, m_nEvents};
+	pfds[dwIndex++] = {m_evSend.GetFD(), POLLIN};
+	pfds[dwIndex++] = {m_evRecv.GetFD(), POLLIN};
+	pfds[dwIndex++] = {m_evStop.GetFD(), POLLIN};
+
+	unique_ptr<CTimerEvent> evDetectPtr;
+
+	if(bDetect)
+	{
+		evDetectPtr.reset(new CTimerEvent());
+		evDetectPtr->Set(m_dwDetectInterval);
+		pfds[dwIndex++] = {evDetectPtr->GetFD(), POLLIN};
+	}
+
+	if(IS_VALID_FD(fdUserEvt))
+		pfds[dwIndex++] = {fdUserEvt, POLLIN};
+
+	m_rcBuffer.Malloc(m_dwMaxDatagramSize);
+
+	while(HasStarted())
+	{
+		int rs = (int)::PollForMultipleObjects(pfds, dwSize);
+		ASSERT(rs > TIMEOUT);
+
+		if(rs <= 0)
+		{
+			m_ccContext.Reset(TRUE, SO_UNKNOWN, ::WSAGetLastError());
+			goto EXIT_WORKER_THREAD;
+		}
+
+		for(DWORD i = 0; i < dwSize; i++)
+		{
+			if((1 << i) & rs)
+			{
+				SHORT revents = pfds[i].revents;
+
+				if(i == 0)
+				{
+					if(!ProcessNetworkEvent(revents))
+						goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 1)
+				{
+					m_evSend.Reset();
+
+					if(!SendData())
+						goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 2)
+				{
+					m_evRecv.Reset();
+
+					if(!ReadData())
+						goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 3)
+				{
+					m_evStop.Reset();
+
+					bCallStop = FALSE;
+					goto EXIT_WORKER_THREAD;
+				}
+				else if(i == 4)
+				{
+					if(bDetect)
+					{
+						evDetectPtr->Reset();
+
+						if(!CheckConnection())
+							goto EXIT_WORKER_THREAD;
+					}
+					else
+					{
+						if(!OnUserEvent())
+						{
+							m_ccContext.Reset(TRUE, SO_CLOSE, ENSURE_ERROR_CANCELLED);
+							goto EXIT_WORKER_THREAD;
+						}
+					}
+				}
+				else if(i == 5)
+				{
+					if(!OnUserEvent())
+					{
+						m_ccContext.Reset(TRUE, SO_CLOSE, ENSURE_ERROR_CANCELLED);
+						goto EXIT_WORKER_THREAD;
+					}
+				}
+				else
+					VERIFY(FALSE);
+			}
+		}
+
+		m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
+		pfds[0].events = m_nEvents;
+	}
+
+EXIT_WORKER_THREAD:
+
+	OnWorkerThreadEnd(SELF_THREAD_ID);
+
+	if(bCallStop && HasStarted())
+		Stop();
+
+	TRACE("---------------> Client Worker Thread 0x%08X stoped <---------------", SELF_THREAD_ID);
+
+	return 0;
+}
+
+BOOL CUdpClient::CheckConnection()
+{
+	if(m_dwDetectFails++ >= m_dwDetectAttempts)
+	{
+		m_ccContext.Reset(TRUE, SO_CLOSE, NO_ERROR, FALSE);
+		return FALSE;
+	}
+
+	DetectConnection();
+
+	return TRUE;
+}
+
+BOOL CUdpClient::DetectConnection()
+{
+	int result = NO_ERROR;
+	
+	if((int)send(m_soClient, nullptr, 0, 0) == SOCKET_ERROR)
+	{
+		result = ::WSAGetLastError();
+		if(result == ERROR_WOULDBLOCK)
+			result = NO_ERROR;
+	}
+
+	BOOL isOK = (result == NO_ERROR);
+	
+	if(isOK)
+	{
+		TRACE("<C-CNNID: %zu> send 0 bytes (detect package succ)", m_dwConnID);
+	}
+	else
+	{
+		TRACE("<C-CNNID: %zu> send 0 bytes (detect package fail [%d])", m_dwConnID, result);
+	}
+
+	return isOK;
+}
+
+BOOL CUdpClient::ProcessNetworkEvent(SHORT events)
+{
+	BOOL bContinue = TRUE;
+
+	if(bContinue && events & POLLERR)
+		bContinue = HandleClose(events);
+
+	if(bContinue && !IsConnected())
+		bContinue = HandleConnect(events);
+
+	if(bContinue && events & POLLIN)
+		bContinue = HandleRead(events);
+
+	if(bContinue && events & POLLOUT)
+		bContinue = HandleWrite(events);
+
+	if(bContinue && events & _POLL_HUNGUP_EVENTS)
+		bContinue = HandleClose(events);
+
+	return bContinue;
+}
+
+BOOL CUdpClient::HandleConnect(SHORT events)
+{
+	ASSERT(events & POLLOUT);
+
+	int code = ::SSO_GetError(m_soClient);
+
+	if(!IS_NO_ERROR(code) || (events & _POLL_ERROR_EVENTS))
+	{
+		m_ccContext.Reset(TRUE, SO_CONNECT, code);
+		return FALSE;
+	}
+
+	if(events & _POLL_HUNGUP_EVENTS)
+	{
+		m_ccContext.Reset(TRUE, SO_CONNECT, NO_ERROR);
+		return FALSE;
+	}
+
+	SetConnected();
+
+	if(TRIGGER(FireConnect()) != HR_ERROR)
+		VERIFY(DetectConnection());
+	else
+	{
+		m_ccContext.Reset(FALSE, SO_CLOSE, ENSURE_ERROR_CANCELLED, FALSE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpClient::HandleClose(SHORT events)
+{
+	EnSocketOperation enOperation = SO_CLOSE;
+
+	if(events & _POLL_HUNGUP_EVENTS)
+		enOperation = SO_CLOSE;
+	else if(events & POLLIN)
+		enOperation = SO_RECEIVE;
+	else if(events & POLLOUT)
+		enOperation = SO_SEND;
+
+	m_ccContext.Reset(TRUE, enOperation, ::SSO_GetError(m_soClient));
+
+	return FALSE;
+}
+
+BOOL CUdpClient::HandleRead(SHORT events)
+{
+	return ReadData();
+}
+
+BOOL CUdpClient::HandleWrite(SHORT events)
+{
+	return SendData();
+}
+
+BOOL CUdpClient::ReadData()
+{
+	while(TRUE)
+	{
+		if(m_bPaused)
+			break;
+
+		int rc = (int)recv(m_soClient, (char*)(BYTE*)m_rcBuffer, m_dwMaxDatagramSize, MSG_TRUNC);
+
+		if(rc > 0)
+		{
+			m_dwDetectFails = 0;
+
+			if(::IsUdpCloseNotify(m_rcBuffer, rc))
+			{
+				m_ccContext.Reset(TRUE, SO_CLOSE, NO_ERROR, FALSE);
+				return FALSE;
+			}
+
+			if(rc > (int)m_dwMaxDatagramSize)
+			{
+				m_ccContext.Reset(TRUE, SO_RECEIVE, ERROR_BAD_LENGTH);
+				return FALSE;
+			}
+
+			if(TRIGGER(FireReceive(m_rcBuffer, rc)) == HR_ERROR)
+			{
+				TRACE("<C-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", m_dwConnID);
+
+				m_ccContext.Reset(TRUE, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
+				return FALSE;
+			}
+		}
+		else if(rc == SOCKET_ERROR)
+		{
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+				break;
+			else
+			{
+				m_ccContext.Reset(TRUE, SO_RECEIVE, code);
+				return FALSE;
+			}
+		}
+		else if(rc == 0)
+		{
+			m_dwDetectFails = 0;
+			TRACE("<C-CNNID: %zu> recv 0 bytes (detect ack package)", m_dwConnID);
+		}
+		else
+			ASSERT(FALSE);
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpClient::PauseReceive(BOOL bPause)
+{
+	if(!IsConnected())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(m_bPaused == bPause)
+		return TRUE;
+
+	m_bPaused = bPause;
+
+	if(!bPause)
+		return m_evRecv.Set();
+	
+	return TRUE;
+}
+
+BOOL CUdpClient::SendData()
+{
+	BOOL bBlocked = FALSE;
+
+	while(m_lsSend.Length() > 0)
+	{
+		TItemPtr itPtr(m_itPool);
+
+		{
+			CCriSecLock locallock(m_csSend);
+			itPtr = m_lsSend.PopFront();
+		}
+
+		if(!itPtr.IsValid())
+			break;
+
+		ASSERT(!itPtr->IsEmpty());
+
+		if(!DoSendData(itPtr, bBlocked))
+			return FALSE;
+
+		if(bBlocked)
+		{
+			CCriSecLock locallock(m_csSend);
+			m_lsSend.PushFront(itPtr.Detach());
+			break;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpClient::DoSendData(TItem* pItem, BOOL& bBlocked)
+{
+	int rc = (int)send(m_soClient, (char*)pItem->Ptr(), pItem->Size(), 0);
+
+	if(rc > 0)
+	{
+		ASSERT(rc == pItem->Size());
+
+		if(TRIGGER(FireSend(pItem->Ptr(), rc)) == HR_ERROR)
+		{
+			TRACE("<C-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", m_dwConnID);
+			ASSERT(FALSE);
+		}
+	}
+	else if(rc == SOCKET_ERROR)
+	{
+		int code = ::WSAGetLastError();
+
+		if(code == ERROR_WOULDBLOCK)
+			bBlocked = TRUE;
+		else
+		{
+			m_ccContext.Reset(TRUE, SO_SEND, code);
+			return FALSE;
+		}
+	}
+	else
+		ASSERT(FALSE);
+
+	return TRUE;
+}
+
+BOOL CUdpClient::DoSend(const BYTE* pBuffer, int iLength, int iOffset)
+{
+	ASSERT(pBuffer && iLength > 0 && iLength <= (int)m_dwMaxDatagramSize);
+
+	int result = NO_ERROR;
+
+	if(pBuffer && iLength > 0 && iLength <= (int)m_dwMaxDatagramSize)
+	{
+		if(IsConnected())
+		{
+			if(iOffset != 0) pBuffer += iOffset;
+
+			TItemPtr itPtr(m_itPool, m_itPool.PickFreeItem());
+			itPtr->Cat(pBuffer, iLength);
+
+			result = SendInternal(itPtr);
+		}
+		else
+			result = ERROR_INVALID_STATE;
+	}
+	else
+		result = ERROR_INVALID_PARAMETER;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+BOOL CUdpClient::SendPackets(const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	if(!pBuffers || iCount <= 0)
+		return ERROR_INVALID_PARAMETER;
+	if(!IsConnected())
+		return ERROR_INVALID_STATE;
+
+	int result	= NO_ERROR;
+	int iLength	= 0;
+	int iMaxLen	= (int)m_dwMaxDatagramSize;
+
+	TItemPtr itPtr(m_itPool, m_itPool.PickFreeItem());
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			iLength += iBufLen;
+
+			if(iLength <= iMaxLen)
+				itPtr->Cat(pBuffer, iBufLen);
+			else
+				break;
+		}
+	}
+
+	if(iLength > 0 && iLength <= iMaxLen)
+		result = SendInternal(itPtr);
+	else
+		result = ERROR_INCORRECT_SIZE;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+int CUdpClient::SendInternal(TItemPtr& itPtr)
+{
+	int iPending;
+
+	{
+		CCriSecLock locallock(m_csSend);
+
+		if(!IsConnected())
+			return ERROR_INVALID_STATE;
+
+		iPending = m_lsSend.Length();
+
+		m_lsSend.PushBack(itPtr.Detach());
+		ASSERT(m_lsSend.Length() > 0);
+	}
+
+	if(iPending == 0 && m_lsSend.Length() > 0) m_evSend.Set();
+
+	return NO_ERROR;
+}
+
+void CUdpClient::SetLastError(EnSocketError code, LPCSTR func, int ec)
+{
+	TRACE("%s --> Error: %d, EC: %d", func, code, ec);
+
+	m_enLastError = code;
+	::SetLastError(ec);
+}
+
+BOOL CUdpClient::GetLocalAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	return ::GetSocketLocalAddress(m_soClient, lpszAddress, iAddressLen, usPort);
+}
+
+void CUdpClient::SetRemoteHost(LPCTSTR lpszHost, USHORT usPort)
+{
+	m_strHost = lpszHost;
+	m_usPort  = usPort;
+}
+
+BOOL CUdpClient::GetRemoteHost(TCHAR lpszHost[], int& iHostLen, USHORT& usPort)
+{
+	BOOL isOK = FALSE;
+
+	if(m_strHost.IsEmpty())
+		return isOK;
+
+	int iLen = m_strHost.GetLength() + 1;
+
+	if(iHostLen >= iLen)
+	{
+		memcpy(lpszHost, CA2CT(m_strHost), iLen * sizeof(TCHAR));
+		usPort = m_usPort;
+
+		isOK = TRUE;
+	}
+
+	iHostLen = iLen;
+
+	return isOK;
+}
+
+BOOL CUdpClient::GetRemoteHost(LPCSTR* lpszHost, USHORT* pusPort)
+{
+	*lpszHost = m_strHost;
+
+	if(pusPort != nullptr)
+		*pusPort = m_usPort;
+
+	return !m_strHost.IsEmpty();
+}
+
+#endif

+ 234 - 0
UdpClient.h

@@ -0,0 +1,234 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "SocketHelper.h"
+#include "./common/GeneralHelper.h"
+
+#ifdef _UDP_SUPPORT
+
+class CUdpClient : public IUdpClient
+{
+public:
+	virtual BOOL Start	(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect = TRUE, LPCTSTR lpszBindAddress = nullptr, USHORT usLocalPort = 0);
+	virtual BOOL Stop	();
+	virtual BOOL Send	(const BYTE* pBuffer, int iLength, int iOffset = 0)	{return DoSend(pBuffer, iLength, iOffset);}
+	virtual BOOL SendPackets	(const WSABUF pBuffers[], int iCount);
+	virtual BOOL PauseReceive	(BOOL bPause = TRUE);
+	virtual BOOL Wait			(DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
+	virtual BOOL			HasStarted			()	{return m_enState == SS_STARTED || m_enState == SS_STARTING;}
+	virtual EnServiceState	GetState			()	{return m_enState;}
+	virtual CONNID			GetConnectionID		()	{return m_dwConnID;}
+	virtual EnSocketError	GetLastError		()	{return m_enLastError;}
+	virtual LPCTSTR			GetLastErrorDesc	()	{return ::GetSocketErrorDesc(m_enLastError);}
+
+	virtual BOOL GetLocalAddress		(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL GetRemoteHost			(TCHAR lpszHost[], int& iHostLen, USHORT& usPort);
+	virtual BOOL GetPendingDataLength	(int& iPending) {iPending = m_lsSend.Length(); return HasStarted();}
+	virtual BOOL IsPauseReceive			(BOOL& bPaused) {bPaused = m_bPaused; return HasStarted();}
+	virtual BOOL IsConnected			()				{return m_bConnected;}
+
+public:
+	virtual BOOL IsSecure				() {return FALSE;}
+
+	virtual void SetReuseAddressPolicy	(EnReuseAddressPolicy enReusePolicy){ENSURE_HAS_STOPPED(); m_enReusePolicy			= enReusePolicy;}
+	virtual void SetMaxDatagramSize		(DWORD dwMaxDatagramSize)			{ENSURE_HAS_STOPPED(); m_dwMaxDatagramSize		= dwMaxDatagramSize;}
+	virtual void SetDetectAttempts		(DWORD dwDetectAttempts)			{ENSURE_HAS_STOPPED(); m_dwDetectAttempts		= dwDetectAttempts;}
+	virtual void SetDetectInterval		(DWORD dwDetectInterval)			{ENSURE_HAS_STOPPED(); m_dwDetectInterval		= dwDetectInterval;}
+	virtual void SetFreeBufferPoolSize	(DWORD dwFreeBufferPoolSize)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolSize	= dwFreeBufferPoolSize;}
+	virtual void SetFreeBufferPoolHold	(DWORD dwFreeBufferPoolHold)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolHold	= dwFreeBufferPoolHold;}
+	virtual void SetExtra				(PVOID pExtra)						{m_pExtra										= pExtra;}						
+
+	virtual EnReuseAddressPolicy GetReuseAddressPolicy	()	{return m_enReusePolicy;}
+	virtual DWORD GetMaxDatagramSize	()	{return m_dwMaxDatagramSize;}
+	virtual DWORD GetDetectAttempts		()	{return m_dwDetectAttempts;}
+	virtual DWORD GetDetectInterval		()	{return m_dwDetectInterval;}
+	virtual DWORD GetFreeBufferPoolSize	()	{return m_dwFreeBufferPoolSize;}
+	virtual DWORD GetFreeBufferPoolHold	()	{return m_dwFreeBufferPoolHold;}
+	virtual PVOID GetExtra				()	{return m_pExtra;}
+
+protected:
+	virtual EnHandleResult FirePrepareConnect(SOCKET socket)
+		{return DoFirePrepareConnect(this, socket);}
+	virtual EnHandleResult FireConnect()
+		{
+			EnHandleResult rs		= DoFireConnect(this);
+			if(rs != HR_ERROR) rs	= FireHandShake();
+			return rs;
+		}
+	virtual EnHandleResult FireHandShake()
+		{return DoFireHandShake(this);}
+	virtual EnHandleResult FireSend(const BYTE* pData, int iLength)
+		{return DoFireSend(this, pData, iLength);}
+	virtual EnHandleResult FireReceive(const BYTE* pData, int iLength)
+		{return DoFireReceive(this, pData, iLength);}
+	virtual EnHandleResult FireReceive(int iLength)
+		{return DoFireReceive(this, iLength);}
+	virtual EnHandleResult FireClose(EnSocketOperation enOperation, int iErrorCode)
+		{return DoFireClose(this, enOperation, iErrorCode);}
+
+	virtual EnHandleResult DoFirePrepareConnect(IUdpClient* pSender, SOCKET socket)
+		{return m_pListener->OnPrepareConnect(pSender, pSender->GetConnectionID(), socket);}
+	virtual EnHandleResult DoFireConnect(IUdpClient* pSender)
+		{return m_pListener->OnConnect(pSender, pSender->GetConnectionID());}
+	virtual EnHandleResult DoFireHandShake(IUdpClient* pSender)
+		{return m_pListener->OnHandShake(pSender, pSender->GetConnectionID());}
+	virtual EnHandleResult DoFireSend(IUdpClient* pSender, const BYTE* pData, int iLength)
+		{return m_pListener->OnSend(pSender, pSender->GetConnectionID(), pData, iLength);}
+	virtual EnHandleResult DoFireReceive(IUdpClient* pSender, const BYTE* pData, int iLength)
+		{return m_pListener->OnReceive(pSender, pSender->GetConnectionID(), pData, iLength);}
+	virtual EnHandleResult DoFireReceive(IUdpClient* pSender, int iLength)
+		{return m_pListener->OnReceive(pSender, pSender->GetConnectionID(), iLength);}
+	virtual EnHandleResult DoFireClose(IUdpClient* pSender, EnSocketOperation enOperation, int iErrorCode)
+		{return m_pListener->OnClose(pSender, pSender->GetConnectionID(), enOperation, iErrorCode);}
+
+	void SetLastError(EnSocketError code, LPCSTR func, int ec);
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual void OnWorkerThreadStart(THR_ID tid) {}
+	virtual void OnWorkerThreadEnd(THR_ID tid) {}
+
+	virtual FD GetUserEvent() {return INVALID_FD;}
+	virtual BOOL OnUserEvent() {return TRUE;}
+
+	static BOOL DoSend(CUdpClient* pClient, const BYTE* pBuffer, int iLength, int iOffset = 0)
+		{return pClient->DoSend(pBuffer, iLength, iOffset);}
+
+protected:
+	void SetReserved	(PVOID pReserved)	{m_pReserved = pReserved;}						
+	PVOID GetReserved	()					{return m_pReserved;}
+	BOOL GetRemoteHost	(LPCSTR* lpszHost, USHORT* pusPort = nullptr);
+
+private:
+	BOOL DoSend			(const BYTE* pBuffer, int iLength, int iOffset = 0);
+
+	void SetRemoteHost	(LPCTSTR lpszHost, USHORT usPort);
+	void SetConnected	(BOOL bConnected = TRUE) {m_bConnected = bConnected; if(bConnected) m_enState = SS_STARTED;}
+
+	BOOL CheckStarting();
+	BOOL CheckStoping();
+	BOOL CreateClientSocket(LPCTSTR lpszRemoteAddress, HP_SOCKADDR& addrRemote, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& addrBind);
+	BOOL BindClientSocket(const HP_SOCKADDR& addrBind, const HP_SOCKADDR& addrRemote, USHORT usLocalPort);
+	BOOL ConnectToServer(const HP_SOCKADDR& addrRemote, BOOL bAsyncConnect);
+	BOOL CreateWorkerThread();
+	BOOL ProcessNetworkEvent(SHORT events);
+	BOOL ReadData();
+	BOOL SendData();
+	BOOL DoSendData(TItem* pItem, BOOL& bBlocked);
+	int SendInternal(TItemPtr& itPtr);
+	void WaitForWorkerThreadEnd();
+	void CheckConnected();
+
+	BOOL HandleConnect	(SHORT events);
+	BOOL HandleClose	(SHORT events);
+	BOOL HandleRead		(SHORT events);
+	BOOL HandleWrite	(SHORT events);
+
+	BOOL CheckConnection	();
+	BOOL DetectConnection	();
+	BOOL IsNeedDetect		() {return m_dwDetectAttempts > 0 && m_dwDetectInterval > 0;}
+
+	UINT WINAPI WorkerThreadProc(LPVOID pv);
+
+public:
+	CUdpClient(IUdpClientListener* pListener)
+	: m_pListener			(pListener)
+	, m_lsSend				(m_itPool)
+	, m_soClient			(INVALID_SOCKET)
+	, m_nEvents				(0)
+	, m_dwConnID			(0)
+	, m_usPort				(0)
+	, m_bPaused				(FALSE)
+	, m_bConnected			(FALSE)
+	, m_enLastError			(SE_OK)
+	, m_enState				(SS_STOPPED)
+	, m_dwDetectFails		(0)
+	, m_pExtra				(nullptr)
+	, m_pReserved			(nullptr)
+	, m_enReusePolicy		(RAP_ADDR_ONLY)
+	, m_dwMaxDatagramSize	(DEFAULT_UDP_MAX_DATAGRAM_SIZE)
+	, m_dwFreeBufferPoolSize(DEFAULT_CLIENT_FREE_BUFFER_POOL_SIZE)
+	, m_dwFreeBufferPoolHold(DEFAULT_CLIENT_FREE_BUFFER_POOL_HOLD)
+	, m_dwDetectAttempts	(DEFAULT_UDP_DETECT_ATTEMPTS)
+	, m_dwDetectInterval	(DEFAULT_UDP_DETECT_INTERVAL)
+	{
+		ASSERT(m_pListener);
+	}
+
+	virtual ~CUdpClient()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	CSEM				m_evWait;
+
+	IUdpClientListener*	m_pListener;
+	TClientCloseContext m_ccContext;
+
+	SOCKET				m_soClient;
+	SHORT				m_nEvents;
+	CONNID				m_dwConnID;
+
+	EnReuseAddressPolicy m_enReusePolicy;
+	DWORD				m_dwMaxDatagramSize;
+	DWORD				m_dwFreeBufferPoolSize;
+	DWORD				m_dwFreeBufferPoolHold;
+	DWORD				m_dwDetectAttempts;
+	DWORD				m_dwDetectInterval;
+
+	EnSocketError		m_enLastError;
+	volatile BOOL		m_bConnected;
+	volatile EnServiceState	m_enState;
+
+	PVOID				m_pExtra;
+	PVOID				m_pReserved;
+
+	CBufferPtr			m_rcBuffer;
+
+protected:
+	CStringA			m_strHost;
+	USHORT				m_usPort;
+
+	CItemPool			m_itPool;
+
+private:
+	CSpinGuard			m_csState;
+
+	CCriSec				m_csSend;
+	TItemListExV		m_lsSend;
+
+	CEvt				m_evSend;
+	CEvt				m_evRecv;
+	CEvt				m_evStop;
+
+	DWORD				m_dwDetectFails;
+	volatile BOOL		m_bPaused;
+
+	CThread<CUdpClient, VOID, UINT> m_thWorker;
+};
+
+#endif

+ 757 - 0
UdpNode.cpp

@@ -0,0 +1,757 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(__GNUC__) && __GNUC__ >= 11
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
+
+#include "UdpNode.h"
+
+#ifdef _UDP_SUPPORT
+
+BOOL CUdpNode::Start(LPCTSTR lpszBindAddress, USHORT usPort, EnCastMode enCastMode, LPCTSTR lpszCastAddress)
+{
+	m_enCastMode = enCastMode;
+
+	if(!CheckParams() || !CheckStarting())
+		return FALSE;
+
+	PrepareStart();
+
+	HP_SOCKADDR bindAddr(AF_UNSPEC, TRUE);
+
+	if(ParseBindAddr(lpszBindAddress, usPort, lpszCastAddress, bindAddr))
+		if(CreateListenSocket(bindAddr))
+			if(CreateWorkerThreads())
+				if(StartAccept())
+				{
+					m_enState = SS_STARTED;
+					return TRUE;
+				}
+
+	EXECUTE_RESTORE_ERROR(Stop());
+
+	return FALSE;
+}
+
+BOOL CUdpNode::CheckParams()
+{
+	if	(((int)m_dwFreeBufferPoolSize >= 0)														&&
+		((int)m_dwFreeBufferPoolHold >= 0)														&&
+		((int)m_dwPostReceiveCount > 0)															&&
+		((int)m_dwWorkerThreadCount > 0 && m_dwWorkerThreadCount <= MAX_WORKER_THREAD_COUNT)	&&
+		(m_enCastMode >= CM_UNICAST && m_enCastMode <= CM_BROADCAST)							&&
+		(m_iMCTtl >= 0 && m_iMCTtl <= 255)														&&
+		(m_bMCLoop == TRUE || m_bMCLoop == FALSE)												&&
+		((int)m_dwMaxDatagramSize > 0 && m_dwMaxDatagramSize <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE)	)
+		return TRUE;
+
+	SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+	return FALSE;
+}
+
+BOOL CUdpNode::CheckStarting()
+{
+	CReentrantWriteLock locallock(m_lcState);
+
+	if(m_enState == SS_STOPPED)
+		m_enState = SS_STARTING;
+	else
+	{
+		SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void CUdpNode::PrepareStart()
+{
+	m_bfObjPool.SetItemCapacity(m_dwMaxDatagramSize);
+	m_bfObjPool.SetPoolSize(m_dwFreeBufferPoolSize);
+	m_bfObjPool.SetPoolHold(m_dwFreeBufferPoolHold);
+
+	m_bfObjPool.Prepare();
+
+	TNodeBufferObjList* pBufferObjList = (TNodeBufferObjList*)malloc(m_dwWorkerThreadCount * sizeof(TNodeBufferObjList));
+
+	for(int i = 0; i < (int)m_dwWorkerThreadCount; i++)
+		new (pBufferObjList + i) TNodeBufferObjList(m_bfObjPool);
+
+	m_sndBuffs.reset(pBufferObjList);
+
+	m_csSends	= make_unique<CCriSec[]>(m_dwWorkerThreadCount);
+
+	m_rcBuffers = make_unique<CBufferPtr[]>(m_dwWorkerThreadCount);
+	for_each(m_rcBuffers.get(), m_rcBuffers.get() + m_dwWorkerThreadCount, [this](CBufferPtr& buff) {buff.Malloc(m_dwMaxDatagramSize);});
+
+	m_soListens	= make_unique<SOCKET[]>(m_dwWorkerThreadCount);
+	for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock) {sock = INVALID_FD;});
+}
+
+BOOL CUdpNode::ParseBindAddr(LPCTSTR lpszBindAddress, USHORT usPort, LPCTSTR lpszCastAddress, HP_SOCKADDR& bindAddr)
+{
+	if(::IsStrEmpty(lpszCastAddress))
+	{
+		if(m_enCastMode == CM_BROADCAST)
+			lpszCastAddress = DEFAULT_IPV4_BROAD_CAST_ADDRESS;
+		else if(m_enCastMode == CM_MULTICAST)
+		{
+			SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ERROR_ADDRNOTAVAIL);
+			return FALSE;
+		}
+	}
+
+	if(m_enCastMode != CM_UNICAST && !::sockaddr_A_2_IN(lpszCastAddress, usPort, m_castAddr))
+	{
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+
+	if(::IsStrEmpty(lpszBindAddress))
+	{
+		bindAddr.family = (m_enCastMode != CM_UNICAST) ? m_castAddr.family : AF_INET;
+		bindAddr.SetPort(usPort);
+	}
+	else
+	{
+		if(!::sockaddr_A_2_IN(lpszBindAddress, usPort, bindAddr))
+		{
+			SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+	}
+
+	if(m_enCastMode == CM_BROADCAST && bindAddr.IsIPv6())
+	{
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ERROR_PFNOSUPPORT);
+		return FALSE;
+	}
+
+	if(m_enCastMode != CM_UNICAST && m_castAddr.family != bindAddr.family)
+	{
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ERROR_AFNOSUPPORT);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpNode::CreateListenSocket(const HP_SOCKADDR& bindAddr)
+{
+	for(DWORD i = 0; i < m_dwWorkerThreadCount; i++)
+	{
+		m_soListens[i]  = socket(bindAddr.family, SOCK_DGRAM, IPPROTO_UDP);
+		SOCKET soListen = m_soListens[i];
+
+		if(IS_INVALID_FD(soListen))
+		{
+			SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+
+		::fcntl_SETFL(soListen, O_NOATIME | O_NONBLOCK | O_CLOEXEC);
+		VERIFY(IS_NO_ERROR(::SSO_ReuseAddress(soListen, m_enReusePolicy)));
+
+		if(IS_HAS_ERROR(::bind(soListen, bindAddr.Addr(), bindAddr.AddrSize())))
+		{
+			SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+
+		if(i == 0)
+		{
+			socklen_t dwAddrLen = (socklen_t)bindAddr.AddrSize();
+			ENSURE(IS_NO_ERROR(::getsockname(soListen, m_localAddr.Addr(), &dwAddrLen)));
+		}
+
+		if(m_enCastMode == CM_MULTICAST)
+		{
+			if(!::SetMultiCastSocketOptions(soListen, bindAddr, m_castAddr, m_iMCTtl, m_bMCLoop))
+			{
+				SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
+				return FALSE;
+			}
+		}
+		else if(m_enCastMode == CM_BROADCAST)
+		{
+			ASSERT(m_castAddr.IsIPv4());
+
+			BOOL bSet = TRUE;
+			if(IS_HAS_ERROR(::SSO_SetSocketOption(soListen, SOL_SOCKET, SO_BROADCAST, &bSet, sizeof(BOOL))))
+			{
+				SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
+				return FALSE;
+			}
+		}
+
+		if(TRIGGER(FirePrepareListen(soListen)) == HR_ERROR)
+		{
+			SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpNode::CreateWorkerThreads()
+{
+	return m_ioDispatcher.Start(this, m_dwPostReceiveCount, m_dwWorkerThreadCount);
+}
+
+BOOL CUdpNode::StartAccept()
+{
+	for(int i = 0; i < (int)m_dwWorkerThreadCount; i++)
+	{
+		SOCKET& soListen = m_soListens[i];
+
+		if(!m_ioDispatcher.AddFD(i, soListen, EPOLLIN | EPOLLOUT | EPOLLET, TO_PVOID(&soListen)))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpNode::Stop()
+{
+	if(!CheckStoping())
+		return FALSE;
+
+	CloseListenSocket();
+
+	WaitForWorkerThreadEnd();
+
+	FireShutdown();
+
+	ReleaseFreeBuffer();
+
+	Reset();
+
+	return TRUE;
+}
+
+BOOL CUdpNode::CheckStoping()
+{
+	if(m_enState != SS_STOPPED)
+	{
+		CReentrantWriteLock locallock(m_lcState);
+
+		if(HasStarted())
+		{
+			m_enState = SS_STOPPING;
+			return TRUE;
+		}
+	}
+
+	SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+
+	return FALSE;
+}
+
+void CUdpNode::CloseListenSocket()
+{
+	if(m_soListens)
+	{
+		for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock)
+		{
+			if(sock != INVALID_FD)
+			{
+				::ManualCloseSocket(sock);
+				sock = INVALID_FD;
+			}
+		});
+
+		::WaitFor(100);
+	}
+}
+
+void CUdpNode::WaitForWorkerThreadEnd()
+{
+	m_ioDispatcher.Stop();
+}
+
+void CUdpNode::ReleaseFreeBuffer()
+{
+	for_each(m_sndBuffs.get(), m_sndBuffs.get() + m_dwWorkerThreadCount, [](TNodeBufferObjList& sndBuff)
+	{
+		sndBuff.Clear();
+		sndBuff.~TNodeBufferObjList();
+	});
+
+	free(m_sndBuffs.release());
+
+	m_csSends = nullptr;
+
+	m_bfObjPool.Clear();
+}
+
+void CUdpNode::Reset()
+{
+	m_castAddr.Reset();
+	m_localAddr.Reset();
+
+	m_soListens = nullptr;
+	m_rcBuffers	= nullptr;
+	 
+	m_iSending	= 0;
+	m_enState	= SS_STOPPED;
+
+	m_evWait.SyncNotifyAll();
+}
+
+int CUdpNode::GenerateBufferIndex(const HP_SOCKADDR& addrRemote)
+{
+	return (int)(addrRemote.Hash() % m_dwWorkerThreadCount);
+}
+
+BOOL CUdpNode::Send(LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	HP_SOCKADDR addrRemote;
+
+	if(!::GetSockAddrByHostName(lpszRemoteAddress, usRemotePort, addrRemote))
+		return FALSE;
+
+	return DoSend(addrRemote, pBuffer, iLength, iOffset);
+}
+
+BOOL CUdpNode::SendPackets(LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const WSABUF pBuffers[], int iCount)
+{
+	HP_SOCKADDR addrRemote;
+
+	if(!::GetSockAddrByHostName(lpszRemoteAddress, usRemotePort, addrRemote))
+		return FALSE;
+
+	return DoSendPackets(addrRemote, pBuffers, iCount);
+}
+
+BOOL CUdpNode::SendCast(const BYTE* pBuffer, int iLength, int iOffset)
+{
+	if(m_enCastMode == CM_UNICAST)
+	{
+		::SetLastError(ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	return DoSend(m_castAddr, pBuffer, iLength, iOffset);
+}
+
+BOOL CUdpNode::SendCastPackets(const WSABUF pBuffers[], int iCount)
+{
+	if(m_enCastMode == CM_UNICAST)
+	{
+		::SetLastError(ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	return DoSendPackets(m_castAddr, pBuffers, iCount);
+}
+
+BOOL CUdpNode::DoSend(const HP_SOCKADDR& addrRemote, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	ASSERT(pBuffer && iLength >= 0 && iLength <= (int)m_dwMaxDatagramSize);
+
+	int result = NO_ERROR;
+
+	if(IsValid())
+	{
+		if(addrRemote.family == m_localAddr.family)
+		{
+			if(pBuffer && iLength >= 0 && iLength <= (int)m_dwMaxDatagramSize)
+			{
+				if(iOffset != 0) pBuffer += iOffset;
+
+				TNodeBufferObjPtr bufPtr(m_bfObjPool, m_bfObjPool.PickFreeItem());
+				bufPtr->Cat(pBuffer, iLength);
+
+				result = SendInternal(addrRemote, bufPtr);
+			}
+			else
+				result = ERROR_INVALID_PARAMETER;
+		}
+		else
+			result = ERROR_AFNOSUPPORT;
+	}
+	else
+		result = ERROR_INVALID_STATE;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+BOOL CUdpNode::DoSendPackets(const HP_SOCKADDR& addrRemote, const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	if(!pBuffers || iCount <= 0)
+		return ERROR_INVALID_PARAMETER;
+
+	if(!IsValid())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	if(addrRemote.family != m_localAddr.family)
+	{
+		::SetLastError(ERROR_AFNOSUPPORT);
+		return FALSE;
+	}
+
+	int result	= NO_ERROR;
+	int iLength = 0;
+	int iMaxLen = (int)m_dwMaxDatagramSize;
+
+	TNodeBufferObjPtr bufPtr(m_bfObjPool, m_bfObjPool.PickFreeItem());
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			iLength += iBufLen;
+
+			if(iLength <= iMaxLen)
+				bufPtr->Cat(pBuffer, iBufLen);
+			else
+				break;
+		}
+	}
+
+	if(iLength > 0 && iLength <= iMaxLen)
+		result = SendInternal(addrRemote, bufPtr);
+	else
+		result = ERROR_INCORRECT_SIZE;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+int CUdpNode::SendInternal(const HP_SOCKADDR& addrRemote, TNodeBufferObjPtr& bufPtr)
+{
+	BOOL bPending;
+	int iBufferSize	= bufPtr->Size();
+	int idx			= GenerateBufferIndex(addrRemote);
+
+	addrRemote.Copy(bufPtr->remoteAddr);
+
+	{
+		CReentrantReadLock locallock(m_lcState);
+
+		if(!IsValid())
+			return ERROR_INVALID_STATE;
+
+		TNodeBufferObjList& sndBuff = m_sndBuffs[idx];
+
+		CCriSecLock locallock2(m_csSends[idx]);
+
+		bPending = IsPending(idx);
+		sndBuff.PushBack(bufPtr.Detach());
+
+		if(iBufferSize == 0) sndBuff.IncreaseLength(1);
+
+		ASSERT(sndBuff.Length() > 0);
+	}
+
+	if(!bPending && IsPending(idx))
+		VERIFY(m_ioDispatcher.SendCommandByIndex(idx, DISP_CMD_SEND));
+
+	return NO_ERROR;
+}
+
+BOOL CUdpNode::OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	ASSERT(pv == &m_soListens[pContext->GetIndex()]);
+	
+	return TRUE;
+}
+
+VOID CUdpNode::OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)
+{
+
+}
+
+VOID CUdpNode::OnCommand(const TDispContext* pContext, TDispCommand* pCmd)
+{
+	int idx  = pContext->GetIndex();
+	int flag = (int)(pCmd->wParam);
+
+	switch(pCmd->type)
+	{
+	case DISP_CMD_SEND:
+		HandleCmdSend(idx, flag);
+		break;
+	}
+}
+
+BOOL CUdpNode::OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleReceive(pContext, RETRIVE_EVENT_FLAG_H(events));
+}
+
+BOOL CUdpNode::OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleSend(pContext, RETRIVE_EVENT_FLAG_H(events), RETRIVE_EVENT_FLAG_R(events));
+}
+
+BOOL CUdpNode::OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleClose(pContext->GetIndex(), nullptr, SO_CLOSE, 0);
+}
+
+BOOL CUdpNode::OnError(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleClose(pContext->GetIndex(), nullptr, SO_CLOSE, -1);
+}
+
+VOID CUdpNode::OnDispatchThreadStart(THR_ID tid)
+{
+	OnWorkerThreadStart(tid);
+}
+
+VOID CUdpNode::OnDispatchThreadEnd(THR_ID tid)
+{
+	OnWorkerThreadEnd(tid);
+}
+
+BOOL CUdpNode::HandleClose(int idx, TNodeBufferObj* pBufferObj, EnSocketOperation enOperation, int iErrorCode)
+{
+	if(!HasStarted())
+		return FALSE;
+
+	if(iErrorCode == -1)
+		iErrorCode = ::SSO_GetError(m_soListens[idx]);
+
+	if(pBufferObj != nullptr)
+		TRIGGER(FireError(&pBufferObj->remoteAddr, pBufferObj->Ptr(), pBufferObj->Size(), enOperation, iErrorCode));
+	else
+		TRIGGER(FireError(nullptr, nullptr, 0, enOperation, iErrorCode));
+
+	return TRUE;
+}
+
+BOOL CUdpNode::HandleReceive(const TDispContext* pContext, int flag)
+{
+	int idx				= pContext->GetIndex();
+	CBufferPtr& buffer	= m_rcBuffers[idx];
+	int iBufferLen		= (int)buffer.Size();
+
+	while(TRUE)
+	{
+		HP_SOCKADDR addr;
+		socklen_t dwAddrLen = (socklen_t)addr.AddrSize();
+
+		int rc = (int)recvfrom(m_soListens[idx], buffer.Ptr(), iBufferLen, MSG_TRUNC, addr.Addr(), &dwAddrLen);
+
+		if(rc >= 0)
+		{
+			if(rc > iBufferLen)
+			{
+				TRIGGER(FireError(&addr, buffer.Ptr(), iBufferLen, SO_RECEIVE, ERROR_BAD_LENGTH));
+				continue;
+			}
+
+			TRIGGER(FireReceive(&addr, buffer.Ptr(), rc));
+		}
+		else if(rc == SOCKET_ERROR)
+		{
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+				break;
+			else if(!HandleClose(idx, nullptr, SO_RECEIVE, code))
+				return FALSE;
+		}
+		else
+		{
+			ASSERT(FALSE);
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpNode::HandleSend(const TDispContext* pContext, int flag, int rd)
+{
+	HandleCmdSend(pContext->GetIndex(), flag);
+
+	return TRUE;
+}
+
+VOID CUdpNode::HandleCmdSend(int idx, int flag)
+{
+	BOOL bBlocked				= FALSE;
+	TNodeBufferObjList& sndBuff	= m_sndBuffs[idx];
+
+	TNodeBufferObjPtr bufPtr(m_bfObjPool);
+
+	while(IsPending(idx))
+	{
+		{
+			CCriSecLock locallock(m_csSends[idx]);
+			bufPtr = sndBuff.PopFront();
+		}
+
+		if(!bufPtr.IsValid())
+			break;
+
+		if(!SendItem(idx, sndBuff, bufPtr, bBlocked))
+			return;
+
+		if(bBlocked)
+		{
+			{
+				CCriSecLock locallock(m_csSends[idx]);
+				sndBuff.PushFront(bufPtr.Detach());
+			}
+
+			break;
+		}
+	}
+
+	if(!bBlocked && IsPending(idx))
+		VERIFY(m_ioDispatcher.SendCommandByIndex(idx, DISP_CMD_SEND));
+}
+
+BOOL CUdpNode::SendItem(int idx, TNodeBufferObjList& sndBuff, TNodeBufferObj* pBufferObj, BOOL& bBlocked)
+{
+	int rc = (int)sendto(m_soListens[idx], pBufferObj->Ptr(), pBufferObj->Size(), 0, pBufferObj->remoteAddr.Addr(), pBufferObj->remoteAddr.AddrSize());
+
+	if(rc >= 0)
+	{
+		ASSERT(rc == pBufferObj->Size());
+
+		if(rc == 0)
+		{
+			CCriSecLock locallock(m_csSends[idx]);
+			sndBuff.ReduceLength(1);
+		}
+
+		TRIGGER(FireSend(pBufferObj));
+	}
+	else if(rc == SOCKET_ERROR)
+	{
+		int code = ::WSAGetLastError();
+
+		if(code == ERROR_WOULDBLOCK)
+			bBlocked = TRUE;
+		else if(!HandleClose(idx, pBufferObj, SO_SEND, code))
+			return FALSE;
+	}
+	else
+	{
+		ASSERT(FALSE);
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpNode::GetLocalAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ADDRESS_FAMILY usFamily;
+	return ::sockaddr_IN_2_A(m_localAddr, usFamily, lpszAddress, iAddressLen, usPort);
+}
+
+BOOL CUdpNode::GetCastAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ADDRESS_FAMILY usFamily;
+	return ::sockaddr_IN_2_A(m_castAddr, usFamily, lpszAddress, iAddressLen, usPort);
+}
+
+void CUdpNode::SetLastError(EnSocketError code, LPCSTR func, int ec)
+{
+	m_enLastError = code;
+	::SetLastError(ec);
+}
+
+BOOL CUdpNode::GetPendingDataLength(int& iPending)
+{
+	iPending = 0;
+
+	{
+		CReentrantReadLock locallock(m_lcState);
+
+		if(!IsValid())
+			return FALSE;
+
+		for_each(m_sndBuffs.get(), m_sndBuffs.get() + m_dwWorkerThreadCount, [&iPending](TNodeBufferObjList& sndBuff) { iPending += sndBuff.Length(); });
+	}
+
+	return TRUE;
+}
+
+EnHandleResult CUdpNode::FireSend(TNodeBufferObj* pBufferObj)
+{
+	TCHAR szAddress[60];
+	int iAddressLen = ARRAY_SIZE(szAddress);
+	ADDRESS_FAMILY usFamily;
+	USHORT usPort;
+
+	::sockaddr_IN_2_A(pBufferObj->remoteAddr, usFamily, szAddress, iAddressLen, usPort);
+
+	return m_pListener->OnSend(this, szAddress, usPort, pBufferObj->Ptr(), pBufferObj->Size());
+}
+
+EnHandleResult CUdpNode::FireReceive(const HP_SOCKADDR* pRemoteAddr, const BYTE* pData, int iLength)
+{
+	TCHAR szAddress[60];
+	int iAddressLen = ARRAY_SIZE(szAddress);
+	ADDRESS_FAMILY usFamily;
+	USHORT usPort;
+
+	::sockaddr_IN_2_A(*pRemoteAddr, usFamily, szAddress, iAddressLen, usPort);
+	return m_pListener->OnReceive(this, szAddress, usPort, pData, iLength);
+}
+
+EnHandleResult CUdpNode::FireError(const HP_SOCKADDR* pRemoteAddr, const BYTE* pData, int iLength, EnSocketOperation enOperation, int iErrorCode)
+{
+	TCHAR szAddress[60];
+	int iAddressLen = ARRAY_SIZE(szAddress);
+	ADDRESS_FAMILY usFamily;
+	USHORT usPort;
+
+	if(pRemoteAddr == nullptr)
+	{
+		::sockaddr_IN_2_A(m_localAddr, usFamily, szAddress, iAddressLen, usPort);
+		return m_pListener->OnError(this, enOperation, iErrorCode, szAddress, usPort, nullptr, 0);
+	}
+
+	::sockaddr_IN_2_A(*pRemoteAddr, usFamily, szAddress, iAddressLen, usPort);
+	return m_pListener->OnError(this, enOperation, iErrorCode, szAddress, usPort, pData, iLength);
+}
+
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 11
+#pragma GCC diagnostic pop
+#endif

+ 198 - 0
UdpNode.h

@@ -0,0 +1,198 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "SocketHelper.h"
+#include "./common/GeneralHelper.h"
+#include "./common/IODispatcher.h"
+
+#ifdef _UDP_SUPPORT
+
+class CUdpNode : public IUdpNode, private CIOHandler
+{
+public:
+	virtual BOOL Start	(LPCTSTR lpszBindAddress = nullptr, USHORT usPort = 0, EnCastMode enCastMode = CM_UNICAST, LPCTSTR lpszCastAddress = nullptr);
+	virtual BOOL Stop	();
+	virtual BOOL Send	(LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength, int iOffset = 0);
+	virtual BOOL SendPackets	(LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const WSABUF pBuffers[], int iCount);
+	virtual BOOL SendCast		(const BYTE* pBuffer, int iLength, int iOffset = 0);
+	virtual BOOL SendCastPackets(const WSABUF pBuffers[], int iCount);
+	virtual BOOL Wait			(DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
+
+	virtual BOOL			HasStarted			()	{return m_enState == SS_STARTED || m_enState == SS_STARTING;}
+	virtual EnServiceState	GetState			()	{return m_enState;}
+	virtual BOOL			GetLocalAddress		(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL			GetCastAddress		(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+
+	virtual EnSocketError GetLastError	()	{return m_enLastError;}
+	virtual LPCTSTR GetLastErrorDesc	()	{return ::GetSocketErrorDesc(m_enLastError);}
+	virtual BOOL GetPendingDataLength	(int& iPending);
+
+private:
+	virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)			override;
+	virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)	override;
+	virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd)					override;
+	virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)				override;
+	virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)				override;
+	virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)					override;
+	virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events)					override;
+	virtual VOID OnDispatchThreadStart(THR_ID tid)												override;
+	virtual VOID OnDispatchThreadEnd(THR_ID tid)												override;
+
+public:
+	virtual void SetReuseAddressPolicy	(EnReuseAddressPolicy enReusePolicy){ENSURE_HAS_STOPPED(); ASSERT(m_enReusePolicy == enReusePolicy);}
+	virtual void SetWorkerThreadCount	(DWORD dwWorkerThreadCount)			{ENSURE_HAS_STOPPED(); m_dwWorkerThreadCount	= dwWorkerThreadCount;}
+	virtual void SetFreeBufferPoolSize	(DWORD dwFreeBufferPoolSize)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolSize	= dwFreeBufferPoolSize;}
+	virtual void SetFreeBufferPoolHold	(DWORD dwFreeBufferPoolHold)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolHold	= dwFreeBufferPoolHold;}
+	virtual void SetPostReceiveCount	(DWORD dwPostReceiveCount)			{ENSURE_HAS_STOPPED(); m_dwPostReceiveCount		= dwPostReceiveCount;}
+	virtual void SetMaxDatagramSize		(DWORD dwMaxDatagramSize)			{ENSURE_HAS_STOPPED(); m_dwMaxDatagramSize		= dwMaxDatagramSize;}
+	virtual void SetMultiCastTtl		(int iMCTtl)						{ENSURE_HAS_STOPPED(); m_iMCTtl					= iMCTtl;}
+	virtual void SetMultiCastLoop		(BOOL bMCLoop)						{ENSURE_HAS_STOPPED(); m_bMCLoop				= bMCLoop;}
+	virtual void SetExtra				(PVOID pExtra)						{m_pExtra										= pExtra;}						
+
+	virtual EnReuseAddressPolicy GetReuseAddressPolicy	()	{return m_enReusePolicy;}
+	virtual DWORD GetWorkerThreadCount	()	{return m_dwWorkerThreadCount;}
+	virtual DWORD GetFreeBufferPoolSize	()	{return m_dwFreeBufferPoolSize;}
+	virtual DWORD GetFreeBufferPoolHold	()	{return m_dwFreeBufferPoolHold;}
+	virtual DWORD GetPostReceiveCount	()	{return m_dwPostReceiveCount;}
+	virtual DWORD GetMaxDatagramSize	()	{return m_dwMaxDatagramSize;}
+	virtual EnCastMode GetCastMode		()	{return m_enCastMode;}
+	virtual int GetMultiCastTtl			()	{return m_iMCTtl;}
+	virtual BOOL IsMultiCastLoop		()	{return m_bMCLoop;}
+	virtual PVOID GetExtra				()	{return m_pExtra;}
+
+protected:
+	EnHandleResult FirePrepareListen(SOCKET soListen)
+		{return m_pListener->OnPrepareListen(this, soListen);}
+	EnHandleResult FireShutdown()
+		{return m_pListener->OnShutdown(this);}
+
+	EnHandleResult FireSend(TNodeBufferObj* pBufferObj);
+	EnHandleResult FireReceive(const HP_SOCKADDR* pRemoteAddr, const BYTE* pData, int iLength);
+	EnHandleResult FireError(const HP_SOCKADDR*	pRemoteAddr, const BYTE* pData, int iLength, EnSocketOperation enOperation, int iErrorCode);
+
+	void SetLastError(EnSocketError code, LPCSTR func, int ec);
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual void OnWorkerThreadStart(THR_ID dwThreadID) {}
+	virtual void OnWorkerThreadEnd(THR_ID dwThreadID) {}
+
+	BOOL DoSend(const HP_SOCKADDR& addrRemote, const BYTE* pBuffer, int iLength, int iOffset = 0);
+	BOOL DoSendPackets(const HP_SOCKADDR& addrRemote, const WSABUF pBuffers[], int iCount);
+	int SendInternal(const HP_SOCKADDR& addrRemote, TNodeBufferObjPtr& bufPtr);
+
+private:
+	BOOL CheckStarting();
+	BOOL CheckStoping();
+	BOOL ParseBindAddr(LPCTSTR lpszBindAddress, USHORT usPort, LPCTSTR lpszCastAddress, HP_SOCKADDR& bindAddr);
+	BOOL CreateListenSocket(const HP_SOCKADDR& bindAddr);
+	BOOL CreateWorkerThreads();
+	BOOL StartAccept();
+
+	void CloseListenSocket();
+	void WaitForWorkerThreadEnd();
+	void ReleaseFreeBuffer();
+
+	int GenerateBufferIndex(const HP_SOCKADDR& addrRemote);
+
+private:
+	BOOL HandleReceive(const TDispContext* pContext, int flag = 0);
+	BOOL HandleSend(const TDispContext* pContext, int flag = 0, int rd = 0);
+	BOOL HandleClose(int idx, TNodeBufferObj* pBufferObj, EnSocketOperation enOperation, int iErrorCode);
+
+	VOID HandleCmdSend(int idx, int flag);
+
+	BOOL SendItem(int idx, TNodeBufferObjList& sndBuff, TNodeBufferObj* pBufferObj, BOOL& bBlocked);
+
+private:
+	BOOL IsValid			()			{return m_enState == SS_STARTED;}
+	BOOL IsPending			(int idx)	{return m_sndBuffs[idx].Length() > 0;}
+
+public:
+	CUdpNode(IUdpNodeListener* pListener)
+	: m_pListener				(pListener)
+	, m_iSending				(0)
+	, m_enLastError				(SE_OK)
+	, m_enState					(SS_STOPPED)
+	, m_enReusePolicy			(RAP_ADDR_AND_PORT)
+	, m_dwWorkerThreadCount		(DEFAULT_WORKER_THREAD_COUNT)
+	, m_dwFreeBufferPoolSize	(DEFAULT_FREE_BUFFEROBJ_POOL)
+	, m_dwFreeBufferPoolHold	(DEFAULT_FREE_BUFFEROBJ_HOLD)
+	, m_dwPostReceiveCount		(DEFAULT_UDP_POST_RECEIVE_COUNT)
+	, m_dwMaxDatagramSize		(DEFAULT_UDP_MAX_DATAGRAM_SIZE)
+	, m_pExtra					(nullptr)
+	, m_iMCTtl					(1)
+	, m_bMCLoop					(FALSE)
+	, m_enCastMode				(CM_UNICAST)
+	, m_castAddr				(AF_UNSPEC, TRUE)
+	, m_localAddr				(AF_UNSPEC, TRUE)
+	{
+		ASSERT(m_pListener);
+	}
+
+	virtual ~CUdpNode()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	EnReuseAddressPolicy m_enReusePolicy;
+	DWORD m_dwWorkerThreadCount;
+	DWORD m_dwFreeBufferPoolSize;
+	DWORD m_dwFreeBufferPoolHold;
+	DWORD m_dwPostReceiveCount;
+	DWORD m_dwMaxDatagramSize;
+	PVOID m_pExtra;
+
+	int					m_iMCTtl;
+	BOOL				m_bMCLoop;
+	EnCastMode			m_enCastMode;
+
+private:
+	CSEM				m_evWait;
+
+	HP_SOCKADDR			m_castAddr;
+	HP_SOCKADDR			m_localAddr;
+
+	CNodeBufferObjPool	m_bfObjPool;
+	CNodeCriSecs		m_csSends;
+	TNodeBufferObjLists	m_sndBuffs;
+
+	IUdpNodeListener*	m_pListener;
+	ListenSocketsPtr	m_soListens;
+	EnServiceState		m_enState;
+	EnSocketError		m_enLastError;
+
+	CReceiveBuffersPtr	m_rcBuffers;
+
+	CRWLock				m_lcState;
+
+	volatile long		m_iSending;
+
+	CIODispatcher		m_ioDispatcher;
+};
+
+#endif

+ 1231 - 0
UdpServer.cpp

@@ -0,0 +1,1231 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include "UdpServer.h"
+
+#ifdef _UDP_SUPPORT
+
+EnHandleResult CUdpServer::TriggerFireAccept(TUdpSocketObj* pSocketObj)
+{
+	EnHandleResult rs = TRIGGER(FireAccept(pSocketObj));
+
+	return rs;
+}
+
+BOOL CUdpServer::Start(LPCTSTR lpszBindAddress, USHORT usPort)
+{
+	if(!CheckParams() || !CheckStarting())
+		return FALSE;
+
+	PrepareStart();
+
+	if(CreateListenSocket(lpszBindAddress, usPort))
+		if(CreateWorkerThreads())
+			if(StartAccept())
+			{
+				m_enState = SS_STARTED;
+				return TRUE;
+			}
+
+	EXECUTE_RESTORE_ERROR(Stop());
+
+	return FALSE;
+}
+
+void CUdpServer::SetLastError(EnSocketError code, LPCSTR func, int ec)
+{
+	m_enLastError = code;
+	::SetLastError(ec);
+}
+
+BOOL CUdpServer::CheckParams()
+{
+	if	((m_enSendPolicy >= SP_PACK && m_enSendPolicy <= SP_DIRECT)								&&
+		(m_enOnSendSyncPolicy >= OSSP_NONE && m_enOnSendSyncPolicy <= OSSP_RECEIVE)				&&
+		((int)m_dwMaxConnectionCount > 0 && m_dwMaxConnectionCount <= MAX_CONNECTION_COUNT)		&&
+		((int)m_dwWorkerThreadCount > 0 && m_dwWorkerThreadCount <= MAX_WORKER_THREAD_COUNT)	&&
+		((int)m_dwFreeSocketObjLockTime >= 1000)												&&
+		((int)m_dwFreeSocketObjPool >= 0)														&&
+		((int)m_dwFreeBufferObjPool >= 0)														&&
+		((int)m_dwFreeSocketObjHold >= 0)														&&
+		((int)m_dwFreeBufferObjHold >= 0)														&&
+		((int)m_dwMaxDatagramSize > 0 && m_dwMaxDatagramSize <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE)	&&
+		((int)m_dwPostReceiveCount > 0)															&&
+		((int)m_dwDetectAttempts >= 0)															&&
+		((int)m_dwDetectInterval >= 1000 || m_dwDetectInterval == 0)							)
+		return TRUE;
+
+	SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
+	return FALSE;
+}
+
+void CUdpServer::PrepareStart()
+{
+	m_bfActiveSockets.Reset(m_dwMaxConnectionCount);
+	m_lsFreeSocket.Reset(m_dwFreeSocketObjPool);
+
+	m_bfObjPool.SetItemCapacity(m_dwMaxDatagramSize);
+	m_bfObjPool.SetPoolSize(m_dwFreeBufferObjPool);
+	m_bfObjPool.SetPoolHold(m_dwFreeBufferObjHold);
+
+	m_bfObjPool.Prepare();
+
+	m_quSends	= make_unique<CSendQueue[]>(m_dwWorkerThreadCount);
+
+	m_rcBuffers = make_unique<CBufferPtr[]>(m_dwWorkerThreadCount);
+	for_each(m_rcBuffers.get(), m_rcBuffers.get() + m_dwWorkerThreadCount, [this](CBufferPtr& buff) {buff.Malloc(m_dwMaxDatagramSize);});
+
+	m_soListens	= make_unique<SOCKET[]>(m_dwWorkerThreadCount);
+	for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock) {sock = INVALID_FD;});
+}
+
+BOOL CUdpServer::CheckStarting()
+{
+	CSpinLock locallock(m_csState);
+
+	if(m_enState == SS_STOPPED)
+		m_enState = SS_STARTING;
+	else
+	{
+		SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpServer::CheckStoping()
+{
+	if(m_enState != SS_STOPPED)
+	{
+		CSpinLock locallock(m_csState);
+
+		if(HasStarted())
+		{
+			m_enState = SS_STOPPING;
+			return TRUE;
+		}
+	}
+
+	SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
+
+	return FALSE;
+}
+
+BOOL CUdpServer::CreateListenSocket(LPCTSTR lpszBindAddress, USHORT usPort)
+{
+	if(::IsStrEmpty(lpszBindAddress))
+		lpszBindAddress = DEFAULT_IPV4_BIND_ADDRESS;
+
+	HP_SOCKADDR addr;
+
+	if(!::sockaddr_A_2_IN(lpszBindAddress, usPort, addr))
+	{
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+
+	for(DWORD i = 0; i < m_dwWorkerThreadCount; i++)
+	{
+		m_soListens[i]  = socket(addr.family, SOCK_DGRAM, IPPROTO_UDP);
+		SOCKET soListen = m_soListens[i];
+
+		if(IS_INVALID_FD(soListen))
+		{
+			SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+
+		::fcntl_SETFL(soListen, O_NOATIME | O_NONBLOCK | O_CLOEXEC);
+		VERIFY(IS_NO_ERROR(::SSO_ReuseAddress(soListen, m_enReusePolicy)));
+
+		if(IS_HAS_ERROR(::bind(soListen, addr.Addr(), addr.AddrSize())))
+		{
+			SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+
+		if(TRIGGER(FirePrepareListen(soListen)) == HR_ERROR)
+		{
+			SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpServer::CreateWorkerThreads()
+{
+	DWORD dwWorkerThreadCount = m_dwWorkerThreadCount
+#ifdef USE_EXTERNAL_GC
+														+ 1
+#endif
+														;
+
+	if(!m_ioDispatcher.Start(this, m_dwPostReceiveCount, dwWorkerThreadCount))
+	{
+		SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+
+#ifdef USE_EXTERNAL_GC
+	m_fdGCTimer = m_ioDispatcher.AddTimer(m_dwWorkerThreadCount, GC_CHECK_INTERVAL, this);
+
+	if(IS_INVALID_FD(m_fdGCTimer))
+	{
+		SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+#endif
+
+	return TRUE;
+}
+
+BOOL CUdpServer::StartAccept()
+{
+	for(int i = 0; i < (int)m_dwWorkerThreadCount; i++)
+	{
+		SOCKET& soListen = m_soListens[i];
+
+		if(!m_ioDispatcher.AddFD(i, soListen, EPOLLIN | EPOLLOUT | EPOLLET, TO_PVOID(&soListen)))
+		{
+			SetLastError(SE_SOCKE_ATTACH_TO_CP, __FUNCTION__, ::WSAGetLastError());
+			return FALSE;
+		}
+	}
+
+#ifdef USE_EXTERNAL_GC
+	m_fdGCTimer = m_ioDispatcher.AddTimer(m_dwWorkerThreadCount, GC_CHECK_INTERVAL, this);
+
+	if(IS_INVALID_FD(m_fdGCTimer))
+	{
+		SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError());
+		return FALSE;
+	}
+#endif
+
+	return TRUE;
+}
+
+BOOL CUdpServer::Stop()
+{
+	if(!CheckStoping())
+		return FALSE;
+
+	SendCloseNotify();
+
+	CloseListenSocket();
+
+	DisconnectClientSocket();
+	WaitForClientSocketClose();
+
+	WaitForWorkerThreadEnd();
+	
+	ReleaseClientSocket();
+
+	FireShutdown();
+
+	ReleaseFreeSocket();
+	ClearSendQueues();
+
+	Reset();
+
+	return TRUE;
+}
+
+void CUdpServer::SendCloseNotify()
+{
+	if(!m_soListens)
+		return;
+	if(m_bfActiveSockets.Elements() == 0)
+		return;
+
+	TUdpSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+	{
+		CONNID connID			  = *it;
+		TUdpSocketObj* pSocketObj = FindSocketObj(connID);
+
+		if(TUdpSocketObj::IsValid(pSocketObj))
+			::SendUdpCloseNotify(m_soListens[pSocketObj->index], pSocketObj->remoteAddr);
+	}
+
+	::WaitFor(30);
+}
+
+void CUdpServer::CloseListenSocket()
+{
+	if(m_soListens)
+	{
+		for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock)
+		{
+			if(sock != INVALID_FD)
+			{
+				::ManualCloseSocket(sock);
+				sock = INVALID_FD;
+			}
+		});
+
+		::WaitFor(70);
+	}
+}
+
+void CUdpServer::DisconnectClientSocket()
+{
+	if(m_bfActiveSockets.Elements() == 0)
+		return;
+
+	TUdpSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+		Disconnect(*it);
+}
+
+void CUdpServer::WaitForClientSocketClose()
+{
+	while(m_bfActiveSockets.Elements() > 0)
+		::WaitFor(50);
+}
+
+void CUdpServer::WaitForWorkerThreadEnd()
+{
+	m_ioDispatcher.Stop();
+}
+
+void CUdpServer::ReleaseClientSocket()
+{
+	VERIFY(m_bfActiveSockets.IsEmpty());
+	m_bfActiveSockets.Reset();
+
+	CWriteLock locallock(m_csClientSocket);
+	m_mpClientAddr.clear();
+}
+
+void CUdpServer::ReleaseFreeSocket()
+{
+	m_lsFreeSocket.Clear();
+
+#ifdef USE_EXTERNAL_GC
+	if(IS_VALID_FD(m_fdGCTimer))
+	{
+		close(m_fdGCTimer);
+		m_fdGCTimer = INVALID_FD;
+	}
+#endif
+
+	ReleaseGCSocketObj(TRUE);
+	VERIFY(m_lsGCSocket.IsEmpty());
+}
+
+void CUdpServer::ClearSendQueues()
+{
+	if(m_quSends) for_each(m_quSends.get(), m_quSends.get() + m_dwWorkerThreadCount, [](CSendQueue& queue) {queue.UnsafeClear();});
+}
+
+void CUdpServer::Reset()
+{
+	m_phSocket.Reset();
+	m_bfObjPool.Clear();
+
+	m_soListens	= nullptr;
+	m_rcBuffers	= nullptr;
+	m_quSends	= nullptr;
+
+	m_enState = SS_STOPPED;
+
+	m_evWait.SyncNotifyAll();
+}
+
+TUdpSocketObj* CUdpServer::GetFreeSocketObj(CONNID dwConnID)
+{
+	DWORD dwIndex;
+	TUdpSocketObj* pSocketObj = nullptr;
+
+	if(m_lsFreeSocket.TryLock(&pSocketObj, dwIndex))
+	{
+		if(::GetTimeGap32(pSocketObj->freeTime) >= m_dwFreeSocketObjLockTime)
+			VERIFY(m_lsFreeSocket.ReleaseLock(nullptr, dwIndex));
+		else
+		{
+			VERIFY(m_lsFreeSocket.ReleaseLock(pSocketObj, dwIndex));
+			pSocketObj = nullptr;
+		}
+	}
+
+	if(!pSocketObj) pSocketObj = CreateSocketObj();
+	pSocketObj->Reset(dwConnID);
+
+	return pSocketObj;
+}
+
+TUdpSocketObj* CUdpServer::CreateSocketObj()
+{
+	return TUdpSocketObj::Construct(m_phSocket, m_bfObjPool);
+}
+
+void CUdpServer::DeleteSocketObj(TUdpSocketObj* pSocketObj)
+{
+	TUdpSocketObj::Destruct(pSocketObj);
+}
+
+void CUdpServer::AddFreeSocketObj(TUdpSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode, BOOL bNotify)
+{
+	if(!InvalidSocketObj(pSocketObj))
+		return;
+
+	CloseClientSocketObj(pSocketObj, enFlag, enOperation, iErrorCode, bNotify);
+
+	{
+		m_bfActiveSockets.Remove(pSocketObj->connID);
+
+		CWriteLock locallock(m_csClientSocket);
+		m_mpClientAddr.erase(&pSocketObj->remoteAddr);
+	}
+
+	m_ioDispatcher.DelTimer(pSocketObj->index, pSocketObj->fdTimer);
+	TUdpSocketObj::Release(pSocketObj);
+
+#ifndef USE_EXTERNAL_GC
+	ReleaseGCSocketObj();
+#endif
+
+	if(!m_lsFreeSocket.TryPut(pSocketObj))
+		m_lsGCSocket.PushBack(pSocketObj);
+}
+
+void CUdpServer::ReleaseGCSocketObj(BOOL bForce)
+{
+	::ReleaseGCObj(m_lsGCSocket, m_dwFreeSocketObjLockTime, bForce);
+}
+
+BOOL CUdpServer::InvalidSocketObj(TUdpSocketObj* pSocketObj)
+{
+	return TUdpSocketObj::InvalidSocketObj(pSocketObj);
+}
+
+void CUdpServer::AddClientSocketObj(int idx, CONNID dwConnID, TUdpSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr)
+{
+	ASSERT(FindSocketObj(dwConnID) == nullptr);
+
+	pSocketObj->index		= idx;
+	pSocketObj->pHolder		= this;
+	pSocketObj->connTime	= ::TimeGetTime();
+	pSocketObj->activeTime	= pSocketObj->connTime;
+
+	if(IsNeedDetectConnection())
+		pSocketObj->fdTimer = m_ioDispatcher.AddTimer(pSocketObj->index, m_dwDetectInterval, pSocketObj);
+
+	remoteAddr.Copy(pSocketObj->remoteAddr);
+	pSocketObj->SetConnected();
+
+	VERIFY(m_bfActiveSockets.ReleaseLock(dwConnID, pSocketObj));
+
+	CWriteLock locallock(m_csClientSocket);
+	m_mpClientAddr[&pSocketObj->remoteAddr]	= dwConnID;
+}
+
+TUdpSocketObj* CUdpServer::FindSocketObj(CONNID dwConnID)
+{
+	TUdpSocketObj* pSocketObj = nullptr;
+
+	if(m_bfActiveSockets.Get(dwConnID, &pSocketObj) != TUdpSocketObjPtrPool::GR_VALID)
+		pSocketObj = nullptr;
+
+	return pSocketObj;
+}
+
+CONNID CUdpServer::FindConnectionID(const HP_SOCKADDR* pAddr)
+{
+	CONNID dwConnID = 0;
+
+	CReadLock locallock(m_csClientSocket);
+
+	TSockAddrMapCI it = m_mpClientAddr.find(pAddr);
+	if(it != m_mpClientAddr.end())
+		dwConnID = it->second;
+
+	return dwConnID;
+}
+
+void CUdpServer::CloseClientSocketObj(TUdpSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode, BOOL bNotify)
+{
+	ASSERT(TUdpSocketObj::IsExist(pSocketObj));
+
+	SOCKET soListen = m_soListens[pSocketObj->index];
+
+	if(bNotify && soListen != INVALID_SOCKET)
+		::SendUdpCloseNotify(soListen, pSocketObj->remoteAddr);
+
+	if(enFlag == SCF_CLOSE)
+		FireClose(pSocketObj, SO_CLOSE, SE_OK);
+	else if(enFlag == SCF_ERROR)
+		FireClose(pSocketObj, enOperation, iErrorCode);
+}
+
+BOOL CUdpServer::GetListenAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	if(!HasStarted())
+	{
+		::SetLastError(ERROR_INVALID_STATE);
+		return FALSE;
+	}
+
+	return ::GetSocketLocalAddress(m_soListens[0], lpszAddress, iAddressLen, usPort);
+}
+
+BOOL CUdpServer::GetLocalAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TUdpSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return ::GetSocketLocalAddress(m_soListens[pSocketObj->index], lpszAddress, iAddressLen, usPort);
+}
+
+BOOL CUdpServer::GetRemoteAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TUdpSocketObj::IsExist(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	ADDRESS_FAMILY usFamily;
+	return ::sockaddr_IN_2_A(pSocketObj->remoteAddr, usFamily, lpszAddress, iAddressLen, usPort);
+}
+
+BOOL CUdpServer::SetConnectionExtra(CONNID dwConnID, PVOID pExtra)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return SetConnectionExtra(pSocketObj, pExtra);
+}
+
+BOOL CUdpServer::SetConnectionExtra(TUdpSocketObj* pSocketObj, PVOID pExtra)
+{
+	if(!TUdpSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		pSocketObj->extra = pExtra;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CUdpServer::GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return GetConnectionExtra(pSocketObj, ppExtra);
+}
+
+BOOL CUdpServer::GetConnectionExtra(TUdpSocketObj* pSocketObj, PVOID* ppExtra)
+{
+	ASSERT(ppExtra != nullptr);
+
+	if(!TUdpSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*ppExtra = pSocketObj->extra;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CUdpServer::SetConnectionReserved(CONNID dwConnID, PVOID pReserved)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return SetConnectionReserved(pSocketObj, pReserved);
+}
+
+BOOL CUdpServer::SetConnectionReserved(TUdpSocketObj* pSocketObj, PVOID pReserved)
+{
+	if(!TUdpSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		pSocketObj->reserved = pReserved;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CUdpServer::GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return GetConnectionReserved(pSocketObj, ppReserved);
+}
+
+BOOL CUdpServer::GetConnectionReserved(TUdpSocketObj* pSocketObj, PVOID* ppReserved)
+{
+	ASSERT(ppReserved != nullptr);
+
+	if(!TUdpSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*ppReserved = pSocketObj->reserved;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CUdpServer::SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return SetConnectionReserved2(pSocketObj, pReserved2);
+}
+
+BOOL CUdpServer::SetConnectionReserved2(TUdpSocketObj* pSocketObj, PVOID pReserved2)
+{
+	if(!TUdpSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		pSocketObj->reserved2 = pReserved2;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CUdpServer::GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+	return GetConnectionReserved2(pSocketObj, ppReserved2);
+}
+
+BOOL CUdpServer::GetConnectionReserved2(TUdpSocketObj* pSocketObj, PVOID* ppReserved2)
+{
+	ASSERT(ppReserved2 != nullptr);
+
+	if(!TUdpSocketObj::IsExist(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		*ppReserved2 = pSocketObj->reserved2;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CUdpServer::IsPauseReceive(CONNID dwConnID, BOOL& bPaused)
+{
+	::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+
+	bPaused = FALSE;
+
+	return FALSE;
+}
+
+BOOL CUdpServer::IsConnected(CONNID dwConnID)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TUdpSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return pSocketObj->HasConnected();
+}
+
+BOOL CUdpServer::GetPendingDataLength(CONNID dwConnID, int& iPending)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TUdpSocketObj::IsValid(pSocketObj))
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+	else
+	{
+		iPending = pSocketObj->Pending();
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+DWORD CUdpServer::GetConnectionCount()
+{
+	return m_bfActiveSockets.Elements();
+}
+
+BOOL CUdpServer::GetAllConnectionIDs(CONNID pIDs[], DWORD& dwCount)
+{
+	return m_bfActiveSockets.GetAllElementIndexes(pIDs, dwCount);
+}
+
+BOOL CUdpServer::GetConnectPeriod(CONNID dwConnID, DWORD& dwPeriod)
+{
+	BOOL isOK					= TRUE;
+	TUdpSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(TUdpSocketObj::IsValid(pSocketObj))
+		dwPeriod = ::GetTimeGap32(pSocketObj->connTime);
+	else
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+BOOL CUdpServer::GetSilencePeriod(CONNID dwConnID, DWORD& dwPeriod)
+{
+	if(!m_bMarkSilence)
+		return FALSE;
+
+	BOOL isOK					= TRUE;
+	TUdpSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(TUdpSocketObj::IsValid(pSocketObj))
+		dwPeriod = ::GetTimeGap32(pSocketObj->activeTime);
+	else
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+BOOL CUdpServer::Disconnect(CONNID dwConnID, BOOL bForce)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TUdpSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	return m_ioDispatcher.SendCommandByIndex(pSocketObj->index, DISP_CMD_DISCONNECT, dwConnID, bForce);
+}
+
+BOOL CUdpServer::DisconnectLongConnections(DWORD dwPeriod, BOOL bForce)
+{
+	if(dwPeriod > MAX_CONNECTION_PERIOD)
+		return FALSE;
+	if(m_bfActiveSockets.Elements() == 0)
+		return TRUE;
+
+	DWORD now = ::TimeGetTime();
+
+	TUdpSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+	{
+		CONNID connID				= *it;
+		TUdpSocketObj* pSocketObj	= FindSocketObj(connID);
+
+		if(TUdpSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->connTime) >= (int)dwPeriod)
+			Disconnect(connID, bForce);
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpServer::DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce)
+{
+	if(!m_bMarkSilence)
+		return FALSE;
+	if(dwPeriod > MAX_CONNECTION_PERIOD)
+		return FALSE;
+	if(m_bfActiveSockets.Elements() == 0)
+		return TRUE;
+
+	DWORD now = ::TimeGetTime();
+
+	TUdpSocketObjPtrPool::IndexSet indexes;
+	m_bfActiveSockets.CopyIndexes(indexes);
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+	{
+		CONNID connID				= *it;
+		TUdpSocketObj* pSocketObj	= FindSocketObj(connID);
+
+		if(TUdpSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->activeTime) >= (int)dwPeriod)
+			Disconnect(connID, bForce);
+	}
+
+	return TRUE;
+}
+
+BOOL CUdpServer::PauseReceive(CONNID dwConnID, BOOL bPause)
+{
+	::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+	return FALSE;
+}
+
+BOOL CUdpServer::OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	if(pv == &m_soListens[pContext->GetIndex()])
+		return TRUE;
+	else if(pv == this)
+	{
+		ReleaseGCSocketObj(FALSE);
+		::ReadTimer(m_fdGCTimer);
+
+		return FALSE;
+	}
+
+	if(!(events & _EPOLL_ALL_ERROR_EVENTS))
+		DetectConnection(pv);
+
+	return FALSE;
+}
+
+VOID CUdpServer::OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)
+{
+
+}
+
+VOID CUdpServer::OnCommand(const TDispContext* pContext, TDispCommand* pCmd)
+{
+	CONNID dwConnID = (CONNID)(pCmd->wParam);
+
+	switch(pCmd->type)
+	{
+	case DISP_CMD_SEND:
+		HandleCmdSend(dwConnID, (int)(pCmd->lParam));
+		break;
+	case DISP_CMD_DISCONNECT:
+		HandleCmdDisconnect(dwConnID, (BOOL)pCmd->lParam);
+		break;
+	case DISP_CMD_TIMEOUT:
+		HandleCmdTimeout(dwConnID);
+		break;
+	}
+}
+
+VOID CUdpServer::HandleCmdDisconnect(CONNID dwConnID, BOOL bForce)
+{
+	AddFreeSocketObj(FindSocketObj(dwConnID), SCF_CLOSE);
+}
+
+VOID CUdpServer::HandleCmdTimeout(CONNID dwConnID)
+{
+	AddFreeSocketObj(FindSocketObj(dwConnID), SCF_CLOSE, SO_UNKNOWN, 0, FALSE);
+}
+
+BOOL CUdpServer::OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleReceive(pContext, RETRIVE_EVENT_FLAG_H(events));
+}
+
+BOOL CUdpServer::OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleSend(pContext, RETRIVE_EVENT_FLAG_H(events));
+}
+
+BOOL CUdpServer::OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleClose(nullptr, SO_CLOSE, 0);
+}
+
+BOOL CUdpServer::OnError(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	return HandleClose(nullptr, SO_CLOSE, -1);
+}
+
+VOID CUdpServer::OnDispatchThreadStart(THR_ID tid)
+{
+	OnWorkerThreadStart(tid);
+}
+
+VOID CUdpServer::OnDispatchThreadEnd(THR_ID tid)
+{
+	OnWorkerThreadEnd(tid);
+}
+
+BOOL CUdpServer::HandleClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+{
+	if(!HasStarted())
+		return FALSE;
+	else if(pSocketObj == nullptr)
+	{
+		TRACE("HandleClose() -> OP: %d, EC: %d", enOperation, iErrorCode);
+		return TRUE;
+	}
+
+	if(iErrorCode == -1)
+		iErrorCode = ::SSO_GetError(m_soListens[pSocketObj->index]);
+
+	EnSocketCloseFlag enFlag = IS_NO_ERROR(iErrorCode) ? SCF_CLOSE : SCF_ERROR;
+
+	AddFreeSocketObj(pSocketObj, enFlag, enOperation, iErrorCode);
+
+	return FALSE;
+}
+
+BOOL CUdpServer::HandleReceive(const TDispContext* pContext, int flag)
+{
+	int idx				= pContext->GetIndex();
+	CBufferPtr& buffer	= m_rcBuffers[idx];
+	int iBufferLen		= (int)buffer.Size();
+
+	while(TRUE)
+	{
+		HP_SOCKADDR addr;
+		socklen_t dwAddrLen = (socklen_t)addr.AddrSize();
+
+		int rc = (int)recvfrom(m_soListens[idx], buffer.Ptr(), iBufferLen, MSG_TRUNC, addr.Addr(), &dwAddrLen);
+
+		if(rc >= 0)
+		{
+			CONNID dwConnID = FindConnectionID(&addr);
+
+			if(dwConnID == 0)
+			{
+				if(rc > iBufferLen)
+					continue;
+
+				if((dwConnID = HandleAccept(pContext, addr)) == 0)
+					continue;
+			}
+
+			TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+			ASSERT(pSocketObj->index == idx);
+
+			if(!TUdpSocketObj::IsValid(pSocketObj))
+				continue;
+
+			if(rc == 0)
+			{
+				HandleZeroBytes(pSocketObj);
+				continue;
+			}
+
+			if(rc > iBufferLen)
+			{
+				AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ERROR_BAD_LENGTH);
+				continue;
+			}
+
+			if(::IsUdpCloseNotify(buffer.Ptr(), rc))
+			{
+				AddFreeSocketObj(pSocketObj, SCF_CLOSE, SO_CLOSE, SE_OK, FALSE);
+				continue;
+			}
+
+			if(TRIGGER(FireReceive(pSocketObj, buffer.Ptr(), rc)) == HR_ERROR)
+			{
+				TRACE("<S-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", dwConnID);
+
+				AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED);;
+				continue;
+			}
+		}
+		else if(rc == SOCKET_ERROR)
+		{
+			int code = ::WSAGetLastError();
+
+			if(code == ERROR_WOULDBLOCK)
+				break;
+			else if(!HandleClose(nullptr, SO_RECEIVE, code))
+				return FALSE;
+		}
+		else
+		{
+			ASSERT(FALSE);
+		}
+	}
+
+	return TRUE;
+}
+
+CONNID CUdpServer::HandleAccept(const TDispContext* pContext, HP_SOCKADDR& addr)
+{
+	int idx						= pContext->GetIndex();
+	CONNID dwConnID				= 0;
+	TUdpSocketObj* pSocketObj	= nullptr;
+
+	if(!m_bfActiveSockets.AcquireLock(dwConnID))
+	{
+		::SendUdpCloseNotify(m_soListens[idx], addr);
+		return 0;
+	}
+
+	pSocketObj = GetFreeSocketObj(dwConnID);
+	AddClientSocketObj(idx, dwConnID, pSocketObj, addr);
+
+	if(TriggerFireAccept(pSocketObj) == HR_ERROR)
+	{
+		AddFreeSocketObj(pSocketObj);
+		dwConnID = 0;
+	}
+
+	return dwConnID;
+}
+
+void CUdpServer::HandleZeroBytes(TUdpSocketObj* pSocketObj)
+{
+	TRACE("<S-CNNID: %zu> recv 0 bytes (detect package)", pSocketObj->connID);
+
+	pSocketObj->detectFails = 0;
+
+#if defined(DEBUG_TRACE)
+	int rc = (int)
+#endif
+	sendto(m_soListens[pSocketObj->index], nullptr, 0, 0, pSocketObj->remoteAddr.Addr(), pSocketObj->remoteAddr.AddrSize());
+
+	TRACE("<S-CNNID: %zu> send 0 bytes (detect ack package - %s)", pSocketObj->connID, IS_HAS_ERROR(rc) ? "fail" : "succ");
+}
+
+BOOL CUdpServer::HandleSend(const TDispContext* pContext, int flag)
+{
+	CSendQueue& quSend	= m_quSends[pContext->GetIndex()];
+	CONNID dwConnID		= 0;
+
+	while(quSend.PopFront(&dwConnID))
+		HandleCmdSend(dwConnID, flag);
+
+	return TRUE;
+}
+
+VOID CUdpServer::HandleCmdSend(CONNID dwConnID, int flag)
+{
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TUdpSocketObj::IsValid(pSocketObj) || !pSocketObj->IsPending())
+		return;
+
+	BOOL bBlocked	= FALSE;
+	int writes		= flag ? -1 : MAX_CONTINUE_WRITES;
+
+	TBufferObjList& sndBuff = pSocketObj->sndBuff;
+	TItemPtr itPtr(sndBuff);
+
+	for(int i = 0; i < writes || writes < 0; i++)
+	{
+		{
+			CReentrantCriSecLock locallock(pSocketObj->csSend);
+			itPtr = sndBuff.PopFront();
+		}
+
+		if(!itPtr.IsValid())
+			break;
+
+		ASSERT(!itPtr->IsEmpty());
+
+		if(!SendItem(pSocketObj, itPtr, bBlocked))
+			return;
+
+		if(bBlocked)
+		{
+			{
+				CReentrantCriSecLock locallock(pSocketObj->csSend);
+				sndBuff.PushFront(itPtr.Detach());
+			}
+
+			m_quSends[pSocketObj->index].PushBack(dwConnID);
+
+			break;
+		}
+	}
+
+	if(!bBlocked && pSocketObj->IsPending())
+		VERIFY(m_ioDispatcher.SendCommandByIndex(pSocketObj->index, DISP_CMD_SEND, dwConnID));
+}
+
+BOOL CUdpServer::SendItem(TUdpSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked)
+{
+	int rc = (int)sendto(m_soListens[pSocketObj->index], pItem->Ptr(), pItem->Size(), 0, pSocketObj->remoteAddr.Addr(), pSocketObj->remoteAddr.AddrSize());
+
+	if(rc > 0)
+	{
+		ASSERT(rc == pItem->Size());
+
+		if(TRIGGER(FireSend(pSocketObj, pItem->Ptr(), rc)) == HR_ERROR)
+		{
+			TRACE("<S-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", pSocketObj->connID);
+			ASSERT(FALSE);
+		}
+	}
+	else if(rc == SOCKET_ERROR)
+	{
+		int code = ::WSAGetLastError();
+
+		if(code == ERROR_WOULDBLOCK)
+			bBlocked = TRUE;
+		else if(!HandleClose(pSocketObj, SO_SEND, code))
+			return FALSE;
+	}
+	else
+		ASSERT(FALSE);
+
+	return TRUE;
+}
+
+BOOL CUdpServer::Send(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	ASSERT(pBuffer && iLength > 0 && iLength <= (int)m_dwMaxDatagramSize);
+
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	return DoSend(pSocketObj, pBuffer, iLength, iOffset);
+}
+
+BOOL CUdpServer::DoSend(TUdpSocketObj* pSocketObj, const BYTE* pBuffer, int iLength, int iOffset)
+{
+	int result = NO_ERROR;
+
+	if(TUdpSocketObj::IsValid(pSocketObj))
+	{
+		if(pBuffer && iLength > 0 && iLength <= (int)m_dwMaxDatagramSize)
+		{
+			if(iOffset != 0) pBuffer += iOffset;
+
+			TItemPtr itPtr(m_bfObjPool, m_bfObjPool.PickFreeItem());
+			itPtr->Cat(pBuffer, iLength);
+
+			result = SendInternal(pSocketObj, itPtr);
+		}
+		else
+			result = ERROR_INVALID_PARAMETER;
+	}
+	else
+		result = ERROR_OBJECT_NOT_FOUND;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+}
+
+BOOL CUdpServer::SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
+{
+	ASSERT(pBuffers && iCount > 0);
+
+	if(!pBuffers || iCount <= 0)
+		return ERROR_INVALID_PARAMETER;
+
+	TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(!TUdpSocketObj::IsValid(pSocketObj))
+	{
+		::SetLastError(ERROR_OBJECT_NOT_FOUND);
+		return FALSE;
+	}
+
+	int result	= NO_ERROR;
+	int iLength = 0;
+	int iMaxLen = (int)m_dwMaxDatagramSize;
+	
+	TItemPtr itPtr(m_bfObjPool, m_bfObjPool.PickFreeItem());
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			iLength += iBufLen;
+
+			if(iLength <= iMaxLen)
+				itPtr->Cat(pBuffer, iBufLen);
+			else
+				break;
+		}
+	}
+
+	if(iLength > 0 && iLength <= iMaxLen)
+		result = SendInternal(pSocketObj, itPtr);
+	else
+		result = ERROR_INCORRECT_SIZE;
+
+	if(result != NO_ERROR)
+		::SetLastError(result);
+
+	return (result == NO_ERROR);
+
+}
+
+int CUdpServer::SendInternal(TUdpSocketObj* pSocketObj, TItemPtr& itPtr)
+{
+	BOOL bPending;
+
+	{
+		CLocalSafeCounter localcounter(*pSocketObj);
+		CReentrantCriSecLock locallock(pSocketObj->csSend);
+
+		if(!TUdpSocketObj::IsValid(pSocketObj))
+			return ERROR_OBJECT_NOT_FOUND;
+
+		bPending = pSocketObj->IsPending();
+
+		pSocketObj->sndBuff.PushBack(itPtr.Detach());
+		ASSERT(pSocketObj->sndBuff.Length() > 0);
+	}
+
+	if(!bPending && pSocketObj->IsPending())
+		VERIFY(m_ioDispatcher.SendCommandByIndex(pSocketObj->index, DISP_CMD_SEND, pSocketObj->connID));
+
+	return NO_ERROR;
+}
+
+void CUdpServer::DetectConnection(PVOID pv)
+{
+	TUdpSocketObj* pSocketObj = (TUdpSocketObj*)pv;
+
+	if(TUdpSocketObj::IsValid(pSocketObj))
+	{
+		CUdpServer* pServer = (CUdpServer*)pSocketObj->pHolder;
+
+		if(pSocketObj->detectFails >= pServer->m_dwDetectAttempts)
+			VERIFY(m_ioDispatcher.SendCommandByIndex(pSocketObj->index, DISP_CMD_TIMEOUT, pSocketObj->connID));
+		else
+			::InterlockedIncrement(&pSocketObj->detectFails);
+
+		::ReadTimer(pSocketObj->fdTimer);
+	}
+}
+
+#endif

+ 301 - 0
UdpServer.h

@@ -0,0 +1,301 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Author	: Bruce Liang
+ * Website	: https://github.com/ldcsaa
+ * Project	: https://github.com/ldcsaa/HP-Socket
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 44636872, 75375912
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#pragma once
+
+#include "SocketHelper.h"
+#include "./common/GeneralHelper.h"
+#include "./common/IODispatcher.h"
+
+#ifdef _UDP_SUPPORT
+
+class CUdpServer : public IUdpServer, private CIOHandler
+{
+	using CWorkerThread	 = CThread<CUdpServer, VOID, UINT>;
+	using CSendQueue	 = CCASSimpleQueue<CONNID>;
+	using CSendQueuesPtr = unique_ptr<CSendQueue[]>;
+
+public:
+	virtual BOOL Start	(LPCTSTR lpszBindAddress, USHORT usPort);
+	virtual BOOL Stop	();
+	virtual BOOL Send	(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset = 0);
+	virtual BOOL SendPackets	(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
+	virtual BOOL PauseReceive	(CONNID dwConnID, BOOL bPause = TRUE);
+	virtual BOOL Wait			(DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
+	virtual BOOL			HasStarted					()	{return m_enState == SS_STARTED || m_enState == SS_STARTING;}
+	virtual EnServiceState	GetState					()	{return m_enState;}
+	virtual BOOL			Disconnect					(CONNID dwConnID, BOOL bForce = TRUE);
+	virtual BOOL			DisconnectLongConnections	(DWORD dwPeriod, BOOL bForce = TRUE);
+	virtual BOOL			DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce = TRUE);
+	virtual BOOL			GetListenAddress			(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL			GetLocalAddress				(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+	virtual BOOL			GetRemoteAddress			(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
+
+	virtual BOOL IsConnected			(CONNID dwConnID);
+	virtual BOOL IsPauseReceive			(CONNID dwConnID, BOOL& bPaused);
+	virtual BOOL GetPendingDataLength	(CONNID dwConnID, int& iPending);
+	virtual DWORD GetConnectionCount	();
+	virtual BOOL GetAllConnectionIDs	(CONNID pIDs[], DWORD& dwCount);
+	virtual BOOL GetConnectPeriod		(CONNID dwConnID, DWORD& dwPeriod);
+	virtual BOOL GetSilencePeriod		(CONNID dwConnID, DWORD& dwPeriod);
+	virtual EnSocketError GetLastError	()	{return m_enLastError;}
+	virtual LPCTSTR GetLastErrorDesc	()	{return ::GetSocketErrorDesc(m_enLastError);}
+
+private:
+	virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)			override;
+	virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)	override;
+	virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd)					override;
+	virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)				override;
+	virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)				override;
+	virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)					override;
+	virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events)					override;
+	virtual VOID OnDispatchThreadStart(THR_ID tid)												override;
+	virtual VOID OnDispatchThreadEnd(THR_ID tid)												override;
+
+public:
+	virtual BOOL IsSecure					() {return FALSE;}
+
+	virtual BOOL SetConnectionExtra(CONNID dwConnID, PVOID pExtra);
+	virtual BOOL GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra);
+
+	virtual void SetReuseAddressPolicy		(EnReuseAddressPolicy enReusePolicy)	{ENSURE_HAS_STOPPED(); ASSERT(m_enReusePolicy == enReusePolicy);}
+	virtual void SetSendPolicy				(EnSendPolicy enSendPolicy)				{ENSURE_HAS_STOPPED(); ASSERT(m_enSendPolicy == enSendPolicy);}
+	virtual void SetOnSendSyncPolicy		(EnOnSendSyncPolicy enOnSendSyncPolicy)	{ENSURE_HAS_STOPPED(); ASSERT(m_enOnSendSyncPolicy == enOnSendSyncPolicy);}
+	virtual void SetMaxConnectionCount		(DWORD dwMaxConnectionCount)	{ENSURE_HAS_STOPPED(); m_dwMaxConnectionCount		= dwMaxConnectionCount;}
+	virtual void SetWorkerThreadCount		(DWORD dwWorkerThreadCount)		{ENSURE_HAS_STOPPED(); m_dwWorkerThreadCount		= dwWorkerThreadCount;}
+	virtual void SetFreeSocketObjLockTime	(DWORD dwFreeSocketObjLockTime)	{ENSURE_HAS_STOPPED(); m_dwFreeSocketObjLockTime	= dwFreeSocketObjLockTime;}
+	virtual void SetFreeSocketObjPool		(DWORD dwFreeSocketObjPool)		{ENSURE_HAS_STOPPED(); m_dwFreeSocketObjPool		= dwFreeSocketObjPool;}
+	virtual void SetFreeBufferObjPool		(DWORD dwFreeBufferObjPool)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferObjPool		= dwFreeBufferObjPool;}
+	virtual void SetFreeSocketObjHold		(DWORD dwFreeSocketObjHold)		{ENSURE_HAS_STOPPED(); m_dwFreeSocketObjHold		= dwFreeSocketObjHold;}
+	virtual void SetFreeBufferObjHold		(DWORD dwFreeBufferObjHold)		{ENSURE_HAS_STOPPED(); m_dwFreeBufferObjHold		= dwFreeBufferObjHold;}
+	virtual void SetMaxDatagramSize			(DWORD dwMaxDatagramSize)		{ENSURE_HAS_STOPPED(); m_dwMaxDatagramSize			= dwMaxDatagramSize;}
+	virtual void SetPostReceiveCount		(DWORD dwPostReceiveCount)		{ENSURE_HAS_STOPPED(); m_dwPostReceiveCount			= dwPostReceiveCount;}
+	virtual void SetDetectAttempts			(DWORD dwDetectAttempts)		{ENSURE_HAS_STOPPED(); m_dwDetectAttempts			= dwDetectAttempts;}
+	virtual void SetDetectInterval			(DWORD dwDetectInterval)		{ENSURE_HAS_STOPPED(); m_dwDetectInterval			= dwDetectInterval;}
+	virtual void SetMarkSilence				(BOOL bMarkSilence)				{ENSURE_HAS_STOPPED(); m_bMarkSilence				= bMarkSilence;}
+
+	virtual EnReuseAddressPolicy GetReuseAddressPolicy	()	{return m_enReusePolicy;}
+	virtual EnSendPolicy GetSendPolicy					()	{return m_enSendPolicy;}
+	virtual EnOnSendSyncPolicy GetOnSendSyncPolicy		()	{return m_enOnSendSyncPolicy;}
+	virtual DWORD GetMaxConnectionCount		()	{return m_dwMaxConnectionCount;}
+	virtual DWORD GetWorkerThreadCount		()	{return m_dwWorkerThreadCount;}
+	virtual DWORD GetFreeSocketObjLockTime	()	{return m_dwFreeSocketObjLockTime;}
+	virtual DWORD GetFreeSocketObjPool		()	{return m_dwFreeSocketObjPool;}
+	virtual DWORD GetFreeBufferObjPool		()	{return m_dwFreeBufferObjPool;}
+	virtual DWORD GetFreeSocketObjHold		()	{return m_dwFreeSocketObjHold;}
+	virtual DWORD GetFreeBufferObjHold		()	{return m_dwFreeBufferObjHold;}
+	virtual DWORD GetMaxDatagramSize		()	{return m_dwMaxDatagramSize;}
+	virtual DWORD GetPostReceiveCount		()	{return m_dwPostReceiveCount;}
+	virtual DWORD GetDetectAttempts			()	{return m_dwDetectAttempts;}
+	virtual DWORD GetDetectInterval			()	{return m_dwDetectInterval;}
+	virtual BOOL  IsMarkSilence				()	{return m_bMarkSilence;}
+
+protected:
+	virtual EnHandleResult FirePrepareListen(SOCKET soListen)
+		{return DoFirePrepareListen(soListen);}
+	virtual EnHandleResult FireAccept(TUdpSocketObj* pSocketObj)
+		{
+			EnHandleResult rs		= DoFireAccept(pSocketObj);
+			if(rs != HR_ERROR) rs	= FireHandShake(pSocketObj);
+			return rs;
+		}
+	virtual EnHandleResult FireHandShake(TUdpSocketObj* pSocketObj)
+		{return DoFireHandShake(pSocketObj);}
+	virtual EnHandleResult FireReceive(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return DoFireReceive(pSocketObj, pData, iLength);}
+	virtual EnHandleResult FireReceive(TUdpSocketObj* pSocketObj, int iLength)
+		{return DoFireReceive(pSocketObj, iLength);}
+	virtual EnHandleResult FireSend(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return DoFireSend(pSocketObj, pData, iLength);}
+	virtual EnHandleResult FireClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+		{return DoFireClose(pSocketObj, enOperation, iErrorCode);}
+	virtual EnHandleResult FireShutdown()
+		{return DoFireShutdown();}
+
+	virtual EnHandleResult DoFirePrepareListen(SOCKET soListen)
+		{return m_pListener->OnPrepareListen(this, soListen);}
+	virtual EnHandleResult DoFireAccept(TUdpSocketObj* pSocketObj)
+		{return m_pListener->OnAccept(this, pSocketObj->connID, (UINT_PTR)(&pSocketObj->remoteAddr));}
+	virtual EnHandleResult DoFireHandShake(TUdpSocketObj* pSocketObj)
+		{return m_pListener->OnHandShake(this, pSocketObj->connID);}
+	virtual EnHandleResult DoFireReceive(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnReceive(this, pSocketObj->connID, pData, iLength);}
+	virtual EnHandleResult DoFireReceive(TUdpSocketObj* pSocketObj, int iLength)
+		{return m_pListener->OnReceive(this, pSocketObj->connID, iLength);}
+	virtual EnHandleResult DoFireSend(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
+		{return m_pListener->OnSend(this, pSocketObj->connID, pData, iLength);}
+	virtual EnHandleResult DoFireClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+		{return m_pListener->OnClose(this, pSocketObj->connID, enOperation, iErrorCode);}
+	virtual EnHandleResult DoFireShutdown()
+		{return m_pListener->OnShutdown(this);}
+
+	void SetLastError(EnSocketError code, LPCSTR func, int ec);
+	virtual BOOL CheckParams();
+	virtual void PrepareStart();
+	virtual void Reset();
+
+	virtual void OnWorkerThreadStart(THR_ID tid)	{}
+	virtual void OnWorkerThreadEnd(THR_ID tid)		{}
+
+	virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
+
+	TUdpSocketObj*	FindSocketObj(CONNID dwConnID);
+	int				SendInternal(TUdpSocketObj* pSocketObj, TItemPtr& itPtr);
+
+	BOOL DoSend(TUdpSocketObj* pSocketObj, const BYTE* pBuffer, int iLength, int iOffset = 0);
+
+protected:
+	BOOL SetConnectionExtra(TUdpSocketObj* pSocketObj, PVOID pExtra);
+	BOOL GetConnectionExtra(TUdpSocketObj* pSocketObj, PVOID* ppExtra);
+	BOOL SetConnectionReserved(CONNID dwConnID, PVOID pReserved);
+	BOOL GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved);
+	BOOL SetConnectionReserved(TUdpSocketObj* pSocketObj, PVOID pReserved);
+	BOOL GetConnectionReserved(TUdpSocketObj* pSocketObj, PVOID* ppReserved);
+	BOOL SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2);
+	BOOL GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2);
+	BOOL SetConnectionReserved2(TUdpSocketObj* pSocketObj, PVOID pReserved2);
+	BOOL GetConnectionReserved2(TUdpSocketObj* pSocketObj, PVOID* ppReserved2);
+
+private:
+	BOOL CheckStarting();
+	BOOL CheckStoping();
+	BOOL CreateListenSocket(LPCTSTR lpszBindAddress, USHORT usPort);
+	BOOL CreateWorkerThreads();
+	BOOL StartAccept();
+
+	void SendCloseNotify();
+	void CloseListenSocket();
+	void DisconnectClientSocket();
+	void WaitForClientSocketClose();
+	void ReleaseClientSocket();
+	void ReleaseFreeSocket();
+	void ClearSendQueues();
+	void WaitForWorkerThreadEnd();
+
+	TUdpSocketObj* GetFreeSocketObj(CONNID dwConnID);
+	TUdpSocketObj* CreateSocketObj();
+	CONNID FindConnectionID(const HP_SOCKADDR* pAddr);
+	void AddFreeSocketObj(TUdpSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0, BOOL bNotify = TRUE);
+	void DeleteSocketObj(TUdpSocketObj* pSocketObj);
+	BOOL InvalidSocketObj(TUdpSocketObj* pSocketObj);
+	void AddClientSocketObj(int idx, CONNID dwConnID, TUdpSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr);
+	void CloseClientSocketObj(TUdpSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0, BOOL bNotify = TRUE);
+
+	EnHandleResult TriggerFireAccept(TUdpSocketObj* pSocketObj);
+
+private:
+	VOID HandleCmdSend		(CONNID dwConnID, int flag);
+	VOID HandleCmdDisconnect(CONNID dwConnID, BOOL bForce);
+	VOID HandleCmdTimeout	(CONNID dwConnID);
+
+	CONNID HandleAccept		(const TDispContext* pContext, HP_SOCKADDR& addr);
+	BOOL HandleReceive		(const TDispContext* pContext, int flag = 0);
+	BOOL HandleSend			(const TDispContext* pContext, int flag = 0);
+	BOOL HandleClose		(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
+	void HandleZeroBytes	(TUdpSocketObj* pSocketObj);
+
+	BOOL SendItem			(TUdpSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked);
+
+	void DetectConnection		(PVOID pv);
+	BOOL IsNeedDetectConnection	() const {return m_dwDetectAttempts > 0 && m_dwDetectInterval > 0;}
+
+public:
+	CUdpServer(IUdpServerListener* pListener)
+	: m_pListener				(pListener)
+	, m_enLastError				(SE_OK)
+	, m_enState					(SS_STOPPED)
+	, m_fdGCTimer				(INVALID_FD)
+	, m_enSendPolicy			(SP_PACK)
+	, m_enOnSendSyncPolicy		(OSSP_RECEIVE)
+	, m_enReusePolicy			(RAP_ADDR_AND_PORT)
+	, m_dwMaxConnectionCount	(DEFAULT_CONNECTION_COUNT)
+	, m_dwWorkerThreadCount		(DEFAULT_WORKER_THREAD_COUNT)
+	, m_dwFreeSocketObjLockTime	(DEFAULT_FREE_SOCKETOBJ_LOCK_TIME)
+	, m_dwFreeSocketObjPool		(DEFAULT_FREE_SOCKETOBJ_POOL)
+	, m_dwFreeBufferObjPool		(DEFAULT_FREE_BUFFEROBJ_POOL)
+	, m_dwFreeSocketObjHold		(DEFAULT_FREE_SOCKETOBJ_HOLD)
+	, m_dwFreeBufferObjHold		(DEFAULT_FREE_BUFFEROBJ_HOLD)
+	, m_dwMaxDatagramSize		(DEFAULT_UDP_MAX_DATAGRAM_SIZE)
+	, m_dwPostReceiveCount		(DEFAULT_UDP_POST_RECEIVE_COUNT)
+	, m_dwDetectAttempts		(DEFAULT_UDP_DETECT_ATTEMPTS)
+	, m_dwDetectInterval		(DEFAULT_UDP_DETECT_INTERVAL)
+	, m_bMarkSilence			(TRUE)
+	{
+		ASSERT(m_pListener);
+	}
+
+	virtual ~CUdpServer()
+	{
+		ENSURE_STOP();
+	}
+
+private:
+	EnReuseAddressPolicy m_enReusePolicy;
+	EnSendPolicy m_enSendPolicy;
+	EnOnSendSyncPolicy m_enOnSendSyncPolicy;
+	DWORD m_dwMaxConnectionCount;
+	DWORD m_dwWorkerThreadCount;
+	DWORD m_dwFreeSocketObjLockTime;
+	DWORD m_dwFreeSocketObjPool;
+	DWORD m_dwFreeBufferObjPool;
+	DWORD m_dwFreeSocketObjHold;
+	DWORD m_dwFreeBufferObjHold;
+	DWORD m_dwMaxDatagramSize;
+	DWORD m_dwPostReceiveCount;
+	DWORD m_dwDetectAttempts;
+	DWORD m_dwDetectInterval;
+	BOOL  m_bMarkSilence;
+
+protected:
+	CBufferObjPool			m_bfObjPool;
+
+private:
+	CSEM					m_evWait;
+
+	IUdpServerListener*		m_pListener;
+	ListenSocketsPtr		m_soListens;
+	EnServiceState			m_enState;
+	EnSocketError			m_enLastError;
+
+	CReceiveBuffersPtr		m_rcBuffers;
+
+	CPrivateHeap			m_phSocket;
+
+	CSpinGuard				m_csState;
+
+	FD						m_fdGCTimer;
+
+	TUdpSocketObjPtrPool	m_bfActiveSockets;
+
+	CSimpleRWLock			m_csClientSocket;
+	TSockAddrMap			m_mpClientAddr;
+
+	TUdpSocketObjPtrList	m_lsFreeSocket;
+	TUdpSocketObjPtrQueue	m_lsGCSocket;
+
+	CSendQueuesPtr			m_quSends;
+
+	CIODispatcher			m_ioDispatcher;
+};
+
+#endif

+ 409 - 0
brotli/decode.h

@@ -0,0 +1,409 @@
+/* Copyright 2013 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+/**
+ * @file
+ * API for Brotli decompression.
+ */
+
+#ifndef BROTLI_DEC_DECODE_H_
+#define BROTLI_DEC_DECODE_H_
+
+#include <brotli/port.h>
+#include <brotli/shared_dictionary.h>
+#include <brotli/types.h>
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/**
+ * Opaque structure that holds decoder state.
+ *
+ * Allocated and initialized with ::BrotliDecoderCreateInstance.
+ * Cleaned up and deallocated with ::BrotliDecoderDestroyInstance.
+ */
+typedef struct BrotliDecoderStateStruct BrotliDecoderState;
+
+/**
+ * Result type for ::BrotliDecoderDecompress and
+ * ::BrotliDecoderDecompressStream functions.
+ */
+typedef enum {
+  /** Decoding error, e.g. corrupted input or memory allocation problem. */
+  BROTLI_DECODER_RESULT_ERROR = 0,
+  /** Decoding successfully completed. */
+  BROTLI_DECODER_RESULT_SUCCESS = 1,
+  /** Partially done; should be called again with more input. */
+  BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT = 2,
+  /** Partially done; should be called again with more output. */
+  BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT = 3
+} BrotliDecoderResult;
+
+/**
+ * Template that evaluates items of ::BrotliDecoderErrorCode.
+ *
+ * Example: @code {.cpp}
+ * // Log Brotli error code.
+ * switch (brotliDecoderErrorCode) {
+ * #define CASE_(PREFIX, NAME, CODE) \
+ *   case BROTLI_DECODER ## PREFIX ## NAME: \
+ *     LOG(INFO) << "error code:" << #NAME; \
+ *     break;
+ * #define NEWLINE_
+ * BROTLI_DECODER_ERROR_CODES_LIST(CASE_, NEWLINE_)
+ * #undef CASE_
+ * #undef NEWLINE_
+ *   default: LOG(FATAL) << "unknown brotli error code";
+ * }
+ * @endcode
+ */
+#define BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE, SEPARATOR)      \
+  BROTLI_ERROR_CODE(_, NO_ERROR, 0) SEPARATOR                              \
+  /* Same as BrotliDecoderResult values */                                 \
+  BROTLI_ERROR_CODE(_, SUCCESS, 1) SEPARATOR                               \
+  BROTLI_ERROR_CODE(_, NEEDS_MORE_INPUT, 2) SEPARATOR                      \
+  BROTLI_ERROR_CODE(_, NEEDS_MORE_OUTPUT, 3) SEPARATOR                     \
+                                                                           \
+  /* Errors caused by invalid input */                                     \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_NIBBLE, -1) SEPARATOR        \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, RESERVED, -2) SEPARATOR                \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_META_NIBBLE, -3) SEPARATOR   \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_ALPHABET, -4) SEPARATOR \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_SAME, -5) SEPARATOR     \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, CL_SPACE, -6) SEPARATOR                \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, HUFFMAN_SPACE, -7) SEPARATOR           \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, CONTEXT_MAP_REPEAT, -8) SEPARATOR      \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_1, -9) SEPARATOR          \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_2, -10) SEPARATOR         \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, TRANSFORM, -11) SEPARATOR              \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, DICTIONARY, -12) SEPARATOR             \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, WINDOW_BITS, -13) SEPARATOR            \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR              \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR              \
+  BROTLI_ERROR_CODE(_ERROR_FORMAT_, DISTANCE, -16) SEPARATOR               \
+                                                                           \
+  /* -17 code is reserved */                                               \
+                                                                           \
+  BROTLI_ERROR_CODE(_ERROR_, COMPOUND_DICTIONARY, -18) SEPARATOR           \
+  BROTLI_ERROR_CODE(_ERROR_, DICTIONARY_NOT_SET, -19) SEPARATOR            \
+  BROTLI_ERROR_CODE(_ERROR_, INVALID_ARGUMENTS, -20) SEPARATOR             \
+                                                                           \
+  /* Memory allocation problems */                                         \
+  BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MODES, -21) SEPARATOR           \
+  /* Literal, insert and distance trees together */                        \
+  BROTLI_ERROR_CODE(_ERROR_ALLOC_, TREE_GROUPS, -22) SEPARATOR             \
+  /* -23..-24 codes are reserved for distinct tree groups */               \
+  BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MAP, -25) SEPARATOR             \
+  BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_1, -26) SEPARATOR           \
+  BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_2, -27) SEPARATOR           \
+  /* -28..-29 codes are reserved for dynamic ring-buffer allocation */     \
+  BROTLI_ERROR_CODE(_ERROR_ALLOC_, BLOCK_TYPE_TREES, -30) SEPARATOR        \
+                                                                           \
+  /* "Impossible" states */                                                \
+  BROTLI_ERROR_CODE(_ERROR_, UNREACHABLE, -31)
+
+/**
+ * Error code for detailed logging / production debugging.
+ *
+ * See ::BrotliDecoderGetErrorCode and ::BROTLI_LAST_ERROR_CODE.
+ */
+typedef enum {
+#define BROTLI_COMMA_ ,
+#define BROTLI_ERROR_CODE_ENUM_ITEM_(PREFIX, NAME, CODE) \
+    BROTLI_DECODER ## PREFIX ## NAME = CODE
+  BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE_ENUM_ITEM_, BROTLI_COMMA_)
+} BrotliDecoderErrorCode;
+#undef BROTLI_ERROR_CODE_ENUM_ITEM_
+#undef BROTLI_COMMA_
+
+/**
+ * The value of the last error code, negative integer.
+ *
+ * All other error code values are in the range from ::BROTLI_LAST_ERROR_CODE
+ * to @c -1. There are also 4 other possible non-error codes @c 0 .. @c 3 in
+ * ::BrotliDecoderErrorCode enumeration.
+ */
+#define BROTLI_LAST_ERROR_CODE BROTLI_DECODER_ERROR_UNREACHABLE
+
+/** Options to be used with ::BrotliDecoderSetParameter. */
+typedef enum BrotliDecoderParameter {
+  /**
+   * Disable "canny" ring buffer allocation strategy.
+   *
+   * Ring buffer is allocated according to window size, despite the real size of
+   * the content.
+   */
+  BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION = 0,
+  /**
+   * Flag that determines if "Large Window Brotli" is used.
+   */
+  BROTLI_DECODER_PARAM_LARGE_WINDOW = 1
+} BrotliDecoderParameter;
+
+/**
+ * Sets the specified parameter to the given decoder instance.
+ *
+ * @param state decoder instance
+ * @param param parameter to set
+ * @param value new parameter value
+ * @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid
+ * @returns ::BROTLI_TRUE if value is accepted
+ */
+BROTLI_DEC_API BROTLI_BOOL BrotliDecoderSetParameter(
+    BrotliDecoderState* state, BrotliDecoderParameter param, uint32_t value);
+
+/**
+ * Adds LZ77 prefix dictionary, adds or replaces built-in static dictionary and
+ * transforms.
+ *
+ * Attached dictionary ownership is not transferred.
+ * Data provided to this method should be kept accessible until
+ * decoding is finished and decoder instance is destroyed.
+ *
+ * @note Dictionaries can NOT be attached after actual decoding is started.
+ *
+ * @param state decoder instance
+ * @param type dictionary data format
+ * @param data_size length of memory region pointed by @p data
+ * @param data dictionary data in format corresponding to @p type
+ * @returns ::BROTLI_FALSE if dictionary is corrupted,
+ *          or dictionary count limit is reached
+ * @returns ::BROTLI_TRUE if dictionary is accepted / attached
+ */
+BROTLI_DEC_API BROTLI_BOOL BrotliDecoderAttachDictionary(
+    BrotliDecoderState* state, BrotliSharedDictionaryType type,
+    size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]);
+
+/**
+ * Creates an instance of ::BrotliDecoderState and initializes it.
+ *
+ * The instance can be used once for decoding and should then be destroyed with
+ * ::BrotliDecoderDestroyInstance, it cannot be reused for a new decoding
+ * session.
+ *
+ * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
+ * case they are both zero, default memory allocators are used. @p opaque is
+ * passed to @p alloc_func and @p free_func when they are called. @p free_func
+ * has to return without doing anything when asked to free a NULL pointer.
+ *
+ * @param alloc_func custom memory allocation function
+ * @param free_func custom memory free function
+ * @param opaque custom memory manager handle
+ * @returns @c 0 if instance can not be allocated or initialized
+ * @returns pointer to initialized ::BrotliDecoderState otherwise
+ */
+BROTLI_DEC_API BrotliDecoderState* BrotliDecoderCreateInstance(
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+
+/**
+ * Deinitializes and frees ::BrotliDecoderState instance.
+ *
+ * @param state decoder instance to be cleaned up and deallocated
+ */
+BROTLI_DEC_API void BrotliDecoderDestroyInstance(BrotliDecoderState* state);
+
+/**
+ * Performs one-shot memory-to-memory decompression.
+ *
+ * Decompresses the data in @p encoded_buffer into @p decoded_buffer, and sets
+ * @p *decoded_size to the decompressed length.
+ *
+ * @param encoded_size size of @p encoded_buffer
+ * @param encoded_buffer compressed data buffer with at least @p encoded_size
+ *        addressable bytes
+ * @param[in, out] decoded_size @b in: size of @p decoded_buffer; \n
+ *                 @b out: length of decompressed data written to
+ *                 @p decoded_buffer
+ * @param decoded_buffer decompressed data destination buffer
+ * @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory
+ *          allocation failed, or @p decoded_buffer is not large enough;
+ * @returns ::BROTLI_DECODER_RESULT_SUCCESS otherwise
+ */
+BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompress(
+    size_t encoded_size,
+    const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)],
+    size_t* decoded_size,
+    uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]);
+
+/**
+ * Decompresses the input stream to the output stream.
+ *
+ * The values @p *available_in and @p *available_out must specify the number of
+ * bytes addressable at @p *next_in and @p *next_out respectively.
+ * When @p *available_out is @c 0, @p next_out is allowed to be @c NULL.
+ *
+ * After each call, @p *available_in will be decremented by the amount of input
+ * bytes consumed, and the @p *next_in pointer will be incremented by that
+ * amount. Similarly, @p *available_out will be decremented by the amount of
+ * output bytes written, and the @p *next_out pointer will be incremented by
+ * that amount.
+ *
+ * @p total_out, if it is not a null-pointer, will be set to the number
+ * of bytes decompressed since the last @p state initialization.
+ *
+ * @note Input is never overconsumed, so @p next_in and @p available_in could be
+ * passed to the next consumer after decoding is complete.
+ *
+ * @param state decoder instance
+ * @param[in, out] available_in @b in: amount of available input; \n
+ *                 @b out: amount of unused input
+ * @param[in, out] next_in pointer to the next compressed byte
+ * @param[in, out] available_out @b in: length of output buffer; \n
+ *                 @b out: remaining size of output buffer
+ * @param[in, out] next_out output buffer cursor;
+ *                 can be @c NULL if @p available_out is @c 0
+ * @param[out] total_out number of bytes decompressed so far; can be @c NULL
+ * @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory
+ *          allocation failed, arguments were invalid, etc.;
+ *          use ::BrotliDecoderGetErrorCode to get detailed error code
+ * @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT decoding is blocked until
+ *          more input data is provided
+ * @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT decoding is blocked until
+ *          more output space is provided
+ * @returns ::BROTLI_DECODER_RESULT_SUCCESS decoding is finished, no more
+ *          input might be consumed and no more output will be produced
+ */
+BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompressStream(
+  BrotliDecoderState* state, size_t* available_in, const uint8_t** next_in,
+  size_t* available_out, uint8_t** next_out, size_t* total_out);
+
+/**
+ * Checks if decoder has more output.
+ *
+ * @param state decoder instance
+ * @returns ::BROTLI_TRUE, if decoder has some unconsumed output
+ * @returns ::BROTLI_FALSE otherwise
+ */
+BROTLI_DEC_API BROTLI_BOOL BrotliDecoderHasMoreOutput(
+    const BrotliDecoderState* state);
+
+/**
+ * Acquires pointer to internal output buffer.
+ *
+ * This method is used to make language bindings easier and more efficient:
+ *  -# push data to ::BrotliDecoderDecompressStream,
+ *     until ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT is reported
+ *  -# use ::BrotliDecoderTakeOutput to peek bytes and copy to language-specific
+ *     entity
+ *
+ * Also this could be useful if there is an output stream that is able to
+ * consume all the provided data (e.g. when data is saved to file system).
+ *
+ * @attention After every call to ::BrotliDecoderTakeOutput @p *size bytes of
+ *            output are considered consumed for all consecutive calls to the
+ *            instance methods; returned pointer becomes invalidated as well.
+ *
+ * @note Decoder output is not guaranteed to be contiguous. This means that
+ *       after the size-unrestricted call to ::BrotliDecoderTakeOutput,
+ *       immediate next call to ::BrotliDecoderTakeOutput may return more data.
+ *
+ * @param state decoder instance
+ * @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if
+ *                 any amount could be handled; \n
+ *                 @b out: amount of data pointed by returned pointer and
+ *                 considered consumed; \n
+ *                 out value is never greater than in value, unless it is @c 0
+ * @returns pointer to output data
+ */
+BROTLI_DEC_API const uint8_t* BrotliDecoderTakeOutput(
+    BrotliDecoderState* state, size_t* size);
+
+/**
+ * Checks if instance has already consumed input.
+ *
+ * Instance that returns ::BROTLI_FALSE is considered "fresh" and could be
+ * reused.
+ *
+ * @param state decoder instance
+ * @returns ::BROTLI_TRUE if decoder has already used some input bytes
+ * @returns ::BROTLI_FALSE otherwise
+ */
+BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsUsed(const BrotliDecoderState* state);
+
+/**
+ * Checks if decoder instance reached the final state.
+ *
+ * @param state decoder instance
+ * @returns ::BROTLI_TRUE if decoder is in a state where it reached the end of
+ *          the input and produced all of the output
+ * @returns ::BROTLI_FALSE otherwise
+ */
+BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsFinished(
+    const BrotliDecoderState* state);
+
+/**
+ * Acquires a detailed error code.
+ *
+ * Should be used only after ::BrotliDecoderDecompressStream returns
+ * ::BROTLI_DECODER_RESULT_ERROR.
+ *
+ * See also ::BrotliDecoderErrorString
+ *
+ * @param state decoder instance
+ * @returns last saved error code
+ */
+BROTLI_DEC_API BrotliDecoderErrorCode BrotliDecoderGetErrorCode(
+    const BrotliDecoderState* state);
+
+/**
+ * Converts error code to a c-string.
+ */
+BROTLI_DEC_API const char* BrotliDecoderErrorString(BrotliDecoderErrorCode c);
+
+/**
+ * Gets a decoder library version.
+ *
+ * Look at BROTLI_MAKE_HEX_VERSION for more information.
+ */
+BROTLI_DEC_API uint32_t BrotliDecoderVersion(void);
+
+/**
+ * Callback to fire on metadata block start.
+ *
+ * After this callback is fired, if @p size is not @c 0, it is followed by
+ * ::brotli_decoder_metadata_chunk_func as more metadata block contents become
+ * accessible.
+ *
+ * @param opaque callback handle
+ * @param size size of metadata block
+ */
+typedef void (*brotli_decoder_metadata_start_func)(void* opaque, size_t size);
+
+/**
+ * Callback to fire on metadata block chunk becomes available.
+ *
+ * This function can be invoked multiple times per metadata block; block should
+ * be considered finished when sum of @p size matches the announced metadata
+ * block size. Chunks contents pointed by @p data are transient and shouln not
+ * be accessed after leaving the callback.
+ *
+ * @param opaque callback handle
+ * @param data pointer to metadata contents
+ * @param size size of metadata block chunk, at least @c 1
+ */
+typedef void (*brotli_decoder_metadata_chunk_func)(void* opaque,
+                                                   const uint8_t* data,
+                                                   size_t size);
+
+/**
+ * Sets callback for receiving metadata blocks.
+ *
+ * @param state decoder instance
+ * @param start_func callback on metadata block start
+ * @param chunk_func callback on metadata block chunk
+ * @param opaque callback handle
+ */
+BROTLI_DEC_API void BrotliDecoderSetMetadataCallbacks(
+    BrotliDecoderState* state,
+    brotli_decoder_metadata_start_func start_func,
+    brotli_decoder_metadata_chunk_func chunk_func, void* opaque);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} /* extern "C" */
+#endif
+
+#endif  /* BROTLI_DEC_DECODE_H_ */

+ 501 - 0
brotli/encode.h

@@ -0,0 +1,501 @@
+/* Copyright 2013 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+/**
+ * @file
+ * API for Brotli compression.
+ */
+
+#ifndef BROTLI_ENC_ENCODE_H_
+#define BROTLI_ENC_ENCODE_H_
+
+#include <brotli/port.h>
+#include <brotli/shared_dictionary.h>
+#include <brotli/types.h>
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/** Minimal value for ::BROTLI_PARAM_LGWIN parameter. */
+#define BROTLI_MIN_WINDOW_BITS 10
+/**
+ * Maximal value for ::BROTLI_PARAM_LGWIN parameter.
+ *
+ * @note equal to @c BROTLI_MAX_DISTANCE_BITS constant.
+ */
+#define BROTLI_MAX_WINDOW_BITS 24
+/**
+ * Maximal value for ::BROTLI_PARAM_LGWIN parameter
+ * in "Large Window Brotli" (32-bit).
+ */
+#define BROTLI_LARGE_MAX_WINDOW_BITS 30
+/** Minimal value for ::BROTLI_PARAM_LGBLOCK parameter. */
+#define BROTLI_MIN_INPUT_BLOCK_BITS 16
+/** Maximal value for ::BROTLI_PARAM_LGBLOCK parameter. */
+#define BROTLI_MAX_INPUT_BLOCK_BITS 24
+/** Minimal value for ::BROTLI_PARAM_QUALITY parameter. */
+#define BROTLI_MIN_QUALITY 0
+/** Maximal value for ::BROTLI_PARAM_QUALITY parameter. */
+#define BROTLI_MAX_QUALITY 11
+
+/** Options for ::BROTLI_PARAM_MODE parameter. */
+typedef enum BrotliEncoderMode {
+  /**
+   * Default compression mode.
+   *
+   * In this mode compressor does not know anything in advance about the
+   * properties of the input.
+   */
+  BROTLI_MODE_GENERIC = 0,
+  /** Compression mode for UTF-8 formatted text input. */
+  BROTLI_MODE_TEXT = 1,
+  /** Compression mode used in WOFF 2.0. */
+  BROTLI_MODE_FONT = 2
+} BrotliEncoderMode;
+
+/** Default value for ::BROTLI_PARAM_QUALITY parameter. */
+#define BROTLI_DEFAULT_QUALITY 11
+/** Default value for ::BROTLI_PARAM_LGWIN parameter. */
+#define BROTLI_DEFAULT_WINDOW 22
+/** Default value for ::BROTLI_PARAM_MODE parameter. */
+#define BROTLI_DEFAULT_MODE BROTLI_MODE_GENERIC
+
+/** Operations that can be performed by streaming encoder. */
+typedef enum BrotliEncoderOperation {
+  /**
+   * Process input.
+   *
+   * Encoder may postpone producing output, until it has processed enough input.
+   */
+  BROTLI_OPERATION_PROCESS = 0,
+  /**
+   * Produce output for all processed input.
+   *
+   * Actual flush is performed when input stream is depleted and there is enough
+   * space in output stream. This means that client should repeat
+   * ::BROTLI_OPERATION_FLUSH operation until @p available_in becomes @c 0, and
+   * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired
+   * via ::BrotliEncoderTakeOutput, then operation should be repeated after
+   * output buffer is drained.
+   *
+   * @warning Until flush is complete, client @b SHOULD @b NOT swap,
+   *          reduce or extend input stream.
+   *
+   * When flush is complete, output data will be sufficient for decoder to
+   * reproduce all the given input.
+   */
+  BROTLI_OPERATION_FLUSH = 1,
+  /**
+   * Finalize the stream.
+   *
+   * Actual finalization is performed when input stream is depleted and there is
+   * enough space in output stream. This means that client should repeat
+   * ::BROTLI_OPERATION_FINISH operation until @p available_in becomes @c 0, and
+   * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired
+   * via ::BrotliEncoderTakeOutput, then operation should be repeated after
+   * output buffer is drained.
+   *
+   * @warning Until finalization is complete, client @b SHOULD @b NOT swap,
+   *          reduce or extend input stream.
+   *
+   * Helper function ::BrotliEncoderIsFinished checks if stream is finalized and
+   * output fully dumped.
+   *
+   * Adding more input data to finalized stream is impossible.
+   */
+  BROTLI_OPERATION_FINISH = 2,
+  /**
+   * Emit metadata block to stream.
+   *
+   * Metadata is opaque to Brotli: neither encoder, nor decoder processes this
+   * data or relies on it. It may be used to pass some extra information from
+   * encoder client to decoder client without interfering with main data stream.
+   *
+   * @note Encoder may emit empty metadata blocks internally, to pad encoded
+   *       stream to byte boundary.
+   *
+   * @warning Until emitting metadata is complete client @b SHOULD @b NOT swap,
+   *          reduce or extend input stream.
+   *
+   * @warning The whole content of input buffer is considered to be the content
+   *          of metadata block. Do @b NOT @e append metadata to input stream,
+   *          before it is depleted with other operations.
+   *
+   * Stream is soft-flushed before metadata block is emitted. Metadata block
+   * @b MUST be no longer than than 16MiB.
+   */
+  BROTLI_OPERATION_EMIT_METADATA = 3
+} BrotliEncoderOperation;
+
+/** Options to be used with ::BrotliEncoderSetParameter. */
+typedef enum BrotliEncoderParameter {
+  /**
+   * Tune encoder for specific input.
+   *
+   * ::BrotliEncoderMode enumerates all available values.
+   */
+  BROTLI_PARAM_MODE = 0,
+  /**
+   * The main compression speed-density lever.
+   *
+   * The higher the quality, the slower the compression. Range is
+   * from ::BROTLI_MIN_QUALITY to ::BROTLI_MAX_QUALITY.
+   */
+  BROTLI_PARAM_QUALITY = 1,
+  /**
+   * Recommended sliding LZ77 window size.
+   *
+   * Encoder may reduce this value, e.g. if input is much smaller than
+   * window size.
+   *
+   * Window size is `(1 << value) - 16`.
+   *
+   * Range is from ::BROTLI_MIN_WINDOW_BITS to ::BROTLI_MAX_WINDOW_BITS.
+   */
+  BROTLI_PARAM_LGWIN = 2,
+  /**
+   * Recommended input block size.
+   *
+   * Encoder may reduce this value, e.g. if input is much smaller than input
+   * block size.
+   *
+   * Range is from ::BROTLI_MIN_INPUT_BLOCK_BITS to
+   * ::BROTLI_MAX_INPUT_BLOCK_BITS.
+   *
+   * @note Bigger input block size allows better compression, but consumes more
+   *       memory. \n The rough formula of memory used for temporary input
+   *       storage is `3 << lgBlock`.
+   */
+  BROTLI_PARAM_LGBLOCK = 3,
+  /**
+   * Flag that affects usage of "literal context modeling" format feature.
+   *
+   * This flag is a "decoding-speed vs compression ratio" trade-off.
+   */
+  BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING = 4,
+  /**
+   * Estimated total input size for all ::BrotliEncoderCompressStream calls.
+   *
+   * The default value is 0, which means that the total input size is unknown.
+   */
+  BROTLI_PARAM_SIZE_HINT = 5,
+  /**
+   * Flag that determines if "Large Window Brotli" is used.
+   */
+  BROTLI_PARAM_LARGE_WINDOW = 6,
+  /**
+   * Recommended number of postfix bits (NPOSTFIX).
+   *
+   * Encoder may change this value.
+   *
+   * Range is from 0 to ::BROTLI_MAX_NPOSTFIX.
+   */
+  BROTLI_PARAM_NPOSTFIX = 7,
+  /**
+   * Recommended number of direct distance codes (NDIRECT).
+   *
+   * Encoder may change this value.
+   *
+   * Range is from 0 to (15 << NPOSTFIX) in steps of (1 << NPOSTFIX).
+   */
+  BROTLI_PARAM_NDIRECT = 8,
+  /**
+   * Number of bytes of input stream already processed by a different instance.
+   *
+   * @note It is important to configure all the encoder instances with same
+   *       parameters (except this one) in order to allow all the encoded parts
+   *       obey the same restrictions implied by header.
+   *
+   * If offset is not 0, then stream header is omitted.
+   * In any case output start is byte aligned, so for proper streams stitching
+   * "predecessor" stream must be flushed.
+   *
+   * Range is not artificially limited, but all the values greater or equal to
+   * maximal window size have the same effect. Values greater than 2**30 are not
+   * allowed.
+   */
+  BROTLI_PARAM_STREAM_OFFSET = 9
+} BrotliEncoderParameter;
+
+/**
+ * Opaque structure that holds encoder state.
+ *
+ * Allocated and initialized with ::BrotliEncoderCreateInstance.
+ * Cleaned up and deallocated with ::BrotliEncoderDestroyInstance.
+ */
+typedef struct BrotliEncoderStateStruct BrotliEncoderState;
+
+/**
+ * Sets the specified parameter to the given encoder instance.
+ *
+ * @param state encoder instance
+ * @param param parameter to set
+ * @param value new parameter value
+ * @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid
+ * @returns ::BROTLI_FALSE if value of parameter can not be changed at current
+ *          encoder state (e.g. when encoding is started, window size might be
+ *          already encoded and therefore it is impossible to change it)
+ * @returns ::BROTLI_TRUE if value is accepted
+ * @warning invalid values might be accepted in case they would not break
+ *          encoding process.
+ */
+BROTLI_ENC_API BROTLI_BOOL BrotliEncoderSetParameter(
+    BrotliEncoderState* state, BrotliEncoderParameter param, uint32_t value);
+
+/**
+ * Creates an instance of ::BrotliEncoderState and initializes it.
+ *
+ * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
+ * case they are both zero, default memory allocators are used. @p opaque is
+ * passed to @p alloc_func and @p free_func when they are called. @p free_func
+ * has to return without doing anything when asked to free a NULL pointer.
+ *
+ * @param alloc_func custom memory allocation function
+ * @param free_func custom memory free function
+ * @param opaque custom memory manager handle
+ * @returns @c 0 if instance can not be allocated or initialized
+ * @returns pointer to initialized ::BrotliEncoderState otherwise
+ */
+BROTLI_ENC_API BrotliEncoderState* BrotliEncoderCreateInstance(
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+
+/**
+ * Deinitializes and frees ::BrotliEncoderState instance.
+ *
+ * @param state decoder instance to be cleaned up and deallocated
+ */
+BROTLI_ENC_API void BrotliEncoderDestroyInstance(BrotliEncoderState* state);
+
+/* Opaque type for pointer to different possible internal structures containing
+   dictionary prepared for the encoder */
+typedef struct BrotliEncoderPreparedDictionaryStruct
+    BrotliEncoderPreparedDictionary;
+
+/**
+ * Prepares a shared dictionary from the given file format for the encoder.
+ *
+ * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
+ * case they are both zero, default memory allocators are used. @p opaque is
+ * passed to @p alloc_func and @p free_func when they are called. @p free_func
+ * has to return without doing anything when asked to free a NULL pointer.
+ *
+ * @param type type of dictionary stored in data
+ * @param data_size size of @p data buffer
+ * @param data pointer to the dictionary data
+ * @param quality the maximum Brotli quality to prepare the dictionary for,
+ *        use BROTLI_MAX_QUALITY by default
+ * @param alloc_func custom memory allocation function
+ * @param free_func custom memory free function
+ * @param opaque custom memory manager handle
+ */
+BROTLI_ENC_API BrotliEncoderPreparedDictionary*
+BrotliEncoderPrepareDictionary(BrotliSharedDictionaryType type,
+    size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)],
+    int quality,
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+
+BROTLI_ENC_API void BrotliEncoderDestroyPreparedDictionary(
+    BrotliEncoderPreparedDictionary* dictionary);
+
+/**
+ * Attaches a prepared dictionary of any type to the encoder. Can be used
+ * multiple times to attach multiple dictionaries. The dictionary type was
+ * determined by BrotliEncoderPrepareDictionary. Multiple raw prefix
+ * dictionaries and/or max 1 serialized dictionary with custom words can be
+ * attached.
+ *
+ * @returns ::BROTLI_FALSE in case of error
+ * @returns ::BROTLI_TRUE otherwise
+ */
+BROTLI_ENC_API BROTLI_BOOL BrotliEncoderAttachPreparedDictionary(
+    BrotliEncoderState* state,
+    const BrotliEncoderPreparedDictionary* dictionary);
+
+/**
+ * Calculates the output size bound for the given @p input_size.
+ *
+ * @warning Result is only valid if quality is at least @c 2 and, in
+ *          case ::BrotliEncoderCompressStream was used, no flushes
+ *          (::BROTLI_OPERATION_FLUSH) were performed.
+ *
+ * @param input_size size of projected input
+ * @returns @c 0 if result does not fit @c size_t
+ */
+BROTLI_ENC_API size_t BrotliEncoderMaxCompressedSize(size_t input_size);
+
+/**
+ * Performs one-shot memory-to-memory compression.
+ *
+ * Compresses the data in @p input_buffer into @p encoded_buffer, and sets
+ * @p *encoded_size to the compressed length.
+ *
+ * @note If ::BrotliEncoderMaxCompressedSize(@p input_size) returns non-zero
+ *       value, then output is guaranteed to be no longer than that.
+ *
+ * @note If @p lgwin is greater than ::BROTLI_MAX_WINDOW_BITS then resulting
+ *       stream might be incompatible with RFC 7932; to decode such streams,
+ *       decoder should be configured with
+ *       ::BROTLI_DECODER_PARAM_LARGE_WINDOW = @c 1
+ *
+ * @param quality quality parameter value, e.g. ::BROTLI_DEFAULT_QUALITY
+ * @param lgwin lgwin parameter value, e.g. ::BROTLI_DEFAULT_WINDOW
+ * @param mode mode parameter value, e.g. ::BROTLI_DEFAULT_MODE
+ * @param input_size size of @p input_buffer
+ * @param input_buffer input data buffer with at least @p input_size
+ *        addressable bytes
+ * @param[in, out] encoded_size @b in: size of @p encoded_buffer; \n
+ *                 @b out: length of compressed data written to
+ *                 @p encoded_buffer, or @c 0 if compression fails
+ * @param encoded_buffer compressed data destination buffer
+ * @returns ::BROTLI_FALSE in case of compression error
+ * @returns ::BROTLI_FALSE if output buffer is too small
+ * @returns ::BROTLI_TRUE otherwise
+ */
+BROTLI_ENC_API int BrotliEncoderCompress(
+    int quality, int lgwin, BrotliEncoderMode mode, size_t input_size,
+    const uint8_t input_buffer[BROTLI_ARRAY_PARAM(input_size)],
+    size_t* encoded_size,
+    uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(*encoded_size)]);
+
+/**
+ * Compresses input stream to output stream.
+ *
+ * The values @p *available_in and @p *available_out must specify the number of
+ * bytes addressable at @p *next_in and @p *next_out respectively.
+ * When @p *available_out is @c 0, @p next_out is allowed to be @c NULL.
+ *
+ * After each call, @p *available_in will be decremented by the amount of input
+ * bytes consumed, and the @p *next_in pointer will be incremented by that
+ * amount. Similarly, @p *available_out will be decremented by the amount of
+ * output bytes written, and the @p *next_out pointer will be incremented by
+ * that amount.
+ *
+ * @p total_out, if it is not a null-pointer, will be set to the number
+ * of bytes compressed since the last @p state initialization.
+ *
+ *
+ *
+ * Internally workflow consists of 3 tasks:
+ *  -# (optionally) copy input data to internal buffer
+ *  -# actually compress data and (optionally) store it to internal buffer
+ *  -# (optionally) copy compressed bytes from internal buffer to output stream
+ *
+ * Whenever all 3 tasks can't move forward anymore, or error occurs, this
+ * method returns the control flow to caller.
+ *
+ * @p op is used to perform flush, finish the stream, or inject metadata block.
+ * See ::BrotliEncoderOperation for more information.
+ *
+ * Flushing the stream means forcing encoding of all input passed to encoder and
+ * completing the current output block, so it could be fully decoded by stream
+ * decoder. To perform flush set @p op to ::BROTLI_OPERATION_FLUSH.
+ * Under some circumstances (e.g. lack of output stream capacity) this operation
+ * would require several calls to ::BrotliEncoderCompressStream. The method must
+ * be called again until both input stream is depleted and encoder has no more
+ * output (see ::BrotliEncoderHasMoreOutput) after the method is called.
+ *
+ * Finishing the stream means encoding of all input passed to encoder and
+ * adding specific "final" marks, so stream decoder could determine that stream
+ * is complete. To perform finish set @p op to ::BROTLI_OPERATION_FINISH.
+ * Under some circumstances (e.g. lack of output stream capacity) this operation
+ * would require several calls to ::BrotliEncoderCompressStream. The method must
+ * be called again until both input stream is depleted and encoder has no more
+ * output (see ::BrotliEncoderHasMoreOutput) after the method is called.
+ *
+ * @warning When flushing and finishing, @p op should not change until operation
+ *          is complete; input stream should not be swapped, reduced or
+ *          extended as well.
+ *
+ * @param state encoder instance
+ * @param op requested operation
+ * @param[in, out] available_in @b in: amount of available input; \n
+ *                 @b out: amount of unused input
+ * @param[in, out] next_in pointer to the next input byte
+ * @param[in, out] available_out @b in: length of output buffer; \n
+ *                 @b out: remaining size of output buffer
+ * @param[in, out] next_out compressed output buffer cursor;
+ *                 can be @c NULL if @p available_out is @c 0
+ * @param[out] total_out number of bytes produced so far; can be @c NULL
+ * @returns ::BROTLI_FALSE if there was an error
+ * @returns ::BROTLI_TRUE otherwise
+ */
+BROTLI_ENC_API BROTLI_BOOL BrotliEncoderCompressStream(
+    BrotliEncoderState* state, BrotliEncoderOperation op, size_t* available_in,
+    const uint8_t** next_in, size_t* available_out, uint8_t** next_out,
+    size_t* total_out);
+
+/**
+ * Checks if encoder instance reached the final state.
+ *
+ * @param state encoder instance
+ * @returns ::BROTLI_TRUE if encoder is in a state where it reached the end of
+ *          the input and produced all of the output
+ * @returns ::BROTLI_FALSE otherwise
+ */
+BROTLI_ENC_API BROTLI_BOOL BrotliEncoderIsFinished(BrotliEncoderState* state);
+
+/**
+ * Checks if encoder has more output.
+ *
+ * @param state encoder instance
+ * @returns ::BROTLI_TRUE, if encoder has some unconsumed output
+ * @returns ::BROTLI_FALSE otherwise
+ */
+BROTLI_ENC_API BROTLI_BOOL BrotliEncoderHasMoreOutput(
+    BrotliEncoderState* state);
+
+/**
+ * Acquires pointer to internal output buffer.
+ *
+ * This method is used to make language bindings easier and more efficient:
+ *  -# push data to ::BrotliEncoderCompressStream,
+ *     until ::BrotliEncoderHasMoreOutput returns BROTLI_TRUE
+ *  -# use ::BrotliEncoderTakeOutput to peek bytes and copy to language-specific
+ *     entity
+ *
+ * Also this could be useful if there is an output stream that is able to
+ * consume all the provided data (e.g. when data is saved to file system).
+ *
+ * @attention After every call to ::BrotliEncoderTakeOutput @p *size bytes of
+ *            output are considered consumed for all consecutive calls to the
+ *            instance methods; returned pointer becomes invalidated as well.
+ *
+ * @note Encoder output is not guaranteed to be contiguous. This means that
+ *       after the size-unrestricted call to ::BrotliEncoderTakeOutput,
+ *       immediate next call to ::BrotliEncoderTakeOutput may return more data.
+ *
+ * @param state encoder instance
+ * @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if
+ *                 any amount could be handled; \n
+ *                 @b out: amount of data pointed by returned pointer and
+ *                 considered consumed; \n
+ *                 out value is never greater than in value, unless it is @c 0
+ * @returns pointer to output data
+ */
+BROTLI_ENC_API const uint8_t* BrotliEncoderTakeOutput(
+    BrotliEncoderState* state, size_t* size);
+
+/* Returns the estimated peak memory usage (in bytes) of the BrotliCompress()
+   function, not counting the memory needed for the input and output. */
+BROTLI_ENC_EXTRA_API size_t BrotliEncoderEstimatePeakMemoryUsage(
+    int quality, int lgwin, size_t input_size);
+/* Returns 0 if dictionary is not valid; otherwise returns allocation size. */
+BROTLI_ENC_EXTRA_API size_t BrotliEncoderGetPreparedDictionarySize(
+    const BrotliEncoderPreparedDictionary* dictionary);
+
+/**
+ * Gets an encoder library version.
+ *
+ * Look at BROTLI_MAKE_HEX_VERSION for more information.
+ */
+BROTLI_ENC_API uint32_t BrotliEncoderVersion(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}  /* extern "C" */
+#endif
+
+#endif  /* BROTLI_ENC_ENCODE_H_ */

+ 305 - 0
brotli/port.h

@@ -0,0 +1,305 @@
+/* Copyright 2016 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+/* Macros for compiler / platform specific API declarations. */
+
+#ifndef BROTLI_COMMON_PORT_H_
+#define BROTLI_COMMON_PORT_H_
+
+/* The following macros were borrowed from https://github.com/nemequ/hedley
+ * with permission of original author - Evan Nemerson <evan@nemerson.com> */
+
+/* >>> >>> >>> hedley macros */
+
+#define BROTLI_MAKE_VERSION(major, minor, revision) \
+  (((major) * 1000000) + ((minor) * 1000) + (revision))
+
+#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)
+#define BROTLI_GNUC_VERSION \
+  BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#elif defined(__GNUC__)
+#define BROTLI_GNUC_VERSION BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, 0)
+#endif
+
+#if defined(BROTLI_GNUC_VERSION)
+#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) \
+  (BROTLI_GNUC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
+#else
+#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) (0)
+#endif
+
+#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000)
+#define BROTLI_MSVC_VERSION                                \
+  BROTLI_MAKE_VERSION((_MSC_FULL_VER / 10000000),          \
+                      (_MSC_FULL_VER % 10000000) / 100000, \
+                      (_MSC_FULL_VER % 100000) / 100)
+#elif defined(_MSC_FULL_VER)
+#define BROTLI_MSVC_VERSION                              \
+  BROTLI_MAKE_VERSION((_MSC_FULL_VER / 1000000),         \
+                      (_MSC_FULL_VER % 1000000) / 10000, \
+                      (_MSC_FULL_VER % 10000) / 10)
+#elif defined(_MSC_VER)
+#define BROTLI_MSVC_VERSION \
+  BROTLI_MAKE_VERSION(_MSC_VER / 100, _MSC_VER % 100, 0)
+#endif
+
+#if !defined(_MSC_VER)
+#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) (0)
+#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \
+  (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \
+  (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))
+#else
+#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \
+  (_MSC_VER >= ((major * 100) + (minor)))
+#endif
+
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE)
+#define BROTLI_INTEL_VERSION                   \
+  BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100,  \
+                      __INTEL_COMPILER % 100,  \
+                      __INTEL_COMPILER_UPDATE)
+#elif defined(__INTEL_COMPILER)
+#define BROTLI_INTEL_VERSION \
+  BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif
+
+#if defined(BROTLI_INTEL_VERSION)
+#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) \
+  (BROTLI_INTEL_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
+#else
+#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) (0)
+#endif
+
+#if defined(__PGI) && \
+    defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)
+#define BROTLI_PGI_VERSION \
+  BROTLI_MAKE_VERSION(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)
+#endif
+
+#if defined(BROTLI_PGI_VERSION)
+#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) \
+  (BROTLI_PGI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
+#else
+#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) (0)
+#endif
+
+#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
+#define BROTLI_SUNPRO_VERSION                                       \
+  BROTLI_MAKE_VERSION(                                              \
+    (((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \
+    (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf),   \
+    (__SUNPRO_C & 0xf) * 10)
+#elif defined(__SUNPRO_C)
+#define BROTLI_SUNPRO_VERSION                  \
+  BROTLI_MAKE_VERSION((__SUNPRO_C >> 8) & 0xf, \
+                      (__SUNPRO_C >> 4) & 0xf, \
+                      (__SUNPRO_C) & 0xf)
+#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
+#define BROTLI_SUNPRO_VERSION                                         \
+  BROTLI_MAKE_VERSION(                                                \
+    (((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \
+    (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf),   \
+    (__SUNPRO_CC & 0xf) * 10)
+#elif defined(__SUNPRO_CC)
+#define BROTLI_SUNPRO_VERSION                   \
+  BROTLI_MAKE_VERSION((__SUNPRO_CC >> 8) & 0xf, \
+                      (__SUNPRO_CC >> 4) & 0xf, \
+                      (__SUNPRO_CC) & 0xf)
+#endif
+
+#if defined(BROTLI_SUNPRO_VERSION)
+#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) \
+  (BROTLI_SUNPRO_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
+#else
+#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) (0)
+#endif
+
+#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)
+#define BROTLI_ARM_VERSION                                       \
+  BROTLI_MAKE_VERSION((__ARMCOMPILER_VERSION / 1000000),         \
+                      (__ARMCOMPILER_VERSION % 1000000) / 10000, \
+                      (__ARMCOMPILER_VERSION % 10000) / 100)
+#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)
+#define BROTLI_ARM_VERSION                                 \
+  BROTLI_MAKE_VERSION((__ARMCC_VERSION / 1000000),         \
+                      (__ARMCC_VERSION % 1000000) / 10000, \
+                      (__ARMCC_VERSION % 10000) / 100)
+#endif
+
+#if defined(BROTLI_ARM_VERSION)
+#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) \
+  (BROTLI_ARM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
+#else
+#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) (0)
+#endif
+
+#if defined(__ibmxl__)
+#define BROTLI_IBM_VERSION                    \
+  BROTLI_MAKE_VERSION(__ibmxl_version__,      \
+                      __ibmxl_release__,      \
+                      __ibmxl_modification__)
+#elif defined(__xlC__) && defined(__xlC_ver__)
+#define BROTLI_IBM_VERSION \
+  BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)
+#elif defined(__xlC__)
+#define BROTLI_IBM_VERSION BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, 0)
+#endif
+
+#if defined(BROTLI_IBM_VERSION)
+#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) \
+  (BROTLI_IBM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
+#else
+#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) (0)
+#endif
+
+#if defined(__TI_COMPILER_VERSION__)
+#define BROTLI_TI_VERSION                                         \
+  BROTLI_MAKE_VERSION((__TI_COMPILER_VERSION__ / 1000000),        \
+                      (__TI_COMPILER_VERSION__ % 1000000) / 1000, \
+                      (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(BROTLI_TI_VERSION)
+#define BROTLI_TI_VERSION_CHECK(major, minor, patch) \
+  (BROTLI_TI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
+#else
+#define BROTLI_TI_VERSION_CHECK(major, minor, patch) (0)
+#endif
+
+#if defined(__IAR_SYSTEMS_ICC__)
+#if __VER__ > 1000
+#define BROTLI_IAR_VERSION                     \
+  BROTLI_MAKE_VERSION((__VER__ / 1000000),     \
+                      (__VER__ / 1000) % 1000, \
+                      (__VER__ % 1000))
+#else
+#define BROTLI_IAR_VERSION BROTLI_MAKE_VERSION(VER / 100, __VER__ % 100, 0)
+#endif
+#endif
+
+#if defined(BROTLI_IAR_VERSION)
+#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) \
+  (BROTLI_IAR_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
+#else
+#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) (0)
+#endif
+
+#if defined(__TINYC__)
+#define BROTLI_TINYC_VERSION \
+  BROTLI_MAKE_VERSION(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)
+#endif
+
+#if defined(BROTLI_TINYC_VERSION)
+#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) \
+  (BROTLI_TINYC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
+#else
+#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) (0)
+#endif
+
+#if defined(__has_attribute)
+#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \
+  __has_attribute(attribute)
+#else
+#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \
+  BROTLI_GNUC_VERSION_CHECK(major, minor, patch)
+#endif
+
+#if defined(__has_builtin)
+#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \
+  __has_builtin(builtin)
+#else
+#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \
+  BROTLI_GNUC_VERSION_CHECK(major, minor, patch)
+#endif
+
+#if defined(__has_feature)
+#define BROTLI_HAS_FEATURE(feature) __has_feature(feature)
+#else
+#define BROTLI_HAS_FEATURE(feature) (0)
+#endif
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define BROTLI_PUBLIC
+#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) ||                         \
+    BROTLI_TI_VERSION_CHECK(8, 0, 0) ||                             \
+    BROTLI_INTEL_VERSION_CHECK(16, 0, 0) ||                         \
+    BROTLI_ARM_VERSION_CHECK(4, 1, 0) ||                            \
+    BROTLI_IBM_VERSION_CHECK(13, 1, 0) ||                           \
+    BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) ||                        \
+    (BROTLI_TI_VERSION_CHECK(7, 3, 0) &&                            \
+     defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__))
+#define BROTLI_PUBLIC __attribute__ ((visibility ("default")))
+#else
+#define BROTLI_PUBLIC
+#endif
+
+/* BROTLI_INTERNAL could be defined to override visibility, e.g. for tests. */
+#if !defined(BROTLI_INTERNAL)
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define BROTLI_INTERNAL
+#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) ||                         \
+    BROTLI_TI_VERSION_CHECK(8, 0, 0) ||                             \
+    BROTLI_INTEL_VERSION_CHECK(16, 0, 0) ||                         \
+    BROTLI_ARM_VERSION_CHECK(4, 1, 0) ||                            \
+    BROTLI_IBM_VERSION_CHECK(13, 1, 0) ||                           \
+    BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) ||                        \
+    (BROTLI_TI_VERSION_CHECK(7, 3, 0) &&                            \
+     defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__))
+#define BROTLI_INTERNAL __attribute__ ((visibility ("hidden")))
+#else
+#define BROTLI_INTERNAL
+#endif
+#endif
+
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) &&   \
+    !defined(__STDC_NO_VLA__) && !defined(__cplusplus) &&           \
+    !defined(__PGI) && !defined(__PGIC__) && !defined(__TINYC__) && \
+    !defined(__clang__)
+#define BROTLI_ARRAY_PARAM(name) (name)
+#else
+#define BROTLI_ARRAY_PARAM(name)
+#endif
+
+/* <<< <<< <<< end of hedley macros. */
+
+#if defined(BROTLI_SHARED_COMPILATION)
+#if defined(_WIN32)
+#if defined(BROTLICOMMON_SHARED_COMPILATION)
+#define BROTLI_COMMON_API __declspec(dllexport)
+#else
+#define BROTLI_COMMON_API __declspec(dllimport)
+#endif  /* BROTLICOMMON_SHARED_COMPILATION */
+#if defined(BROTLIDEC_SHARED_COMPILATION)
+#define BROTLI_DEC_API __declspec(dllexport)
+#else
+#define BROTLI_DEC_API __declspec(dllimport)
+#endif  /* BROTLIDEC_SHARED_COMPILATION */
+#if defined(BROTLIENC_SHARED_COMPILATION)
+#define BROTLI_ENC_API __declspec(dllexport)
+#else
+#define BROTLI_ENC_API __declspec(dllimport)
+#endif  /* BROTLIENC_SHARED_COMPILATION */
+#else  /* _WIN32 */
+#define BROTLI_COMMON_API BROTLI_PUBLIC
+#define BROTLI_DEC_API BROTLI_PUBLIC
+#define BROTLI_ENC_API BROTLI_PUBLIC
+#endif  /* _WIN32 */
+#else  /* BROTLI_SHARED_COMPILATION */
+#define BROTLI_COMMON_API
+#define BROTLI_DEC_API
+#define BROTLI_ENC_API
+#endif
+
+#if defined(BROTLI_BUILD_ENC_EXTRA_API)
+#define BROTLI_ENC_EXTRA_API BROTLI_ENC_API
+#else
+#define BROTLI_ENC_EXTRA_API BROTLI_INTERNAL
+#endif
+
+#endif  /* BROTLI_COMMON_PORT_H_ */

+ 100 - 0
brotli/shared_dictionary.h

@@ -0,0 +1,100 @@
+/* Copyright 2017 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+/* (Opaque) Shared Dictionary definition and utilities. */
+
+#ifndef BROTLI_COMMON_SHARED_DICTIONARY_H_
+#define BROTLI_COMMON_SHARED_DICTIONARY_H_
+
+#include <brotli/port.h>
+#include <brotli/types.h>
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH 4
+#define SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH 31
+#define SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS 64
+#define SHARED_BROTLI_MAX_COMPOUND_DICTS 15
+
+/**
+ * Opaque structure that holds shared dictionary data.
+ *
+ * Allocated and initialized with ::BrotliSharedDictionaryCreateInstance.
+ * Cleaned up and deallocated with ::BrotliSharedDictionaryDestroyInstance.
+ */
+typedef struct BrotliSharedDictionaryStruct BrotliSharedDictionary;
+
+/**
+ * Input data type for ::BrotliSharedDictionaryAttach.
+ */
+typedef enum BrotliSharedDictionaryType {
+  /** Raw LZ77 prefix dictionary. */
+  BROTLI_SHARED_DICTIONARY_RAW = 0,
+  /** Serialized shared dictionary.
+   *
+   * DO NOT USE: methods accepting this value will fail.
+   */
+  BROTLI_SHARED_DICTIONARY_SERIALIZED = 1
+} BrotliSharedDictionaryType;
+
+/**
+ * Creates an instance of ::BrotliSharedDictionary.
+ *
+ * Fresh instance has default word dictionary and transforms
+ * and no LZ77 prefix dictionary.
+ *
+ * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
+ * case they are both zero, default memory allocators are used. @p opaque is
+ * passed to @p alloc_func and @p free_func when they are called. @p free_func
+ * has to return without doing anything when asked to free a NULL pointer.
+ *
+ * @param alloc_func custom memory allocation function
+ * @param free_func custom memory free function
+ * @param opaque custom memory manager handle
+ * @returns @c 0 if instance can not be allocated or initialized
+ * @returns pointer to initialized ::BrotliSharedDictionary otherwise
+ */
+BROTLI_COMMON_API BrotliSharedDictionary* BrotliSharedDictionaryCreateInstance(
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+
+/**
+ * Deinitializes and frees ::BrotliSharedDictionary instance.
+ *
+ * @param dict shared dictionary instance to be cleaned up and deallocated
+ */
+BROTLI_COMMON_API void BrotliSharedDictionaryDestroyInstance(
+    BrotliSharedDictionary* dict);
+
+/**
+ * Attaches dictionary to a given instance of ::BrotliSharedDictionary.
+ *
+ * Dictionary to be attached is represented in a serialized format as a region
+ * of memory.
+ *
+ * Provided data it partially referenced by a resulting (compound) dictionary,
+ * and should be kept untouched, while at least one compound dictionary uses it.
+ * This way memory overhead is kept minimal by the cost of additional resource
+ * management.
+ *
+ * @param dict dictionary to extend
+ * @param type type of dictionary to attach
+ * @param data_size size of @p data
+ * @param data serialized dictionary of type @p type, with at least @p data_size
+ *        addressable bytes
+ * @returns ::BROTLI_TRUE if provided dictionary is successfully attached
+ * @returns ::BROTLI_FALSE otherwise
+ */
+BROTLI_COMMON_API BROTLI_BOOL BrotliSharedDictionaryAttach(
+    BrotliSharedDictionary* dict, BrotliSharedDictionaryType type,
+    size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}  /* extern "C" */
+#endif
+
+#endif  /* BROTLI_COMMON_SHARED_DICTIONARY_H_ */

+ 83 - 0
brotli/types.h

@@ -0,0 +1,83 @@
+/* Copyright 2013 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+/**
+ * @file
+ * Common types used in decoder and encoder API.
+ */
+
+#ifndef BROTLI_COMMON_TYPES_H_
+#define BROTLI_COMMON_TYPES_H_
+
+#include <stddef.h>  /* for size_t */
+
+#if defined(_MSC_VER) && (_MSC_VER < 1600)
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef __int64 int64_t;
+#else
+#include <stdint.h>
+#endif  /* defined(_MSC_VER) && (_MSC_VER < 1600) */
+
+/**
+ * A portable @c bool replacement.
+ *
+ * ::BROTLI_BOOL is a "documentation" type: actually it is @c int, but in API it
+ * denotes a type, whose only values are ::BROTLI_TRUE and ::BROTLI_FALSE.
+ *
+ * ::BROTLI_BOOL values passed to Brotli should either be ::BROTLI_TRUE or
+ * ::BROTLI_FALSE, or be a result of ::TO_BROTLI_BOOL macros.
+ *
+ * ::BROTLI_BOOL values returned by Brotli should not be tested for equality
+ * with @c true, @c false, ::BROTLI_TRUE, ::BROTLI_FALSE, but rather should be
+ * evaluated, for example: @code{.cpp}
+ * if (SomeBrotliFunction(encoder, BROTLI_TRUE) &&
+ *     !OtherBrotliFunction(decoder, BROTLI_FALSE)) {
+ *   bool x = !!YetAnotherBrotliFunction(encoder, TO_BROLTI_BOOL(2 * 2 == 4));
+ *   DoSomething(x);
+ * }
+ * @endcode
+ */
+#define BROTLI_BOOL int
+/** Portable @c true replacement. */
+#define BROTLI_TRUE 1
+/** Portable @c false replacement. */
+#define BROTLI_FALSE 0
+/** @c bool to ::BROTLI_BOOL conversion macros. */
+#define TO_BROTLI_BOOL(X) (!!(X) ? BROTLI_TRUE : BROTLI_FALSE)
+
+#define BROTLI_MAKE_UINT64_T(high, low) ((((uint64_t)(high)) << 32) | low)
+
+#define BROTLI_UINT32_MAX (~((uint32_t)0))
+#define BROTLI_SIZE_MAX (~((size_t)0))
+
+/**
+ * Allocating function pointer type.
+ *
+ * @param opaque custom memory manager handle provided by client
+ * @param size requested memory region size; can not be @c 0
+ * @returns @c 0 in the case of failure
+ * @returns a valid pointer to a memory region of at least @p size bytes
+ *          long otherwise
+ */
+typedef void* (*brotli_alloc_func)(void* opaque, size_t size);
+
+/**
+ * Deallocating function pointer type.
+ *
+ * This function @b SHOULD do nothing if @p address is @c 0.
+ *
+ * @param opaque custom memory manager handle provided by client
+ * @param address memory region pointer returned by ::brotli_alloc_func, or @c 0
+ */
+typedef void (*brotli_free_func)(void* opaque, void* address);
+
+#endif  /* BROTLI_COMMON_TYPES_H_ */

+ 298 - 0
common/BufferPool.cpp

@@ -0,0 +1,298 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "BufferPool.h"
+#include "FuncHelper.h"
+
+const DWORD TItem::DEFAULT_ITEM_CAPACITY			= DEFAULT_BUFFER_CACHE_CAPACITY;
+const DWORD CBufferPool::DEFAULT_MAX_CACHE_SIZE		= 0;
+const DWORD CBufferPool::DEFAULT_ITEM_CAPACITY		= CItemPool::DEFAULT_ITEM_CAPACITY;
+const DWORD CBufferPool::DEFAULT_ITEM_POOL_SIZE		= CItemPool::DEFAULT_POOL_SIZE;
+const DWORD CBufferPool::DEFAULT_ITEM_POOL_HOLD		= CItemPool::DEFAULT_POOL_HOLD;
+const DWORD CBufferPool::DEFAULT_BUFFER_LOCK_TIME	= DEFAULT_OBJECT_CACHE_LOCK_TIME;
+const DWORD CBufferPool::DEFAULT_BUFFER_POOL_SIZE	= DEFAULT_OBJECT_CACHE_POOL_SIZE;
+const DWORD CBufferPool::DEFAULT_BUFFER_POOL_HOLD	= DEFAULT_OBJECT_CACHE_POOL_HOLD;
+
+int TItem::Cat(const BYTE* pData, int length)
+{
+	ASSERT(pData != nullptr && length >= 0);
+
+	int cat = MIN(Remain(), length);
+
+	if(cat > 0)
+	{
+		memcpy(end, pData, cat);
+		end += cat;
+	}
+
+	return cat;
+}
+
+int TItem::Cat(const TItem& other)
+{
+	ASSERT(this != &other);
+	return Cat(other.Ptr(), other.Size());
+}
+
+int TItem::Fetch(BYTE* pData, int length)
+{
+	ASSERT(pData != nullptr && length > 0);
+
+	int fetch = MIN(Size(), length);
+	memcpy(pData, begin, fetch);
+	begin	 += fetch;
+
+	return fetch;
+}
+
+int TItem::Peek(BYTE* pData, int length)
+{
+	ASSERT(pData != nullptr && length > 0);
+
+	int peek = MIN(Size(), length);
+	memcpy(pData, begin, peek);
+
+	return peek;
+}
+
+int TItem::Increase(int length)
+{
+	ASSERT(length >= 0);
+
+	int increase = MIN(Remain(), length);
+	end			+= increase;
+
+	return increase;
+}
+
+int TItem::Reduce(int length)
+{
+	ASSERT(length >= 0);
+
+	int reduce = MIN(Size(), length);
+	begin	  += reduce;
+
+	return reduce;
+}
+
+void TItem::Reset(int first, int last)
+{
+	ASSERT(first >= -1 && first <= capacity);
+	ASSERT(last >= -1 && last <= capacity);
+
+	if(first >= 0)	begin	= head + MIN(first, capacity);
+	if(last >= 0)	end		= head + MIN(last, capacity);
+}
+
+TBuffer* TBuffer::Construct(CBufferPool& pool, ULONG_PTR dwID)
+{
+	ASSERT(dwID != 0);
+
+	CPrivateHeap& heap	= pool.GetPrivateHeap();
+	TBuffer* pBuffer	= (TBuffer*)heap.Alloc(sizeof(TBuffer));
+
+	return ::ConstructObject(pBuffer, heap, pool.GetItemPool(), dwID);
+}
+
+void TBuffer::Destruct(TBuffer* pBuffer)
+{
+	ASSERT(pBuffer != nullptr);
+
+	CPrivateHeap& heap = pBuffer->heap;
+	::DestructObject(pBuffer);
+	heap.Free(pBuffer);
+}
+
+void TBuffer::Reset()
+{
+	id		 = 0;
+	length	 = 0;
+	freeTime = ::TimeGetTime();
+}
+
+int TBuffer::Cat(const BYTE* pData, int len)
+{
+	items.Cat(pData, len);
+	return IncreaseLength(len);
+}
+
+int TBuffer::Cat(const TItem* pItem)
+{
+	items.Cat(pItem);
+	return IncreaseLength(pItem->Size());
+}
+
+int TBuffer::Cat(const TItemList& other)
+{
+	ASSERT(&items != &other);
+
+	for(TItem* pItem = other.Front(); pItem != nullptr; pItem = pItem->next)
+		Cat(pItem);
+
+	return length;
+}
+
+int TBuffer::Fetch(BYTE* pData, int len)
+{
+	int fetch = items.Fetch(pData, len);
+	DecreaseLength(fetch);
+
+	return fetch;
+}
+
+int TBuffer::Peek(BYTE* pData, int len)
+{
+	return items.Peek(pData, len);
+}
+
+int TBuffer::Reduce(int len)
+{
+	int reduce = items.Reduce(len);
+	DecreaseLength(reduce);
+
+	return reduce;
+}
+
+void CBufferPool::PutFreeBuffer(ULONG_PTR dwID)
+{
+	ASSERT(dwID != 0);
+
+	TBuffer* pBuffer = FindCacheBuffer(dwID);
+
+	if(pBuffer != nullptr)
+		PutFreeBuffer(pBuffer);
+}
+
+void CBufferPool::PutFreeBuffer(TBuffer* pBuffer)
+{
+	ASSERT(pBuffer != nullptr);
+
+	if(!pBuffer->IsValid())
+		return;
+
+	m_bfCache.RemoveEx(pBuffer->ID());
+
+	BOOL bOK = FALSE;
+
+	{
+		CCriSecLock locallock(pBuffer->cs);
+
+		if(pBuffer->IsValid())
+		{
+			pBuffer->Reset();
+			bOK = TRUE;
+		}
+	}
+
+	if(bOK)
+	{
+		m_itPool.PutFreeItem(pBuffer->items);
+
+#ifndef USE_EXTERNAL_GC
+		ReleaseGCBuffer();
+#endif
+		if(!m_lsFreeBuffer.TryPut(pBuffer))
+			m_lsGCBuffer.PushBack(pBuffer);
+	}
+}
+
+void CBufferPool::ReleaseGCBuffer(BOOL bForce)
+{
+	::ReleaseGCObj(m_lsGCBuffer, m_dwBufferLockTime, bForce);
+}
+
+TBuffer* CBufferPool::PutCacheBuffer(ULONG_PTR dwID)
+{
+	ASSERT(dwID != 0);
+
+	TBuffer* pBuffer = PickFreeBuffer(dwID);
+	m_bfCache.SetEx(dwID, pBuffer);
+
+	return pBuffer;
+}
+
+TBuffer* CBufferPool::PickFreeBuffer(ULONG_PTR dwID)
+{
+	ASSERT( dwID != 0);
+
+	DWORD dwIndex;
+	TBuffer* pBuffer = nullptr;
+
+	if(m_lsFreeBuffer.TryLock(&pBuffer, dwIndex))
+	{
+		if(::GetTimeGap32(pBuffer->freeTime) >= m_dwBufferLockTime)
+			VERIFY(m_lsFreeBuffer.ReleaseLock(nullptr, dwIndex));
+		else
+		{
+			VERIFY(m_lsFreeBuffer.ReleaseLock(pBuffer, dwIndex));
+			pBuffer = nullptr;
+		}
+	}
+
+	if(pBuffer)	pBuffer->id	= dwID;
+	else		pBuffer		= TBuffer::Construct(*this, dwID);
+
+	ASSERT(pBuffer);
+	return pBuffer;
+}
+
+TBuffer* CBufferPool::FindCacheBuffer(ULONG_PTR dwID)
+{
+	ASSERT(dwID != 0);
+
+	TBuffer* pBuffer = nullptr;
+
+	if(m_bfCache.GetEx(dwID, &pBuffer) != TBufferCache::GR_VALID)
+		pBuffer = nullptr;
+
+	return pBuffer;
+}
+
+void CBufferPool::Prepare()
+{
+	m_itPool.Prepare();
+
+	m_bfCache.Reset(m_dwMaxCacheSize);
+	m_lsFreeBuffer.Reset(m_dwBufferPoolSize);
+}
+
+void CBufferPool::Clear()
+{
+	TBufferCache::IndexSet& indexes = m_bfCache.Indexes();
+
+	for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
+	{
+		TBuffer* pBuffer = FindCacheBuffer(*it);
+		if(pBuffer) TBuffer::Destruct(pBuffer);
+	}
+
+	m_bfCache.Reset();
+
+	m_lsFreeBuffer.Clear();
+
+	ReleaseGCBuffer(TRUE);
+	VERIFY(m_lsGCBuffer.IsEmpty());
+
+	m_itPool.Clear();
+	m_heap.Reset();
+}

+ 869 - 0
common/BufferPool.h

@@ -0,0 +1,869 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "Singleton.h"
+#include "STLHelper.h"
+#include "RingBuffer.h"
+#include "PrivateHeap.h"
+#include "CriSec.h"
+
+template<class T> T* ConstructItemT(T*, CPrivateHeap& heap, int capacity, BYTE* pData, int length)
+{
+	ASSERT(capacity > 0);
+
+	int item_size	= sizeof(T);
+	T* pItem		= (T*)heap.Alloc(item_size + capacity);
+	BYTE* pHead		= (BYTE*)pItem + item_size;
+
+	return ::ConstructObject(pItem, heap, pHead, capacity, pData, length);
+}
+
+template<class T> void DestructItemT(T* pItem)
+{
+	ASSERT(pItem != nullptr);
+
+	CPrivateHeap& heap = pItem->GetPrivateHeap();
+
+	::DestructObject(pItem);
+	heap.Free(pItem);
+}
+
+struct TItem
+{
+	template<typename T> friend struct	TSimpleList;
+	template<typename T> friend class	CNodePoolT;
+	template<typename T> friend struct	TItemListT;
+
+	friend struct						TBuffer;
+
+public:
+	int Cat		(const BYTE* pData, int length);
+	int Cat		(const TItem& other);
+	int Fetch	(BYTE* pData, int length);
+	int Peek	(BYTE* pData, int length);
+	int Increase(int length);
+	int Reduce	(int length);
+	void Reset	(int first = 0, int last = 0);
+
+	BYTE*		Ptr		()			{return begin;}
+	const BYTE*	Ptr		()	const	{return begin;}
+	int			Size	()	const	{return (int)(end - begin);}
+	int			Remain	()	const	{return capacity - (int)(end - head);}
+	int			Capacity()	const	{return capacity;}
+	bool		IsEmpty	()	const	{return Size()	 == 0;}
+	bool		IsFull	()	const	{return Remain() == 0;}
+	CPrivateHeap& GetPrivateHeap()	{return heap;}
+
+	operator		BYTE* ()		{return Ptr();}
+	operator const	BYTE* () const	{return Ptr();}
+
+public:
+	static TItem* Construct(CPrivateHeap& heap,
+							int		capacity	= DEFAULT_ITEM_CAPACITY,
+							BYTE*	pData		= nullptr,
+							int		length		= 0)
+	{
+		return ::ConstructItemT((TItem*)(nullptr), heap, capacity, pData, length);
+	}
+
+	static void Destruct(TItem* pItem)
+	{
+		::DestructItemT(pItem);
+	}
+
+	TItem(CPrivateHeap& hp, BYTE* pHead, int cap = DEFAULT_ITEM_CAPACITY, BYTE* pData = nullptr, int length = 0)
+	: heap(hp), head(pHead), begin(pHead), end(pHead), capacity(cap), next(nullptr), last(nullptr)
+	{
+		if(pData != nullptr && length != 0)
+			Cat(pData, length);
+	}
+
+	~TItem() {}
+
+	DECLARE_NO_COPY_CLASS(TItem)
+
+public:
+	static const DWORD DEFAULT_ITEM_CAPACITY;
+
+private:
+	CPrivateHeap& heap;
+
+private:
+	TItem* next;
+	TItem* last;
+
+	int		capacity;
+	BYTE*	head;
+	BYTE*	begin;
+	BYTE*	end;
+};
+
+template<class T> struct TSimpleList
+{
+public:
+	T* PushFront(T* pItem)
+	{
+		if(pFront != nullptr)
+		{
+			pFront->last = pItem;
+			pItem->next	 = pFront;
+		}
+		else
+		{
+			pItem->last = nullptr;
+			pItem->next = nullptr;
+			pBack		= pItem;
+		}
+
+		pFront = pItem;
+		++size;
+
+		return pItem;
+	}
+
+	T* PushBack(T* pItem)
+	{
+		if(pBack != nullptr)
+		{
+			pBack->next	= pItem;
+			pItem->last	= pBack;
+		}
+		else
+		{
+			pItem->last = nullptr;
+			pItem->next = nullptr;
+			pFront		= pItem;
+		}
+
+		pBack = pItem;
+		++size;
+
+		return pItem;
+	}
+
+	T* PopFront()
+	{
+		T* pItem = pFront;
+
+		if(pFront != pBack)
+		{
+			pFront = (T*)pFront->next;
+			pFront->last = nullptr;
+		}
+		else if(pFront != nullptr)
+		{
+			pFront	= nullptr;
+			pBack	= nullptr;
+		}
+
+		if(pItem != nullptr)
+		{
+			pItem->next = nullptr;
+			pItem->last = nullptr;
+
+			--size;
+		}
+
+		return pItem;
+	}
+
+	T* PopBack()
+	{
+		T* pItem = pBack;
+
+		if(pFront != pBack)
+		{
+			pBack = (T*)pBack->last;
+			pBack->next	= nullptr;
+		}
+		else if(pBack != nullptr)
+		{
+			pFront	= nullptr;
+			pBack	= nullptr;
+		}
+
+		if(pItem != nullptr)
+		{
+			pItem->next = nullptr;
+			pItem->last = nullptr;
+
+			--size;
+		}
+
+		return pItem;
+	}
+
+	TSimpleList<T>& Shift(TSimpleList<T>& other)
+	{
+		if(&other != this && other.size > 0)
+		{
+			if(size > 0)
+			{
+				pBack->next = other.pFront;
+				other.pFront->last = pBack;
+			}
+			else
+			{
+				pFront = other.pFront;
+			}
+
+			pBack	 = other.pBack;
+			size	+= other.size;
+
+			other.Reset();
+		}
+
+		return *this;
+	}
+
+	void Clear()
+	{
+		if(size > 0)
+		{
+			T* pItem;
+			while((pItem = PopFront()) != nullptr)
+				T::Destruct(pItem);
+		}
+	}
+
+	T*		Front	()	const	{return pFront;}
+	T*		Back	()	const	{return pBack;}
+	int		Size	()	const	{return size;}
+	bool	IsEmpty	()	const	{return size == 0;}
+
+public:
+	TSimpleList()	{Reset();}
+	~TSimpleList()	{Clear();}
+
+	DECLARE_NO_COPY_CLASS(TSimpleList<T>)
+
+private:
+	void Reset()
+	{
+		pFront	= nullptr;
+		pBack	= nullptr;
+		size	= 0;
+	}
+
+private:
+	int	size;
+	T*	pFront;
+	T*	pBack;
+};
+
+template<class T> class CNodePoolT
+{
+public:
+	void PutFreeItem(T* pItem)
+	{
+		ASSERT(pItem != nullptr);
+
+		if(!m_lsFreeItem.TryPut(pItem))
+			T::Destruct(pItem);
+	}
+
+	void PutFreeItem(TSimpleList<T>& lsItem)
+	{
+		if(lsItem.IsEmpty())
+			return;
+
+		T* pItem;
+		while((pItem = lsItem.PopFront()) != nullptr)
+			PutFreeItem(pItem);
+	}
+
+	T* PickFreeItem()
+	{
+		T* pItem = nullptr;
+
+		if(!m_lsFreeItem.TryGet(&pItem))
+			pItem = T::Construct(m_heap, m_dwItemCapacity);
+
+		ASSERT(pItem);
+		pItem->Reset();
+		
+		return pItem;
+	}
+
+	void Prepare()
+	{
+		m_lsFreeItem.Reset(m_dwPoolSize);
+	}
+
+	void Clear()
+	{
+		m_lsFreeItem.Clear();
+
+		m_heap.Reset();
+	}
+
+public:
+	void SetItemCapacity(DWORD dwItemCapacity)	{m_dwItemCapacity	= dwItemCapacity;}
+	void SetPoolSize	(DWORD dwPoolSize)		{m_dwPoolSize		= dwPoolSize;}
+	void SetPoolHold	(DWORD dwPoolHold)		{m_dwPoolHold		= dwPoolHold;}
+	DWORD GetItemCapacity	()					{return m_dwItemCapacity;}
+	DWORD GetPoolSize		()					{return m_dwPoolSize;}
+	DWORD GetPoolHold		()					{return m_dwPoolHold;}
+
+	CPrivateHeap& GetPrivateHeap()				{return m_heap;}
+
+public:
+	CNodePoolT(	DWORD dwPoolSize	 = DEFAULT_POOL_SIZE,
+				DWORD dwPoolHold	 = DEFAULT_POOL_HOLD,
+				DWORD dwItemCapacity = DEFAULT_ITEM_CAPACITY)
+				: m_dwPoolSize(dwPoolSize)
+				, m_dwPoolHold(dwPoolHold)
+				, m_dwItemCapacity(dwItemCapacity)
+	{
+	}
+
+	~CNodePoolT()	{Clear();}
+
+	DECLARE_NO_COPY_CLASS(CNodePoolT)
+
+public:
+	static const DWORD DEFAULT_ITEM_CAPACITY;
+	static const DWORD DEFAULT_POOL_SIZE;
+	static const DWORD DEFAULT_POOL_HOLD;
+
+private:
+	CPrivateHeap	m_heap;
+
+	DWORD			m_dwItemCapacity;
+	DWORD			m_dwPoolSize;
+	DWORD			m_dwPoolHold;
+
+	CRingPool<T>	m_lsFreeItem;
+};
+
+template<class T> const DWORD CNodePoolT<T>::DEFAULT_ITEM_CAPACITY	= TItem::DEFAULT_ITEM_CAPACITY;
+template<class T> const DWORD CNodePoolT<T>::DEFAULT_POOL_SIZE		= DEFAULT_BUFFER_CACHE_POOL_SIZE;
+template<class T> const DWORD CNodePoolT<T>::DEFAULT_POOL_HOLD		= DEFAULT_BUFFER_CACHE_POOL_HOLD;
+
+using CItemPool = CNodePoolT<TItem>;
+
+template<class T> struct TItemListT : public TSimpleList<T>
+{
+	using __super = TSimpleList<T>;
+
+public:
+	int PushTail(const BYTE* pData, int length)
+	{
+		ASSERT(length <= (int)itPool.GetItemCapacity());
+
+		if(length > (int)itPool.GetItemCapacity())
+			return 0;
+
+		T* pItem = __super::PushBack(itPool.PickFreeItem());
+		return pItem->Cat(pData, length);
+	}
+
+	int Cat(const BYTE* pData, int length)
+	{
+		int remain = length;
+
+		while(remain > 0)
+		{
+			T* pItem = __super::Back();
+
+			if(pItem == nullptr || pItem->IsFull())
+				pItem = __super::PushBack(itPool.PickFreeItem());
+
+			int cat  = pItem->Cat(pData, remain);
+
+			pData	+= cat;
+			remain	-= cat;
+		}
+
+		return length;
+	}
+
+	int Cat(const T* pItem)
+	{
+		return Cat(pItem->Ptr(), pItem->Size());
+	}
+
+	int Cat(const TItemListT<T>& other)
+	{
+		ASSERT(this != &other);
+
+		int length = 0;
+
+		for(T* pItem = other.Front(); pItem != nullptr; pItem = pItem->next)
+			length += Cat(pItem);
+
+		return length;
+	}
+
+	int Fetch(BYTE* pData, int length)
+	{
+		int remain = length;
+
+		while(remain > 0 && __super::Size() > 0)
+		{
+			T* pItem  = __super::Front();
+			int fetch = pItem->Fetch(pData, remain);
+
+			pData	+= fetch;
+			remain	-= fetch;
+
+			if(pItem->IsEmpty())
+				itPool.PutFreeItem(__super::PopFront());
+		}
+
+		return length - remain;
+	}
+
+	int Peek(BYTE* pData, int length)
+	{
+		int remain	= length;
+		T* pItem	= __super::Front();
+
+		while(remain > 0 && pItem != nullptr)
+		{
+			int peek = pItem->Peek(pData, remain);
+
+			pData	+= peek;
+			remain	-= peek;
+			pItem	 = pItem->next;
+		}
+
+		return length - remain;
+	}
+
+	int Increase(int length)
+	{
+		int remain = length;
+
+		while(remain > 0)
+		{
+			T* pItem = __super::Back();
+
+			if(pItem == nullptr || pItem->IsFull())
+			{
+				pItem = itPool.PickFreeItem();
+				__super::PushBack(pItem);
+			}
+
+			remain -= pItem->Increase(remain);
+		}
+
+		return length - remain;
+	}
+
+	int Reduce(int length)
+	{
+		int remain = length;
+
+		while(remain > 0 && __super::Size() > 0)
+		{
+			T* pItem = __super::Front();
+			remain  -= pItem->Reduce(remain);
+
+			if(pItem->IsEmpty())
+				itPool.PutFreeItem(__super::PopFront());
+		}
+
+		return length - remain;
+	}
+
+	void Release()
+	{
+		itPool.PutFreeItem(*this);
+	}
+
+	CNodePoolT<T>& GetItemPool() {return itPool;}
+
+public:
+	TItemListT(CNodePoolT<T>& pool) : itPool(pool)
+	{
+	}
+
+private:
+	CNodePoolT<T>& itPool;
+};
+
+using TItemList = TItemListT<TItem>;
+
+template<class T, class length_t = int, typename = enable_if_t<is_integral<typename decay<length_t>::type>::value>>
+struct TItemListExT : public TItemListT<T>
+{
+	using __super = TItemListT<T>;
+
+public:
+	T* PushFront(T* pItem)
+	{
+		length += pItem->Size();
+		return __super::PushFront(pItem);
+	}
+
+	T* PushBack(T* pItem)
+	{
+		length += pItem->Size();
+		return __super::PushBack(pItem);
+	}
+
+	T* PopFront()
+	{
+		T* pItem = __super::PopFront();
+
+		if(pItem != nullptr)
+			length -= pItem->Size();
+
+		return pItem;
+	}
+
+	T* PopBack()
+	{
+		T* pItem = __super::PopBack();
+
+		if(pItem != nullptr)
+			length -= pItem->Size();
+
+		return pItem;
+	}
+
+	TItemListExT& Shift(TItemListExT<T>& other)
+	{
+		if(&other != this && other.length > 0)
+		{
+			length += other.length;
+			other.length = 0;
+
+			__super::Shift(other);
+		}
+
+		return *this;
+	}
+
+	void Clear()
+	{
+		__super::Clear();
+		length = 0;
+	}
+
+	void Release()
+	{
+		__super::Release();
+		length = 0;
+	}
+
+public:
+	int PushTail(const BYTE* pData, int length)
+	{
+		int cat = __super::PushTail(pData, length);
+		this->length += cat;
+
+		return cat;
+	}
+
+	int Cat(const BYTE* pData, int length)
+	{
+		int cat = __super::Cat(pData, length);
+		this->length += cat;
+
+		return cat;
+	}
+
+	int Cat(const T* pItem)
+	{
+		int cat = __super::Cat(pItem->Ptr(), pItem->Size());
+		this->length += cat;
+
+		return cat;
+	}
+
+	int Cat(const TItemListT<T>& other)
+	{
+		int cat = __super::Cat(other);
+		this->length += cat;
+
+		return cat;
+	}
+
+	int Fetch(BYTE* pData, int length)
+	{
+		int fetch	  = __super::Fetch(pData, length);
+		this->length -= fetch;
+
+		return fetch;
+	}
+
+	int Increase(int length)
+	{
+		int increase  = __super::Increase(length);
+		this->length += increase;
+
+		return increase;
+	}
+
+	int Reduce(int length)
+	{
+		int reduce = __super::Reduce(length);
+		this->length -= reduce;
+
+		return reduce;
+	}
+
+	typename decay<length_t>::type Length() const {return length;}
+
+	int IncreaseLength	(int length) {return (this->length += length);}
+	int ReduceLength	(int length) {return (this->length -= length);}
+
+public:
+	TItemListExT(CNodePoolT<T>& pool) : TItemListT<T>(pool), length(0)
+	{
+	}
+
+	~TItemListExT()
+	{
+		ASSERT(length >= 0);
+	}
+
+	DECLARE_NO_COPY_CLASS(TItemListExT)
+
+private:
+	length_t length;
+};
+
+using TItemListEx	= TItemListExT<TItem>;
+using TItemListExV	= TItemListExT<TItem, volatile int>;
+
+template<class T> struct TItemPtrT
+{
+public:
+	T* Reset(T* pItem = nullptr)
+	{
+		if(m_pItem != nullptr)
+			itPool.PutFreeItem(m_pItem);
+
+		m_pItem = pItem;
+
+		return m_pItem;
+	}
+
+	T* Attach(T* pItem)
+	{
+		return Reset(pItem);
+	}
+
+	T* Detach()
+	{
+		T* pItem = m_pItem;
+		m_pItem	 = nullptr;
+
+		return pItem;
+	}
+
+	T* New()
+	{
+		return Attach(itPool.PickFreeItem());
+	}
+
+	bool IsValid		()			{return m_pItem != nullptr;}
+	T* operator ->		()			{return m_pItem;}
+	T* operator =		(T* pItem)	{return Reset(pItem);}
+	operator T*			()			{return m_pItem;}
+	T*& PtrRef			()			{return m_pItem;}
+	T* Ptr				()			{return m_pItem;}
+	const T* Ptr		()	const	{return m_pItem;}
+	operator const T*	()	const	{return m_pItem;}
+
+public:
+	TItemPtrT(CNodePoolT<T>& pool, T* pItem = nullptr)
+	: itPool(pool), m_pItem(pItem)
+	{
+
+	}
+
+	TItemPtrT(TItemListT<T>& ls, T* pItem = nullptr)
+	: itPool(ls.GetItemPool()), m_pItem(pItem)
+	{
+
+	}
+
+	~TItemPtrT()
+	{
+		Reset();
+	}
+
+	DECLARE_NO_COPY_CLASS(TItemPtrT)
+
+private:
+	CNodePoolT<T>&	itPool;
+	T*				m_pItem;
+};
+
+using TItemPtr = TItemPtrT<TItem>;
+
+class CBufferPool;
+
+struct TBuffer
+{
+	template<typename T> friend struct	TSimpleList;
+	friend class						CBufferPool;
+
+public:
+	static TBuffer* Construct(CBufferPool& pool, ULONG_PTR dwID);
+	static void Destruct(TBuffer* pBuffer);
+
+public:
+	int Cat		(const BYTE* pData, int len);
+	int Cat		(const TItem* pItem);
+	int Cat		(const TItemList& other);
+	int Fetch	(BYTE* pData, int length);
+	int Peek	(BYTE* pData, int length);
+	int Reduce	(int len);
+
+public:
+	CCriSec&	CriSec	()	{return cs;}
+	TItemList&	ItemList()	{return items;}
+
+	ULONG_PTR ID		()	const	{return id;}
+	int Length			()	const	{return length;}
+	bool IsValid		()	const	{return id != 0;}
+
+	DWORD GetFreeTime	()	const	{return freeTime;}
+	int GetCount		()	const	{return 0;}
+
+private:
+	int IncreaseLength	(int len)	{return (length += len);}
+	int DecreaseLength	(int len)	{return (length -= len);}
+
+	void Reset			();
+
+private:
+	friend TBuffer* ConstructObject<>(TBuffer*, CPrivateHeap&, CItemPool&, ULONG_PTR&);
+	friend void DestructObject<>(TBuffer*);
+
+	TBuffer(CPrivateHeap& hp, CItemPool& itPool, ULONG_PTR dwID = 0)
+	: heap(hp), items(itPool), id(dwID), length(0)
+	{
+	}
+
+	~TBuffer()	{}
+
+	DECLARE_NO_COPY_CLASS(TBuffer)
+
+private:
+	CPrivateHeap&	heap;
+
+private:
+	ULONG_PTR		id;
+	int				length;
+	DWORD			freeTime;
+
+private:
+	TBuffer*		next;
+	TBuffer*		last;
+
+	CCriSec			cs;
+	TItemList		items;
+};
+
+class CBufferPool
+{
+	using TBufferList	= CRingPool<TBuffer>;
+	using TBufferQueue	= CCASQueue<TBuffer>;
+	using TBufferCache	= CRingCache<TBuffer, ULONG_PTR, true>;
+
+public:
+	void		PutFreeBuffer	(ULONG_PTR dwID);
+	TBuffer*	PutCacheBuffer	(ULONG_PTR dwID);
+	TBuffer*	FindCacheBuffer	(ULONG_PTR dwID);
+	TBuffer*	PickFreeBuffer	(ULONG_PTR dwID);
+	void		PutFreeBuffer	(TBuffer* pBuffer);
+
+	void		Prepare			();
+	void		Clear			();
+
+	void ReleaseGCBuffer	(BOOL bForce = FALSE);
+
+public:
+	void SetItemCapacity	(DWORD dwItemCapacity)		{m_itPool.SetItemCapacity(dwItemCapacity);}
+	void SetItemPoolSize	(DWORD dwItemPoolSize)		{m_itPool.SetPoolSize(dwItemPoolSize);}
+	void SetItemPoolHold	(DWORD dwItemPoolHold)		{m_itPool.SetPoolHold(dwItemPoolHold);}
+
+	void SetMaxCacheSize	(DWORD dwMaxCacheSize)		{m_dwMaxCacheSize	= dwMaxCacheSize;}
+	void SetBufferLockTime	(DWORD dwBufferLockTime)	{m_dwBufferLockTime	= dwBufferLockTime;}
+	void SetBufferPoolSize	(DWORD dwBufferPoolSize)	{m_dwBufferPoolSize	= dwBufferPoolSize;}
+	void SetBufferPoolHold	(DWORD dwBufferPoolHold)	{m_dwBufferPoolHold	= dwBufferPoolHold;}
+
+	DWORD GetItemCapacity	()							{return m_itPool.GetItemCapacity();}
+	DWORD GetItemPoolSize	()							{return m_itPool.GetPoolSize();}
+	DWORD GetItemPoolHold	()							{return m_itPool.GetPoolHold();}
+
+	DWORD GetMaxCacheSize	()							{return m_dwMaxCacheSize;}
+	DWORD GetBufferLockTime	()							{return m_dwBufferLockTime;}
+	DWORD GetBufferPoolSize	()							{return m_dwBufferPoolSize;}
+	DWORD GetBufferPoolHold	()							{return m_dwBufferPoolHold;}
+
+	TBuffer* operator []	(ULONG_PTR dwID)			{return FindCacheBuffer(dwID);}
+
+public:
+	CBufferPool(DWORD dwPoolSize	 = DEFAULT_BUFFER_POOL_SIZE,
+				DWORD dwPoolHold	 = DEFAULT_BUFFER_POOL_HOLD,
+				DWORD dwLockTime	 = DEFAULT_BUFFER_LOCK_TIME,
+				DWORD dwMaxCacheSize = DEFAULT_MAX_CACHE_SIZE)
+	: m_dwBufferPoolSize(dwPoolSize)
+	, m_dwBufferPoolHold(dwPoolHold)
+	, m_dwBufferLockTime(dwLockTime)
+	, m_dwMaxCacheSize(dwMaxCacheSize)
+	{
+
+	}
+
+	~CBufferPool()	{Clear();}
+
+	DECLARE_NO_COPY_CLASS(CBufferPool)
+
+public:
+	CPrivateHeap&	GetPrivateHeap()	{return m_heap;}
+	CItemPool&		GetItemPool()		{return m_itPool;}
+
+public:
+	static const DWORD DEFAULT_MAX_CACHE_SIZE;
+	static const DWORD DEFAULT_ITEM_CAPACITY;
+	static const DWORD DEFAULT_ITEM_POOL_SIZE;
+	static const DWORD DEFAULT_ITEM_POOL_HOLD;
+	static const DWORD DEFAULT_BUFFER_LOCK_TIME;
+	static const DWORD DEFAULT_BUFFER_POOL_SIZE;
+	static const DWORD DEFAULT_BUFFER_POOL_HOLD;
+
+private:
+	DWORD			m_dwMaxCacheSize;
+	DWORD			m_dwBufferLockTime;
+	DWORD			m_dwBufferPoolSize;
+	DWORD			m_dwBufferPoolHold;
+
+	CPrivateHeap	m_heap;
+	CItemPool		m_itPool;
+
+	TBufferCache	m_bfCache;
+
+	TBufferList		m_lsFreeBuffer;
+	TBufferQueue	m_lsGCBuffer;
+};

+ 198 - 0
common/BufferPtr.h

@@ -0,0 +1,198 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+
+#include <memory.h>
+#include <malloc.h>
+
+template<class T, size_t MAX_CACHE_SIZE = 0>
+class CBufferPtrT
+{
+public:
+	explicit CBufferPtrT(size_t size = 0, bool zero = false)		{Reset(); Malloc(size, zero);}
+	explicit CBufferPtrT(const T* pch, size_t size)					{Reset(); Copy(pch, size);}
+	CBufferPtrT(const CBufferPtrT& other)							{Reset(); Copy(other);}
+	template<size_t S> CBufferPtrT(const CBufferPtrT<T, S>& other)	{Reset(); Copy(other);}
+
+	~CBufferPtrT() {Free();}
+
+	T* Malloc(size_t size = 1, bool zero = false)
+	{
+		Free();
+		return Alloc(size, zero, false);
+	}
+
+	T* Realloc(size_t size, bool zero = false)
+	{
+		return Alloc(size, zero, true);
+	}
+
+	void Free()
+	{
+		if(m_pch)
+		{
+			free(m_pch);
+			Reset();
+		}
+	}
+
+	template<size_t S> CBufferPtrT& Copy(const CBufferPtrT<T, S>& other)
+	{
+		if((void*)&other != (void*)this)
+			Copy(other.Ptr(), other.Size());
+
+		return *this;
+	}
+
+	CBufferPtrT& Copy(const T* pch, size_t size)
+	{
+		Malloc(size);
+
+		if(m_pch)
+			memcpy(m_pch, pch, size * sizeof(T));
+
+		return *this;
+	}
+
+	template<size_t S> CBufferPtrT& Cat(const CBufferPtrT<T, S>& other)
+	{
+		if((void*)&other != (void*)this)
+			Cat(other.Ptr(), other.Size());
+
+		return *this;
+	}
+
+	CBufferPtrT& Cat(const T* pch, size_t size = 1)
+	{
+		size_t pre_size = m_size;
+		Realloc(m_size + size);
+
+		if(m_pch)
+			memcpy(m_pch + pre_size, pch, size * sizeof(T));
+
+		return *this;
+	}
+
+	template<size_t S> bool Equal(const CBufferPtrT<T, S>& other) const
+	{
+		if((void*)&other == (void*)this)
+			return true;
+		else if(m_size != other.Size())
+			return false;
+		else if(m_size == 0)
+			return true;
+		else
+			return (memcmp(m_pch, other.Ptr(), m_size * sizeof(T)) == 0);
+	}
+
+	bool Equal(T* pch) const
+	{
+		if(m_pch == pch)
+			return true;
+		else if(!m_pch || !pch)
+			return false;
+		else
+			return (memcmp(m_pch, pch, m_size * sizeof(T)) == 0);
+	}
+
+	size_t SetSize(size_t size)
+	{
+		if(size < 0 || size > m_capacity)
+			size = m_capacity;
+
+		return (m_size = size);
+	}
+
+	T*			Ptr()					{return m_pch;}
+	const T*	Ptr()			const	{return m_pch;}
+	T&			Get(int i)				{return *(m_pch + i);}
+	const T&	Get(int i)		const	{return *(m_pch + i);}
+	size_t		Size()			const	{return m_size;}
+	size_t		Capacity()		const	{return m_capacity;}
+	bool		IsValid()		const	{return m_pch != 0;}
+
+	operator							T*	()									{return Ptr();}
+	operator const						T*	()			const					{return Ptr();}
+	T& operator							[]	(int i)								{return Get(i);}
+	const T& operator					[]	(int i)		const					{return Get(i);}
+	bool operator						==	(T* pv)		const					{return Equal(pv);}
+	template<size_t S> bool operator	==	(const CBufferPtrT<T, S>& other)	{return Equal(other);}
+	CBufferPtrT& operator				=	(const CBufferPtrT& other)			{return Copy(other);}
+	template<size_t S> CBufferPtrT& operator = (const CBufferPtrT<T, S>& other)	{return Copy(other);}
+
+private:
+	void Reset()						{m_pch = 0; m_size = 0; m_capacity = 0;}
+	size_t GetAllocSize(size_t size)	{return MAX(size, MIN(size * 2, m_size + MAX_CACHE_SIZE));}
+
+	T* Alloc(size_t size, bool zero = false, bool is_realloc = false)
+	{
+		if(size != m_size)
+		{
+			size_t rsize = GetAllocSize(size);
+			if(size > m_capacity || rsize < m_size)
+			{
+				T* pch = is_realloc							?
+					(T*)realloc(m_pch, rsize * sizeof(T))	:
+					(T*)malloc(rsize * sizeof(T))			;
+
+				if(pch || rsize == 0)
+				{
+					m_pch		= pch;
+					m_size		= size;
+					m_capacity	= rsize;
+				}
+				else
+				{
+					Free();
+					throw std::bad_alloc();
+				}
+			}
+			else
+				m_size = size;
+		}
+
+		if(zero && m_pch)
+			memset(m_pch, 0, m_size * sizeof(T));
+
+		return m_pch;
+	}
+
+private:
+	T*		m_pch;
+	size_t	m_size;
+	size_t	m_capacity;
+};
+
+typedef CBufferPtrT<char>			CCharBufferPtr;
+typedef CBufferPtrT<wchar_t>		CWCharBufferPtr;
+typedef CBufferPtrT<unsigned char>	CByteBufferPtr;
+typedef CByteBufferPtr				CBufferPtr;
+
+#ifdef _UNICODE
+	typedef CWCharBufferPtr			CTCharBufferPtr;
+#else
+	typedef CCharBufferPtr			CTCharBufferPtr;
+#endif

+ 291 - 0
common/CriSec.h

@@ -0,0 +1,291 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "Singleton.h"
+#include "FuncHelper.h"
+
+#include <mutex>
+#include <atomic>
+
+using namespace std;
+
+class CSpinGuard
+{
+public:
+	CSpinGuard() : m_atFlag(FALSE)
+	{
+
+	}
+
+	~CSpinGuard()
+	{
+		ASSERT(!m_atFlag);
+	}
+
+	void Lock(BOOL bWeek = TRUE, memory_order m = memory_order_acquire)
+	{
+		for(UINT i = 0; !TryLock(bWeek, m); ++i)
+			YieldThread(i);
+	}
+
+	BOOL TryLock(BOOL bWeek = FALSE, memory_order m = memory_order_acquire)
+	{
+		BOOL bExpect = FALSE;
+
+		return bWeek
+			? m_atFlag.compare_exchange_weak(bExpect, TRUE, m)
+			: m_atFlag.compare_exchange_strong(bExpect, TRUE, m);
+	}
+
+	void Unlock(memory_order m = memory_order_release)
+	{
+		ASSERT(m_atFlag);
+		m_atFlag.store(FALSE, m);
+	}
+
+	DECLARE_NO_COPY_CLASS(CSpinGuard)
+
+private:
+	atomic<BOOL>	m_atFlag;
+};
+
+class CReentrantSpinGuard
+{
+public:
+	CReentrantSpinGuard()
+	: m_atThreadID	(0)
+	, m_iCount		(0)
+	{
+
+	}
+
+	~CReentrantSpinGuard()
+	{
+		ASSERT(m_atThreadID	== 0);
+		ASSERT(m_iCount		== 0);
+	}
+
+	void Lock(BOOL bWeek = TRUE, memory_order m = memory_order_acquire)
+	{
+		for(UINT i = 0; !_TryLock(i == 0, bWeek, m); ++i)
+			YieldThread(i);
+	}
+
+	BOOL TryLock(BOOL bWeek = FALSE, memory_order m = memory_order_acquire)
+	{
+		return _TryLock(TRUE, bWeek, m);
+	}
+
+	void Unlock(memory_order m = memory_order_release)
+	{
+		ASSERT(::IsSelfThread(m_atThreadID));
+
+		if((--m_iCount) == 0)
+			m_atThreadID.store(0, m);
+	}
+
+private:
+	BOOL _TryLock(BOOL bFirst, BOOL bWeek = FALSE, memory_order m = memory_order_acquire)
+	{
+		THR_ID dwCurrentThreadID = SELF_THREAD_ID;
+
+		if(bFirst && ::IsSameThread(m_atThreadID, dwCurrentThreadID))
+		{
+			++m_iCount;
+			return TRUE;
+		}
+
+		THR_ID ulExpect = 0;
+
+		BOOL isOK = bWeek
+			? m_atThreadID.compare_exchange_weak(ulExpect, dwCurrentThreadID, m)
+			: m_atThreadID.compare_exchange_strong(ulExpect, dwCurrentThreadID, m);
+
+
+		if(isOK)
+		{
+			ASSERT(m_iCount == 0);
+
+			m_iCount = 1;
+
+			return TRUE;
+		}
+
+		return FALSE;
+	}
+
+	DECLARE_NO_COPY_CLASS(CReentrantSpinGuard)
+
+private:
+	atomic_tid	m_atThreadID;
+	int			m_iCount;
+};
+
+class CFakeGuard
+{
+public:
+	void Lock()		{}
+	void Unlock()	{}
+	BOOL TryLock()	{return TRUE;}
+};
+
+template<class CLockObj> class CLocalLock
+{
+public:
+	CLocalLock(CLockObj& obj) : m_lock(obj) {m_lock.Lock();}
+	~CLocalLock() {m_lock.Unlock();}
+private:
+	CLockObj& m_lock;
+};
+
+template<class CLockObj> class CLocalTryLock
+{
+public:
+	CLocalTryLock(CLockObj& obj) : m_lock(obj) {m_bValid = m_lock.TryLock();}
+	~CLocalTryLock() {if(m_bValid) m_lock.Unlock();}
+
+	BOOL IsValid() {return m_bValid;}
+
+private:
+	CLockObj&	m_lock;
+	BOOL		m_bValid;
+};
+
+template<class CMTXObj> class CMTXTryLock
+{
+public:
+	CMTXTryLock(CMTXObj& obj) : m_lock(obj) {m_bValid = m_lock.try_lock();}
+	~CMTXTryLock() {if(m_bValid) m_lock.unlock();}
+
+	BOOL IsValid() {return m_bValid;}
+
+private:
+	CMTXObj&	m_lock;
+	BOOL		m_bValid;
+};
+
+using CSpinLock					= CLocalLock<CSpinGuard>;
+using CReentrantSpinLock		= CLocalLock<CReentrantSpinGuard>;
+using CFakeLock					= CLocalLock<CFakeGuard>;
+
+using CCriSec					= mutex;
+using CCriSecLock				= lock_guard<mutex>;
+using CCriSecLock2				= unique_lock<mutex>;
+using CCriSecTryLock			= CMTXTryLock<mutex>;
+
+using CMTX						= CCriSec;
+using CMutexLock				= CCriSecLock;
+using CMutexLock2				= CCriSecLock2;
+using CMutexTryLock				= CCriSecTryLock;
+
+using CReentrantCriSec			= recursive_mutex;
+using CReentrantCriSecLock		= lock_guard<recursive_mutex>;
+using CReentrantCriSecLock2		= unique_lock<recursive_mutex>;
+using CReentrantCriSecTryLock	= CMTXTryLock<recursive_mutex>;
+
+using CReentrantMTX				= CReentrantCriSec;
+using CReentrantMutexLock		= CReentrantCriSecLock;
+using CReentrantMutexLock2		= CReentrantCriSecLock2;
+using CReentrantMutexTryLock	= CReentrantCriSecTryLock;
+
+template<typename T, typename = enable_if_t<is_arithmetic<T>::value>> class CSafeCounterT
+{
+public:
+	T Increment()				{return ::InterlockedIncrement(&m_iCount);}
+	T Decrement()				{return ::InterlockedDecrement(&m_iCount);}
+	T AddFetch(T iCount)		{return ::InterlockedAdd(&m_iCount, iCount);}
+	T SubFetch(T iCount)		{return ::InterlockedSub(&m_iCount, iCount);}
+	T FetchAdd(T iCount)		{return ::InterlockedExchangeAdd(&m_iCount, iCount);}
+	T FetchSub(T iCount)		{return ::InterlockedExchangeSub(&m_iCount, iCount);}
+
+	T SetCount(T iCount)		{return (m_iCount = iCount);}
+	T ResetCount()				{return SetCount(0);}
+	T GetCount()				{return m_iCount;}
+
+	T operator ++ ()			{return Increment();}
+	T operator -- ()			{return Decrement();}
+	T operator ++ (int)			{return FetchAdd(1);}
+	T operator -- (int)			{return FetchSub(1);}
+	T operator += (T iCount)	{return AddFetch(iCount);}
+	T operator -= (T iCount)	{return SubFetch(iCount);}
+	T operator  = (T iCount)	{return SetCount(iCount);}
+	operator T	  ()			{return GetCount();}
+
+public:
+	CSafeCounterT(T iCount = 0) : m_iCount(iCount) {}
+
+protected:
+	volatile T m_iCount;
+};
+
+template<typename T, typename = enable_if_t<is_arithmetic<T>::value>> class CUnsafeCounterT
+{
+public:
+	T Increment()				{return ++m_iCount;}
+	T Decrement()				{return --m_iCount;}
+	T AddFetch(T iCount)		{return m_iCount += iCount;}
+	T SubFetch(T iCount)		{return m_iCount -= iCount;}
+	T FetchAdd(T iCount)		{T rs = m_iCount; m_iCount += iCount; return rs;}
+	T FetchSub(T iCount)		{T rs = m_iCount; m_iCount -= iCount; return rs;}
+
+	T SetCount(T iCount)		{return (m_iCount = iCount);}
+	T ResetCount()				{return SetCount(0);}
+	T GetCount()				{return m_iCount;}
+
+	T operator ++ ()			{return Increment();}
+	T operator -- ()			{return Decrement();}
+	T operator ++ (int)			{return FetchAdd(1);}
+	T operator -- (int)			{return FetchSub(1);}
+	T operator += (T iCount)	{return AddFetch(iCount);}
+	T operator -= (T iCount)	{return SubFetch(iCount);}
+	T operator  = (T iCount)	{return SetCount(iCount);}
+	operator T	  ()			{return GetCount();}
+
+public:
+	CUnsafeCounterT(T iCount = 0) : m_iCount(iCount) {}
+
+protected:
+	T m_iCount;
+};
+
+template<class CCounter> class CLocalCounter
+{
+public:
+	CLocalCounter(CCounter& obj) : m_counter(obj) {m_counter.Increment();}
+	~CLocalCounter() {m_counter.Decrement();}
+private:
+	CCounter& m_counter;
+};
+
+using CSafeCounter				= CSafeCounterT<INT>;
+using CSafeBigCounter			= CSafeCounterT<LONGLONG>;
+using CUnsafeCounter			= CUnsafeCounterT<INT>;
+using CUnsafeBigCounter			= CUnsafeCounterT<LONGLONG>;
+
+using CLocalSafeCounter			= CLocalCounter<CSafeCounter>;
+using CLocalSafeBigCounter		= CLocalCounter<CSafeBigCounter>;
+using CLocalUnsafeCounter		= CLocalCounter<CUnsafeCounter>;
+using CLocalUnsafeBigCounter	= CLocalCounter<CUnsafeBigCounter>;

+ 24 - 0
common/Event.cpp

@@ -0,0 +1,24 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "Event.h"

+ 530 - 0
common/Event.h

@@ -0,0 +1,530 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "Singleton.h"
+#include "FuncHelper.h"
+#include "PollHelper.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/eventfd.h>
+#include <sys/timerfd.h>
+#include <sys/signalfd.h>
+
+class CPipeEvent
+{
+public:
+
+	enum
+	{
+		EVT_1		= 0x01,
+		EVT_WAKEUP	= EVT_1,
+		EVT_EXIT	= 0x7F,
+		EVT_SIG_0	= 0x80,
+		EVT_SIG_MAX	= EVT_SIG_0 + _NSIG,
+	};
+
+public:
+
+	int Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
+	{
+		pollfd pfd = {m_fd[0], POLLIN};
+
+		while(TRUE)
+		{
+			int rs = (int)::PollForSingleObject(pfd, lTimeout, pSigSet);
+
+			if(rs <= TIMEOUT) return rs;
+
+			if(pfd.revents & POLLIN)
+			{
+				BYTE v;
+
+				if(!Get(v))
+					return HAS_ERROR;
+
+				if(v == 0)
+					continue;
+
+				return (int)v;
+			}
+
+			if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
+			{
+				::SetLastError(ERROR_BROKEN_PIPE);
+				return HAS_ERROR;
+			}
+
+			ASSERT(FALSE);
+		}
+	}
+
+	BOOL Set(BYTE bVal = EVT_WAKEUP)
+	{
+		ASSERT_CHECK_EINVAL(bVal != 0);
+
+		return VERIFY(write(m_fd[1], &bVal, 1) > 0);
+	}
+
+	BOOL Get(BYTE& v)
+	{
+		ASSERT(IsValid());
+
+		int rs = (int)read(m_fd[0], &v, 1);
+
+		if(IS_HAS_ERROR(rs))
+		{
+			if(IS_WOULDBLOCK_ERROR())
+				v = 0;
+			else
+				return FALSE;
+		}
+		else if(rs == 0)
+		{
+			::SetLastError(ERROR_BROKEN_PIPE);
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	BOOL Reset()
+	{
+		BYTE v;
+
+		while(TRUE)
+		{
+			if(!Get(v))
+				return FALSE;
+
+			if(v == 0)
+				break;
+		}
+
+		return TRUE;
+	}
+
+	BOOL SetSignal(BYTE bSigVal)
+	{
+		ASSERT_CHECK_EINVAL(bSigVal > 0 && bSigVal < _NSIG);
+
+		return Set((BYTE)(EVT_SIG_0 + bSigVal));
+	}
+
+	static inline BYTE ToSignalValue(int iWaitResult)
+	{
+		if(iWaitResult <= EVT_SIG_0 || iWaitResult >= EVT_SIG_MAX)
+			return 0;
+
+		return (BYTE)(iWaitResult - EVT_SIG_0);
+	}
+
+	BOOL IsValid()	{return IS_VALID_FD(m_fd[0]) && IS_VALID_FD(m_fd[1]);}
+
+	operator FD	()	{return m_fd[0];}
+	FD GetFD	()	{return m_fd[0];}
+
+public:
+	CPipeEvent()
+	{
+		VERIFY_IS_NO_ERROR(pipe2(m_fd, O_NONBLOCK | O_CLOEXEC));
+		VERIFY(::fcntl_SETFL(m_fd[0], O_NOATIME));
+		VERIFY(::fcntl_SETFL(m_fd[1], O_NOATIME));
+	}
+
+	~CPipeEvent()
+	{
+		close(m_fd[1]);
+		close(m_fd[0]);
+	}
+
+	DECLARE_NO_COPY_CLASS(CPipeEvent)
+
+private:
+	FD m_fd[2] = {INVALID_FD, INVALID_FD};
+};
+
+template<bool is_sem_mode = false> class CCounterEvent
+{
+public:
+
+	eventfd_t Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
+	{
+		pollfd pfd = {m_evt, POLLIN};
+
+		while(TRUE)
+		{
+			long rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
+
+			if(rs <= TIMEOUT) return (eventfd_t)rs;
+
+			if(pfd.revents & POLLIN)
+			{
+				eventfd_t v;
+
+				if(!Get(v))
+					return HAS_ERROR;
+
+				if(v == 0)
+					continue;
+
+				return v;
+			}
+
+			if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
+			{
+				::SetLastError(ERROR_HANDLES_CLOSED);
+				return HAS_ERROR;
+			}
+
+			ASSERT(FALSE);
+		}
+	}
+
+	BOOL Set(eventfd_t val = 1)
+	{
+		ASSERT_CHECK_EINVAL(val > 0);
+
+		int rs = eventfd_write(m_evt, val);
+		return VERIFY_IS_NO_ERROR(rs);
+	}
+
+	BOOL Get(eventfd_t& v)
+	{
+		ASSERT(IsValid());
+
+		if(IS_HAS_ERROR(eventfd_read(m_evt, &v)))
+		{
+			if(IS_WOULDBLOCK_ERROR())
+				v = 0;
+			else
+				return FALSE;
+		}
+		
+		return TRUE;
+	}
+
+	BOOL Reset()
+	{
+		eventfd_t v;
+
+		while(TRUE)
+		{
+			if(!Get(v))
+				return FALSE;
+
+			if(v == 0)
+				break;
+		}
+
+		return TRUE;
+	}
+
+	BOOL IsValid()	{return IS_VALID_FD(m_evt);}
+
+	operator FD	()	{return m_evt;}
+	FD GetFD	()	{return m_evt;}
+
+public:
+	CCounterEvent(int iInitCount = 0)
+	{ 
+		int iFlag	= EFD_NONBLOCK | EFD_CLOEXEC | (is_sem_mode ? EFD_SEMAPHORE : 0);
+		m_evt		= eventfd(iInitCount, iFlag);
+
+		VERIFY(IsValid());
+	}
+
+	~CCounterEvent()
+	{
+		if(IsValid()) close(m_evt);
+	}
+
+	DECLARE_NO_COPY_CLASS(CCounterEvent)
+
+private:
+	FD m_evt = INVALID_FD;
+};
+
+using CSimpleEvent		= CCounterEvent<false>;
+using CSemaphoreEvent	= CCounterEvent<true>;
+using CEvt				= CSimpleEvent;
+
+class CTimerEvent
+{
+public:
+
+	ULLONG Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
+	{
+		pollfd pfd = {m_tmr, POLLIN};
+
+		while(TRUE)
+		{
+			SSIZE_T rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
+
+			if(rs <= TIMEOUT) return (ULLONG)rs;
+
+			if(pfd.revents & POLLIN)
+			{
+				BOOL	ok;
+				ULLONG	v;
+
+				if(!Get(v, ok))
+					return HAS_ERROR;
+
+				if(!ok)
+					continue;
+
+				return v;
+			}
+
+			if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
+			{
+				::SetLastError(ERROR_HANDLES_CLOSED);
+				return HAS_ERROR;
+			}
+
+			ASSERT(FALSE);
+		}
+	}
+
+	BOOL Set(LLONG llInterval, LLONG llStart = -1)
+	{
+		ASSERT_CHECK_EINVAL(llInterval >= 0L);
+
+		if(llStart < 0)
+			llStart = llInterval;
+
+		itimerspec its;
+
+		::MillisecondToTimespec(llStart, its.it_value);
+		::MillisecondToTimespec(llInterval, its.it_interval);
+
+		int rs = timerfd_settime(m_tmr, 0, &its, nullptr);
+		return VERIFY_IS_NO_ERROR(rs);
+	}
+
+	BOOL Get(ULLONG &v, BOOL& ok)
+	{
+		ASSERT(IsValid());
+
+		return ::ReadTimer(m_tmr, &v, &ok);
+	}
+
+	BOOL Reset()
+	{
+		BOOL	ok;
+		ULLONG	v;
+
+		while(TRUE)
+		{
+			if(!Get(v, ok))
+				return FALSE;
+
+			if(!ok)
+				break;
+		}
+
+		return TRUE;
+	}
+
+	BOOL GetTime(LLONG& lStart, LLONG& lInterval)
+	{
+		itimerspec its;
+
+		if(IS_HAS_ERROR(timerfd_gettime(m_tmr, &its)))
+			return FALSE;
+
+		lStart		= ::TimespecToMillisecond(its.it_value);
+		lInterval	= ::TimespecToMillisecond(its.it_interval);
+
+		return TRUE;
+	}
+
+	BOOL IsValid()	{return IS_VALID_FD(m_tmr);}
+
+	operator FD	()	{return m_tmr;}
+	FD GetFD	()	{return m_tmr;}
+
+public:
+	CTimerEvent(bool bRealTimeClock = FALSE)
+	{ 
+		int iCID = (bRealTimeClock ? CLOCK_REALTIME : CLOCK_MONOTONIC);
+		m_tmr	 = timerfd_create(iCID, TFD_NONBLOCK | TFD_CLOEXEC);
+
+		VERIFY(IsValid());
+	}
+
+	~CTimerEvent()
+	{
+		if(IsValid()) close(m_tmr);
+	}
+
+	DECLARE_NO_COPY_CLASS(CTimerEvent)
+
+private:
+	FD m_tmr = INVALID_FD;
+};
+
+class CSignalEvent
+{
+public:
+
+	int Wait(signalfd_siginfo& sgInfo, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
+	{
+		m_dwTID		= SELF_THREAD_ID;
+		pollfd pfd	= {m_sig, POLLIN};
+
+		while(TRUE)
+		{
+			long rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
+
+			if(rs <= TIMEOUT) return (int)rs;
+
+			if(pfd.revents & POLLIN)
+			{
+				BOOL ok;
+
+				if(!Get(sgInfo, ok))
+					return HAS_ERROR;
+
+				if(!ok)
+					continue;
+
+				return sgInfo.ssi_signo;
+			}
+
+			if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
+			{
+				::SetLastError(ERROR_HANDLES_CLOSED);
+				return HAS_ERROR;
+			}
+
+			ASSERT(FALSE);
+		}
+
+		m_dwTID = 0;
+	}
+
+	BOOL Set(int iSig, const sigval sgVal, THR_ID dwTID = 0)
+	{
+		if(dwTID == 0)
+		{
+			dwTID = m_dwTID;
+
+			if(dwTID == 0)
+			{
+				::SetLastError(ERROR_INVALID_STATE);
+				return FALSE;
+			}
+		}
+
+#if !defined(__ANDROID__)
+		int rs = pthread_sigqueue(dwTID, iSig, sgVal);
+#else
+		int rs = pthread_kill(dwTID, iSig);
+#endif
+
+		return IS_NO_ERROR(rs);
+	}
+
+	BOOL Get(signalfd_siginfo& v, BOOL& ok)
+	{
+		ASSERT(IsValid());
+
+		static const SSIZE_T SIZE = sizeof(signalfd_siginfo);
+
+		if(read(m_sig, &v, SIZE) == SIZE)
+			ok = TRUE;
+		{
+			if(IS_WOULDBLOCK_ERROR())
+				ok = FALSE;
+			else
+				return FALSE;
+		}
+
+		return ok;
+	}
+
+	BOOL Reset()
+	{
+		BOOL			 ok;
+		signalfd_siginfo v;
+
+		while(TRUE)
+		{
+			if(!Get(v, ok))
+				return FALSE;
+
+			if(!ok)
+				break;
+		}
+
+		return TRUE;
+	}
+
+	BOOL Mask(const sigset_t* pSigMask)
+	{
+		if(!pSigMask)
+		{
+			if(!IsValid()) return TRUE;
+			return IS_NO_ERROR(close(m_sig));
+		}
+
+		FD sig = signalfd(m_sig, pSigMask, SFD_NONBLOCK | SFD_CLOEXEC);
+
+		if(IS_VALID_FD(sig))
+		{
+			m_sig = sig;
+			return TRUE;
+		}
+
+		return FALSE;
+	}
+
+	BOOL IsValid()	{return IS_VALID_FD(m_sig);}
+
+	operator FD	()	{return m_sig;}
+	FD GetFD	()	{return m_sig;}
+
+public:
+	CSignalEvent(const sigset_t* pSigMask = nullptr)
+	{
+		if(pSigMask) VERIFY(Mask(pSigMask));
+	}
+
+	~CSignalEvent()
+	{
+		if(IsValid()) close(m_sig);
+	}
+
+	DECLARE_NO_COPY_CLASS(CSignalEvent)
+
+private:
+	FD m_sig		= INVALID_FD;
+	THR_ID m_dwTID	= 0;
+};

+ 237 - 0
common/FileHelper.cpp

@@ -0,0 +1,237 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "FileHelper.h"
+
+#include <sys/stat.h>
+
+CString GetCurrentDirectory()
+{
+	char szPath[MAX_PATH];
+
+	if(getcwd(szPath, sizeof(szPath) - 1) == nullptr)
+		szPath[0] = 0;
+
+	return szPath;
+}
+
+CString GetModuleFileName(pid_t pid)
+{
+	if(pid == 0)
+		pid = SELF_PROCESS_ID;
+
+	char szLink[MAX_PATH];
+	char szPath[MAX_PATH];
+
+	sprintf(szLink, "/proc/%d/exe", pid);
+
+	SSIZE_T rs = readlink(szLink, szPath, sizeof(szPath) - 1);
+
+	if(rs < 0) rs = 0;
+	szPath[rs]	  = 0;
+
+	return szPath;
+}
+
+BOOL SetCurrentPathToModulePath(pid_t pid)
+{
+	CString strPath = GetModuleFileName(pid);
+
+	if(strPath.IsEmpty())
+		return FALSE;
+	
+	CString::size_type pos = strPath.rfind('/');
+
+	if(pos == CString::npos)
+		return FALSE;
+
+	return IS_NO_ERROR(chdir(strPath.substr(0, pos + 1)));
+}
+
+BOOL CFile::Open(LPCTSTR lpszFilePath, int iFlag, mode_t iMode)
+{
+	CHECK_ERROR(!IsValid(), ERROR_INVALID_STATE);
+
+	m_fd = open(lpszFilePath, iFlag, iMode);
+
+	return IS_VALID_FD(m_fd);
+}
+
+BOOL CFile::Close()
+{
+	CHECK_ERROR(IsValid(), ERROR_INVALID_STATE);
+
+	if(IS_NO_ERROR(close(m_fd)))
+	{
+		m_fd = INVALID_FD;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CFile::Stat(struct stat& st)
+{
+	CHECK_ERROR_INVOKE(fstat(m_fd, &st));
+	return TRUE;
+}
+
+BOOL CFile::GetSize(SIZE_T& dwSize)
+{
+	struct stat st;
+	CHECK_IS_OK(Stat(st));
+
+	dwSize = st.st_size;
+
+	return TRUE;
+}
+
+BOOL CFile::IsDirectory()
+{
+	struct stat st;
+	CHECK_IS_OK(Stat(st));
+
+	return S_ISDIR(st.st_mode);
+}
+
+BOOL CFile::IsFile()
+{
+	struct stat st;
+	CHECK_IS_OK(Stat(st));
+
+	return S_ISREG(st.st_mode);
+}
+
+BOOL CFile::IsExist(LPCTSTR lpszFilePath)
+{
+	return IS_NO_ERROR(access(lpszFilePath, F_OK));
+}
+
+BOOL CFile::IsDirectory(LPCTSTR lpszFilePath)
+{
+	struct stat st;
+	CHECK_ERROR_INVOKE(stat(lpszFilePath, &st));
+
+	return S_ISDIR(st.st_mode);
+}
+
+BOOL CFile::IsFile(LPCTSTR lpszFilePath)
+{
+	struct stat st;
+	CHECK_ERROR_INVOKE(stat(lpszFilePath, &st));
+
+	return S_ISREG(st.st_mode);
+}
+
+BOOL CFile::IsLink(LPCTSTR lpszFilePath)
+{
+	struct stat st;
+	CHECK_ERROR_INVOKE(lstat(lpszFilePath, &st));
+
+	return S_ISLNK(st.st_mode);
+}
+
+BOOL CFileMapping::Map(LPCTSTR lpszFilePath, SIZE_T dwSize, SIZE_T dwOffset, int iProtected, int iFlag)
+{
+	CHECK_ERROR(!IsValid(), ERROR_INVALID_STATE);
+
+	FD fd = INVALID_FD;
+
+	if(lpszFilePath != nullptr)
+	{
+		int iFileFlag = O_RDONLY;
+
+		if(iProtected & PROT_WRITE)
+		{
+			if(iProtected & PROT_READ)
+				iFileFlag = O_RDWR;
+			else
+				iFileFlag = O_WRONLY;
+		}
+
+		fd = open(lpszFilePath, iFileFlag);
+		CHECK_ERROR_FD(fd);
+	}
+
+	BOOL isOK = Map(fd, dwSize, dwOffset, iProtected, iFlag);
+
+	if(IS_VALID_FD(fd)) EXECUTE_RESTORE_ERROR(close(fd));
+
+	return isOK;
+}
+
+BOOL CFileMapping::Map(FD fd, SIZE_T dwSize, SIZE_T dwOffset, int iProtected, int iFlag)
+{
+	CHECK_ERROR(!IsValid(), ERROR_INVALID_STATE);
+
+	if(IS_INVALID_FD(fd))
+	{
+		CHECK_EINVAL((iFlag & MAP_ANONYMOUS) && (dwSize > 0));
+	}
+	else
+	{
+		CHECK_EINVAL((iFlag & MAP_ANONYMOUS) == 0);
+
+		struct stat st;
+		CHECK_ERROR_INVOKE(fstat(fd, &st));
+
+		CHECK_ERROR(S_ISREG(st.st_mode), ERROR_BAD_FILE_TYPE);
+
+		if(dwSize == 0)
+			dwSize = st.st_size;
+	}
+
+	m_pv = (PBYTE)mmap(nullptr, dwSize, iProtected, iFlag, fd, dwOffset);
+
+	if(IsValid())
+	{
+		m_dwSize = dwSize;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CFileMapping::Unmap()
+{
+	CHECK_ERROR(IsValid(), ERROR_INVALID_STATE);
+		
+	if(IS_NO_ERROR(munmap(m_pv, m_dwSize)))
+	{
+		m_pv	 = INVALID_MAP_ADDR;
+		m_dwSize = 0;
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CFileMapping::MSync(int iFlag, SIZE_T dwSize)
+{
+	CHECK_ERROR(IsValid(), ERROR_INVALID_STATE);
+
+	if(dwSize == 0) dwSize = m_dwSize;
+
+	return IS_NO_ERROR(msync(m_pv, dwSize, iFlag));
+}

+ 123 - 0
common/FileHelper.h

@@ -0,0 +1,123 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "FuncHelper.h"
+#include "StringT.h"
+
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+
+#define INVALID_MAP_ADDR	((PBYTE)(MAP_FAILED))
+
+CString GetCurrentDirectory();
+CString GetModuleFileName(pid_t pid = 0);
+BOOL SetCurrentPathToModulePath(pid_t pid = 0);
+
+class CFile
+{
+public:
+	BOOL Open(LPCTSTR lpszFilePath, int iFlag, mode_t iMode = 0);
+	BOOL Close();
+	BOOL Stat(struct stat& st);
+	BOOL GetSize(SIZE_T& dwSize);
+
+	SSIZE_T Read(PVOID pBuffer, SIZE_T dwCount)
+		{return read(m_fd, pBuffer, dwCount);}
+	SSIZE_T Write(PVOID pBuffer, SIZE_T dwCount)
+		{return write(m_fd, pBuffer, dwCount);}
+	SSIZE_T PRead(PVOID pBuffer, SIZE_T dwCount, SIZE_T dwOffset)
+		{return pread(m_fd, pBuffer, dwCount, dwOffset);}
+	SSIZE_T PWrite(PVOID pBuffer, SIZE_T dwCount, SIZE_T dwOffset)
+		{return pwrite(m_fd, pBuffer, dwCount, dwOffset);}
+	SSIZE_T ReadV(const iovec* pVec, int iVecCount)
+		{return readv(m_fd, pVec, iVecCount);}
+	SSIZE_T WriteV(const iovec* pVec, int iVecCount)
+		{return writev(m_fd, pVec, iVecCount);}
+	SSIZE_T Seek(SSIZE_T lOffset, int iWhence)
+		{return lseek(m_fd, lOffset, iWhence);}
+
+	BOOL IsValid()	{return IS_VALID_FD(m_fd);}
+	operator FD ()	{return m_fd;}
+
+	BOOL IsExist()	{return IsValid();}
+
+	BOOL IsDirectory();
+	BOOL IsFile();
+
+	static BOOL IsExist(LPCTSTR lpszFilePath);
+	static BOOL IsDirectory(LPCTSTR lpszFilePath);
+	static BOOL IsFile(LPCTSTR lpszFilePath);
+	static BOOL IsLink(LPCTSTR lpszFilePath);
+
+public:
+	CFile(LPCTSTR lpszFilePath = nullptr, int iFlag = O_RDONLY, mode_t iMode = 0)
+	: m_fd(INVALID_FD)
+	{
+		if(lpszFilePath != nullptr)
+			Open(lpszFilePath, iFlag, iMode);
+	}
+
+	~CFile()
+	{
+		if(IsValid())
+			Close();
+	}
+
+private:
+	FD m_fd;
+};
+
+class CFileMapping
+{
+public:
+	BOOL Map(LPCTSTR lpszFilePath, SIZE_T dwSize = 0, SIZE_T dwOffset = 0, int iProtected = PROT_READ, int iFlag = MAP_PRIVATE);
+	BOOL Map(FD fd, SIZE_T dwSize = 0, SIZE_T dwOffset = 0, int iProtected = PROT_READ, int iFlag = MAP_PRIVATE);
+	BOOL Unmap();
+	BOOL MSync(int iFlag = MS_SYNC, SIZE_T dwSize = 0);
+	
+	BOOL IsValid	()		{return m_pv != INVALID_MAP_ADDR;}
+	SIZE_T Size		()		{return m_dwSize;}
+	LPBYTE Ptr		()		{return m_pv;}
+	operator LPBYTE	()		{return Ptr();}
+
+public:
+	CFileMapping()
+	: m_pv(INVALID_MAP_ADDR)
+	, m_dwSize(0)
+	{
+		
+	}
+
+	~CFileMapping()
+	{
+		if(IsValid())
+			Unmap();
+	}
+
+private:
+	PBYTE	m_pv;
+	SIZE_T	m_dwSize;
+};

+ 393 - 0
common/FuncHelper.cpp

@@ -0,0 +1,393 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "FuncHelper.h"
+#include "Thread.h"
+
+#include <ctype.h>
+#include <sched.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <sys/timerfd.h>
+
+#if !defined(__ANDROID__)
+	#include <sys/timeb.h>
+	#include <execinfo.h>
+	#ifdef __GNUC__
+		#include <cxxabi.h>
+	#endif
+#endif
+
+INT WaitFor(DWORD dwMillSecond, DWORD dwSecond, BOOL bExceptThreadInterrupted)
+{
+	timeval tv {(time_t)dwSecond, (suseconds_t)(dwMillSecond * 1000)};
+
+	if(bExceptThreadInterrupted)
+		return NO_EINTR_EXCEPT_THR_INTR_INT(select(0, nullptr, nullptr, nullptr, &tv));
+
+	return NO_EINTR_INT(select(0, nullptr, nullptr, nullptr, &tv));
+}
+
+INT Sleep(DWORD dwMillSecond, DWORD dwSecond, BOOL bExceptThreadInterrupted)
+{
+	timespec ts_req	= {(time_t)dwSecond, (long)(dwMillSecond * 1000000)};
+	timespec ts_rem	= ts_req;
+	INT rs			= NO_ERROR;
+
+	while(IS_HAS_ERROR(rs = nanosleep(&ts_req, &ts_rem)))
+	{
+		if(!IS_INTR_ERROR())
+			break;
+		else
+		{
+			if(bExceptThreadInterrupted && ::IsThreadInterrupted())
+				break;
+		}
+
+		ts_req = ts_rem;
+	}
+
+	return rs;
+}
+
+__time64_t _time64(time_t* ptm)
+{
+	return (__time64_t)time(ptm);
+}
+
+__time64_t _mkgmtime64(tm* ptm)
+{
+	return (__time64_t)timegm(ptm);
+}
+
+tm* _gmtime64(tm* ptm, __time64_t* pt)
+{
+	time_t t = (time_t)(*pt);
+
+	return gmtime_r(&t, ptm);
+}
+
+DWORD TimeGetTime()
+{
+	return (DWORD)TimeGetTime64();
+}
+
+ULLONG TimeGetTime64()
+{
+#if !defined(__ANDROID__)
+
+	timeb tb;
+
+	if(ftime(&tb) == NO_ERROR)
+		return (((ULLONG)(tb.time)) * 1000 + tb.millitm);
+
+#else
+
+	timespec ts;
+
+	if(clock_gettime(CLOCK_MONOTONIC, &ts) == NO_ERROR)
+		return (((ULLONG)(ts.tv_sec)) * 1000 + ts.tv_nsec / 1000000);
+
+#endif
+
+	return 0ull;
+}
+
+DWORD GetTimeGap32(DWORD dwOriginal, DWORD dwCurrent)
+{
+	if(dwCurrent == 0)
+		dwCurrent = ::TimeGetTime();
+
+	return dwCurrent - dwOriginal;
+}
+
+ULLONG GetTimeGap64(ULLONG ullOriginal, ULONGLONG ullCurrent)
+{
+	if(ullCurrent == 0)
+		ullCurrent = ::TimeGetTime64();
+
+	return ullCurrent - ullOriginal;
+}
+
+LLONG TimevalToMillisecond(const timeval& tv)
+{
+	return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+timeval& MillisecondToTimeval(LLONG ms, timeval& tv)
+{
+	tv.tv_sec	= (time_t)(ms / 1000);
+	tv.tv_usec	= (suseconds_t)((ms % 1000) * 1000);
+
+	return tv;
+}
+
+LLONG TimespecToMillisecond(const timespec& ts)
+{
+	return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+}
+
+timespec& MillisecondToTimespec(LLONG ms, timespec& ts)
+{
+	ts.tv_sec	= (time_t)(ms / 1000);
+	ts.tv_nsec	= (long)((ms % 1000) * 1000000);
+
+	return ts;
+}
+
+timeval& GetFutureTimeval(LLONG ms, timeval& tv, struct timezone* ptz)
+{
+	gettimeofday(&tv, ptz);
+
+	tv.tv_sec	+= (time_t)(ms / 1000);
+	tv.tv_usec	+= (suseconds_t)((ms % 1000) * 1000);
+
+	return tv;
+}
+
+timespec& GetFutureTimespec(LLONG ms, timespec& ts, clockid_t clkid)
+{
+	clock_gettime(clkid, &ts);
+
+	ts.tv_sec	+= (time_t)(ms / 1000);
+	ts.tv_nsec	+= (long)((ms % 1000) * 1000000);
+
+	return ts;
+}
+
+FD CreateTimer(LLONG llInterval, LLONG llStart, BOOL bRealTimeClock)
+{
+	ASSERT_CHECK_EINVAL(llInterval >= 0L);
+
+	if(llStart < 0)
+		llStart = llInterval;
+
+	FD fdTimer = timerfd_create((bRealTimeClock ? CLOCK_REALTIME : CLOCK_MONOTONIC), TFD_NONBLOCK | TFD_CLOEXEC);
+
+	itimerspec its;
+
+	::MillisecondToTimespec(llStart, its.it_value);
+	::MillisecondToTimespec(llInterval, its.it_interval);
+
+	if(IS_HAS_ERROR(timerfd_settime(fdTimer, 0, &its, nullptr)))
+	{
+		close(fdTimer);
+		fdTimer = INVALID_FD;
+	}
+
+	return fdTimer;
+}
+
+BOOL ReadTimer(FD tmr, ULLONG* pVal, BOOL* pRs)
+{
+	static const SSIZE_T SIZE = sizeof(ULLONG);
+
+	if(pVal == nullptr)
+		pVal = CreateLocalObject(ULLONG);
+	if(pRs == nullptr)
+		pRs = CreateLocalObject(BOOL);
+
+	if(read(tmr, pVal, SIZE) == SIZE)
+		*pRs = TRUE;
+	else
+	{
+		*pRs = FALSE;
+
+		if(!IS_WOULDBLOCK_ERROR())
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL fcntl_SETFL(FD fd, INT fl, BOOL bSet)
+{
+	int val = fcntl(fd, F_GETFL);
+
+	if(IS_HAS_ERROR(val))
+		return FALSE;
+
+	val = bSet ? (val | fl) : (val & (~fl));
+
+	return IS_NO_ERROR(fcntl(fd, F_SETFL , val));
+}
+
+void PrintStackTrace()
+{
+#if !defined(__ANDROID__)
+
+	const int MAX_SIZE = 51;
+	void* arr[MAX_SIZE];
+
+	int size		= backtrace(arr, MAX_SIZE);
+	char** messages	= backtrace_symbols(arr, size);
+
+	for(int i = 1; i < size && messages != nullptr; i++)
+	{
+		char* mangled_name		 = nullptr;
+		char* offset_end		 = nullptr;
+		const char* offset_begin = nullptr;
+
+		for(char* p = messages[i]; *p; ++p)
+		{
+			if(*p == '(')
+			{
+				mangled_name = p;
+			}
+			else if(*p == '+')
+			{
+				offset_begin = p;
+			}
+			else if(*p == ')')
+			{
+				offset_end = p;
+				break;
+			}
+		}
+
+		if(mangled_name && offset_end && mangled_name < offset_end)
+		{
+			*mangled_name++	= 0;
+			*offset_end++	= 0;
+
+			if(offset_begin == nullptr)
+				offset_begin = "";
+			else
+				*(char*)offset_begin++ = 0;
+
+			while(*offset_end == ' ')
+				++offset_end;
+
+#ifdef __GNUC__
+			int status;
+			char* real_name = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status);
+
+			if(status == 0)
+				FPRINTLN(stderr, "  -> [%02d] %s : (%s+%s) %s", i, messages[i], real_name, offset_begin, offset_end);
+			else
+				FPRINTLN(stderr, "  -> [%02d] %s : (%s+%s) %s", i, messages[i], mangled_name, offset_begin, offset_end);
+
+			if(real_name != nullptr)
+				free(real_name);
+#else
+			FPRINTLN(stderr, "  -> [%02d] %s : (%s+%s) %s", i, messages[i], mangled_name, offset_begin, offset_end);
+#endif
+		}
+		else
+		{
+			FPRINTLN(stderr, "  -> [%02d] %s", i, messages[i]);
+		}
+	}
+
+	free(messages);
+
+#endif
+}
+
+void __EXIT_FN_(void (*fn)(int), LPCSTR lpszFnName, int* lpiExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
+{
+	if(iErrno >= 0)
+		SetLastError(iErrno);
+	else
+		iErrno = GetLastError();
+
+	if(!lpszTitle)
+	{
+		lpszTitle = CreateLocalObjects(char, 64);
+
+		if(lpiExitCode)
+			sprintf((LPSTR)lpszTitle, "(#%d, 0x%zX) > %s(%d) [%d]", SELF_PROCESS_ID, (SIZE_T)SELF_THREAD_ID, lpszFnName, *lpiExitCode, iErrno);
+		else
+			sprintf((LPSTR)lpszTitle, "(#%d, 0x%zX) > %s() [%d]", SELF_PROCESS_ID, (SIZE_T)SELF_THREAD_ID, lpszFnName, iErrno);
+	}
+
+	if(lpszFile && iLine > 0)
+		FPRINTLN(stderr, "%s : %s\n  => %s (%d) : %s", lpszTitle, strerror(iErrno), lpszFile, iLine, lpszFunc ? lpszFunc : "");
+	else
+		FPRINTLN(stderr, "%s : %s", lpszTitle, strerror(iErrno));
+
+	if(lpiExitCode)
+		fn(*lpiExitCode);
+	else
+		((void (*)())fn)();
+}
+
+void EXIT(int iExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
+{
+	__EXIT_FN_(exit, "exit", &iExitCode, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
+}
+
+void _EXIT(int iExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
+{
+	__EXIT_FN_(_exit, "_exit", &iExitCode, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
+}
+
+void ABORT(int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
+{
+	__EXIT_FN_((void (*)(int))abort, "abort", nullptr, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
+}
+
+BOOL SetSequenceThreadName(THR_ID tid, LPCTSTR lpszPrefix, volatile UINT& vuiSeq)
+{
+	UINT uiSequence = InterlockedIncrement(&vuiSeq);
+	return SetThreadName(tid, lpszPrefix, uiSequence);
+}
+
+BOOL SetThreadName(THR_ID tid, LPCTSTR lpszPrefix, UINT uiSequence)
+{
+	int iMaxSeqLength = (int)(MAX_THREAD_NAME_LENGTH - lstrlen(lpszPrefix));
+
+	ASSERT(iMaxSeqLength > 0);
+
+	if(iMaxSeqLength <= 0)
+	{
+		::SetLastError(ERROR_OUT_OF_RANGE);
+		return FALSE;
+	}
+
+	ULONGLONG uiDiv = 1;
+
+	for(int i = 0; i < iMaxSeqLength; i++)
+		uiDiv *= 10;
+
+	uiSequence = (UINT)(uiSequence % uiDiv);
+
+	CString strName;
+	strName.Format(_T("%s%u"), lpszPrefix, uiSequence);
+
+	return SetThreadName(tid, strName);
+}
+
+BOOL SetThreadName(THR_ID tid, LPCTSTR lpszName)
+{
+	ASSERT(lstrlen(lpszName) <= MAX_THREAD_NAME_LENGTH);
+
+	if(tid == 0)
+		tid = SELF_THREAD_ID;
+
+	int rs = pthread_setname_np(tid, CT2A(lpszName));
+
+	CHECK_ERROR_CODE(rs)
+
+	return TRUE;
+}

+ 426 - 0
common/FuncHelper.h

@@ -0,0 +1,426 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "hpsocket/GlobalErrno.h"
+#include "SysHelper.h"
+
+#include <stdlib.h>
+#include <sysexits.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <alloca.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <atomic>
+#include <utility>
+
+using namespace std;
+
+#if !defined(__ANDROID__)
+	typedef atomic_ulong				atomic_tid;
+
+	#define FPRINTLN(fd, fmt, ...)		fprintf((fd), fmt "\n", ##__VA_ARGS__)
+#else
+	typedef atomic_long					atomic_tid;
+
+	#if defined(stdout)
+		#undef stdout
+	#endif
+
+	#if defined(stderr)
+		#undef stderr
+	#endif
+
+	#define stdout	nullptr
+	#define stderr	nullptr
+
+	#define FPRINTLN(fd, fmt, ...)		printf(fmt "\n", ##__VA_ARGS__)
+#endif
+
+#define PRINTLN(fmt, ...)				FPRINTLN(stdout, fmt, ##__VA_ARGS__)
+
+#if defined(DEBUG) && defined(DEBUG_TRACE)
+	#define TRACE(fmt, ...)				PRINTLN("> TRC (0x%zX, %d) " fmt, (SIZE_T)SELF_THREAD_ID, SELF_NATIVE_THREAD_ID, ##__VA_ARGS__)
+	#define ASSERT(expr)				((expr) ? TRUE : (::PrintStackTrace(), assert(FALSE), FALSE))
+#else
+	#define TRACE(fmt, ...)
+	#define ASSERT(expr)				assert(expr)
+#endif
+
+#define VERIFY(expr)					((expr) ? TRUE : (::PrintStackTrace(), ERROR_ABORT2(ERROR_VERIFY_CHECK), FALSE))
+#define ASSERT_IS_NO_ERROR(expr)		ASSERT(IS_NO_ERROR(expr))
+#define VERIFY_IS_NO_ERROR(expr)		VERIFY(IS_NO_ERROR(expr))
+#define	ENSURE(expr)					VERIFY(expr)
+#define ENSURE_IS_NO_ERROR(expr)		VERIFY_IS_NO_ERROR(expr)
+
+#define TEMP_FAILURE_RETRY_INT(exp)		((int)TEMP_FAILURE_RETRY(exp))
+
+#define NO_EINTR						TEMP_FAILURE_RETRY
+#define NO_EINTR_INT					TEMP_FAILURE_RETRY_INT
+
+#define CHECK_IS_OK(expr)				{if(IS_NOT_OK(expr)) return FALSE;}
+#define CHECK_ERROR_FD(fd)				{if(IS_INVALID_FD(fd)) return FALSE;}
+#define CHECK_ERROR_INVOKE(expr)		{if(!IS_NO_ERROR(expr)) return FALSE;}
+#define CHECK_ERROR_CODE(rs)			{if(!IS_NO_ERROR(rs)) {::SetLastError(rs); return FALSE;}}
+#define CHECK_ERROR(expr, code)			{if(!(expr)) {::SetLastError(code); return FALSE;}}
+#define CHECK_EINVAL(expr)				CHECK_ERROR(expr, ERROR_INVALID_PARAMETER)
+#define ASSERT_CHECK_ERROR(expr, code)	{ASSERT(expr); CHECK_ERROR(expr, code);}
+#define ASSERT_CHECK_EINVAL(expr)		{ASSERT(expr); CHECK_EINVAL(expr);}
+
+#define SUCCEEDED(rs)					IS_NO_ERROR(rs)
+#define FAILED(rs)						(!SUCCEEDED(rs))
+#define IS_OK(rs)						((BOOL)(rs))
+#define IS_NOT_OK(rs)					(!IS_OK(rs))
+
+#define IS_ERROR(code)					(::GetLastError() == (code))
+#define CONTINUE_IF_ERROR(code)			{if(IS_ERROR(code)) continue;}
+#define BREAK_IF_ERROR(code)			{if(IS_ERROR(code)) break;}
+
+#define IS_WOULDBLOCK_ERROR()			IS_ERROR(ERROR_WOULDBLOCK)
+#define CONTINUE_WOULDBLOCK_ERROR()		CONTINUE_IF_ERROR(ERROR_WOULDBLOCK)
+#define BREAK_WOULDBLOCK_ERROR()		BREAK_IF_ERROR(ERROR_WOULDBLOCK)
+#define IS_IO_PENDING_ERROR()			IS_ERROR(ERROR_IO_PENDING)
+#define CONTINUE_IO_PENDING_ERROR()		CONTINUE_IF_ERROR(ERROR_IO_PENDING)
+#define BREAK_IO_PENDING_ERROR()		BREAK_IF_ERROR(ERROR_IO_PENDING)
+#define IS_INTR_ERROR()					IS_ERROR(ERROR_INTR)
+#define CONTINUE_INTR_ERROR()			CONTINUE_IF_ERROR(ERROR_INTR)
+#define BREAK_INTR_ERROR()				BREAK_IF_ERROR(ERROR_INTR)
+
+#define EqualMemory(dest, src, len)		(!memcmp((dest), (src), (len)))
+#define MoveMemory(dest, src, len)		memmove((dest), (src), (len))
+#define CopyMemory(dest, src, len)		memcpy((dest), (src), (len))
+#define FillMemory(dest, len, ch)		memset((dest), (ch), (len))
+#define ZeroMemory(dest, len)			FillMemory((dest), (len), 0)
+#define ZeroObject(obj)					ZeroMemory((&(obj)), sizeof(obj))
+
+inline void SetLastError(int code)		{errno = code;}
+inline int GetLastError()				{return errno;}
+inline LPCSTR GetErrorStr(int code)		{return strerror(code);}
+inline LPCSTR GetLastErrorStr()			{return GetErrorStr(errno);}
+inline void PrintError(LPCSTR subject)	{perror(subject);}
+
+#define EXECUTE_RESET_ERROR(expr)		(::SetLastError(0), (expr))
+#define EXECUTE_RESTORE_ERROR(expr)		{int __le_ = ::GetLastError(); (expr); ::SetLastError(__le_);}
+#define EXECUTE_RESTORE_ERROR_RT(T, expr)\
+										({int __le_ = ::GetLastError(); T __rs_ = (expr); ::SetLastError(__le_); __rs_;})
+#define ENSURE_ERROR(def_code)			({int __le_ = ::GetLastError(); if(__le_ == NO_ERROR) __le_ = (def_code);  __le_;})
+#define ENSURE_ERROR_CANCELLED			ENSURE_ERROR(ERROR_CANCELLED)
+#define TRIGGER(expr)					EXECUTE_RESET_ERROR((expr))
+
+#define _msize(p)						malloc_usable_size(p)
+#define CreateLocalObjects(T, n)		((T*)alloca(sizeof(T) * (n)))
+#define CreateLocalObject(T)			CreateLocalObjects(T, 1)
+#define CallocObjects(T, n)				((T*)calloc((n), sizeof(T)))
+
+#define MALLOC(T, n)					((T*)malloc(sizeof(T) * (n)))
+#define REALLOC(T, p, n)				((T*)realloc((PVOID)(p), sizeof(T) * (n)))
+#define FREE(p)							free((PVOID)(p))
+#define CALLOC(n, s)					calloc((n), (s))
+
+#define InterlockedExchangeAdd(p, n)	__atomic_fetch_add((p), (n), memory_order_seq_cst)
+#define InterlockedExchangeSub(p, n)	__atomic_fetch_sub((p), (n), memory_order_seq_cst)
+#define InterlockedAdd(p, n)			__atomic_add_fetch((p), (n), memory_order_seq_cst)
+#define InterlockedSub(p, n)			__atomic_sub_fetch((p), (n), memory_order_seq_cst)
+#define InterlockedIncrement(p)			InterlockedAdd((p), 1)
+#define InterlockedDecrement(p)			InterlockedSub((p), 1)
+
+#define ERROR_EXIT2(code, err)			EXIT((code), (err), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ERROR__EXIT2(code, err)			_EXIT((code), (err), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ERROR_ABORT2(err)				ABORT((err), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ERROR_EXIT(code)				ERROR_EXIT2((code), -1)
+#define ERROR__EXIT(code)				ERROR__EXIT2((code), -1)
+#define ERROR_ABORT()					ERROR_ABORT2(-1)
+
+#define IS_VALID_FD(fd)					((fd) != INVALID_FD)
+#define IS_INVALID_FD(fd)				(!IS_VALID_FD(fd))
+
+#define IS_VALID_PVOID(pv)				((pv) != INVALID_PVOID)
+#define IS_INVALID_PVOID(pv)			(!IS_VALID_PVOID(pv))
+
+#define TO_PVOID(v)						((PVOID)(UINT_PTR)(v))
+#define FROM_PVOID(T, pv)				((T)(UINT_PTR)(pv))
+
+#define IS_NULL(v)						((v) == nullptr)
+#define IS_NOT_NULL(v)					(!IS_NULL(v))
+
+#define stricmp							strcasecmp
+#define strnicmp						strncasecmp
+#define wcsicmp							wcscasecmp
+#define wcsnicmp						wcsncasecmp
+
+#ifdef _UNICODE
+	#define tstrchr						wcschr
+	#define tstrrchr					wcsrchr
+	#define tstrstr						wcsstr
+	#define tstrpbrk					wcspbrk
+	#define tstrtok						wcstok
+
+	#define stscanf						swscanf
+	#define tstrlen						wcslen
+	#define tstrcpy						wcscpy
+	#define tstrcmp						wcscmp
+	#define tstricmp					wcsicmp
+	#define tstrncpy					wcsncpy
+	#define tstrncmp					wcsncmp
+	#define tstrnicmp					wcsnicmp
+	#define tstrspn						wcsspn
+	#define tstrcspn					wcscspn
+	#define wsprintf					swprintf
+#else
+	#define tstrchr						strchr
+	#define tstrrchr					strrchr
+	#define tstrstr						strstr
+	#define tstrpbrk					strpbrk
+	#define tstrtok						strtok_r
+
+	#define stscanf						sscanf
+	#define tstrlen						strlen
+	#define tstrcpy						strcpy
+	#define tstrcmp						strcmp
+	#define tstricmp					stricmp
+	#define tstrncpy					strncpy
+	#define tstrncmp					strncmp
+	#define tstrnicmp					strnicmp
+	#define tstrspn						strspn
+	#define tstrcspn					strcspn
+	#define wsprintf					sprintf
+#endif
+
+inline const char* StrChr(const char* s, char c)					{return strchr(s, c);}
+inline const char* StrRChr(const char* s, char c)					{return strrchr(s, c);}
+inline const char* StrStr(const char* h, const char* n)				{return strstr(h, n);}
+inline const char* StrPBrk(const char* s, const char* a)			{return strpbrk(s, a);}
+inline const wchar_t* StrChr(const wchar_t* s, wchar_t c)			{return wcschr(s, c);}
+inline const wchar_t* StrRChr(const wchar_t* s, wchar_t c)			{return wcsrchr(s, c);}
+inline const wchar_t* StrStr(const wchar_t* h, const wchar_t* n)	{return wcsstr(h, n);}
+inline const wchar_t* StrPBrk(const wchar_t* s, const wchar_t* a)	{return wcspbrk(s, a);}
+inline LPSTR StrSep2(LPSTR* lpStr, LPCSTR lpDelim = " \t\r\n")		{LPSTR lpTok; while((lpTok = strsep(lpStr, lpDelim)) != nullptr && lpTok[0] == 0); return lpTok;}
+inline LPSTR TrimLeft(LPSTR* lpStr, LPCSTR lpDelim = " \t\r\n")		{while((*lpStr)[0] != 0 && ::StrChr(lpDelim, (*lpStr)[0]) != nullptr) ++(*lpStr); return (*lpStr);}
+inline LPSTR TrimRitht(LPSTR* lpStr, LPCSTR lpDelim = " \t\r\n")
+{
+	LPSTR lpEnd = (*lpStr) + strlen(*lpStr) - 1;
+	LPSTR lpCur = lpEnd;
+
+	while(lpCur >= (*lpStr) && ::StrChr(lpDelim, lpCur[0]) != nullptr)
+		--lpCur;
+
+	if(lpCur != lpEnd)
+		lpCur[1] = 0;
+
+	return (*lpStr);
+}
+
+inline BOOL IsStrEmptyA(LPCSTR lpsz)	{return (lpsz == nullptr || lpsz[0] == 0);}
+inline BOOL IsStrEmptyW(LPCWSTR lpsz)	{return (lpsz == nullptr || lpsz[0] == 0);}
+inline BOOL IsStrNotEmptyA(LPCSTR lpsz)	{return !IsStrEmptyA(lpsz);}
+inline BOOL IsStrNotEmptyW(LPCWSTR lpsz){return !IsStrEmptyW(lpsz);}
+inline LPCSTR SafeStrA(LPCSTR lpsz)		{return (lpsz != nullptr) ? lpsz : "";}
+inline LPCWSTR SafeStrW(LPCWSTR lpsz)	{return (lpsz != nullptr) ? lpsz : L"";}
+
+#ifdef _UNICODE
+	#define IsStrEmpty(lpsz)			IsStrEmptyW(lpsz)
+	#define IsStrNotEmpty(lpsz)			IsStrNotEmptyW(lpsz)
+	#define SafeStr(lpsz)				SafeStrW(lpsz)
+#else
+	#define IsStrEmpty(lpsz)			IsStrEmptyA(lpsz)
+	#define IsStrNotEmpty(lpsz)			IsStrNotEmptyA(lpsz)
+	#define SafeStr(lpsz)				SafeStrA(lpsz)
+#endif
+
+inline int lstrlen(LPCTSTR p)							{return (int)tstrlen(p);}
+inline LPTSTR lstrcpy(LPTSTR d, LPCTSTR s)				{return tstrcpy(d, s);}
+inline LPTSTR lstrncpy(LPTSTR d, LPCTSTR s, size_t n)	{return tstrncpy(d, s, n);}
+inline int lstrcmp(LPCTSTR s1, LPCTSTR s2)				{return tstrcmp(s1, s2);}
+inline int lstrncmp(LPCTSTR s1, LPCTSTR s2, size_t n)	{return tstrncmp(s1, s2, n);}
+inline int lstricmp(LPCTSTR s1, LPCTSTR s2)				{return tstricmp(s1, s2);}
+inline int lstrnicmp(LPCTSTR s1, LPCTSTR s2, size_t n)	{return tstrnicmp(s1, s2, n);}
+inline int lstrspn(LPCTSTR s, LPCTSTR accept)			{return (int)tstrspn(s, accept);}
+inline int lstrcspn(LPCTSTR s, LPCTSTR accept)			{return (int)tstrcspn(s, accept);}
+
+template <typename T, size_t N> char (&_ArraySizeHelper(T(&arr)[N]))[N];
+template <typename T, size_t N> char (&_ArraySizeHelper(const T(&arr)[N]))[N];
+
+#define ARRAY_SIZE(arr)		(sizeof(_ArraySizeHelper(arr)))
+
+#ifndef _countof
+	#define _countof(arr)	ARRAY_SIZE(arr)
+#endif
+
+#ifndef __countof
+	#define __countof(arr)	ARRAY_SIZE(arr)
+#endif
+
+#define THREAD_YIELD_CYCLE	63
+#define THREAD_SWITCH_CYCLE	4095
+
+inline void YieldThread(UINT i = THREAD_YIELD_CYCLE)
+{
+	if((i & THREAD_SWITCH_CYCLE) == THREAD_SWITCH_CYCLE)
+		::SwitchToThread();
+	else if((i & THREAD_YIELD_CYCLE) == THREAD_YIELD_CYCLE)
+		::YieldProcessor();
+}
+
+INT WaitFor(DWORD dwMillSecond, DWORD dwSecond = 0, BOOL bExceptThreadInterrupted = FALSE);
+INT Sleep(DWORD dwMillSecond, DWORD dwSecond = 0, BOOL bExceptThreadInterrupted = FALSE);
+
+__time64_t	_time64(time_t* ptm = nullptr);
+__time64_t	_mkgmtime64(tm* ptm);
+tm*			_gmtime64(tm* ptm, __time64_t* pt);
+
+DWORD		TimeGetTime();
+ULLONG		TimeGetTime64();
+DWORD		GetTimeGap32(DWORD dwOriginal, DWORD dwCurrent = 0);
+ULLONG		GetTimeGap64(ULLONG ullOriginal, ULONGLONG ullCurrent = 0);
+LLONG		TimevalToMillisecond(const timeval& tv);
+timeval&	MillisecondToTimeval(LLONG ms, timeval& tv);
+LLONG		TimespecToMillisecond(const timespec& ts);
+timespec&	MillisecondToTimespec(LLONG ms, timespec& ts);
+timeval&	GetFutureTimeval(LLONG ms, timeval& tv, struct timezone* ptz = nullptr);
+timespec&	GetFutureTimespec(LLONG ms, timespec& ts, clockid_t clkid = CLOCK_MONOTONIC);
+
+FD			CreateTimer(LLONG llInterval, LLONG llStart = -1, BOOL bRealTimeClock = FALSE);
+BOOL		ReadTimer(FD tmr, ULLONG* pVal = nullptr, BOOL* pRs = nullptr);
+
+BOOL fcntl_SETFL(FD fd, INT fl, BOOL bSet = TRUE);
+
+void PrintStackTrace();
+void EXIT(int iExitCode = 0, int iErrno = -1, LPCSTR lpszFile = nullptr, int iLine = 0, LPCSTR lpszFunc = nullptr, LPCSTR lpszTitle = nullptr);
+void _EXIT(int iExitCode = 0, int iErrno = -1, LPCSTR lpszFile = nullptr, int iLine = 0, LPCSTR lpszFunc = nullptr, LPCSTR lpszTitle = nullptr);
+void ABORT(int iErrno = -1, LPCSTR lpszFile = nullptr, int iLine = 0, LPCSTR lpszFunc = nullptr, LPCSTR lpszTitle = nullptr);
+
+/* ¹¤×÷Ïß³ÌÃû³Æ×î´ó³¤¶È */
+#define MAX_THREAD_NAME_LENGTH	15
+
+BOOL SetSequenceThreadName(THR_ID tid, LPCTSTR lpszPrefix, volatile UINT& vuiSeq);
+BOOL SetThreadName(THR_ID tid, LPCTSTR lpszPrefix, UINT uiSequence);
+BOOL SetThreadName(THR_ID tid, LPCTSTR lpszName);
+
+template<typename T, typename = enable_if_t<is_integral<T>::value>>
+inline bool IS_INFINITE(T v)
+{
+	return v == (T)INFINITE;
+}
+
+template<typename T, typename = enable_if_t<is_integral<T>::value>>
+inline bool IS_HAS_ERROR(T v)
+{
+	return v == (T)HAS_ERROR;
+}
+
+template<typename T, typename = enable_if_t<is_integral<T>::value>>
+inline bool IS_NO_ERROR(T v)
+{
+	return v == (T)NO_ERROR;
+}
+
+template<typename T>
+inline T InterlockedCompareExchange(volatile T* _Tgt, T _Value, T _Exp, BOOL _bWeek = FALSE, memory_order m1 = memory_order_seq_cst, memory_order m2 = memory_order_seq_cst)
+{
+	__atomic_compare_exchange_n(_Tgt, &_Exp, _Value, _bWeek, m1, m2);
+	return _Exp;
+}
+
+template<typename T, typename V, typename E, typename = enable_if_t<is_same<decay_t<T>, decay_t<V>>::value && is_same<decay_t<V>, decay_t<E>>::value>>
+inline V* InterlockedCompareExchangePointer(volatile T** _Tgt, V* _Value, E* _Exp, BOOL _bWeek = FALSE, memory_order m1 = memory_order_seq_cst, memory_order m2 = memory_order_seq_cst)
+{
+	return (V*)(ULONG_PTR)InterlockedCompareExchange((volatile ULONG_PTR*)(volatile PVOID*)_Tgt, (ULONG_PTR)(PVOID)_Value, (ULONG_PTR)(PVOID)_Exp, _bWeek, m1, m2);
+}
+
+template<typename T, typename ... A>
+inline T* ConstructObject(T* p, A&& ... args)
+{
+	return new (p) T(forward<A>(args) ...);
+}
+
+template<typename T>
+inline void DestructObject(T* p)
+{
+	p->T::~T();
+}
+
+template<typename T1, typename T2, typename = enable_if_t<is_same<decay_t<T1>, decay_t<T2>>::value>>
+inline void CopyPlainObject(T1* p1, const T2* p2)
+{
+	CopyMemory(p1, p2, sizeof(T1));
+}
+
+template<typename T, typename C, typename = enable_if_t<is_integral<T>::value  && (is_same<C, char>::value || is_same<C, wchar_t>::value)>>
+C* _n_2_c(T value, C* lpszDest, int radix)
+{
+	static const C* dig = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+	bool neg = false;
+
+	if(is_signed<T>::value && value < 0)
+	{
+		value = -value;
+		neg	  = true;
+	}
+
+	int n = 0;
+
+	do
+	{
+		lpszDest[n++] = dig[value % radix];
+		value /= radix;
+	} while(value);
+
+	if(neg) lpszDest[n++] = '-';
+	lpszDest[n]			  = 0;
+
+	C c, *p, *q;
+	for(p = lpszDest, q = p + n - 1; p < q; ++p, --q)
+		c = *p, *p = *q, *q = c;
+
+	return lpszDest;
+}
+
+#define itoa(v, p, r)		_n_2_c<INT, char>((v), (p), (r))
+#define ltoa(v, p, r)		_n_2_c<LONG, char>((v), (p), (r))
+#define lltoa(v, p, r)		_n_2_c<LLONG, char>((v), (p), (r))
+#define uitoa(v, p, r)		_n_2_c<UINT, char>((v), (p), (r))
+#define ultoa(v, p, r)		_n_2_c<ULONG, char>((v), (p), (r))
+#define ulltoa(v, p, r)		_n_2_c<ULLONG, char>((v), (p), (r))
+#define itow(v, p, r)		_n_2_c<INT, wchar_t>((v), (p), (r))
+#define ltow(v, p, r)		_n_2_c<LONG, wchar_t>((v), (p), (r))
+#define lltow(v, p, r)		_n_2_c<LLONG, wchar_t>((v), (p), (r))
+#define uitow(v, p, r)		_n_2_c<UINT, wchar_t>((v), (p), (r))
+#define ultow(v, p, r)		_n_2_c<ULONG, wchar_t>((v), (p), (r))
+#define ulltow(v, p, r)		_n_2_c<ULLONG, wchar_t>((v), (p), (r))
+
+#define HEX_CHAR_TO_VALUE(c)			(c <= '9' ? c - '0' : (c <= 'F' ? c - 'A' + 0x0A : c - 'a' + 0X0A))
+#define HEX_DOUBLE_CHAR_TO_VALUE(pc)	((BYTE)(((HEX_CHAR_TO_VALUE(*(pc))) << 4) | (HEX_CHAR_TO_VALUE(*((pc) + 1)))))
+#define HEX_VALUE_TO_CHAR(n)			(n <= 9 ? n + '0' : (n <= 'F' ? n + 'A' - 0X0A : n + 'a' - 0X0A))
+#define HEX_VALUE_TO_DOUBLE_CHAR(pc, n)	{*(pc) = (BYTE)HEX_VALUE_TO_CHAR((n >> 4)); *((pc) + 1) = (BYTE)HEX_VALUE_TO_CHAR((n & 0X0F));}

+ 40 - 0
common/GeneralHelper.h

@@ -0,0 +1,40 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "hpsocket/GlobalErrno.h"
+#include "Singleton.h"
+#include "STLHelper.h"
+#include "FuncHelper.h"
+#include "StringT.h"
+#include "SysHelper.h"
+#include "PrivateHeap.h"
+#include "Semaphore.h"
+#include "RWLock.h"
+#include "BufferPtr.h"
+#include "Event.h"
+#include "CriSec.h"
+#include "Thread.h"
+#include "SignalHandler.h"

+ 348 - 0
common/IODispatcher.cpp

@@ -0,0 +1,348 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "IODispatcher.h"
+#include "FuncHelper.h"
+
+#include <signal.h>
+#include <pthread.h>
+
+volatile UINT CIODispatcher::sm_uiNum		= MAXUINT;
+LPCTSTR CIODispatcher::WORKER_THREAD_PREFIX	= _T("io-disp-");
+
+BOOL CIODispatcher::Start(IIOHandler* pHandler, int iWorkerMaxEvents, int iWorkers)
+{
+	ASSERT_CHECK_EINVAL(pHandler && iWorkerMaxEvents >= 0 && iWorkers >= 0);
+	CHECK_ERROR(!HasStarted(), ERROR_INVALID_STATE);
+
+	if(iWorkerMaxEvents == 0)	iWorkerMaxEvents = DEF_WORKER_MAX_EVENTS;
+	if(iWorkers == 0)			iWorkers = DEFAULT_WORKER_THREAD_COUNT;
+
+	m_iMaxEvents = iWorkerMaxEvents;
+	m_iWorkers	 = iWorkers;
+	m_pHandler	 = pHandler;
+
+	m_evExit = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC | EFD_SEMAPHORE);
+
+	if(IS_INVALID_FD(m_evExit))
+		goto START_ERROR;
+
+	m_pContexts = make_unique<TDispContext[]>(m_iWorkers);
+
+	for(int i = 0; i < m_iWorkers; i++)
+	{
+		TDispContext& ctx = m_pContexts[i];
+
+		ctx.m_iIndex = i;
+
+		ctx.m_epoll = epoll_create1(EPOLL_CLOEXEC);
+		CHECK_ERROR_FD(ctx.m_epoll);
+
+		ctx.m_evCmd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+
+		if(IS_INVALID_FD(ctx.m_evCmd))
+			goto START_ERROR;
+
+		if(!VERIFY(AddFD(i, ctx.m_evCmd, EPOLLIN | EPOLLET, &ctx.m_evCmd)))
+			goto START_ERROR;
+
+		if(!VERIFY(AddFD(i, m_evExit, EPOLLIN, &m_evExit)))
+			goto START_ERROR;
+
+		sigset_t ss;
+		sigemptyset(&ss);
+		sigaddset(&ss, SIGPIPE);
+
+		VERIFY_IS_NO_ERROR(pthread_sigmask(SIG_BLOCK, &ss, nullptr));
+
+		ctx.m_pWorker = make_unique<CWorkerThread>();
+
+		if(!VERIFY(ctx.m_pWorker->Start(this, &CIODispatcher::WorkerProc, &ctx)))
+			goto START_ERROR;
+	}
+
+	return TRUE;
+
+START_ERROR:
+	EXECUTE_RESTORE_ERROR(Stop(FALSE));
+	return FALSE;
+}
+
+BOOL CIODispatcher::Stop(BOOL bCheck)
+{
+	if(bCheck) CHECK_ERROR(HasStarted(), ERROR_INVALID_STATE);
+
+	BOOL isOK = TRUE;
+
+	if(m_pContexts)
+	{
+		isOK &= IS_NO_ERROR(eventfd_write(m_evExit, m_iWorkers));
+
+		for(int i = 0; i < m_iWorkers; i++)
+		{
+			TDispContext& ctx = m_pContexts[i];
+
+			if(ctx.m_pWorker)
+				isOK &= ctx.m_pWorker->Join();
+
+			if(!ctx.m_queue.IsEmpty())
+			{
+				TDispCommand* pCmd = nullptr;
+
+				while(ctx.m_queue.PopFront(&pCmd))
+					TDispCommand::Destruct(pCmd);
+
+				VERIFY(ctx.m_queue.IsEmpty());
+			}
+
+			if(IS_VALID_FD(ctx.m_evCmd))
+				isOK &= IS_NO_ERROR(close(ctx.m_evCmd));
+
+			if(IS_VALID_FD(ctx.m_epoll))
+				isOK &= IS_NO_ERROR(close(ctx.m_epoll));
+		}
+	}
+
+	if(IS_VALID_FD(m_evExit))
+		isOK &= IS_NO_ERROR(close(m_evExit));
+
+	Reset();
+
+	return isOK;
+}
+
+VOID CIODispatcher::Reset()
+{
+	m_uiSeq		= MAXUINT;
+	m_iWorkers	= 0;
+	m_iMaxEvents= 0;
+	m_evExit	= INVALID_FD;
+	m_pHandler	= nullptr;
+	m_pContexts	= nullptr;
+}
+
+VOID CIODispatcher::MakePrefix()
+{
+	UINT uiNumber = ::InterlockedIncrement(&sm_uiNum);
+
+	m_strPrefix.Format(_T("%s%u-"), WORKER_THREAD_PREFIX, uiNumber);
+}
+
+TDispContext& CIODispatcher::GetContext(int idx, FD fd)
+{
+	if(idx < 0) idx = fd;
+	ASSERT(idx >= 0);
+	if(idx >= m_iWorkers) idx %= m_iWorkers;
+
+	return m_pContexts[idx];
+}
+
+BOOL CIODispatcher::SendCommandByIndex(int idx, USHORT t, UINT_PTR wp, UINT_PTR lp)
+{
+	return SendCommandByIndex(idx, TDispCommand::Construct(t, wp, lp));
+}
+
+BOOL CIODispatcher::SendCommandByIndex(int idx, TDispCommand* pCmd)
+{
+	TDispContext& ctx = GetContextByIndex(idx);
+	return SendCommand(ctx, pCmd);
+}
+
+BOOL CIODispatcher::SendCommandByFD(FD fd, USHORT t, UINT_PTR wp, UINT_PTR lp)
+{
+	return SendCommandByFD(fd, TDispCommand::Construct(t, wp, lp));
+}
+
+BOOL CIODispatcher::SendCommandByFD(FD fd, TDispCommand* pCmd)
+{
+	TDispContext& ctx = GetContextByFD(fd);
+	return SendCommand(ctx, pCmd);
+}
+
+BOOL CIODispatcher::SendCommand(TDispContext& ctx, TDispCommand* pCmd)
+{
+	ctx.m_queue.PushBack(pCmd);
+	return VERIFY_IS_NO_ERROR(eventfd_write(ctx.m_evCmd, 1));
+}
+
+BOOL CIODispatcher::CtlFD(int idx, FD fd, int op, UINT mask, PVOID pv)
+{
+	const TDispContext& ctx = GetContext(idx, fd);
+
+	epoll_event evt = {mask, pv};
+	return IS_NO_ERROR(epoll_ctl(ctx.m_epoll, op, fd, &evt));
+}
+
+int CIODispatcher::WorkerProc(TDispContext* pContext)
+{
+	::SetSequenceThreadName(SELF_THREAD_ID, m_strPrefix, m_uiSeq);
+
+	m_pHandler->OnDispatchThreadStart(SELF_THREAD_ID);
+
+	BOOL bRun						  = TRUE;
+	unique_ptr<epoll_event[]> pEvents = make_unique<epoll_event[]>(m_iMaxEvents);
+
+	while(bRun)
+	{
+		int rs = NO_EINTR_INT(epoll_pwait(pContext->m_epoll, pEvents.get(), m_iMaxEvents, INFINITE, nullptr));
+
+		if(rs <= TIMEOUT)
+			ERROR_ABORT();
+
+		for(int i = 0; i < rs; i++)
+		{
+			UINT events	= pEvents[i].events;
+			PVOID ptr	= pEvents[i].data.ptr;
+
+			if(ptr == &pContext->m_evCmd)
+				ProcessCommand(pContext, events);
+			else if(ptr == &m_evExit)
+				bRun = ProcessExit(pContext, events);
+			else
+				ProcessIo(pContext, ptr, events);
+		}
+	}
+
+	m_pHandler->OnDispatchThreadEnd(SELF_THREAD_ID);
+
+	return 0;
+}
+
+BOOL CIODispatcher::ProcessCommand(TDispContext* pContext, UINT events)
+{
+	if(events & _EPOLL_ALL_ERROR_EVENTS)
+		ERROR_ABORT();
+
+	if(!(events & EPOLLIN))
+		return FALSE;
+
+	BOOL isOK = TRUE;
+
+	eventfd_t v;
+
+	int rs = eventfd_read(pContext->m_evCmd, &v);
+
+	if(IS_NO_ERROR(rs))
+	{
+		ASSERT(v > 0);
+
+		TDispCommand* pCmd = nullptr;
+
+		while(pContext->m_queue.PopFront(&pCmd))
+		{
+			m_pHandler->OnCommand(pContext, pCmd);
+			TDispCommand::Destruct(pCmd);
+		}
+	}
+	else if(IS_HAS_ERROR(rs))
+	{
+		ASSERT(IS_WOULDBLOCK_ERROR());
+
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+BOOL CIODispatcher::ProcessExit(const TDispContext* pContext, UINT events)
+{
+	if(events & _EPOLL_ALL_ERROR_EVENTS)
+		ERROR_ABORT();
+
+	if(!(events & EPOLLIN))
+		return TRUE;
+
+	BOOL bRun = TRUE;
+
+	eventfd_t v;
+
+	int rs = eventfd_read(m_evExit, &v);
+
+	if(IS_HAS_ERROR(rs))
+		ASSERT(IS_WOULDBLOCK_ERROR());
+	else
+	{
+		ASSERT(v == 1);
+		bRun = FALSE;
+	}
+
+	return bRun;
+}
+
+BOOL CIODispatcher::ProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	if(!m_pHandler->OnBeforeProcessIo(pContext, pv, events))
+		return FALSE;
+
+	BOOL rs = DoProcessIo(pContext, pv, events);
+	m_pHandler->OnAfterProcessIo(pContext, pv, events, rs);
+
+	return rs;
+}
+
+BOOL CIODispatcher::DoProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
+{
+	if(events & EPOLLERR)
+		return m_pHandler->OnError(pContext, pv, events);
+	if((events & EPOLLPRI) && !m_pHandler->OnReadyPrivilege(pContext, pv, events))
+		return FALSE;
+	if((events & EPOLLIN) && !m_pHandler->OnReadyRead(pContext, pv, events))
+		return FALSE;
+	if((events & EPOLLOUT) && !m_pHandler->OnReadyWrite(pContext, pv, events))
+		return FALSE;
+	if((events & (_EPOLL_HUNGUP_EVENTS)) && !m_pHandler->OnHungUp(pContext, pv, events))
+		return FALSE;
+
+	return TRUE;
+}
+
+FD CIODispatcher::AddTimer(int idx, LLONG llInterval, PVOID pv)
+{
+	FD fdTimer = ::CreateTimer(llInterval);
+
+	if(IS_VALID_FD(fdTimer))
+	{
+		if(!AddFD(idx, fdTimer, EPOLLIN | EPOLLET, pv))
+		{
+			close(fdTimer);
+			fdTimer = INVALID_FD;
+		}
+	}
+
+	return fdTimer;
+}
+
+BOOL CIODispatcher::DelTimer(int idx, FD fdTimer)
+{
+	BOOL isOK = FALSE;
+
+	if(IS_VALID_FD(fdTimer))
+	{
+		if(DelFD(idx, fdTimer))
+			isOK = TRUE;
+
+		close(fdTimer);
+	}
+
+	return isOK;
+}

+ 272 - 0
common/IODispatcher.h

@@ -0,0 +1,272 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "Singleton.h"
+#include "RingBuffer.h"
+#include "Thread.h"
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/timerfd.h>
+
+#include <memory>
+
+using namespace std;
+
+#define _EPOLL_READ_PRI_EVENTS		(EPOLLPRI | EPOLLRDHUP)
+#define _EPOLL_READ_EVENTS			(EPOLLIN | EPOLLRDHUP)
+#define _EPOLL_ALL_READ_EVENTS		(_EPOLL_READ_EVENTS | _EPOLL_READ_PRI_EVENTS)
+#define _EPOLL_WRITE_EVENTS			(EPOLLOUT)
+#define _EPOLL_NORMAL_RW_EVENTS		(_EPOLL_READ_EVENTS | _EPOLL_WRITE_EVENTS)
+#define _EPOLL_ALL_RW_EVENTS		(_EPOLL_ALL_READ_EVENTS | _EPOLL_WRITE_EVENTS)
+#define _EPOLL_ERROR_EVENTS			(EPOLLERR)
+#define _EPOLL_HUNGUP_EVENTS		(EPOLLHUP | EPOLLRDHUP)
+#define _EPOLL_ALL_ERROR_EVENTS		(_EPOLL_ERROR_EVENTS | _EPOLL_HUNGUP_EVENTS)
+#define _EPOLL_ALL_NORMAL_EVENTS	(_EPOLL_NORMAL_RW_EVENTS | _EPOLL_ALL_ERROR_EVENTS)
+#define _EPOLL_ALL_EVENTS			(_EPOLL_ALL_RW_EVENTS | _EPOLL_ALL_ERROR_EVENTS)
+
+#define DISP_EVENT_FLAG_R			0x1
+#define DISP_EVENT_FLAG_W			0x2
+#define DISP_EVENT_FLAG_H			0x4
+
+#define RETRIVE_EVENT_FLAG_R(evt)	((evt) & (_EPOLL_ALL_READ_EVENTS) ? DISP_EVENT_FLAG_R : 0)
+#define RETRIVE_EVENT_FLAG_W(evt)	((evt) & (_EPOLL_WRITE_EVENTS) ? DISP_EVENT_FLAG_W : 0)
+#define RETRIVE_EVENT_FLAG_RW(evt)	(RETRIVE_EVENT_FLAG_R(evt) | RETRIVE_EVENT_FLAG_W(evt))
+#define RETRIVE_EVENT_FLAG_H(evt)	((evt) & (_EPOLL_HUNGUP_EVENTS) ? DISP_EVENT_FLAG_H : 0)
+
+#ifndef EPOLLEXCLUSIVE
+	#define EPOLLEXCLUSIVE			(1u << 28)
+#endif
+
+#define MAYBE_EPOLLEXCLUSIVE		(::IsKernelVersionAbove(4, 5, 0) ? EPOLLEXCLUSIVE : 0)
+
+// ------------------------------------------------------------------------------------------------------------------------------------------------------- //
+
+struct	TDispCommand;
+class	CIODispatcher;
+
+struct TDispContext
+{
+	friend class CIODispatcher;
+
+	using CCommandQueue = CCASQueue<TDispCommand>;
+	using CWorkerThread = CThread<CIODispatcher, TDispContext, int>;
+
+public:
+	int GetIndex()		 const {return m_iIndex;}
+	THR_ID GetThreadId() const {return m_pWorker != nullptr ? m_pWorker->GetThreadID() : 0;}
+
+public:
+	TDispContext()	{Reset();}
+	~TDispContext() = default;
+
+	DECLARE_NO_COPY_CLASS(TDispContext)
+
+private:
+	VOID Reset()
+	{
+		m_iIndex	= -1;
+		m_epoll		= INVALID_FD;
+		m_evCmd		= INVALID_FD;
+		m_pWorker	= nullptr;
+	}
+
+private:
+	int	m_iIndex;
+	FD	m_epoll;
+	FD	m_evCmd;
+
+	CCommandQueue				m_queue;
+	unique_ptr<CWorkerThread>	m_pWorker;
+};
+
+struct TDispCommand
+{
+	USHORT	 type;
+	UINT_PTR wParam;
+	UINT_PTR lParam;
+
+	static TDispCommand* Construct(USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0)
+		{return new TDispCommand(t, wp, lp);}
+
+	static VOID Destruct(TDispCommand* p)
+		{if(p) delete p;}
+
+private:
+	TDispCommand(USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0)
+	: type(t), wParam(wp), lParam(lp)
+	{
+	}
+
+	~TDispCommand() = default;
+};
+
+// ------------------------------------------------------------------------------------------------------------------------------------------------------- //
+
+class IIOHandler
+{
+public:
+	virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd)					= 0;
+
+	virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)			= 0;
+	virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)	= 0;
+	virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)				= 0;
+	virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)				= 0;
+	virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)					= 0;
+	virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events)					= 0;
+	virtual BOOL OnReadyPrivilege(const TDispContext* pContext, PVOID pv, UINT events)			= 0;
+
+	virtual VOID OnDispatchThreadStart(THR_ID tid)												= 0;
+	virtual VOID OnDispatchThreadEnd(THR_ID tid)												= 0;
+
+public:
+	virtual ~IIOHandler() = default;
+};
+
+class CIOHandler : public IIOHandler
+{
+public:
+	virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd)					override {}
+
+	virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)			override {return TRUE;}
+	virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)	override {}
+	virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)				override {return TRUE;}
+	virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)					override {return TRUE;}
+	virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events)					override {return TRUE;}
+	virtual BOOL OnReadyPrivilege(const TDispContext* pContext, PVOID pv, UINT events)			override {return TRUE;}
+
+	virtual VOID OnDispatchThreadStart(THR_ID tid)												override {}
+	virtual VOID OnDispatchThreadEnd(THR_ID tid)												override {}
+};
+
+// ------------------------------------------------------------------------------------------------------------------------------------------------------- //
+
+class CIODispatcher
+{
+public:
+	static const int DEF_WORKER_MAX_EVENTS	= 64;
+
+	using CCommandQueue	= TDispContext::CCommandQueue;
+	using CWorkerThread	= TDispContext::CWorkerThread;
+
+public:
+	BOOL Start(IIOHandler* pHandler, int iWorkerMaxEvents = DEF_WORKER_MAX_EVENTS, int iWorkers = 0);
+	BOOL Stop(BOOL bCheck = TRUE);
+
+	BOOL SendCommandByIndex(int idx, TDispCommand* pCmd);
+	BOOL SendCommandByIndex(int idx, USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0);
+	BOOL SendCommandByFD(FD fd, TDispCommand* pCmd);
+	BOOL SendCommandByFD(FD fd, USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0);
+	BOOL SendCommand(TDispContext& ctx, TDispCommand* pCmd);
+
+	template<class _List, typename = enable_if_t<is_same<remove_reference_t<typename _List::reference>, TDispCommand*>::value>>
+	BOOL SendCommandsByIndex(int idx, const _List& cmds)
+	{
+		TDispContext& ctx = GetContextByIndex(idx);
+		return SendCommands(ctx, cmds);
+	}
+
+	template<class _List, typename = enable_if_t<is_same<remove_reference_t<typename _List::reference>, TDispCommand*>::value>>
+	BOOL SendCommandsByFD(FD fd, const _List& cmds)
+	{
+		TDispContext& ctx = GetContextByFD(fd);
+		return SendCommands(ctx, cmds);
+	}
+
+	template<class _List, typename = enable_if_t<is_same<remove_reference_t<typename _List::reference>, TDispCommand*>::value>>
+	BOOL SendCommands(TDispContext& ctx, const _List& cmds)
+	{
+		size_t size = cmds.size();
+		if(size == 0) return FALSE;
+
+		for(auto it = cmds.begin(), end = cmds.end(); it != end; ++it)
+			ctx.m_queue.PushBack(*it);
+
+		return VERIFY_IS_NO_ERROR(eventfd_write(ctx.m_evCmd, size));
+	}
+
+	BOOL AddFD(int idx, FD fd, UINT mask, PVOID pv)	{return CtlFD(idx, fd, EPOLL_CTL_ADD, mask, pv);}
+	BOOL ModFD(int idx, FD fd, UINT mask, PVOID pv)	{return CtlFD(idx, fd, EPOLL_CTL_MOD, mask, pv);}
+	BOOL DelFD(int idx, FD fd)						{return CtlFD(idx, fd, EPOLL_CTL_DEL, 0, nullptr);}
+	BOOL CtlFD(int idx, FD fd, int op, UINT mask, PVOID pv);
+
+
+	BOOL AddFD(FD fd, UINT mask, PVOID pv)			{return CtlFD(-1, fd, EPOLL_CTL_ADD, mask, pv);}
+	BOOL ModFD(FD fd, UINT mask, PVOID pv)			{return CtlFD(-1, fd, EPOLL_CTL_MOD, mask, pv);}
+	BOOL DelFD(FD fd)								{return CtlFD(-1, fd, EPOLL_CTL_DEL, 0, nullptr);}
+	BOOL CtlFD(FD fd, int op, UINT mask, PVOID pv)	{return CtlFD(-1, fd, op, mask, pv);}
+											
+	BOOL ProcessIo(const TDispContext* pContext, PVOID pv, UINT events);
+
+	FD AddTimer		(int idx, LLONG llInterval, PVOID pv);
+	BOOL DelTimer	(int idx, FD fdTimer);
+
+	FD AddTimer		(LLONG llInterval, PVOID pv)	{return AddTimer(-1, llInterval, pv);}
+	BOOL DelTimer	(FD fdTimer)					{return DelTimer(-1, fdTimer);}
+
+private:
+	int WorkerProc(TDispContext* pContext);
+	BOOL ProcessExit(const TDispContext* pContext, UINT events);
+	BOOL ProcessCommand(TDispContext* pContext, UINT events);
+	BOOL DoProcessIo(const TDispContext* pContext, PVOID pv, UINT events);
+
+	VOID Reset();
+	VOID MakePrefix();
+
+	TDispContext& GetContextByIndex(int idx)	{return GetContext(idx, -1);}
+	TDispContext& GetContextByFD(FD fd)			{return GetContext(-1, fd);}
+	TDispContext& GetContext(int idx, FD fd);
+
+public:
+	const TDispContext& GetContextRefByIndex(int idx)	{return GetContextByIndex(idx);}
+	const TDispContext& GetContextRefByFD(FD fd)		{return GetContextByFD(fd);}
+	const TDispContext& GetContextRef(int idx, FD fd)	{return GetContext(idx, fd);}
+
+	BOOL HasStarted()	{return m_pHandler && m_pContexts;}
+	int GetWorkers()	{return m_iWorkers;}
+	const TDispContext* GetContexts() {return m_pContexts.get();}
+
+	CIODispatcher()		{MakePrefix(); Reset();}
+	~CIODispatcher()	{if(HasStarted()) Stop();}
+
+	DECLARE_NO_COPY_CLASS(CIODispatcher)
+
+private:
+	static LPCTSTR WORKER_THREAD_PREFIX;
+	static volatile UINT sm_uiNum;
+
+	volatile UINT	m_uiSeq;
+	CString			m_strPrefix;
+
+private:
+	int				m_iWorkers;
+	int				m_iMaxEvents;
+
+	FD				m_evExit;
+
+	IIOHandler*				m_pHandler;
+	unique_ptr<TDispContext[]>	m_pContexts;
+};

+ 62 - 0
common/PollHelper.cpp

@@ -0,0 +1,62 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "PollHelper.h"
+#include "FuncHelper.h"
+
+long PollForSingleObject(pollfd& pfd, long lTimeout, const sigset_t* pSigSet)
+{
+	return PollForMultipleObjects(&pfd, 1, lTimeout, pSigSet);
+}
+
+long PollForMultipleObjects(pollfd pfds[], int iCount, long lTimeout, const sigset_t* pSigSet)
+{
+	ASSERT(iCount > 0 && iCount < (int)(sizeof(LONG) * 8));
+
+	timespec* pts = nullptr;
+
+	if(!IS_INFINITE(lTimeout))
+	{
+		pts = CreateLocalObject(timespec);
+		::MillisecondToTimespec(lTimeout, *pts);
+	}
+
+	while(TRUE)
+	{
+		int rs = NO_EINTR_INT(ppoll(pfds, iCount, pts, pSigSet));
+
+		if(rs <= TIMEOUT) return rs;
+
+		LONG lValue = 0L;
+
+		for(int i = 0; i < iCount; i++)
+		{
+			pollfd& pfd	= pfds[i];
+			
+			if(pfd.revents & _POLL_ALL_EVENTS)
+				lValue |= (1 << i);
+		}
+
+		return lValue;
+	}
+}

+ 51 - 0
common/PollHelper.h

@@ -0,0 +1,51 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+
+#include <poll.h>
+#include <signal.h>
+
+#if __USE_GNU
+	#define _POLL_READ_PRI_EVENTS	(POLLPRI | POLLRDHUP)
+	#define _POLL_READ_EVENTS		(POLLIN | POLLRDHUP)
+	#define _POLL_HUNGUP_EVENTS		(POLLHUP | POLLRDHUP)
+#else
+	#define _POLL_READ_PRI_EVENTS	(POLLPRI)
+	#define _POLL_READ_EVENTS		(POLLIN)
+	#define _POLL_HUNGUP_EVENTS		(POLLHUP)
+#endif
+
+#define _POLL_ALL_READ_EVENTS		(_POLL_READ_EVENTS | _POLL_READ_PRI_EVENTS)
+#define _POLL_WRITE_EVENTS			(POLLOUT)
+#define _POLL_NORMAL_RW_EVENTS		(_POLL_READ_EVENTS | _POLL_WRITE_EVENTS)
+#define _POLL_ALL_RW_EVENTS			(_POLL_ALL_READ_EVENTS | _POLL_WRITE_EVENTS)
+#define _POLL_ERROR_EVENTS			(POLLERR | POLLNVAL)
+#define _POLL_ALL_ERROR_EVENTS		(_POLL_ERROR_EVENTS | _POLL_HUNGUP_EVENTS)
+#define _POLL_ALL_NORMAL_EVENTS		(_POLL_NORMAL_RW_EVENTS | _POLL_ALL_ERROR_EVENTS)
+#define _POLL_ALL_EVENTS			(_POLL_ALL_RW_EVENTS | _POLL_ALL_ERROR_EVENTS)
+
+long PollForSingleObject(pollfd& pfd, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr);
+long PollForMultipleObjects(pollfd pfds[], int iCount, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr);

+ 152 - 0
common/PrivateHeap.h

@@ -0,0 +1,152 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "Singleton.h"
+
+#include <malloc.h>
+
+#define HEAP_ZERO_MEMORY	0x08     
+
+class CGlobalHeapImpl
+{
+public:
+	PVOID Alloc(SIZE_T dwSize, DWORD dwFlags = 0)
+	{
+		PVOID pv = malloc(dwSize);
+
+		if(!pv)
+			throw std::bad_alloc();
+
+		if(dwFlags & HEAP_ZERO_MEMORY)
+			ZeroMemory(pv, dwSize);
+		
+		return pv;
+	}
+
+	PVOID ReAlloc(PVOID pvMemory, SIZE_T dwSize, DWORD dwFlags = 0)
+	{
+		PVOID pv = realloc(pvMemory, dwSize);
+
+		if(!pv)
+		{
+			if(pvMemory)
+				free(pvMemory);
+
+			throw std::bad_alloc();
+		}
+
+		if(dwFlags & HEAP_ZERO_MEMORY)
+			ZeroMemory(pv, dwSize);
+
+		return pv;
+	}
+
+	BOOL Free(PVOID pvMemory, DWORD dwFlags = 0)
+	{
+		if(pvMemory)
+		{
+			free(pvMemory);
+			return TRUE;
+		}
+
+		return FALSE;
+	}
+
+	SIZE_T Compact	(DWORD dwFlags = 0)					{return -1;}
+	SIZE_T Size		(PVOID pvMemory, DWORD dwFlags = 0)	{return _msize(pvMemory);}
+
+	BOOL IsValid()	{return TRUE;}
+	BOOL Reset()	{return TRUE;}
+
+public:
+	CGlobalHeapImpl	(DWORD dwOptions = 0, SIZE_T dwInitSize = 0, SIZE_T dwMaxSize = 0) {}
+	~CGlobalHeapImpl()	{}
+
+	DECLARE_NO_COPY_CLASS(CGlobalHeapImpl)
+};
+
+#if !defined (_USE_CUSTOM_PRIVATE_HEAP)
+	using CPrivateHeap = CGlobalHeapImpl;
+#endif
+
+template<class T> class CPrivateHeapBuffer
+{
+public:
+	CPrivateHeapBuffer(CPrivateHeap& hpPrivate, SIZE_T dwSize = 0)
+	: m_hpPrivate	(hpPrivate)
+	, m_pvMemory	(nullptr)
+	{
+		ASSERT(m_hpPrivate.IsValid());
+		Alloc(dwSize);
+	}
+
+	~CPrivateHeapBuffer() {Free();}
+
+public:
+
+	T* Alloc(SIZE_T dwSize, DWORD dwFlags = 0)
+	{
+		if(IsValid())
+			Free();
+
+		if(dwSize > 0)
+			m_pvMemory = (T*)m_hpPrivate.Alloc(dwSize * sizeof(T), dwFlags);
+
+		return m_pvMemory;
+	}
+
+	T* ReAlloc(SIZE_T dwSize, DWORD dwFlags = 0)
+		{return m_pvMemory = (T*)m_hpPrivate.ReAlloc(m_pvMemory, dwSize * sizeof(T), dwFlags);}
+
+	SIZE_T Size(DWORD dwFlags = 0)
+		{return m_hpPrivate.Size(m_pvMemory, dwFlags) / sizeof(T);}
+
+	BOOL Free(DWORD dwFlags = 0)
+	{
+		BOOL isOK = TRUE;
+
+		if(IsValid())
+		{
+			isOK		= m_hpPrivate.Free(m_pvMemory, dwFlags);
+			m_pvMemory	= nullptr;
+		}
+
+		return isOK;
+	}
+
+	BOOL IsValid()					{return m_pvMemory != nullptr;}
+	operator T* ()			const	{return m_pvMemory;}
+	T& operator [] (int i)	const	{return *(m_pvMemory + i);}
+
+private:
+	CPrivateHeap&	m_hpPrivate;
+	T*				m_pvMemory;
+
+	DECLARE_NO_COPY_CLASS(CPrivateHeapBuffer)
+};
+
+using CPrivateHeapByteBuffer	= CPrivateHeapBuffer<BYTE>;
+using CPrivateHeapStrBuffer		= CPrivateHeapBuffer<TCHAR>;

+ 228 - 0
common/RWLock.cpp

@@ -0,0 +1,228 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "RWLock.h"
+
+CMutexRWLock::CMutexRWLock()
+	: m_nActive			(0)
+	, m_dwWriterTID		(0)
+{
+
+}
+
+CMutexRWLock::~CMutexRWLock()
+{
+	ASSERT(m_nActive	 == 0);
+	ASSERT(m_dwWriterTID == 0);
+}
+
+VOID CMutexRWLock::WaitToRead()
+{
+	BOOL bWait = FALSE;
+
+	{
+		CSpinLock locallock(m_cs);
+
+		if(m_nActive > 0)
+			++m_nActive;
+		else if(m_nActive == 0)
+		{
+			if(m_mtx.try_lock_shared())
+				++m_nActive;
+			else
+				bWait = TRUE;
+		}
+		else if(!IsOwner())
+			bWait = TRUE;
+	}
+
+	if(bWait)
+	{
+		m_mtx.lock_shared();
+
+		CSpinLock locallock(m_cs);
+		++m_nActive;
+	}
+}
+
+VOID CMutexRWLock::WaitToWrite()
+{
+	BOOL bWait = FALSE;
+
+	{
+		CSpinLock locallock(m_cs);
+
+		if(m_nActive > 0)
+			bWait = TRUE;
+		else if(m_nActive == 0)
+		{
+			if(m_mtx.try_lock())
+			{
+				SetOwner();
+				--m_nActive;
+			}
+			else
+				bWait = TRUE;
+		}
+		else
+		{
+			if(IsOwner())
+				--m_nActive;
+			else
+				bWait = TRUE;
+		}
+	}
+
+	if(bWait)
+	{
+		m_mtx.lock();
+
+		SetOwner();
+		--m_nActive;
+	}
+}
+
+VOID CMutexRWLock::ReadDone()
+{
+	ASSERT(m_nActive != 0);
+
+	if(m_nActive > 0)
+	{
+		{
+			CSpinLock locallock(m_cs);
+			--m_nActive;
+		}
+
+		m_mtx.unlock_shared();
+	}
+	else
+		ASSERT(IsOwner());
+}
+
+VOID CMutexRWLock::WriteDone()
+{
+	ASSERT(IsOwner());
+	ASSERT(m_nActive < 0);
+
+	BOOL bDone;
+
+	{
+		CSpinLock locallock(m_cs);
+		bDone = (++m_nActive == 0);
+	}
+
+	if(bDone)
+	{
+		DetachOwner();
+		m_mtx.unlock();
+	}
+	else
+		ASSERT(IsOwner());
+}
+
+CSEMRWLock::CSEMRWLock()
+	: m_nWaitingReaders	(0)
+	, m_nWaitingWriters	(0)
+	, m_nActive			(0)
+	, m_dwWriterTID		(0)
+{
+
+}
+
+CSEMRWLock::~CSEMRWLock()
+{
+	ASSERT(m_nActive	 == 0);
+	ASSERT(m_dwWriterTID == 0);
+}
+
+VOID CSEMRWLock::WaitToRead()
+{
+	CMutexLock2 lock(m_mtx);
+
+	if(IsOwner())
+		return;
+
+	++m_nWaitingReaders;
+
+	m_cvRead.wait(lock, [=]() -> BOOL
+	{
+		return m_nActive >= 0 && m_nWaitingWriters == 0;
+	});
+
+	--m_nWaitingReaders;
+	++m_nActive;
+}
+
+VOID CSEMRWLock::WaitToWrite()
+{
+	CMutexLock2 lock(m_mtx);
+
+	if(IsOwner())
+	{
+		--m_nActive;
+
+		return;
+	}
+
+	++m_nWaitingWriters;
+
+	m_cvWrite.wait(lock, [=]() -> BOOL
+	{
+		return m_nActive == 0;
+	});
+
+	--m_nWaitingWriters;
+	--m_nActive;
+
+	SetOwner();
+}
+
+VOID CSEMRWLock::ReadDone()
+{
+	CMutexLock2 locallock(m_mtx);
+
+	if(IsOwner())
+		return;
+
+	ASSERT(m_nActive > 0);
+
+	if(--m_nActive == 0 && m_nWaitingWriters > 0)
+		m_cvWrite.notify_one();
+}
+
+VOID CSEMRWLock::WriteDone()
+{
+	ASSERT(IsOwner());
+
+	CMutexLock2 lock(m_mtx);
+
+	if(++m_nActive == 0)
+	{
+		DetachOwner();
+
+		if(m_nWaitingWriters > 0)
+			m_cvWrite.notify_one();
+		else if(m_nWaitingReaders > 0)
+			m_cvRead.notify_all();
+	}
+}

+ 128 - 0
common/RWLock.h

@@ -0,0 +1,128 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "CriSec.h"
+
+#include <shared_mutex>
+#include <condition_variable>
+
+using namespace std;
+
+class CMutexRWLock
+{
+public:
+	VOID WaitToRead();
+	VOID WaitToWrite();
+	VOID ReadDone();
+	VOID WriteDone();
+
+private:
+	BOOL IsOwner()		{return ::IsSelfThread(m_dwWriterTID);}
+	VOID SetOwner()		{m_dwWriterTID = SELF_THREAD_ID;}
+	VOID DetachOwner()	{m_dwWriterTID = 0;}
+
+public:
+	CMutexRWLock();
+	~CMutexRWLock();
+
+	DECLARE_NO_COPY_CLASS(CMutexRWLock)
+
+private:
+	int		m_nActive;
+	THR_ID	m_dwWriterTID;
+
+	CSpinGuard			m_cs;
+	shared_timed_mutex	m_mtx;
+};
+
+class CSEMRWLock
+{
+public:
+	VOID WaitToRead();
+	VOID WaitToWrite();
+	VOID ReadDone();
+	VOID WriteDone();
+
+private:
+	BOOL IsOwner()		{BOOL bOwner = ::IsSelfThread(m_dwWriterTID); ASSERT(!bOwner || m_nActive < 0); return bOwner;}
+	VOID SetOwner()		{m_dwWriterTID = SELF_THREAD_ID;}
+	VOID DetachOwner()	{m_dwWriterTID = 0;}
+
+public:
+	CSEMRWLock();
+	~CSEMRWLock();
+
+	DECLARE_NO_COPY_CLASS(CSEMRWLock)
+
+private:
+	int		m_nWaitingReaders;
+	int		m_nWaitingWriters;
+	int		m_nActive;
+	THR_ID	m_dwWriterTID;
+
+	CMTX				m_mtx;
+	condition_variable	m_cvRead;
+	condition_variable	m_cvWrite;
+};
+
+template<class CLockObj> class CLocalReadLock
+{
+public:
+	CLocalReadLock(CLockObj& obj) : m_wait(obj) {m_wait.WaitToRead();}
+	~CLocalReadLock() {m_wait.ReadDone();}
+
+	DECLARE_NO_COPY_CLASS(CLocalReadLock)
+
+private:
+	CLockObj& m_wait;
+};
+
+template<class CLockObj> class CLocalWriteLock
+{
+public:
+	CLocalWriteLock(CLockObj& obj) : m_wait(obj) {m_wait.WaitToWrite();}
+	~CLocalWriteLock() {m_wait.WriteDone();}
+
+	DECLARE_NO_COPY_CLASS(CLocalWriteLock)
+
+private:
+	CLockObj& m_wait;
+};
+
+
+using CSimpleRWLock = shared_timed_mutex;
+using CReadLock		= shared_lock<shared_timed_mutex>;
+using CWriteLock	= lock_guard<shared_timed_mutex>;
+using CWriteLock2	= unique_lock<shared_timed_mutex>;
+
+#if !defined(_USE_MUTEX_RW_LOCK)
+	using CRWLock = CSEMRWLock;
+#else
+	using CRWLock = CMutexRWLock;
+#endif
+
+using CReentrantReadLock	= CLocalReadLock<CRWLock>;
+using CReentrantWriteLock	= CLocalWriteLock<CRWLock>;

+ 1731 - 0
common/RingBuffer.h

@@ -0,0 +1,1731 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "Singleton.h"
+#include "STLHelper.h"
+#include "FuncHelper.h"
+#include "RWLock.h"
+
+using namespace std;
+
+#define CACHE_LINE		64
+#define PACK_SIZE_OF(T)	(CACHE_LINE - sizeof(T) % CACHE_LINE)
+
+#if __WORDSIZE == 32
+	#pragma pack(push, 4)
+#endif
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+template <class T, class index_type = DWORD, bool adjust_index = false> class CRingCache
+{
+public:
+
+	enum EnGetResult {GR_FAIL = -1, GR_INVALID = 0, GR_VALID = 1};
+
+	typedef T*									TPTR;
+	typedef volatile T*							VTPTR;
+
+	typedef unordered_set<index_type>			IndexSet;
+	typedef typename IndexSet::const_iterator	IndexSetCI;
+	typedef typename IndexSet::iterator			IndexSetI;
+
+	static TPTR const E_EMPTY;
+	static TPTR const E_LOCKED;
+	static TPTR const E_MAX_STATUS;
+
+public:
+
+	static index_type& INDEX_INC(index_type& dwIndex)	{if(adjust_index) ++dwIndex; return dwIndex;}
+	static index_type& INDEX_DEC(index_type& dwIndex)	{if(adjust_index) --dwIndex; return dwIndex;}
+
+private:
+
+	index_type& INDEX_V2R(index_type& dwIndex)	{dwIndex %= m_dwSize; if(dwIndex == 0) dwIndex = m_dwSize; return dwIndex;}
+	VTPTR& INDEX_VAL(index_type dwIndex) {return *(m_pv + dwIndex);}
+
+public:
+
+	BOOL Put(TPTR pElement, index_type& dwIndex)
+	{
+		ASSERT(pElement != nullptr);
+
+		if(!IsValid()) return FALSE;
+
+		BOOL isOK = FALSE;
+
+		while(true)
+		{
+			if(!HasSpace())
+				break;
+
+			DWORD dwCurSeq			= m_dwCurSeq;
+			index_type dwCurIndex	= dwCurSeq % m_dwSize;
+			VTPTR& pValue			= INDEX_VAL(dwCurIndex);
+
+			if(pValue == E_EMPTY)
+			{
+				if(::InterlockedCompareExchangePointer(&pValue, pElement, E_EMPTY) == E_EMPTY)
+				{
+					::InterlockedIncrement(&m_dwCount);
+					::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq);
+
+					dwIndex = INDEX_INC(dwCurIndex);
+					isOK	= TRUE;
+
+					if(pElement != E_LOCKED)
+						EmplaceIndex(dwIndex);
+
+					break;
+				}
+			}
+
+			::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq);
+		}
+
+		return isOK;
+	}
+
+	EnGetResult GetEx(index_type dwIndex, TPTR* ppElement)
+	{
+		return Get(INDEX_V2R(dwIndex), ppElement);
+	}
+
+	EnGetResult Get(index_type dwIndex, TPTR* ppElement)
+	{
+		ASSERT(dwIndex <= m_dwSize);
+		ASSERT(ppElement != nullptr);
+
+		if(!IsValid() || INDEX_DEC(dwIndex) >= m_dwSize)
+		{
+			*ppElement = nullptr;
+			return GR_FAIL;
+		}
+
+		*ppElement = (TPTR)INDEX_VAL(dwIndex);
+
+		return IsValidElement(*ppElement) ? GR_VALID : GR_INVALID;
+	}
+
+	BOOL SetEx(index_type dwIndex, TPTR pElement, TPTR* ppOldElement = nullptr)
+	{
+		return Set(INDEX_V2R(dwIndex), pElement, ppOldElement);
+	}
+
+	BOOL Set(index_type dwIndex, TPTR pElement, TPTR* ppOldElement = nullptr)
+	{
+		TPTR pElement2 = nullptr;
+
+		if(Get(dwIndex, &pElement2) == GR_FAIL)
+			return FALSE;
+
+		if(ppOldElement != nullptr)
+			*ppOldElement = pElement2;
+
+		if(pElement == pElement2)
+			return FALSE;
+
+		int f1 = 0;
+		int f2 = 0;
+
+		if(pElement == E_EMPTY)
+		{
+			if(pElement2 == E_LOCKED)
+				f1 = -1;
+			else
+				f1 = f2 = -1;
+		}
+		else if(pElement == E_LOCKED)
+		{
+			if(pElement2 == E_EMPTY)
+				f1 = 1;
+			else
+				f2 = -1;
+		}
+		else
+		{
+			if(pElement2 == E_EMPTY)
+				f1 = f2 = 1;
+			else if(pElement2 == E_LOCKED)
+				f2 = 1;
+		}
+
+		BOOL bSetValueFirst		= (f1 + f2 >= 0);
+		index_type dwOuterIndex	= dwIndex;
+
+		INDEX_DEC(dwIndex);
+
+		if(bSetValueFirst)	INDEX_VAL(dwIndex) = pElement;
+		if(f1 > 0)			::InterlockedIncrement(&m_dwCount);
+		if(f2 != 0)			(f2 > 0) ? EmplaceIndex(dwOuterIndex) : EraseIndex(dwOuterIndex);
+		if(f1 < 0)			::InterlockedDecrement(&m_dwCount);
+		if(!bSetValueFirst) INDEX_VAL(dwIndex) = pElement;
+
+		ASSERT(Spaces() <= Size());
+
+		return TRUE;
+	}
+
+	BOOL RemoveEx(index_type dwIndex, TPTR* ppElement = nullptr)
+	{
+		return Remove(INDEX_V2R(dwIndex), ppElement);
+	}
+
+	BOOL Remove(index_type dwIndex, TPTR* ppElement = nullptr)
+	{
+		return Set(dwIndex, E_EMPTY, ppElement);
+	}
+
+	BOOL AcquireLock(index_type& dwIndex)
+	{
+		return Put(E_LOCKED, dwIndex);
+	}
+
+	BOOL ReleaseLock(index_type dwIndex, TPTR pElement)
+	{
+		ASSERT(pElement == nullptr || IsValidElement(pElement));
+
+		TPTR pElement2 = nullptr;
+		Get(dwIndex, &pElement2);
+
+		ASSERT(pElement2 == E_LOCKED);
+
+		if(pElement2 != E_LOCKED)
+			return FALSE;
+
+		return Set(dwIndex, pElement);
+	}
+
+public:
+
+	void Reset(DWORD dwSize = 0)
+	{
+		if(IsValid())
+			Destroy();
+		if(dwSize > 0)
+			Create(dwSize);
+	}
+
+	BOOL GetAllElementIndexes(index_type ids[], DWORD& dwCount, BOOL bCopy = TRUE)
+	{
+		DWORD dwSize = Elements();
+
+		if(ids == nullptr || dwCount == 0)
+		{
+			dwCount = dwSize;
+			return FALSE;
+		}
+
+		if(dwSize == 0)
+		{
+			dwCount = 0;
+			return TRUE;
+		}
+
+		IndexSet* pIndexes = &m_indexes;
+
+		if(bCopy)
+		{
+			pIndexes = new IndexSet;
+			CopyIndexes(*pIndexes);
+		}
+
+		DWORD i = 0;
+
+		for(auto it = pIndexes->begin(), end = pIndexes->end(); i < dwCount && it != end; ++i, ++it)
+			ids[i] = *it;
+
+		if(bCopy) delete pIndexes;
+
+		dwCount = i;
+		return TRUE;
+	}
+	
+	unique_ptr<index_type[]> GetAllElementIndexes(DWORD& dwCount, BOOL bCopy = TRUE)
+	{
+		dwCount = (DWORD)m_indexes.size();
+		unique_ptr<index_type[]> ids(new index_type[dwCount]);
+
+		if(dwCount > 0)
+			GetAllElementIndexes(ids.get(), dwCount, bCopy);
+
+		return ids;
+	}
+	
+	IndexSet& CopyIndexes(IndexSet& indexes)
+	{
+		{
+			CReadLock locallock(m_cs);
+			indexes = m_indexes;
+		}
+
+		return indexes;
+	}
+
+	static BOOL IsValidElement(TPTR pElement) {return pElement > E_MAX_STATUS;}
+
+	IndexSet& Indexes	()	{ return m_indexes;}
+	DWORD Size			()	{return m_dwSize;}
+	DWORD Elements		()	{return (DWORD)m_indexes.size();}
+	DWORD Spaces		()	{return m_dwSize - m_dwCount;}
+	BOOL HasSpace		()	{return m_dwCount < m_dwSize;}
+	BOOL IsEmpty		()	{return m_dwCount == 0;}
+	BOOL IsValid		()	{return m_pv != nullptr;}
+
+private:
+
+	void Create(DWORD dwSize)
+	{
+		ASSERT(!IsValid() && dwSize > 0);
+
+		m_dwCurSeq	= 0;
+		m_dwCount	= 0;
+		m_dwSize	= dwSize;
+		m_pv		= (VTPTR*)malloc(m_dwSize * sizeof(TPTR));
+
+		::ZeroMemory(m_pv, m_dwSize * sizeof(TPTR));
+	}
+
+	void Destroy()
+	{
+		ASSERT(IsValid());
+
+		m_indexes.clear();
+		free((void*)m_pv);
+
+		m_pv		= nullptr;
+		m_dwSize	= 0;
+		m_dwCount	= 0;
+		m_dwCurSeq	= 0;
+	}
+
+	void EmplaceIndex(index_type dwIndex)
+	{
+		CWriteLock locallock(m_cs);
+		m_indexes.emplace(dwIndex);
+	}
+
+	void EraseIndex(index_type dwIndex)
+	{
+		CWriteLock locallock(m_cs);
+		m_indexes.erase(dwIndex);
+	}
+
+public:
+	CRingCache	(DWORD dwSize = 0)
+		: m_pv		(nullptr)
+		, m_dwSize	(0)
+		, m_dwCount	(0)
+		, m_dwCurSeq(0)
+	{
+		Reset(dwSize);
+	}
+
+	~CRingCache()
+	{
+		Reset(0);
+	}
+
+private:
+	CRingCache(const CRingCache&);
+	CRingCache operator = (const CRingCache&);
+
+private:
+	DWORD				m_dwSize;
+	VTPTR*				m_pv;
+	char				pack1[PACK_SIZE_OF(VTPTR*)];
+	volatile DWORD		m_dwCurSeq;
+	char				pack2[PACK_SIZE_OF(DWORD)];
+	volatile DWORD		m_dwCount;
+	char				pack3[PACK_SIZE_OF(DWORD)];
+
+	CSimpleRWLock		m_cs;
+	IndexSet			m_indexes;
+};
+
+template <class T, class index_type, bool adjust_index> T* const CRingCache<T, index_type, adjust_index>::E_EMPTY		= (T*)0x00;
+template <class T, class index_type, bool adjust_index> T* const CRingCache<T, index_type, adjust_index>::E_LOCKED		= (T*)0x01;
+template <class T, class index_type, bool adjust_index> T* const CRingCache<T, index_type, adjust_index>::E_MAX_STATUS	= (T*)0x0F;
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+template <class T, class index_type = DWORD, bool adjust_index = false> class CRingCache2
+{
+public:
+
+	enum EnGetResult {GR_FAIL = -1, GR_INVALID = 0, GR_VALID = 1};
+
+	typedef T*									TPTR;
+	typedef volatile T*							VTPTR;
+
+	typedef unordered_set<index_type>			IndexSet;
+	typedef typename IndexSet::const_iterator	IndexSetCI;
+	typedef typename IndexSet::iterator			IndexSetI;
+
+	static TPTR const E_EMPTY;
+	static TPTR const E_LOCKED;
+	static TPTR const E_MAX_STATUS;
+	static DWORD const MAX_SIZE;
+
+public:
+
+	static index_type& INDEX_INC(index_type& dwIndex)	{if(adjust_index) ++dwIndex; return dwIndex;}
+	static index_type& INDEX_DEC(index_type& dwIndex)	{if(adjust_index) --dwIndex; return dwIndex;}
+
+	index_type& INDEX_R2V(index_type& dwIndex)			{dwIndex += *(m_px + dwIndex) * m_dwSize; return dwIndex;}
+
+	BOOL INDEX_V2R(index_type& dwIndex)
+	{
+		index_type m = dwIndex % m_dwSize;
+		BYTE x		 = *(m_px + m);
+
+		if(dwIndex / m_dwSize != x)
+			return FALSE;
+
+		dwIndex = m;
+		return TRUE;
+	}
+
+
+private:
+
+	VTPTR& INDEX_VAL(index_type dwIndex) {return *(m_pv + dwIndex);}
+
+public:
+
+	BOOL Put(TPTR pElement, index_type& dwIndex)
+	{
+		ASSERT(pElement != nullptr);
+
+		if(!IsValid()) return FALSE;
+
+		BOOL isOK = FALSE;
+
+		while(true)
+		{
+			if(!HasSpace())
+				break;
+
+			DWORD dwCurSeq			= m_dwCurSeq;
+			index_type dwCurIndex	= dwCurSeq % m_dwSize;
+			VTPTR& pValue			= INDEX_VAL(dwCurIndex);
+
+			if(pValue == E_EMPTY)
+			{
+				if(::InterlockedCompareExchangePointer(&pValue, pElement, E_EMPTY) == E_EMPTY)
+				{
+					::InterlockedIncrement(&m_dwCount);
+					::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq);
+
+					dwIndex = INDEX_INC(INDEX_R2V(dwCurIndex));
+					isOK	= TRUE;
+
+					if(pElement != E_LOCKED)
+						EmplaceIndex(dwIndex);
+
+					break;
+				}
+			}
+
+			::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq);
+		}
+
+		return isOK;
+	}
+
+	EnGetResult Get(index_type dwIndex, TPTR* ppElement, index_type* pdwRealIndex = nullptr)
+	{
+		ASSERT(ppElement != nullptr);
+
+		if(!IsValid() || !INDEX_V2R(INDEX_DEC(dwIndex)))
+		{
+			*ppElement = nullptr;
+			return GR_FAIL;
+		}
+
+		*ppElement = (TPTR)INDEX_VAL(dwIndex);
+		if(pdwRealIndex) *pdwRealIndex = dwIndex;
+
+		return IsValidElement(*ppElement) ? GR_VALID : GR_INVALID;
+	}
+
+	BOOL Set(index_type dwIndex, TPTR pElement, TPTR* ppOldElement = nullptr, index_type* pdwRealIndex = nullptr)
+	{
+		TPTR pElement2 = nullptr;
+
+		if(pdwRealIndex == nullptr)
+			pdwRealIndex = CreateLocalObject(index_type);
+
+		if(Get(dwIndex, &pElement2, pdwRealIndex) == GR_FAIL)
+			return FALSE;
+
+		if(ppOldElement != nullptr)
+			*ppOldElement = pElement2;
+
+		if(pElement == pElement2)
+			return FALSE;
+
+		int f1 = 0;
+		int f2 = 0;
+
+		if(pElement == E_EMPTY)
+		{
+			if(pElement2 == E_LOCKED)
+				f1 = -1;
+			else
+				f1 = f2 = -1;
+		}
+		else if(pElement == E_LOCKED)
+		{
+			if(pElement2 == E_EMPTY)
+				f1 = 1;
+			else
+				f2 = -1;
+		}
+		else
+		{
+			if(pElement2 == E_EMPTY)
+				f1 = f2 = 1;
+			else if(pElement2 == E_LOCKED)
+				f2 = 1;
+		}
+
+		BOOL bSetValueFirst		= (f1 + f2 >= 0);
+		index_type dwRealIndex	= *pdwRealIndex;
+
+		if(bSetValueFirst)	INDEX_VAL(dwRealIndex) = pElement;
+		if(f1 > 0)			::InterlockedIncrement(&m_dwCount);
+		if(f2 != 0)			(f2 > 0) ? EmplaceIndex(dwIndex) : EraseIndex(dwIndex);
+		if(f1 < 0)			{::InterlockedDecrement(&m_dwCount); ++(*(m_px + dwRealIndex));}
+		if(!bSetValueFirst) INDEX_VAL(dwRealIndex) = pElement;
+
+		ASSERT(Spaces() <= Size());
+
+		return TRUE;
+	}
+
+	BOOL Remove(index_type dwIndex, TPTR* ppElement = nullptr)
+	{
+		return Set(dwIndex, E_EMPTY, ppElement);
+	}
+
+	BOOL AcquireLock(index_type& dwIndex)
+	{
+		return Put(E_LOCKED, dwIndex);
+	}
+
+	BOOL ReleaseLock(index_type dwIndex, TPTR pElement)
+	{
+		ASSERT(pElement == nullptr || IsValidElement(pElement));
+
+		TPTR pElement2 = nullptr;
+		Get(dwIndex, &pElement2);
+
+		ASSERT(pElement2 == E_LOCKED);
+
+		if(pElement2 != E_LOCKED)
+			return FALSE;
+
+		return Set(dwIndex, pElement);
+	}
+
+public:
+
+	void Reset(DWORD dwSize = 0)
+	{
+		if(IsValid())
+			Destroy();
+		if(dwSize > 0)
+			Create(dwSize);
+	}
+	
+	BOOL GetAllElementIndexes(index_type ids[], DWORD& dwCount, BOOL bCopy = TRUE)
+	{
+		DWORD dwSize = Elements();
+
+		if(ids == nullptr || dwCount == 0)
+		{
+			dwCount = dwSize;
+			return FALSE;
+		}
+
+		if(dwSize == 0)
+		{
+			dwCount = 0;
+			return TRUE;
+		}
+
+		IndexSet* pIndexes = &m_indexes;
+
+		if(bCopy)
+		{
+			pIndexes = new IndexSet;
+			CopyIndexes(*pIndexes);
+		}
+
+		DWORD i = 0;
+
+		for(auto it = pIndexes->begin(), end = pIndexes->end(); i < dwCount && it != end; ++i, ++it)
+			ids[i] = *it;
+
+		if(bCopy) delete pIndexes;
+
+		dwCount = i;
+		return TRUE;
+	}
+	
+	unique_ptr<index_type[]> GetAllElementIndexes(DWORD& dwCount, BOOL bCopy = TRUE)
+	{
+		dwCount = (DWORD)m_indexes.size();
+		unique_ptr<index_type[]> ids(new index_type[dwCount]);
+
+		if(dwCount > 0)
+			GetAllElementIndexes(ids.get(), dwCount, bCopy);
+
+		return ids;
+	}
+	
+	IndexSet& CopyIndexes(IndexSet& indexes)
+	{
+		{
+			CReadLock locallock(m_cs);
+			indexes = m_indexes;
+		}
+
+		return indexes;
+	}
+
+	static BOOL IsValidElement(TPTR pElement) {return pElement > E_MAX_STATUS;}
+
+	IndexSet& Indexes	()	{return m_indexes;}
+	DWORD Size			()	{return m_dwSize;}
+	DWORD Elements		()	{return (DWORD)m_indexes.size();}
+	DWORD Spaces		()	{return m_dwSize - m_dwCount;}
+	BOOL HasSpace		()	{return m_dwCount < m_dwSize;}
+	BOOL IsEmpty		()	{return m_dwCount == 0;}
+	BOOL IsValid		()	{return m_pv != nullptr;}
+
+private:
+
+	void Create(DWORD dwSize)
+	{
+		ASSERT(!IsValid() && dwSize > 0 && dwSize <= MAX_SIZE);
+
+		m_dwCurSeq	= 0;
+		m_dwCount	= 0;
+		m_dwSize	= dwSize;
+		m_pv		= (VTPTR*)malloc(m_dwSize * sizeof(TPTR));
+		m_px		= (BYTE*)malloc(m_dwSize * sizeof(BYTE));
+
+		::ZeroMemory(m_pv, m_dwSize * sizeof(TPTR));
+		::ZeroMemory(m_px, m_dwSize * sizeof(BYTE));
+	}
+
+	void Destroy()
+	{
+		ASSERT(IsValid());
+
+		m_indexes.clear();
+		free((void*)m_pv);
+		free((void*)m_px);
+
+		m_pv		= nullptr;
+		m_px		= nullptr;
+		m_dwSize	= 0;
+		m_dwCount	= 0;
+		m_dwCurSeq	= 0;
+	}
+
+	void EmplaceIndex(index_type dwIndex)
+	{
+		CWriteLock locallock(m_cs);
+		m_indexes.emplace(dwIndex);
+	}
+
+	void EraseIndex(index_type dwIndex)
+	{
+		CWriteLock locallock(m_cs);
+		m_indexes.erase(dwIndex);
+	}
+
+public:
+	CRingCache2	(DWORD dwSize = 0)
+	: m_pv		(nullptr)
+	, m_px		(nullptr)
+	, m_dwSize	(0)
+	, m_dwCount	(0)
+	, m_dwCurSeq(0)
+	{
+		Reset(dwSize);
+	}
+
+	~CRingCache2()
+	{
+		Reset(0);
+	}
+
+	DECLARE_NO_COPY_CLASS(CRingCache2)
+
+private:
+	DWORD				m_dwSize;
+	VTPTR*				m_pv;
+	char				pack1[PACK_SIZE_OF(VTPTR*)];
+	BYTE*				m_px;
+	char				pack2[PACK_SIZE_OF(BYTE*)];
+	volatile DWORD		m_dwCurSeq;
+	char				pack3[PACK_SIZE_OF(DWORD)];
+	volatile DWORD		m_dwCount;
+	char				pack4[PACK_SIZE_OF(DWORD)];
+
+	CSimpleRWLock		m_cs;
+	IndexSet			m_indexes;
+};
+
+template <class T, class index_type, bool adjust_index> T* const CRingCache2<T, index_type, adjust_index>::E_EMPTY		= (T*)0x00;
+template <class T, class index_type, bool adjust_index> T* const CRingCache2<T, index_type, adjust_index>::E_LOCKED		= (T*)0x01;
+template <class T, class index_type, bool adjust_index> T* const CRingCache2<T, index_type, adjust_index>::E_MAX_STATUS	= (T*)0x0F;
+
+template <class T, class index_type, bool adjust_index> DWORD const CRingCache2<T, index_type, adjust_index>::MAX_SIZE	= 
+#if __WORDSIZE == 32
+																														  0x00FFFFFF
+#else
+																														  0xFFFFFFFF
+#endif
+																																	;
+// ------------------------------------------------------------------------------------------------------------- //
+
+template <class T> class CRingPool
+{
+private:
+
+	typedef T*			TPTR;
+	typedef volatile T*	VTPTR;
+
+	static TPTR const E_EMPTY;
+	static TPTR const E_LOCKED;
+	static TPTR const E_MAX_STATUS;
+
+private:
+
+	VTPTR& INDEX_VAL(DWORD dwIndex) {return *(m_pv + dwIndex);}
+
+public:
+
+	BOOL TryPut(TPTR pElement)
+	{
+		ASSERT(pElement != nullptr);
+
+		if(!IsValid()) return FALSE;
+
+		BOOL isOK = FALSE;
+
+		for(DWORD i = 0; i < m_dwSize; i++)
+		{
+			DWORD seqPut = m_seqPut;
+
+			if(!HasPutSpace(seqPut))
+				break;
+
+			DWORD dwIndex = seqPut % m_dwSize;
+			VTPTR& pValue = INDEX_VAL(dwIndex);
+			TPTR pCurrent = (TPTR)pValue;
+
+			if(pCurrent == E_EMPTY)
+			{
+				if(::InterlockedCompareExchangePointer(&pValue, pElement, pCurrent) == pCurrent)
+				{
+					::InterlockedCompareExchange(&m_seqPut, seqPut + 1, seqPut);
+
+					isOK = TRUE;
+
+					break;
+				}
+			}
+
+			::InterlockedCompareExchange(&m_seqPut, seqPut + 1, seqPut);
+		}
+
+		return isOK;
+	}
+
+	BOOL TryGet(TPTR* ppElement)
+	{
+		ASSERT(ppElement != nullptr);
+
+		if(!IsValid()) return FALSE;
+
+		BOOL isOK = FALSE;
+
+		while(true)
+		{
+			DWORD seqGet = m_seqGet;
+
+			if(!HasGetSpace(seqGet))
+				break;
+
+			DWORD dwIndex = seqGet % m_dwSize;
+			VTPTR& pValue = INDEX_VAL(dwIndex);
+			TPTR pCurrent = (TPTR)pValue;
+
+			if(pCurrent > E_MAX_STATUS)
+			{
+				if(::InterlockedCompareExchangePointer(&pValue, E_EMPTY, pCurrent) == pCurrent)
+				{
+					::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet);
+
+					*(ppElement) = pCurrent;
+					isOK		 = TRUE;
+
+					break;
+				}
+			}
+
+			::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet);
+		}
+
+		return isOK;
+	}
+
+	BOOL TryLock(TPTR* ppElement, DWORD& dwIndex)
+	{
+		ASSERT(ppElement != nullptr);
+
+		if(!IsValid()) return FALSE;
+
+		BOOL isOK = FALSE;
+
+		while(true)
+		{
+			DWORD seqGet = m_seqGet;
+
+			if(!HasGetSpace(seqGet))
+				break;
+
+			dwIndex		  = seqGet % m_dwSize;
+			VTPTR& pValue = INDEX_VAL(dwIndex);
+			TPTR pCurrent = (TPTR)pValue;
+
+			if(pCurrent > E_MAX_STATUS)
+			{
+				if(::InterlockedCompareExchangePointer(&pValue, E_LOCKED, pCurrent) == pCurrent)
+				{
+					::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet);
+
+					*(ppElement) = pCurrent;
+					isOK		 = TRUE;
+
+					break;
+				}
+			}
+
+			::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet);
+		}
+
+		return isOK;
+	}
+
+	BOOL ReleaseLock(TPTR pElement, DWORD dwIndex)
+	{
+		ASSERT(dwIndex < m_dwSize);
+		ASSERT(pElement == nullptr || pElement > E_MAX_STATUS);
+
+		if(!IsValid()) return FALSE;
+
+		VTPTR& pValue = INDEX_VAL(dwIndex);
+		ENSURE(pValue == E_LOCKED);
+
+		if(pElement == nullptr)
+			pValue = E_EMPTY;
+		else
+			pValue = pElement;
+
+		return TRUE;
+	}
+
+public:
+
+	void Reset(DWORD dwSize = 0)
+	{
+		if(IsValid())
+			Destroy();
+		if(dwSize > 0)
+			Create(dwSize);
+	}
+
+	void Clear()
+	{
+		for(DWORD dwIndex = 0; dwIndex < m_dwSize; dwIndex++)
+		{
+			VTPTR& pValue = INDEX_VAL(dwIndex);
+
+			if(pValue > E_MAX_STATUS)
+			{
+				T::Destruct((TPTR)pValue);
+				pValue = E_EMPTY;
+			}
+		}
+
+		Reset();
+	}
+
+	DWORD Size()		{return m_dwSize;}
+	DWORD Elements()	{return m_seqPut - m_seqGet;}
+	BOOL IsFull()		{return Elements() == Size();}
+	BOOL IsEmpty()		{return Elements() == 0;}
+	BOOL IsValid()		{return m_pv != nullptr;}
+
+private:
+
+	BOOL HasPutSpace(DWORD seqPut)
+	{
+		return ((int)(seqPut - m_seqGet) < (int)m_dwSize);
+	}
+
+	BOOL HasGetSpace(DWORD seqGet)
+	{
+		return ((int)(m_seqPut - seqGet) > 0);
+	}
+
+	void Create(DWORD dwSize)
+	{
+		ASSERT(!IsValid() && dwSize > 0);
+
+		m_seqPut = 0;
+		m_seqGet = 0;
+		m_dwSize = dwSize;
+		m_pv	 = (VTPTR*)malloc(m_dwSize * sizeof(TPTR));
+
+		::ZeroMemory(m_pv, m_dwSize * sizeof(TPTR));
+	}
+
+	void Destroy()
+	{
+		ASSERT(IsValid());
+
+		free((void*)m_pv);
+		m_pv = nullptr;
+		m_dwSize = 0;
+		m_seqPut = 0;
+		m_seqGet = 0;
+	}
+
+public:
+	CRingPool(DWORD dwSize = 0)
+	: m_pv(nullptr)
+	, m_dwSize(0)
+	, m_seqPut(0)
+	, m_seqGet(0)
+	{
+		Reset(dwSize);
+	}
+
+	~CRingPool()
+	{
+		Reset(0);
+	}
+
+private:
+	CRingPool(const CRingPool&);
+	CRingPool operator = (const CRingPool&);
+
+private:
+	DWORD				m_dwSize;
+	VTPTR*				m_pv;
+	char				pack1[PACK_SIZE_OF(VTPTR*)];
+	volatile DWORD		m_seqPut;
+	char				pack2[PACK_SIZE_OF(DWORD)];
+	volatile DWORD		m_seqGet;
+	char				pack3[PACK_SIZE_OF(DWORD)];
+};
+
+template <class T> T* const CRingPool<T>::E_EMPTY		= (T*)0x00;
+template <class T> T* const CRingPool<T>::E_LOCKED		= (T*)0x01;
+template <class T> T* const CRingPool<T>::E_MAX_STATUS	= (T*)0x0F;
+
+// ------------------------------------------------------------------------------------------------------------- //
+
+template <class T> class CCASQueueX
+{
+private:
+	struct Node;
+	typedef Node*			NPTR;
+	typedef volatile Node*	VNPTR;
+	typedef volatile UINT	VUINT;
+
+	struct Node
+	{
+		T*		pValue;
+		VNPTR	pNext;
+
+		Node(T* val, NPTR next = nullptr)
+		: pValue(val), pNext(next)
+		{
+
+		}
+	};
+
+public:
+
+	void PushBack(T* pVal)
+	{
+		ASSERT(pVal != nullptr);
+
+		VNPTR pTail	= nullptr;
+		NPTR pNode	= new Node(pVal);
+
+		while(true)
+		{
+			pTail = m_pTail;
+
+			if(::InterlockedCompareExchangePointer(&m_pTail, pNode, pTail) == pTail)
+			{
+				pTail->pNext = pNode;
+				break;
+			}
+		}
+
+		::InterlockedIncrement(&m_iSize);
+	}
+
+	void UnsafePushBack(T* pVal)
+	{
+		ASSERT(pVal != nullptr);
+
+		NPTR pNode		= new Node(pVal);
+		m_pTail->pNext	= pNode;
+		m_pTail			= pNode;
+		
+		::InterlockedIncrement(&m_iSize);
+	}
+
+	BOOL PopFront(T** ppVal)
+	{
+		ASSERT(ppVal != nullptr);
+
+		if(IsEmpty())
+			return FALSE;
+
+		BOOL isOK	= FALSE;
+		NPTR pHead	= nullptr;
+		NPTR pNext	= nullptr;
+
+		while(true)
+		{
+			Lock();
+
+			pHead = (NPTR)m_pHead;
+			pNext = (NPTR)pHead->pNext;
+
+			if(pNext == nullptr)
+			{
+				Unlock();
+				break;
+			}
+
+			*ppVal	= pNext->pValue;
+			m_pHead	= pNext;
+
+			Unlock();
+
+			isOK	= TRUE;
+
+			::InterlockedDecrement(&m_iSize);
+
+			delete pHead;
+			break;
+		}
+
+		return isOK;
+	}
+
+	BOOL UnsafePopFront(T** ppVal)
+	{
+		if(!UnsafePeekFront(ppVal))
+			return FALSE;
+
+		UnsafePopFrontNotCheck();
+
+		return TRUE;
+	}
+
+	BOOL UnsafePeekFront(T** ppVal)
+	{
+		ASSERT(ppVal != nullptr);
+
+		NPTR pNext = (NPTR)m_pHead->pNext;
+
+		if(pNext == nullptr)
+			return FALSE;
+
+		*ppVal = pNext->pValue;
+
+		return TRUE;
+	}
+
+	void UnsafePopFrontNotCheck()
+	{
+		NPTR pHead	= (NPTR)m_pHead;
+		NPTR pNext	= (NPTR)pHead->pNext;
+		m_pHead		= pNext;
+
+		::InterlockedDecrement(&m_iSize);
+
+		delete pHead;
+	}
+
+	void UnsafeClear()
+	{
+		ASSERT(m_pHead != nullptr);
+
+		m_dwCheckTime = 0;
+
+		while(m_pHead->pNext != nullptr)
+			UnsafePopFrontNotCheck();
+	}
+
+public:
+
+	UINT Size()		{return m_iSize;}
+	BOOL IsEmpty()	{return m_iSize == 0;}
+
+	void Lock()		{while(!TryLock()) ::YieldProcessor();}
+	void Unlock()	{m_iLock = 0;}
+	BOOL TryLock()	{return (::InterlockedCompareExchange(&m_iLock, 1u, 0u) == 0);}
+
+	DWORD GetCheckTime()
+	{
+		return m_dwCheckTime;
+	}
+
+	void UpdateCheckTime(DWORD dwCurrent = 0)
+	{
+		if(dwCurrent == 0)
+			dwCurrent = ::TimeGetTime();
+
+		m_dwCheckTime = dwCurrent;
+	}
+
+	int GetCheckTimeGap(DWORD dwCurrent = 0)
+	{
+		int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent);
+
+		if(rs < -60 * 1000)
+			rs = MAXINT;
+
+		return rs;
+	}
+
+public:
+
+	CCASQueueX() : m_iLock(0), m_iSize(0), m_dwCheckTime(0)
+	{
+		m_pHead = m_pTail = new Node(nullptr);
+	}
+
+	~CCASQueueX()
+	{
+		ASSERT(m_iLock == 0);
+		ASSERT(m_iSize == 0);
+		ASSERT(m_pTail == m_pHead);
+		ASSERT(m_pHead != nullptr);
+		ASSERT(m_pHead->pNext == nullptr);
+
+		UnsafeClear();
+
+		delete m_pHead;
+	}
+
+	DECLARE_NO_COPY_CLASS(CCASQueueX)
+
+private:
+	VUINT	m_iLock;
+	VUINT	m_iSize;
+	VNPTR	m_pHead;
+	VNPTR	m_pTail;
+
+	volatile DWORD m_dwCheckTime;
+};
+
+template <class T> class CCASSimpleQueueX
+{
+private:
+	struct Node;
+	typedef Node*			NPTR;
+	typedef volatile Node*	VNPTR;
+	typedef volatile UINT	VUINT;
+
+	struct Node
+	{
+		T		tValue;
+		VNPTR	pNext;
+
+		Node(T val, NPTR next = nullptr)
+		: tValue(val), pNext(next)
+		{
+
+		}
+	};
+
+public:
+
+	void PushBack(T tVal)
+	{
+		VNPTR pTail	= nullptr;
+		NPTR pNode	= new Node(tVal);
+
+		while(true)
+		{
+			pTail = m_pTail;
+
+			if(::InterlockedCompareExchangePointer(&m_pTail, pNode, pTail) == pTail)
+			{
+				pTail->pNext = pNode;
+				break;
+			}
+		}
+
+		::InterlockedIncrement(&m_iSize);
+	}
+
+	void UnsafePushBack(T tVal)
+	{
+		NPTR pNode		= new Node(tVal);
+		m_pTail->pNext	= pNode;
+		m_pTail			= pNode;
+		
+		::InterlockedIncrement(&m_iSize);
+	}
+
+	BOOL PopFront(T* ptVal)
+	{
+		ASSERT(ptVal != nullptr);
+
+		if(IsEmpty())
+			return FALSE;
+
+		BOOL isOK	= FALSE;
+		NPTR pHead	= nullptr;
+		NPTR pNext	= nullptr;
+
+		while(true)
+		{
+			Lock();
+
+			pHead = (NPTR)m_pHead;
+			pNext = (NPTR)pHead->pNext;
+
+			if(pNext == nullptr)
+			{
+				Unlock();
+				break;
+			}
+
+			*ptVal	= pNext->tValue;
+			m_pHead	= pNext;
+
+			Unlock();
+
+			isOK	= TRUE;
+
+			::InterlockedDecrement(&m_iSize);
+
+			delete pHead;
+			break;
+		}
+
+		return isOK;
+	}
+
+	BOOL UnsafePopFront(T* ptVal)
+	{
+		if(!UnsafePeekFront(ptVal))
+			return FALSE;
+
+		UnsafePopFrontNotCheck();
+
+		return TRUE;
+	}
+
+	BOOL UnsafePeekFront(T* ptVal)
+	{
+		ASSERT(ptVal != nullptr);
+
+		NPTR pNext = (NPTR)m_pHead->pNext;
+
+		if(pNext == nullptr)
+			return FALSE;
+
+		*ptVal = pNext->pValue;
+
+		return TRUE;
+	}
+
+	void UnsafePopFrontNotCheck()
+	{
+		NPTR pHead	= (NPTR)m_pHead;
+		NPTR pNext	= (NPTR)pHead->pNext;
+		m_pHead		= pNext;
+
+		::InterlockedDecrement(&m_iSize);
+
+		delete pHead;
+	}
+
+	void UnsafeClear()
+	{
+		ASSERT(m_pHead != nullptr);
+
+		m_dwCheckTime = 0;
+
+		while(m_pHead->pNext != nullptr)
+			UnsafePopFrontNotCheck();
+	}
+
+public:
+
+	UINT Size()		{return m_iSize;}
+	BOOL IsEmpty()	{return m_iSize == 0;}
+
+	void Lock()		{while(!TryLock()) ::YieldProcessor();}
+	void Unlock()	{m_iLock = 0;}
+	BOOL TryLock()	{return (::InterlockedCompareExchange(&m_iLock, 1u, 0u) == 0);}
+
+	DWORD GetCheckTime()
+	{
+		return m_dwCheckTime;
+	}
+
+	void UpdateCheckTime(DWORD dwCurrent = 0)
+	{
+		if(dwCurrent == 0)
+			dwCurrent = ::TimeGetTime();
+
+		m_dwCheckTime = dwCurrent;
+	}
+
+	int GetCheckTimeGap(DWORD dwCurrent = 0)
+	{
+		int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent);
+
+		if(rs < -60 * 1000)
+			rs = MAXINT;
+
+		return rs;
+	}
+
+public:
+
+	CCASSimpleQueueX() : m_iLock(0), m_iSize(0), m_dwCheckTime(0)
+	{
+		m_pHead = m_pTail = new Node(0);
+	}
+
+	~CCASSimpleQueueX()
+	{
+		ASSERT(m_iLock == 0);
+		ASSERT(m_iSize == 0);
+		ASSERT(m_pTail == m_pHead);
+		ASSERT(m_pHead != nullptr);
+		ASSERT(m_pHead->pNext == nullptr);
+
+		UnsafeClear();
+
+		delete m_pHead;
+	}
+
+	DECLARE_NO_COPY_CLASS(CCASSimpleQueueX)
+
+private:
+	VUINT	m_iLock;
+	VUINT	m_iSize;
+	VNPTR	m_pHead;
+	VNPTR	m_pTail;
+
+	volatile DWORD m_dwCheckTime;
+};
+
+template <class T> class CCASQueueY
+{
+public:
+
+	void PushBack(T* pVal)
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		UnsafePushBack(pVal);
+	}
+
+	void UnsafePushBack(T* pVal)
+	{
+		ASSERT(pVal != nullptr);
+
+		m_lsItems.push_back(pVal);
+	}
+
+	void PushFront(T* pVal)
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		UnsafePushFront(pVal);
+	}
+
+	void UnsafePushFront(T* pVal)
+	{
+		ASSERT(pVal != nullptr);
+
+		m_lsItems.push_front(pVal);
+	}
+
+	BOOL PopFront(T** ppVal)
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		return UnsafePopFront(ppVal);
+	}
+
+	BOOL UnsafePopFront(T** ppVal)
+	{
+		if(!UnsafePeekFront(ppVal))
+			return FALSE;
+
+		UnsafePopFrontNotCheck();
+
+		return TRUE;
+	}
+
+	BOOL PeekFront(T** ppVal)
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		return UnsafePeekFront(ppVal);
+	}
+
+	BOOL UnsafePeekFront(T** ppVal)
+	{
+		ASSERT(ppVal != nullptr);
+
+		if(m_lsItems.empty())
+			return FALSE;
+
+		*ppVal = m_lsItems.front();
+
+		return TRUE;
+	}
+
+	void UnsafePopFrontNotCheck()
+	{
+		m_lsItems.pop_front();
+	}
+
+	void Clear()
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		UnsafeClear();
+	}
+
+	void UnsafeClear()
+	{
+		m_dwCheckTime = 0;
+
+		m_lsItems.clear();
+	}
+
+public:
+
+	ULONG Size()	{return (ULONG)m_lsItems.size();}
+	BOOL IsEmpty()	{return (BOOL)m_lsItems.empty();}
+
+	void Lock()		{m_csGuard.lock();}
+	void Unlock()	{m_csGuard.unlock();}
+	BOOL TryLock()	{return m_csGuard.try_lock();}
+	CCriSec& Guard(){return m_csGuard;}
+
+	DWORD GetCheckTime()
+	{
+		return m_dwCheckTime;
+	}
+
+	void UpdateCheckTime(DWORD dwCurrent = 0)
+	{
+		if(dwCurrent == 0)
+			dwCurrent = ::TimeGetTime();
+
+		m_dwCheckTime = dwCurrent;
+	}
+
+	int GetCheckTimeGap(DWORD dwCurrent = 0)
+	{
+		int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent);
+
+		if(rs < -60 * 1000)
+			rs = MAXINT;
+
+		return rs;
+	}
+
+public:
+
+	CCASQueueY()
+	: m_dwCheckTime(0)
+	{
+
+	}
+
+	~CCASQueueY()
+	{
+		ASSERT(IsEmpty());
+
+		UnsafeClear();
+	}
+
+	DECLARE_NO_COPY_CLASS(CCASQueueY)
+
+private:
+	CCriSec		m_csGuard;
+	deque<T*>	m_lsItems;
+	
+	volatile DWORD m_dwCheckTime;
+};
+
+template <class T> class CCASSimpleQueueY
+{
+public:
+
+	void PushBack(T tVal)
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		UnsafePushBack(tVal);
+	}
+
+	void UnsafePushBack(T tVal)
+	{
+		m_lsItems.push_back(tVal);
+	}
+
+	void PushFront(T tVal)
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		UnsafePushFront(tVal);
+	}
+
+	void UnsafePushFront(T tVal)
+	{
+		m_lsItems.push_front(tVal);
+	}
+
+	BOOL PopFront(T* ptVal)
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		return UnsafePopFront(ptVal);
+	}
+
+	BOOL UnsafePopFront(T* ptVal)
+	{
+		if(!UnsafePeekFront(ptVal))
+			return FALSE;
+
+		UnsafePopFrontNotCheck();
+
+		return TRUE;
+	}
+
+	BOOL PeekFront(T* ptVal)
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		return UnsafePeekFront(ptVal);
+	}
+
+	BOOL UnsafePeekFront(T* ptVal)
+	{
+		ASSERT(ptVal != nullptr);
+
+		if(m_lsItems.empty())
+			return FALSE;
+
+		*ptVal = m_lsItems.front();
+
+		return TRUE;
+	}
+
+	void UnsafePopFrontNotCheck()
+	{
+		m_lsItems.pop_front();
+	}
+
+	void Clear()
+	{
+		CCriSecLock locallock(m_csGuard);
+
+		UnsafeClear();
+	}
+
+	void UnsafeClear()
+	{
+		m_dwCheckTime = 0;
+
+		m_lsItems.clear();
+	}
+
+public:
+
+	ULONG Size()	{return (ULONG)m_lsItems.size();}
+	BOOL IsEmpty()	{return (BOOL)m_lsItems.empty();}
+
+	void Lock()		{m_csGuard.lock();}
+	void Unlock()	{m_csGuard.unlock();}
+	BOOL TryLock()	{return m_csGuard.try_lock();}
+	CCriSec& Guard(){return m_csGuard;}
+
+	DWORD GetCheckTime()
+	{
+		return m_dwCheckTime;
+	}
+
+	void UpdateCheckTime(DWORD dwCurrent = 0)
+	{
+		if(dwCurrent == 0)
+			dwCurrent = ::TimeGetTime();
+
+		m_dwCheckTime = dwCurrent;
+	}
+
+	int GetCheckTimeGap(DWORD dwCurrent = 0)
+	{
+		int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent);
+
+		if(rs < -60 * 1000)
+			rs = MAXINT;
+
+		return rs;
+	}
+
+public:
+
+	CCASSimpleQueueY()
+	: m_dwCheckTime(0)
+	{
+
+	}
+
+	~CCASSimpleQueueY()
+	{
+		ASSERT(IsEmpty());
+
+		UnsafeClear();
+	}
+
+	DECLARE_NO_COPY_CLASS(CCASSimpleQueueY)
+
+private:
+	CCriSec		m_csGuard;
+	deque<T>	m_lsItems;
+	
+	volatile DWORD m_dwCheckTime;
+};
+
+template <class T> using CCASQueue			= CCASQueueX<T>;
+template <class T> using CCASSimpleQueue	= CCASSimpleQueueX<T>;
+
+template<typename T>
+void ReleaseGCObj(CCASQueue<T>& lsGC, DWORD dwLockTime, BOOL bForce = FALSE)
+{
+	static const int MIN_CHECK_INTERVAL = 1 * 1000;
+	static const int MAX_CHECK_INTERVAL = 15 * 1000;
+
+	T* pObj = nullptr;
+
+	if(bForce)
+	{
+		CLocalLock<CCASQueue<T>> locallock(lsGC);
+
+		while(lsGC.UnsafePeekFront(&pObj))
+		{
+			lsGC.UnsafePopFrontNotCheck();
+			T::Destruct(pObj);
+		}
+	}
+	else
+	{
+		if(lsGC.IsEmpty() || lsGC.GetCheckTimeGap() < MAX(MIN((int)(dwLockTime / 3), MAX_CHECK_INTERVAL), MIN_CHECK_INTERVAL))
+			return;
+
+		T* pFirst	= nullptr;
+		BOOL bFirst	= TRUE;
+		DWORD now	= 0;
+
+		while(TRUE)
+		{
+			ASSERT((pObj = nullptr) == nullptr);
+
+			{
+				CLocalTryLock<CCASQueue<T>> locallock(lsGC);
+
+				if(!locallock.IsValid())
+					break;
+
+				if(bFirst)
+				{
+					bFirst	= FALSE;
+					now		= ::TimeGetTime();
+
+					lsGC.UpdateCheckTime(now);
+				}
+
+				if(!lsGC.UnsafePeekFront(&pObj))
+					break;
+
+				if((int)(now - pObj->GetFreeTime()) < (int)dwLockTime)
+					break;
+
+				lsGC.UnsafePopFrontNotCheck();
+
+				if(pObj->GetCount() > 0)
+				{
+					lsGC.PushBack(pObj);
+
+					if(pFirst == nullptr)
+						pFirst = pObj;
+					else if(pFirst == pObj)
+						break;
+
+					continue;
+				}
+			}
+
+			ASSERT(pObj != nullptr);
+			T::Destruct(pObj);
+		}
+	}
+}
+
+#if __WORDSIZE == 32
+	#pragma pack(pop)
+#endif

+ 1068 - 0
common/STLHelper.h

@@ -0,0 +1,1068 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "Singleton.h"
+#include "StringT.h"
+
+#include <math.h>
+
+#include <functional>
+#include <algorithm>
+#include <random>
+#include <vector>
+#include <deque>
+#include <queue>
+#include <stack>
+#include <list>
+#include <set>
+#include <map>
+#include <unordered_set>
+#include <unordered_map>
+#include <type_traits>
+
+#define hash_set		unordered_set
+#define hash_map		unordered_map
+#define hash_multimap	unordered_multimap
+
+using namespace std;
+
+typedef list<short>						short_list;
+typedef list<int>						int_list;
+typedef list<long>						long_list;
+typedef list<int64_t>					int64_list;
+typedef list<unsigned short>			ushort_list;
+typedef list<unsigned int>				uint_list;
+typedef list<unsigned long>				ulong_list;
+typedef list<uint64_t>					uint64_list;
+typedef list<float>						float_list;
+typedef list<double>					double_list;
+typedef stack<short>					short_stack;
+typedef stack<int>						int_stack;
+typedef stack<long>						long_stack;
+typedef stack<int64_t>					int64_stack;
+typedef stack<unsigned short>			ushort_stack;
+typedef stack<unsigned int>				uint_stack;
+typedef stack<unsigned long>			ulong_stack;
+typedef stack<uint64_t>					uint64_stack;
+typedef stack<float>					float_stack;
+typedef stack<double>					double_stack;
+typedef queue<short>					short_queue;
+typedef queue<int>						int_queue;
+typedef queue<long>						long_queue;
+typedef queue<int64_t>					int64_queue;
+typedef queue<unsigned short>			ushort_queue;
+typedef queue<unsigned int>				uint_queue;
+typedef queue<unsigned long>			ulong_queue;
+typedef queue<uint64_t>					uint64_queue;
+typedef queue<float>					float_queue;
+typedef queue<double>					double_queue;
+typedef deque<short>					short_deque;
+typedef deque<int>						int_deque;
+typedef deque<long>						long_deque;
+typedef deque<int64_t>					int64_deque;
+typedef deque<unsigned short>			ushort_deque;
+typedef deque<unsigned int>				uint_deque;
+typedef deque<unsigned long>			ulong_deque;
+typedef deque<uint64_t>					uint64_deque;
+typedef deque<float>					float_deque;
+typedef deque<double>					double_deque;
+typedef vector<short>					short_vector;
+typedef vector<int>						int_vector;
+typedef vector<long>					long_vector;
+typedef vector<int64_t>					int64_vector;
+typedef vector<unsigned short>			ushort_vector;
+typedef vector<unsigned int>			uint_vector;
+typedef vector<unsigned long>			ulong_vector;
+typedef vector<uint64_t>				uint64_vector;
+typedef vector<float>					float_vector;
+typedef vector<double>					double_vector;
+typedef set<short>						short_set;
+typedef set<int>						int_set;
+typedef set<long>						long_set;
+typedef set<int64_t>					int64_set;
+typedef set<unsigned short>				ushort_set;
+typedef set<unsigned int>				uint_set;
+typedef set<unsigned long>				ulong_set;
+typedef set<uint64_t>					uint64_set;
+typedef set<float>						float_set;
+typedef set<double>						double_set;
+typedef hash_set<short>					short_hash_set;
+typedef hash_set<int>					int_hash_set;
+typedef hash_set<long>					long_hash_set;
+typedef hash_set<int64_t>				int64_hash_set;
+typedef hash_set<unsigned short>		ushort_hash_set;
+typedef hash_set<unsigned int>			uint_hash_set;
+typedef hash_set<unsigned long>			ulong_hash_set;
+typedef hash_set<uint64_t>				uint64_hash_set;
+typedef hash_set<float>					float_hash_set;
+typedef hash_set<double>				double_hash_set;
+typedef unordered_set<short>			short_unordered_set;
+typedef unordered_set<int>				int_unordered_set;
+typedef unordered_set<long>				long_unordered_set;
+typedef unordered_set<int64_t>			int64_unordered_set;
+typedef unordered_set<unsigned short>	ushort_unordered_set;
+typedef unordered_set<unsigned int>		uint_unordered_set;
+typedef unordered_set<unsigned long>	ulong_unordered_set;
+typedef unordered_set<uint64_t>			uint64_unordered_set;
+typedef unordered_set<float>			float_unordered_set;
+typedef unordered_set<double>			double_unordered_set;
+
+typedef list<INT_PTR>					int_ptr_list;
+typedef list<LONG_PTR>					long_ptr_list;
+typedef list<UINT_PTR>					uint_ptr_list;
+typedef list<ULONG_PTR>					ulong_ptr_list;
+typedef stack<INT_PTR>					int_ptr_stack;
+typedef stack<LONG_PTR>					long_ptr_stack;
+typedef stack<UINT_PTR>					uint_ptr_stack;
+typedef stack<ULONG_PTR>				ulong_ptr_stack;
+typedef queue<INT_PTR>					int_ptr_queue;
+typedef queue<LONG_PTR>					long_ptr_queue;
+typedef queue<UINT_PTR>					uint_ptr_queue;
+typedef queue<ULONG_PTR>				ulong_ptr_queue;
+typedef deque<INT_PTR>					int_ptr_deque;
+typedef deque<LONG_PTR>					long_ptr_deque;
+typedef deque<UINT_PTR>					uint_ptr_deque;
+typedef deque<ULONG_PTR>				ulong_ptr_deque;
+typedef vector<INT_PTR>					int_ptr_vector;
+typedef vector<LONG_PTR>				long_ptr_vector;
+typedef vector<UINT_PTR>				uint_ptr_vector;
+typedef vector<ULONG_PTR>				ulong_ptr_vector;
+typedef set<INT_PTR>					int_ptr_set;
+typedef set<LONG_PTR>					long_ptr_set;
+typedef set<UINT_PTR>					uint_ptr_set;
+typedef set<ULONG_PTR>					ulong_ptr_set;
+typedef hash_set<INT_PTR>				int_ptr_hash_set;
+typedef hash_set<LONG_PTR>				long_ptr_hash_set;
+typedef hash_set<UINT_PTR>				uint_ptr_hash_set;
+typedef hash_set<ULONG_PTR>				ulong_ptr_hash_set;
+typedef unordered_set<INT_PTR>			int_ptr_unordered_set;
+typedef unordered_set<LONG_PTR>			long_ptr_unordered_set;
+typedef unordered_set<UINT_PTR>			uint_ptr_unordered_set;
+typedef unordered_set<ULONG_PTR>		ulong_ptr_unordered_set;
+
+/*****************************************************************************/
+/******************************** 容器操作函数 *******************************/
+
+/**********************************
+描述: 清除普通集合 , 适用于 vector<Object> / list<Object>
+参数: 
+	v		: vector / list / set
+
+返回值: 		
+**********************************/
+template<class Set> void ClearSet(Set& v)
+{
+	v.clear();
+}
+
+template<class Set> struct Set_Cleaner
+{
+	static void Clear(Set& v) {ClearSet(v);}
+};
+
+/**********************************
+描述: 清除指针集合 (清除前先释放指针), 适用于 vector<Object*> / list<Object*>
+参数: 
+		v		: vector / list / set
+
+返回值: 		
+**********************************/
+template<class PtrSet> void ClearPtrSet(PtrSet& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+		delete (*it);
+
+	v.clear();
+}
+
+template<class PtrSet> struct PtrSet_Cleaner
+{
+	static void Clear(PtrSet& v) {ClearPtrSet(v);}
+};
+
+/**********************************
+描述: 清除指针集合 (指针同时又指向数组), 适用于 vector<Object*[]> / list<Object*[]>
+参数: 
+		v		: vector / list / set
+
+返回值: 		
+**********************************/
+template<class PtrArraySet> void ClearPtrArraySet(PtrArraySet& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+		delete[] (*it);
+
+	v.clear();
+}
+
+template<class PtrArraySet> struct PtrArraySet_Cleaner
+{
+	static void Clear(PtrArraySet& v) {ClearPtrArraySet(v);}
+};
+
+/**********************************
+描述: 清除普通影射 , 适用于 map<key, value>
+参数: 
+	v		: map
+
+返回值: 		
+**********************************/
+template<class Map> void ClearMap(Map& v)
+{
+	v.clear();
+}
+
+template<class Map> struct Map_Cleaner
+{
+	static void Clear(Map& v) {ClearMap(v);}
+};
+
+/**********************************
+描述: 清除指针影射 (清除前先释放指针), 适用于 map<key, Object*>
+参数: 
+		v		: map
+
+返回值: 		
+**********************************/
+template<class PtrMap> void ClearPtrMap(PtrMap& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+		delete it->second;
+
+	v.clear();
+}
+
+template<class PtrMap> struct PtrMap_Cleaner
+{
+	static void Clear(PtrMap& v) {ClearPtrMap(v);}
+};
+
+/**********************************
+描述: 清除指针影射 (指针同时又指向数组), 适用于 map<key, Object*[]>
+参数: 
+		v		: map
+
+返回值: 		
+**********************************/
+template<class PtrArrayMap> void ClearPtrArrayMap(PtrArrayMap& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+		delete[] it->second;
+
+	v.clear();
+}
+
+template<class PtrArrayMap> struct PtrArrayMap_Cleaner
+{
+	static void Clear(PtrArrayMap& v) {ClearPtrArrayMap(v);}
+};
+
+/**********************************
+描述: 清除集合-集合 (清除前先清除内部集合), 适用于 set<vector<Object>*>
+参数: 
+		v		: vector / list / set
+
+返回值: 		
+**********************************/
+template<class SetSet> void ClearSetSet(SetSet& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+	{
+		(*it)->clear();
+		delete (*it);
+	}
+
+	v.clear();
+}
+
+template<class SetSet> struct SetSet_Cleaner
+{
+	static void Clear(SetSet& v) {ClearSetSet(v);}
+};
+
+/**********************************
+描述: 清除指针集合-集合 (清除前先清除内部指针集合), 适用于 set<vector<Object*>*>
+参数: 
+		v		: vector / list / set
+
+返回值: 		
+**********************************/
+template<class PtrSetSet> void ClearPtrSetSet(PtrSetSet& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+	{
+		ClearPtrSet(**it);
+		delete (*it);
+	}
+
+	v.clear();
+}
+
+template<class PtrSetSet> struct PtrSetSet_Cleaner
+{
+	static void Clear(PtrSetSet& v) {ClearPtrSetSet(v);}
+};
+
+/**********************************
+描述: 清除指针数组集合影射 (清除前先清除指针数组集合), 适用于 map<vector<Object*[]>*>
+参数: 
+		v		: vector / list / set
+
+返回值: 		
+**********************************/
+template<class PtrArraySetSet> void ClearPtrArraySetSet(PtrArraySetSet& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+	{
+		ClearPtrArraySet(**it);
+		delete (*it);
+	}
+
+	v.clear();
+}
+
+template<class PtrArraySetSet> struct PtrArraySetSet_Cleaner
+{
+	static void Clear(PtrArraySetSet& v) {ClearPtrArraySetSet(v);}
+};
+
+/**********************************
+描述: 清除集合影射 (清除前先清除集合), 适用于 map<key, vector<Object>*>
+参数: 
+		v		: map
+
+返回值: 		
+**********************************/
+template<class SetMap> void ClearSetMap(SetMap& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+	{
+		it->second->clear();
+		delete it->second;
+	}
+
+	v.clear();
+}
+
+template<class SetMap> struct SetMap_Cleaner
+{
+	static void Clear(SetMap& v) {ClearSetMap(v);}
+};
+
+/**********************************
+描述: 清除指针集合影射 (清除前先清除指针集合), 适用于 map<key, vector<Object*>*>
+参数: 
+		v		: map
+
+返回值: 		
+**********************************/
+template<class PtrSetMap> void ClearPtrSetMap(PtrSetMap& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+	{
+		ClearPtrSet(*(it->second));
+		delete it->second;
+	}
+
+	v.clear();
+}
+
+template<class PtrSetMap> struct PtrSetMap_Cleaner
+{
+	static void Clear(PtrSetMap& v) {ClearPtrSetMap(v);}
+};
+
+/**********************************
+描述: 清除指针数组集合影射 (清除前先清除指针数组集合), 适用于 map<key, vector<Object*[]>*>
+参数: 
+		v		: map
+
+返回值: 		
+**********************************/
+template<class PtrArraySetMap> void ClearPtrArraySetMap(PtrArraySetMap& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+	{
+		ClearPtrArraySet(*(it->second));
+		delete it->second;
+	}
+
+	v.clear();
+}
+
+template<class PtrArraySetMap> struct PtrArraySetMap_Cleaner
+{
+	static void Clear(PtrArraySetMap& v) {ClearPtrArraySetMap(v);}
+};
+
+/**********************************
+描述: 清除映射-影射 (清除前先清除内部映射), 适用于 map<key, map<key2, Object>*>
+参数: 
+v		: map
+
+返回值: 		
+**********************************/
+template<class MapMap> void ClearMapMap(MapMap& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+	{
+		it->second->clear();
+		delete it->second;
+	}
+
+	v.clear();
+}
+
+template<class MapMap> struct MapMap_Cleaner
+{
+	static void Clear(MapMap& v) {ClearMapMap(v);}
+};
+
+/**********************************
+描述: 清除指针映射-影射 (清除前先清除指针内部映射), 适用于 map<key, map<key2, Object*>*>
+参数: 
+		v		: map
+
+返回值: 		
+**********************************/
+template<class PtrMapMap> void ClearPtrMapMap(PtrMapMap& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+	{
+		ClearPtrMap(*(it->second));
+		delete it->second;
+	}
+
+	v.clear();
+}
+
+template<class PtrMapMap> struct PtrMapMap_Cleaner
+{
+	static void Clear(PtrMapMap& v) {ClearPtrMapMap(v);}
+};
+
+/**********************************
+描述: 清除指针映射-影射 (清除前先清除指针数组内部映射), 适用于 map<key, map<key2, Object*[]>*>
+参数: 
+		v		: map
+
+返回值: 		
+**********************************/
+template<class PtrArrayMapMap> void ClearPtrArrayMapMap(PtrArrayMapMap& v)
+{
+	for(auto	it	= v.begin(),
+				end	= v.end(); 
+				it != end;
+				++it)
+	{
+		ClearPtrArrayMap(*(it->second));
+		delete it->second;
+	}
+
+	v.clear();
+}
+
+template<class PtrArrayMapMap> struct PtrArrayMapMap_Cleaner
+{
+	static void Clear(PtrArrayMapMap& v) {ClearPtrArrayMapMap(v);}
+};
+
+/************************************************************************/
+/*                           指针集合容器                               */
+/************************************************************************/
+template<class Set, class Cleaner> struct SetWrapper
+{
+	typedef typename Set::iterator			iterator;
+	typedef typename Set::const_iterator	const_iterator;
+	typedef typename Set::value_type		value_type;
+	typedef typename Set::reference			reference;
+	typedef typename Set::const_reference	const_reference;
+	typedef typename Set::pointer			pointer;
+	typedef typename Set::const_pointer		const_pointer;
+	typedef typename Set::size_type			size_type;
+	typedef typename Set::difference_type	difference_type;
+
+	SetWrapper()
+	{
+	}
+
+	virtual ~SetWrapper()
+	{
+		Clear();
+	}
+
+	void Clear()
+	{
+		if(!IsEmpty())
+		{
+			Cleaner::Clear(m_set);
+		}
+	}
+
+	Set& operator *			()			{return m_set;}
+	const Set& operator *	()	const	{return m_set;}
+	Set* operator ->		()			{return &m_set;}
+	const Set* operator ->	()	const	{return &m_set;}
+	Set& Get				()			{return m_set;}
+	operator Set&			()			{return m_set;}
+	bool IsEmpty			()	const	{return m_set.empty();}
+	size_t Size				()	const	{return m_set.size();}
+
+protected:
+	Set m_set;
+
+	DECLARE_NO_COPY_CLASS(SetWrapper)
+};
+
+template<class Set, class Cleaner> struct VectorWrapper : public SetWrapper<Set, Cleaner>
+{
+	typedef SetWrapper<Set, Cleaner>			__super;
+	typedef typename __super::reference			reference;
+	typedef typename __super::const_reference	const_reference;
+	typedef typename __super::size_type			size_type;
+
+	VectorWrapper()
+	{
+	}
+
+	reference		operator []	(size_type i)			{return __super::m_set[i];}
+	const_reference operator [] (size_type i)	const	{return __super::m_set[i];}
+
+	DECLARE_NO_COPY_CLASS(VectorWrapper)
+};
+
+/************************************************************************/
+/*                         指针数组集合容器                             */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                           指针映射容器                               */
+/************************************************************************/
+template<class Map, class Cleaner> struct MapWrapper
+{
+	typedef typename Map::iterator			iterator;
+	typedef typename Map::const_iterator	const_iterator;
+	typedef typename Map::key_type			key_type;
+	typedef typename Map::mapped_type		mapped_type;
+	typedef typename Map::value_type		value_type;
+	typedef typename Map::reference			reference;
+	typedef typename Map::const_reference	const_reference;
+	typedef typename Map::pointer			pointer;
+	typedef typename Map::size_type			size_type;
+	typedef typename Map::difference_type	difference_type;
+
+	MapWrapper()
+	{
+	}
+
+	~MapWrapper()
+	{
+		Clear();
+	}
+
+	void Clear()
+	{
+		if(!IsEmpty())
+		{
+			Cleaner::Clear(m_map);
+		}
+	}
+
+	Map&				operator *	()								{return m_map;}
+	const Map&			operator *	()						const	{return m_map;}
+	Map*				operator ->	()								{return &m_map;}
+	const Map*			operator ->	()						const	{return &m_map;}
+	mapped_type&		operator []	(const key_type& key)			{return m_map[key];}
+	const mapped_type&	operator []	(const key_type& key)	const	{return m_map[key];}
+	Map& Get						()								{return m_map;}
+	operator Map&					()								{return m_map;}
+	bool IsEmpty					()						const	{return m_map.empty();}
+	size_t Size						()						const	{return m_map.size();}
+
+private:
+	Map m_map;
+
+	DECLARE_NO_COPY_CLASS(MapWrapper)
+};
+
+/************************************************************************/
+/*                            比较仿函数                                */
+/************************************************************************/
+
+template<class T> struct char_comparator
+{
+	typedef T	row_type;
+	static row_type row_type_value(const T& v)		{return (row_type)v;}
+	static bool equal_to(const T& v1, const T& v2)	{return strcmp(v1, v2) == 0;}
+};
+
+template<class T> struct char_nc_comparator
+{
+	typedef T	row_type;
+	static row_type row_type_value(const T& v)		{return (row_type)v;}
+	static bool equal_to(const T& v1, const T& v2)	{return stricmp(v1, v2) == 0;}
+};
+
+template<class T> struct wchar_comparator
+{
+	typedef T	row_type;
+	static row_type row_type_value(const T& v)		{return (row_type)v;}
+	static bool equal_to(const T& v1, const T& v2)	{return wcscmp(v1, v2) == 0;}
+};
+
+template<class T> struct wchar_nc_comparator
+{
+	typedef T	row_type;
+	static row_type row_type_value(const T& v)		{return (row_type)v;}
+	static bool equal_to(const T& v1, const T& v2)	{return wcsicmp(v1, v2) == 0;}
+};
+
+template<class T> struct cstring_comparator
+{
+	typedef typename T::PCXSTR	row_type;
+	static row_type row_type_value(const T& v)		{return (row_type)v;}
+	static bool equal_to(const T& v1, const T& v2)	{return v1.Compare(v2) == 0;}
+};
+
+template<class T> struct cstring_nc_comparator
+{
+	typedef typename T::PCXSTR	row_type;
+	static row_type row_type_value(const T& v)		{return (row_type)v;}
+	static bool equal_to(const T& v1, const T& v2)	{return v1.CompareNoCase(v2) == 0;}
+};
+
+// char/wchar_t/CStringX hash function
+template<class T, class H> struct str_hash_func_t
+{
+	struct hash
+	{
+		size_t operator() (const T& t) const
+		{
+			return hash_value(H::row_type_value(t));
+		}
+	};
+
+	struct equal_to
+	{
+		bool operator() (const T& t1, const T& t2) const
+		{
+			return H::equal_to(t1, t2);
+		}
+	};
+
+};
+
+// char/wchar_t/CStringX hash function (no case)
+template<class T, class H, class B> struct str_nc_hash_func_t
+{
+	struct hash
+	{
+		size_t operator() (const T& t) const
+		{
+			size_t _Val				  = 2166136261U;
+			typename H::row_type lpsz = H::row_type_value(t);
+			B c;
+
+			while((c = *lpsz++) != 0) 
+			{
+				if(c >= 'A' && c <= 'Z')
+					c = (char)(c + 32);
+
+				_Val = 16777619U * _Val ^ c;
+
+			}
+
+			return _Val;
+		}
+	};
+
+	struct equal_to
+	{
+		bool operator() (const T& t1, const T& t2) const
+		{
+			return H::equal_to(t1, t2);
+		}
+	};
+
+};
+
+typedef str_hash_func_t<LPCSTR, char_comparator<LPCSTR>>						str_hash_func;
+typedef str_hash_func_t<LPCWSTR, wchar_comparator<LPCWSTR>>						wstr_hash_func;
+typedef str_hash_func_t<CStringA, cstring_comparator<CStringA>>					cstringa_hash_func;
+typedef str_hash_func_t<CStringW, cstring_comparator<CStringW>>					cstringw_hash_func;
+typedef str_nc_hash_func_t<LPCSTR, char_nc_comparator<LPCSTR>, char>			str_nc_hash_func;
+typedef str_nc_hash_func_t<LPCWSTR, wchar_nc_comparator<LPCWSTR>, wchar_t>		wstr_nc_hash_func;
+typedef str_nc_hash_func_t<CStringA, cstring_nc_comparator<CStringA>, char>		cstringa_nc_hash_func;
+typedef str_nc_hash_func_t<CStringW, cstring_nc_comparator<CStringW>, wchar_t>	cstringw_nc_hash_func;
+
+#ifdef _UNICODE
+	typedef cstringw_hash_func		cstring_hash_func;
+	typedef cstringw_nc_hash_func	cstring_nc_hash_func;
+#else
+	typedef cstringa_hash_func		cstring_hash_func;
+	typedef cstringa_nc_hash_func	cstring_nc_hash_func;
+#endif
+
+struct bool_comp_func
+{
+	bool operator() (bool v1, bool v2) const
+	{
+		if(!v1)
+			return false;
+		if(v1 == v2)
+			return false;
+
+		return true;
+	}
+};
+
+template<class T>
+// T -> (signed / unsigned) short / int / long / int64_t
+struct integer_comp_func
+{
+	bool operator() (T v1, T v2) const
+	{
+		return v1 < v2;
+	}
+};
+
+typedef integer_comp_func<short>			short_comp_func;
+typedef integer_comp_func<int>				int_comp_func;
+typedef integer_comp_func<long>				long_comp_func;
+typedef integer_comp_func<int64_t>			int64_comp_func;
+typedef integer_comp_func<unsigned short>	ushort_comp_func;
+typedef integer_comp_func<unsigned int>		uint_comp_func;
+typedef integer_comp_func<unsigned long>	ulong_comp_func;
+typedef integer_comp_func<uint64_t>			uint64_comp_func;
+
+struct float_comp_func
+{
+	bool operator() (float v1, float v2) const
+	{
+		float disc	= v1 - v2;
+		if(fabsf(disc) < 1E-5)
+			return false;
+
+		return disc < 0;
+	}
+};
+
+struct double_comp_func
+{
+	bool operator() (double v1, double v2) const
+	{
+		double disc	= v1 - v2;
+		if(fabs(disc) < 1E-8)
+			return false;
+
+		return disc < 0;
+	}
+};
+
+template<class T, bool CASE = false>
+// T -> (unsigned) char / wchar_t
+struct character_comp_func
+{
+	bool operator() (T v1, T v2) const
+	{
+		if(!CASE)
+		{
+			if(v1 >= 'A' && v1 <= 'Z')	v1 += 32;
+			if(v2 >= 'A' && v2 <= 'Z')	v2 += 32;
+		}
+
+		return v1 < v2;
+	}
+};
+
+typedef character_comp_func<char, true>				char_case_comp_func;
+typedef character_comp_func<unsigned char, true>	uchar_case_comp_func;
+typedef character_comp_func<wchar_t, true>			wchar_case_comp_func;
+typedef character_comp_func<char, false>			char_ucase_comp_func;
+typedef character_comp_func<unsigned char, false>	uchar_ucase_comp_func;
+typedef character_comp_func<wchar_t, false>			wchar_ucase_comp_func;
+
+template<class T, bool CASE = false>
+// T -> TCHAR* / CString
+struct str_comp_func
+{
+	//比较函数。
+	bool operator() (const T &A, const T &B) const
+	{
+		if(!CASE)
+			return lstricmp((LPCTSTR)A, (LPCTSTR)B) < 0;
+		else
+			return lstrcmp((LPCTSTR)A, (LPCTSTR)B) < 0;
+	}
+};
+
+typedef str_comp_func<LPCTSTR, true>		case_tchar_comp_func;
+typedef str_comp_func<LPCTSTR, false>		uncase_tchar_comp_func;
+typedef str_comp_func<CString, true>		case_string_comp_func;
+typedef str_comp_func<CString, false>		uncase_string_comp_func;
+typedef case_tchar_comp_func				tchar_ptr_case_comp_func;
+typedef uncase_tchar_comp_func				tchar_ptr_ucase_comp_func;
+typedef case_string_comp_func				string_case_comp_func;
+typedef uncase_string_comp_func				string_ucase_comp_func;
+/************************************************************************/
+/*                            排序仿函数                                */
+/************************************************************************/
+template<bool ASC = true>
+struct bool_sort_func
+{
+	bool operator() (bool v1, bool v2) const
+	{
+		if(v1 == v2)
+			return false;
+
+		bool result = !v1;
+		return ASC ? result : !result;
+	}
+};
+
+typedef bool_sort_func<true>	bool_asc_sort_func;
+typedef bool_sort_func<false>	bool_desc_sort_func;
+
+template<class T, bool ASC = true>
+// T -> (signed / unsigned) short / int / long / int64_t
+struct integer_sort_func
+{
+	bool operator() (T v1, T v2) const
+	{
+		if(v1 == v2)
+			return false;
+
+		bool result = v1 < v2;
+		return ASC ? result : !result;
+	}
+};
+
+typedef integer_sort_func<short,			true>		short_asc_sort_func;
+typedef integer_sort_func<unsigned short,	true>		ushort_asc_sort_func;
+typedef integer_sort_func<int,				true>		int_asc_sort_func;
+typedef integer_sort_func<unsigned int,		true>		uint_asc_sort_func;
+typedef integer_sort_func<long,				true>		long_asc_sort_func;
+typedef integer_sort_func<unsigned long,	true>		ulong_asc_sort_func;
+typedef integer_sort_func<int64_t,			true>		int64_asc_sort_func;
+typedef integer_sort_func<uint64_t,			true>		uint64_asc_sort_func;
+typedef integer_sort_func<short,			false>		short_desc_sort_func;
+typedef integer_sort_func<unsigned short,	false>		ushort_desc_sort_func;
+typedef integer_sort_func<int,				false>		int_desc_sort_func;
+typedef integer_sort_func<unsigned int,		false>		uint_desc_sort_func;
+typedef integer_sort_func<long,				false>		long_desc_sort_func;
+typedef integer_sort_func<unsigned long,	false>		ulong_desc_sort_func;
+typedef integer_sort_func<int64_t,			false>		int64_desc_sort_func;
+typedef integer_sort_func<uint64_t,			false>		uint64_desc_sort_func;
+
+template<bool ASC = true>
+struct float_sort_func
+{
+	bool operator() (float v1, float v2) const
+	{
+		float disc	= v1 - v2;
+		if(fabsf(disc) < 1E-5)
+			return false;
+
+		bool result = disc < 0;
+		return ASC ? result : !result;
+	}
+};
+
+typedef float_sort_func<true>		float_asc_sort_func;
+typedef float_sort_func<false>		float_desc_sort_func;
+
+template<bool ASC = true>
+struct double_sort_func
+{
+	bool operator() (double v1, double v2) const
+	{
+		double disc	= v1 - v2;
+		if(fabs(disc) < 1E-8)
+			return false;
+
+		bool result = disc < 0;
+		return ASC ? result : !result;
+	}
+};
+
+typedef double_sort_func<true>		double_asc_sort_func;
+typedef double_sort_func<false>		double_desc_sort_func;
+
+template<class T, bool ASC = true, bool CASE = false>
+// T -> (unsigned) char / wchar_t
+struct character_sort_func
+{
+	bool operator() (T v1, T v2) const
+	{
+		if(!CASE)
+		{
+			if(v1 >= 'A' && v1 <= 'Z')	v1 += 32;
+			if(v2 >= 'A' && v2 <= 'Z')	v2 += 32;
+		}
+
+		if(v1 == v2)
+			return false;
+
+		bool result = v1 < v2;
+		return ASC ? result : !result;
+	}
+};
+
+typedef character_sort_func<char, true, true>				char_asc_case_sort_func;
+typedef character_sort_func<unsigned char, true, true>		uchar_asc_case_sort_func;
+typedef character_sort_func<wchar_t, true, true>			wchar_asc_case_sort_func;
+typedef character_sort_func<char, true, false>				char_asc_ucase_sort_func;
+typedef character_sort_func<unsigned char, true, false>		uchar_asc_ucase_sort_func;
+typedef character_sort_func<wchar_t, true, false>			wchar_asc_ucase_sort_func;
+typedef character_sort_func<char, false, true>				char_desc_case_sort_func;
+typedef character_sort_func<unsigned char, false, true>		uchar_desc_case_sort_func;
+typedef character_sort_func<wchar_t, false, true>			wchar_desc_case_sort_func;
+typedef character_sort_func<char, false, false>				char_desc_ucase_sort_func;
+typedef character_sort_func<unsigned char, false, false>	uchar_desc_ucase_sort_func;
+typedef character_sort_func<wchar_t, false, false>			wchar_desc_ucase_sort_func;
+
+template<class T, bool ASC = true, bool CASE = false>
+// T -> TCHAR* / CString
+struct str_sort_func
+{
+	bool operator() (const T& v1, const T& v2) const
+	{
+		bool result;
+
+		if(CASE)
+		{
+			int v = lstrcmp((LPCTSTR)v1, (LPCTSTR)v2);
+			if(v == 0)
+				result = false;
+			else
+				result = v < 0;
+		}
+		else
+		{
+			int v = tstricmp((LPCTSTR)v1, (LPCTSTR)v2);
+			if(v == 0)
+				result = false;
+			else
+				result = v < 0;
+		}
+
+		return ASC ? result : !result;
+	}
+};
+
+typedef str_sort_func<TCHAR*, true, true>		tchar_ptr_asc_case_sort_func;
+typedef str_sort_func<CString, true, true>		string_asc_case_sort_func;
+typedef str_sort_func<TCHAR*, true, false>		tchar_ptr_asc_ucase_sort_func;
+typedef str_sort_func<CString, true, false>		string_asc_ucase_sort_func;
+typedef str_sort_func<TCHAR*, false, true>		tchar_ptr_desc_case_sort_func;
+typedef str_sort_func<CString, false, true>		string_desc_case_sort_func;
+typedef str_sort_func<TCHAR*, false, false>		tchar_ptr_desc_ucase_sort_func;
+typedef str_sort_func<CString, false, false>	string_desc_ucase_sort_func;
+
+template<typename _IntType = int, typename = enable_if_t<is_integral<_IntType>::value>>
+class CRandomIntegralT
+{
+public:
+	_IntType Generate()		{return dist(gen);}
+	_IntType operator()()	{return Generate();}
+
+public:
+	CRandomIntegralT(_IntType from, _IntType to) : gen(rd()), dist(from, to)
+	{
+
+	}
+
+private:
+	random_device rd;
+	mt19937 gen;
+	uniform_int_distribution<_IntType> dist;
+};
+
+template<typename _RealType = double, typename = enable_if_t<is_floating_point<_RealType>::value>>
+class CRandomRealTypeT
+{
+public:
+	_RealType Generate()	{return dist(gen);}
+	_RealType operator()()	{return Generate();}
+
+public:
+	CRandomRealTypeT(_RealType from, _RealType to) : gen(rd()), dist(from, to)
+	{
+
+	}
+
+private:
+	random_device rd;
+	mt19937 gen;
+	uniform_real_distribution<_RealType> dist;
+};
+
+typedef CRandomIntegralT<int>			CRandomInt;
+typedef CRandomIntegralT<long>			CRandomLong;
+typedef CRandomIntegralT<unsigned int>	CRandomUint;
+typedef CRandomIntegralT<unsigned long>	CRandomUlong;
+typedef CRandomIntegralT<int64_t>		CRandomInt64;
+typedef CRandomIntegralT<uint64_t>		CRandomUint64;
+
+typedef CRandomRealTypeT<float>			CRandomFloat;
+typedef CRandomRealTypeT<double>		CRandomDouble;

+ 117 - 0
common/Semaphore.h

@@ -0,0 +1,117 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "CriSec.h"
+
+#include <condition_variable>
+
+using namespace std;
+
+class CSEM
+{
+public:
+	void Wait()
+	{
+		CMutexLock2 lock(m_mtx);
+
+		m_cv.wait(lock);
+	}
+
+	template<typename _Predicate>
+	void Wait(_Predicate p)
+	{
+		CMutexLock2 lock(m_mtx);
+
+		m_cv.wait(lock, p);
+	}
+	
+	template<typename _Rep, typename _Period>
+	cv_status WaitFor(const chrono::duration<_Rep, _Period>& t)
+	{
+		CMutexLock2 lock(m_mtx);
+
+		return m_cv.wait_for(lock, t);
+	}
+
+	cv_status WaitFor(DWORD dwMilliseconds)
+	{
+		return WaitFor(chrono::milliseconds(dwMilliseconds));
+	}
+
+	template<typename _Rep, typename _Period, typename _Predicate>
+	bool WaitFor(const chrono::duration<_Rep, _Period>& t, _Predicate p)
+	{
+		CMutexLock2 lock(m_mtx);
+
+		return m_cv.wait_for(lock, t, p);
+	}
+
+	template<typename _Predicate>
+	bool WaitFor(DWORD dwMilliseconds, _Predicate p)
+	{
+		if(IS_INFINITE(dwMilliseconds))
+		{
+			Wait(p);
+			return true;
+		}
+
+		return WaitFor(chrono::milliseconds(dwMilliseconds), p);
+
+	}
+
+	void NotifyOne()
+	{
+		m_cv.notify_one();
+	}
+
+	void NotifyAll()
+	{
+		m_cv.notify_all();
+	}
+
+	void SyncNotifyOne()
+	{
+		CMutexLock2 lock(m_mtx);
+
+		NotifyOne();
+	}
+
+	void SyncNotifyAll()
+	{
+		CMutexLock2 lock(m_mtx);
+
+		NotifyAll();
+	}
+
+private:
+	CMTX				m_mtx;
+	condition_variable	m_cv;
+
+	DECLARE_NO_COPY_CLASS(CSEM)
+	DECLARE_PUBLIC_DEFAULT_CONSTRUCTOR(CSEM)
+};
+
+using CCVLock = CSEM;

+ 183 - 0
common/SignalHandler.h

@@ -0,0 +1,183 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "Thread.h"
+
+#include <signal.h>
+
+#include <memory>
+
+using namespace std;
+
+template<class T> class CSignalHandler
+{
+public:
+	using MT				= CSignalHandler<T>;
+	using SI				= siginfo_t;
+	using SS				= sigset_t;
+	using CHandlerThread	= CThread<MT, const SS, VOID>;
+	using F					= VOID (T::*)(const SI*);
+	using SF				= VOID (*)(const SI*);
+	using SSPTR				= unique_ptr<SS>;
+	friend CHandlerThread;
+
+public:
+
+	BOOL Setup(SF pFunc, const SS* pSigSet, BOOL bRestorOnCancel = TRUE)
+	{
+		return Setup((__CFakeRunnerClass_*)nullptr, *(F*)&pFunc, pSigSet, bRestorOnCancel);
+	}
+
+	BOOL Setup(T* pRunner, F pFunc, const SS* pSigSet, BOOL bRestorOnCancel = TRUE)
+	{
+		ASSERT_CHECK_EINVAL(pSigSet != nullptr);
+
+		m_pssPre = make_unique<SS>();
+		int rs	 = pthread_sigmask(SIG_BLOCK, pSigSet, m_pssPre.get());
+
+		if(rs != NO_ERROR)
+		{
+			m_pssPre = nullptr;
+			::SetLastError(rs);
+
+			return FALSE;
+		}
+
+		m_pRunner	= pRunner;
+		m_pFunc		= pFunc;
+		m_pssCur	= make_unique<SS>();
+
+		::CopyPlainObject(m_pssCur.get(), pSigSet);
+
+		BOOL isOK	= m_thHandler.Start(this, &MT::ThreadFunc, m_pssCur.get());
+
+		if(isOK && !bRestorOnCancel)
+			m_pssPre = nullptr;
+		else if(!isOK)
+			EXECUTE_RESTORE_ERROR(Reset());
+
+		return isOK;
+	}
+
+	BOOL Cancel()
+	{
+		BOOL isOK = m_thHandler.IsRunning();
+
+		if(isOK)
+		{
+			isOK  = m_thHandler.Interrupt();
+			isOK &= m_thHandler.Join();
+		}
+
+		isOK &= Reset();
+
+		return isOK;
+	}
+
+	BOOL IsRunning		()	{return m_thHandler.IsRunning();}
+	T* GetRunner		()	{return m_pRunner;}
+	F GetFunc			()	{return m_pFunc;}
+	SF GetSFunc			()	{return *(SF*)&m_pFunc;}
+	THR_ID GetThreadID	()	{return m_thHandler.GetThreadID();}
+	NTHR_ID GetNativeID	()	{return m_thHandler.GetNativeID();}
+
+
+private:
+	VOID ThreadFunc(const SS* pSigSet)
+	{
+		ASSERT(pSigSet == m_pssCur.get());
+
+		SI si;
+
+		ZeroObject(si);
+
+		while(!::IsThreadInterrupted())
+		{
+#if !defined(__ANDROID__)
+			int rs = NO_EINTR_EXCEPT_THR_INTR_INT(sigwaitinfo(pSigSet, &si));
+#else
+			int rs = NO_EINTR_EXCEPT_THR_INTR_INT(sigwait(pSigSet, &si.si_signo));
+#endif
+
+			if(IS_HAS_ERROR(rs))
+			{
+				if(IS_ERROR(EINTR))
+				{
+					ASSERT(::IsThreadInterrupted());
+					break;
+				}
+				
+				ERROR_ABORT();
+			}
+
+			Run((T*)nullptr, &si);
+		}
+	}
+
+	template<typename T_, typename = enable_if_t<!is_same<T_, __CFakeRunnerClass_>::value>>
+	VOID Run(T_*, const SI* pSigSet) {(m_pRunner->*m_pFunc)(pSigSet);}
+
+	VOID Run(__CFakeRunnerClass_*, const SI* pSigSet) {(*(SF*)&m_pFunc)(pSigSet);}
+
+
+	BOOL Reset()
+	{
+		BOOL isOK = TRUE;
+
+		if(m_pssPre)
+		{
+			isOK	 = (pthread_sigmask(SIG_SETMASK, m_pssPre.get(), nullptr) == NO_ERROR);
+			m_pssPre = nullptr;
+		}
+
+		m_pssCur	= nullptr;
+		m_pRunner	= nullptr;
+		m_pFunc		= nullptr;
+
+		return isOK;
+	}
+
+public:
+	CSignalHandler()
+	{
+	}
+
+	virtual ~CSignalHandler()
+	{
+		Cancel();
+	}
+
+	DECLARE_NO_COPY_CLASS(CSignalHandler)
+
+private:
+	T*				m_pRunner;
+	F				m_pFunc;
+	SSPTR			m_pssCur;
+	SSPTR			m_pssPre;
+	CHandlerThread	m_thHandler;
+};
+
+using CStaticSignalHandler = CSignalHandler<__CFakeRunnerClass_>;

+ 117 - 0
common/Singleton.h

@@ -0,0 +1,117 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+
+#define SINGLETON_THIS(ClassName)		ClassName::GetThis()
+#define SINGLETON_INSTANCE(ClassName)	ClassName::GetInstance()
+#define SINGLETON_OBJECT(ObjName)		SINGLETON_INSTANCE(C##ObjName)
+
+#define DEFINE_SINGLETON(ClassName)											\
+	ClassName* ClassName::m_pThis = nullptr;
+
+#define DEFINE_P_THIS(ClassName)											\
+		DEFINE_SINGLETON(ClassName)
+
+#define DECLARE_SINGLETON_INTERFACE(ClassName)								\
+public:																		\
+	static ClassName* GetThis()		{return m_pThis;}						\
+	static ClassName& GetInstance() {return *m_pThis;}						\
+protected:																	\
+	static ClassName* m_pThis;
+
+#define DECLARE_SINGLETON_CREATE_INSTANCE(ClassName)						\
+public:																		\
+	static BOOL CreateInstance()											\
+	{																		\
+		if(!m_pThis)														\
+			m_pThis = new ClassName;										\
+																			\
+		return m_pThis != nullptr;											\
+	}																		\
+																			\
+	static BOOL DeleteInstance()											\
+	{																		\
+		if(m_pThis)															\
+		{																	\
+			delete m_pThis;													\
+			m_pThis = nullptr;												\
+		}																	\
+																			\
+		return m_pThis == nullptr;											\
+	}
+
+#define DECLARE_PUBLIC_DEFAULT_CONSTRUCTOR(ClassName)						\
+public:																		\
+	ClassName() = default;
+
+#define DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName)						\
+private:																	\
+	ClassName() = default;
+
+#define DECLARE_PRIVATE_COPY_CONSTRUCTOR(ClassName)							\
+private:																	\
+	ClassName(const ClassName&);											\
+	ClassName& operator = (const ClassName&);
+
+#define DECLARE_NO_COPY_CLASS(ClassName)									\
+private:																	\
+	ClassName(const ClassName&) = delete;									\
+	ClassName& operator = (const ClassName&) = delete;
+
+
+#define DECLARE_SINGLETON_IMPLEMENT_NO_CREATE_INSTANCE(ClassName)			\
+	DECLARE_SINGLETON_INTERFACE(ClassName)									\
+	DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName)							\
+	DECLARE_NO_COPY_CLASS(ClassName)								
+
+#define DECLARE_SINGLETON_IMPLEMENT_NO_DEFAULT_CONSTRUCTOR(ClassName)		\
+	DECLARE_SINGLETON_CREATE_INSTANCE(ClassName)							\
+	DECLARE_NO_COPY_CLASS(ClassName)
+
+#define DECLARE_SINGLETON_IMPLEMENT(ClassName)								\
+	DECLARE_SINGLETON_IMPLEMENT_NO_DEFAULT_CONSTRUCTOR(ClassName)			\
+	DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName)
+
+#define DECLARE_SINGLETON_NO_DEFAULT_CONSTRUCTOR(ClassName)					\
+	DECLARE_SINGLETON_INTERFACE(ClassName)									\
+	DECLARE_SINGLETON_IMPLEMENT_NO_DEFAULT_CONSTRUCTOR(ClassName)
+
+#define DECLARE_SINGLETON(ClassName)										\
+	DECLARE_SINGLETON_NO_DEFAULT_CONSTRUCTOR(ClassName)						\
+	DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName)
+
+
+template<class T> class CSingleObject
+{
+public:
+	CSingleObject	()	{T::CreateInstance();}
+	~CSingleObject	()	{T::DeleteInstance();}
+	T* GetPointer	()	{return T::GetThis();}
+	T& GetObject	()	{return T::GetInstance();}
+	BOOL IsValid	()	{return GetPointer() != nullptr;}
+};
+
+#define DECLARE_SINGLE_OBJECT(ClassName) CSingleObject<ClassName> _##ClassName##_Single_Object_;

+ 930 - 0
common/StringT.h

@@ -0,0 +1,930 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "FuncHelper.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <string>
+
+using namespace std;
+
+template<typename _CharT, typename _Traits = char_traits<_CharT>, typename _Alloc = allocator<_CharT>>
+class CStringT : public basic_string<_CharT, _Traits, _Alloc>
+{
+public:
+
+	using __super				 = basic_string<_CharT, _Traits, _Alloc>;
+
+	using XCHAR					 = _CharT;
+	using PXSTR					 = _CharT*;
+	using PCXSTR				 = const _CharT*;
+
+	using traits_type			 = typename __super::traits_type;
+	using value_type			 = typename __super::value_type;
+	using allocator_type		 = typename __super::allocator_type;
+	using size_type				 = typename __super::size_type;
+	using difference_type		 = typename __super::difference_type;
+	using reference				 = typename __super::reference;
+	using const_reference		 = typename __super::const_reference;
+	using pointer				 = typename __super::pointer;
+	using const_pointer			 = typename __super::const_pointer;
+	using iterator				 = typename __super::iterator;
+	using const_iterator		 = typename __super::const_iterator;
+	using const_reverse_iterator = typename __super::const_reverse_iterator;
+	using reverse_iterator		 = typename __super::reverse_iterator;
+
+	using __super::clear;
+	using __super::empty;
+	using __super::size;
+	using __super::resize;
+	using __super::data;
+	using __super::c_str;
+
+private:
+
+	constexpr static PCXSTR SPACE_CHARS = _T(" \t\r\n\f\v");
+
+public:
+
+	void Empty()						{clear();}
+	bool IsEmpty()				const	{return empty();}
+	int GetLength()				const	{return (int)size();}
+	const _CharT* GetString()	const	{return c_str();}
+
+	operator const _CharT* ()	const	{return __super::c_str();}
+
+	_CharT* GetBuffer(int length)
+	{
+		resize((size_type)length);
+		return (_CharT*)data();
+	}
+
+	void ReleaseBuffer(int length = -1)
+	{
+		if(length == -1)
+			length = lstrlen(data());
+		
+		resize(length);
+	}
+
+	void ReleaseBufferSetLength(int length)
+	{
+		ASSERT(length >=0);
+		ReleaseBuffer(length);
+	}
+
+	void Truncate(int length)
+	{
+		if(length >= GetLength())
+			return;
+
+		ReleaseBuffer(length);
+	}
+
+	int Format(const _CharT* format, ...)
+	{
+		int rs;
+		va_list ap;
+
+		va_start(ap, format);
+ 		rs = VASprintf(0, format, ap);
+		va_end(ap);
+
+		return rs;
+	}
+
+	int AppendFormat(const _CharT* format, ...)
+	{
+		int rs;
+		va_list ap;
+
+		va_start(ap, format);
+		rs = VASprintf(GetLength(), format, ap);
+		va_end(ap);
+
+		return rs;
+	}
+
+	int VASprintf(int offset, const _CharT* format, va_list ap)
+	{
+		va_list ap_cpy;
+		va_copy(ap_cpy, ap);
+
+		int count = vsnprintf(nullptr, 0, format, ap);
+
+		if(count >= 0)
+		{
+			_CharT* p = GetBuffer(count + offset);
+			vsnprintf(p + offset, count + 1, format, ap_cpy);
+		}
+
+		va_end(ap_cpy);
+
+		return count;
+	}
+
+	CStringT& Append(const _CharT* __s)
+	{
+		append(__s);
+		return *this;
+	}
+
+	CStringT& Append(const _CharT* __s, int __n)
+	{
+		append(__s, __n);
+		return *this;
+	}
+
+	CStringT& AppendChar(_CharT __c)
+	{
+		push_back(__c);
+		return *this;
+	}
+
+	int Compare(const _CharT* __s) const
+	{
+		return lstrcmp(c_str(), __s);
+	}
+
+	int CompareNoCase(const _CharT* __s) const
+	{
+		return lstricmp(c_str(), __s);
+	}
+
+	bool Equals(const _CharT* __s) const
+	{
+		return (Compare(__s) == 0);
+	}
+
+	bool EqualsNoCase(const _CharT* __s) const
+	{
+		return (CompareNoCase(__s) == 0);
+	}
+
+	CStringT& MakeLower()
+	{
+		size_type s	= size();
+		_CharT* p	= (_CharT*)c_str();
+		_CharT c;
+
+		for(size_type i = 0; i < s; i++)
+		{
+			c = p[i];
+
+			if(c >= 'A' && c <= 'Z')
+				p[i] = (_CharT)(c + 32);
+		}
+
+		return *this;
+	}
+
+	CStringT& MakeUpper()
+	{
+		size_type s	= size();
+		_CharT* p	= (_CharT*)c_str();
+		_CharT c;
+
+		for(size_type i = 0; i < s; i++)
+		{
+			c = p[i];
+
+			if(c >= 'a' && c <= 'z')
+				p[i] = (_CharT)(c - 32);
+		}
+
+		return *this;
+	}
+
+	CStringT Mid(int iFirst, int nCount = (int)__super::npos) const
+	{
+		return substr(iFirst, nCount);
+	}
+
+	CStringT Left(int nCount) const
+	{
+		return Mid(0, nCount);
+	}
+
+	CStringT Right(int nCount) const
+	{
+		int nLength = GetLength();
+
+		if(nCount >= nLength)
+			return *this;
+
+		return Mid(nLength - nCount, nCount);
+	}
+
+	CStringT Tokenize(PCXSTR lpszTokens, int& iStart) const
+	{
+		ASSERT(iStart >= 0);
+
+		if((lpszTokens == nullptr) || (*lpszTokens == (_CharT)0))
+		{
+			if(iStart < GetLength())
+				return CStringT(GetString() + iStart);
+		}
+		else
+		{
+			PCXSTR pszPlace	= GetString() + iStart;
+			PCXSTR pszEnd	= GetString() + GetLength();
+
+			if(pszPlace < pszEnd)
+			{
+				int nIncluding = lstrspn(pszPlace, lpszTokens);
+
+				if((pszPlace + nIncluding) < pszEnd)
+				{
+					pszPlace		+= nIncluding;
+					int nExcluding	 = lstrcspn(pszPlace, lpszTokens);
+
+					int iFrom	= iStart + nIncluding;
+					int nUntil	= nExcluding;
+					iStart		= iFrom + nUntil + 1;
+
+					return Mid(iFrom, nUntil);
+				}
+			}
+		}
+
+		iStart = -1;
+
+		return CStringT();
+	}
+
+	CStringT& Trim()
+	{
+		return Trim(SPACE_CHARS);
+	}
+
+	CStringT& TrimRight()
+	{
+		return TrimRight(SPACE_CHARS);
+	}
+
+	CStringT& TrimLeft()
+	{
+		return TrimLeft(SPACE_CHARS);
+	}
+
+	CStringT& Trim(XCHAR c)
+	{
+		return(TrimRight(c).TrimLeft(c));
+	}
+
+	CStringT& TrimRight(XCHAR c)
+	{
+		int iLength = GetLength();
+
+		if(iLength == 0)
+			return *this;
+
+		PCXSTR lpszBegin = GetString();
+		PCXSTR lpszEnd	 = lpszBegin + iLength;
+
+		while(lpszEnd > lpszBegin)
+		{
+			if(*(lpszEnd - 1) != c)
+				break;
+
+			--lpszEnd;
+		}
+
+		int iNewLength = (int)(lpszEnd - lpszBegin);
+
+		if(iNewLength < iLength)
+			Truncate(iNewLength);
+
+		return *this;
+	}
+
+	CStringT& TrimLeft(XCHAR c)
+	{
+		int iLength = GetLength();
+
+		if(iLength == 0)
+			return *this;
+
+		PCXSTR lpszBegin = GetString();
+		PCXSTR lpszEnd	 = lpszBegin;
+		int iOffset		 = 0;
+
+		while(*lpszEnd == c)
+		{
+			++lpszEnd;
+			++iOffset;
+
+			if(iOffset == iLength)
+				break;
+		}
+
+		if(iOffset != 0)
+		{
+			int iNewLength = iLength - iOffset;
+
+			if(iNewLength > 0)
+				memcpy((PXSTR)lpszBegin, lpszEnd, (iLength - iOffset) * sizeof(XCHAR));
+
+			ReleaseBufferSetLength(iNewLength);
+		}
+
+		return *this;
+	}
+
+	CStringT& Trim(PCXSTR lpszChars)
+	{
+		return(TrimRight(lpszChars).TrimLeft(lpszChars));
+	}
+
+	CStringT& TrimRight(PCXSTR lpszChars)
+	{
+		ASSERT(!::IsStrEmpty(lpszChars));
+
+		if(::IsStrEmpty(lpszChars))
+			return *this;
+
+		int iLength = GetLength();
+
+		if(iLength == 0)
+			return *this;
+
+		PCXSTR lpszBegin = GetString();
+		PCXSTR lpszEnd	 = lpszBegin + iLength;
+
+		while(lpszEnd > lpszBegin)
+		{
+			if(::StrChr(lpszChars, *(lpszEnd - 1)) == nullptr)
+				break;
+
+			--lpszEnd;
+		}
+
+		int iNewLength = (int)(lpszEnd - lpszBegin);
+
+		if(iNewLength < iLength)
+			Truncate(iNewLength);
+
+		return *this;
+	}
+
+	CStringT& TrimLeft(PCXSTR lpszChars)
+	{
+		ASSERT(!::IsStrEmpty(lpszChars));
+
+		if(::IsStrEmpty(lpszChars))
+			return *this;
+
+		int iLength = GetLength();
+
+		if(iLength == 0)
+			return *this;
+
+		PCXSTR lpszBegin = GetString();
+		PCXSTR lpszEnd	 = lpszBegin;
+		int iOffset		 = 0;
+
+		while(::StrChr(lpszChars, *lpszEnd) != nullptr)
+		{
+			++lpszEnd;
+			++iOffset;
+
+			if(iOffset == iLength)
+				break;
+		}
+
+		if(iOffset != 0)
+		{
+			int iNewLength = iLength - iOffset;
+
+			if(iNewLength > 0)
+				memcpy((PXSTR)lpszBegin, lpszEnd, (iLength - iOffset) * sizeof(XCHAR));
+
+			ReleaseBufferSetLength(iNewLength);
+		}
+
+		return *this;
+	}
+
+	int Find(XCHAR c, int iStart = 0) const
+	{
+		ASSERT(iStart >= 0);
+
+		int iLength = GetLength();
+
+		if(iStart < 0 || iStart >= iLength)
+			return -1;
+
+		PCXSTR lpszBegin = GetString();
+		PCXSTR lpszFind	 = ::StrChr(lpszBegin + iStart, c);
+
+		return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
+	}
+
+	int Find(PCXSTR lpszSub, int iStart = 0) const
+	{
+		ASSERT(iStart >= 0 && !::IsStrEmpty(lpszSub));
+
+		int iLength = GetLength();
+
+		if(lpszSub == nullptr || iStart < 0 || iStart > iLength)
+			return -1;
+
+		PCXSTR lpszBegin = GetString();
+		PCXSTR lpszFind	 = ::StrStr(lpszBegin + iStart, lpszSub);
+		
+		return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
+	}
+
+	int FindOneOf(PCXSTR lpszChars) const
+	{
+		ASSERT(!::IsStrEmpty(lpszChars));
+
+		if(lpszChars == nullptr)
+			return -1;
+
+		PCXSTR lpszBegin = GetString();
+		PCXSTR lpszFind	 = ::StrPBrk(lpszBegin, lpszChars);
+
+		return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
+	}
+
+	int ReverseFind(XCHAR c) const
+	{
+		PCXSTR lpszBegin = GetString();
+		PCXSTR lpszFind	 = ::StrRChr(lpszBegin, c);
+
+		return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
+	}
+
+	int Remove(XCHAR c)
+	{
+		int iLength = GetLength();
+
+		if(iLength == 0)
+			return 0;
+
+		PCXSTR lpszBegin = GetString();
+		PXSTR lpszCur	 = (PXSTR)lpszBegin;
+		PCXSTR lpszEnd	 = lpszBegin + iLength;
+		int iRemoved	 = 0;
+
+		while(lpszCur < lpszEnd)
+		{
+			if(*lpszCur == c)
+				++iRemoved;
+			else if(iRemoved > 0)
+				*(lpszCur - iRemoved) = *lpszCur;
+
+			++lpszCur;
+		}
+
+		if(iRemoved > 0)
+			ReleaseBufferSetLength(iLength - iRemoved);
+
+		return iRemoved;
+	}
+
+	XCHAR GetAt(int i) const
+	{
+		return (*this)[i];
+	}
+
+	void SetAt(int i, XCHAR c)
+	{
+		(*this)[i] = c;
+	}
+
+	XCHAR operator[](int i) const
+	{
+		ASSERT(i >= 0 && i < GetLength());
+
+		return *(GetString() + i);
+	}
+
+	XCHAR& operator[](int i)
+	{
+		ASSERT(i >= 0 && i < GetLength());
+
+		return *(PXSTR)(GetString() + i);
+	}
+
+	CStringT& Insert(int i, XCHAR c)
+	{
+		return insert((size_type)i, 1, c);
+	}
+
+	CStringT& Insert(int i, PCXSTR lpszChars)
+	{
+		return insert((size_type)i, lpszChars);
+	}
+
+	CStringT& SetString(PCXSTR lpszStr)
+	{
+		return assign(lpszStr);
+	}
+
+	CStringT& SetString(PCXSTR lpszStr, int iLength)
+	{
+		return assign(lpszStr, iLength);
+	}
+
+	friend bool operator==(const CStringT& str1, const CStringT& str2)
+	{
+		return (str1.Compare(str2) == 0);
+	}
+
+	friend bool operator==(const CStringT& str1, const _CharT* psz2)
+	{
+		return (str1.Compare(psz2) == 0);
+	}
+
+	friend bool operator==(const _CharT* psz1, const CStringT& str2)
+	{
+		return (str2.Compare(psz1) == 0);
+	}
+
+	friend bool operator!=(const CStringT& str1, const CStringT& str2)
+	{
+		return !(str1 == str2);
+	}
+
+	friend bool operator!=(const CStringT& str1, const _CharT* psz2)
+	{
+		return !(str1 == psz2);
+	}
+
+	friend bool operator!=(const _CharT* psz1, const CStringT& str2)
+	{
+		return !(psz1 == str2);
+	}
+
+public:
+	CStringT() : __super() {};
+
+	explicit CStringT(const _Alloc& __a)
+	: __super(__a) {}
+
+	CStringT(const __super& __str)
+	: __super(__str) {}
+
+	CStringT(const CStringT& __str)
+	: __super(__str) {}
+
+	CStringT(const __super& __str, size_type __pos, size_type __n = __super::npos)
+	: __super(__str, __pos, __n) {}
+
+	CStringT(const __super& __str, size_type __pos, size_type __n, const _Alloc& __a)
+	: __super(__str, __pos, __n, __a) {}
+
+	CStringT(const _CharT* __s, size_type __n, const _Alloc& __a = _Alloc())
+	: __super(::SafeStr(__s), __n, __a) {}
+
+	CStringT(const _CharT* __s, const _Alloc& __a = _Alloc())
+	: __super(::SafeStr(__s), __a) {}
+
+	CStringT(size_type __n, _CharT __c, const _Alloc& __a = _Alloc())
+	: __super(__n, __c, __a) {}
+
+#if __cplusplus >= 201103L
+	CStringT(__super&& __str)
+	: __super(__str) {}
+
+	CStringT(CStringT&& __str)
+	: __super(__str) {}
+
+	CStringT(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc())
+	: __super(__l, __a) {}
+#endif // C++11
+
+	template<class _InputIterator>
+	CStringT(_InputIterator __beg, _InputIterator __end, const _Alloc& __a = _Alloc())
+	: __super(__beg, __end, __a) {}
+
+	~CStringT() = default;
+
+	CStringT& operator=(const __super& __str)
+	{__super::operator=(__str); return *this;}
+
+	CStringT& operator=(const CStringT& __str)
+	{__super::operator=(__str); return *this;}
+
+	CStringT& operator=(const _CharT* __s)
+	{__super::operator=(::SafeStr(__s)); return *this;}
+
+	CStringT& operator=(_CharT __c)
+	{__super::operator=(__c); return *this;}
+
+#if __cplusplus >= 201103L
+	CStringT& operator=(__super&& __str)
+	{__super::operator=(__str); return *this;}
+
+	CStringT& operator=(CStringT&& __str)
+	{__super::operator=(__str); return *this;}
+
+	CStringT& operator=(initializer_list<_CharT> __l)
+	{__super::operator=(__l); return *this;}
+#endif // C++11
+
+public:
+	CStringT& operator+=(const __super& __str)
+	{__super::operator+=(__str); return *this;}
+
+	CStringT& operator+=(const _CharT* __s)
+	{__super::operator+=(::SafeStr(__s)); return *this;}
+
+	CStringT& operator+=(_CharT __c)
+	{__super::operator+=(__c); return *this;}
+
+#if __cplusplus >= 201103L
+	CStringT& operator+=(initializer_list<_CharT> __l)
+	{__super::operator+=(__l); return *this;}
+#endif // C++11
+
+	CStringT& append(const __super& __str)
+	{__super::append(__str); return *this;}
+
+	CStringT& append(const __super& __str, size_type __pos, size_type __n)
+	{__super::append(__str, __pos, __n); return *this;}
+
+	CStringT& append(const _CharT* __s, size_type __n)
+	{__super::append(::SafeStr(__s), __n); return *this;}
+
+	CStringT& append(const _CharT* __s)
+	{__super::append(::SafeStr(__s)); return *this;}
+
+	CStringT& append(size_type __n, _CharT __c)
+	{__super::append(__n, __c); return *this;}
+
+#if __cplusplus >= 201103L
+	CStringT& append(initializer_list<_CharT> __l)
+	{__super::append(__l); return *this;}
+#endif // C++11
+
+	template<class _InputIterator>
+	CStringT& append(_InputIterator __first, _InputIterator __last)
+	{__super::append(__first, __last); return *this;}
+
+	void push_back(_CharT __c)
+	{__super::push_back(__c);}
+
+	CStringT& assign(const __super& __str)
+	{__super::assign(__str); return *this;}
+
+#if __cplusplus >= 201103L
+	CStringT& assign(__super&& __str)
+	{__super::assign(__str); return *this;}
+#endif // C++11
+
+	CStringT& assign(const __super& __str, size_type __pos, size_type __n)
+	{__super::assign(__str, __pos, __n); return *this;}
+
+	CStringT& assign(const _CharT* __s, size_type __n)
+	{__super::assign(::SafeStr(__s), __n); return *this;}
+
+	CStringT& assign(const _CharT* __s)
+	{__super::assign(::SafeStr(__s)); return *this;}
+
+	CStringT& assign(size_type __n, _CharT __c)
+	{__super::assign(__n, __c); return *this;}
+
+	template<class _InputIterator>
+	CStringT& assign(_InputIterator __first, _InputIterator __last)
+	{__super::assign(__first, __last); return *this;}
+
+#if __cplusplus >= 201103L
+	CStringT& assign(initializer_list<_CharT> __l)
+	{__super::assign(__l); return *this;}
+#endif // C++11
+
+	CStringT& insert(size_type __pos1, const __super& __str)
+	{__super::insert(__pos1, __str); return *this;}
+
+	CStringT& insert(size_type __pos1, const __super& __str, size_type __pos2, size_type __n)
+	{__super::insert(__pos1, __str, __pos2, __n); return *this;}
+
+	CStringT& insert(size_type __pos, const _CharT* __s, size_type __n)
+	{__super::insert(__pos, __s, __n); return *this;}
+
+	CStringT& insert(size_type __pos, const _CharT* __s)
+	{__super::insert(__pos, __s); return *this;}
+
+	CStringT& insert(size_type __pos, size_type __n, _CharT __c)
+	{__super::insert(__pos, __n, __c); return *this;}
+
+	CStringT& erase(size_type __pos = 0, size_type __n = __super::npos)
+	{__super::erase(__pos, __n); return *this;}
+
+	CStringT& replace(size_type __pos, size_type __n, const __super& __str)
+	{__super::replace(__pos, __n, __str); return *this;}
+
+	CStringT& replace(size_type __pos1, size_type __n1, const __super& __str, size_type __pos2, size_type __n2)
+	{__super::replace(__pos1, __n1, __str, __pos2, __n2); return *this;}
+
+	CStringT& replace(size_type __pos, size_type __n1, const _CharT* __s, size_type __n2)
+	{__super::replace(__pos, __n1, __s, __n2); return *this;}
+
+	CStringT& replace(size_type __pos, size_type __n1, const _CharT* __s)
+	{__super::replace(__pos, __n1, __s); return *this;}
+
+	CStringT& replace(size_type __pos, size_type __n1, size_type __n2, _CharT __c)
+	{__super::replace(__pos, __n1, __n2, __c); return *this;}
+
+	CStringT& replace(iterator __i1, iterator __i2, const __super& __str)
+	{__super::replace(__i1, __i2, __str); return *this;}
+
+	CStringT& replace(iterator __i1, iterator __i2, const _CharT* __s, size_type __n)
+	{__super::replace(__i1, __i2, __s, __n); return *this;}
+
+	CStringT& replace(iterator __i1, iterator __i2, const _CharT* __s)
+	{__super::replace(__i1, __i2, __s); return *this;}
+
+	CStringT& replace(iterator __i1, iterator __i2, size_type __n, _CharT __c)
+	{__super::replace(__i1, __i2, __n, __c); return *this;}
+
+	template<class _InputIterator>
+	CStringT& replace(iterator __i1, iterator __i2, _InputIterator __k1, _InputIterator __k2)
+	{__super::replace(__i1, __i2, __k1, __k2); return *this;}
+
+	CStringT& replace(iterator __i1, iterator __i2, _CharT* __k1, _CharT* __k2)
+	{__super::replace(__i1, __i2, __k1, __k2); return *this;}
+
+	CStringT& replace(iterator __i1, iterator __i2, const _CharT* __k1, const _CharT* __k2)
+	{__super::replace(__i1, __i2, __k1, __k2); return *this;}
+
+	CStringT& replace(iterator __i1, iterator __i2, iterator __k1, iterator __k2)
+	{__super::replace(__i1, __i2, __k1, __k2); return *this;}
+
+	CStringT& replace(iterator __i1, iterator __i2, const_iterator __k1, const_iterator __k2)
+	{__super::replace(__i1, __i2, __k1, __k2); return *this;}
+
+#if __cplusplus >= 201103L
+	CStringT& replace(iterator __i1, iterator __i2, initializer_list<_CharT> __l)
+	{__super::replace(__i1, __i2, __l); return *this;}
+#endif // C++11
+
+	CStringT substr(size_type __pos = 0, size_type __n = __super::npos) const
+	{return __super::substr(__pos, __n);}
+
+};
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+CStringT<_CharT, _Traits, _Alloc>
+	operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, const CStringT<_CharT, _Traits, _Alloc>& __rhs)
+{
+	CStringT<_CharT, _Traits, _Alloc> __str(__lhs);
+	__str.append(__rhs);
+	return __str;
+}
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+CStringT<_CharT,_Traits,_Alloc>
+	operator+(const _CharT* __lhs, const CStringT<_CharT,_Traits,_Alloc>& __rhs);
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+CStringT<_CharT,_Traits,_Alloc>
+	operator+(_CharT __lhs, const CStringT<_CharT,_Traits,_Alloc>& __rhs);
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+inline CStringT<_CharT, _Traits, _Alloc>
+	operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, const _CharT* __rhs)
+{
+	CStringT<_CharT, _Traits, _Alloc> __str(__lhs);
+	__str.append(__rhs);
+	return __str;
+}
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+inline CStringT<_CharT, _Traits, _Alloc>
+	operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, _CharT __rhs)
+{
+	typedef CStringT<_CharT, _Traits, _Alloc>	__string_type;
+	typedef typename __string_type::size_type	__size_type;
+	__string_type __str(__lhs);
+	__str.append(__size_type(1), __rhs);
+	return __str;
+}
+
+#if __cplusplus >= 201103L
+template<typename _CharT, typename _Traits, typename _Alloc>
+inline CStringT<_CharT, _Traits, _Alloc>
+	operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, const CStringT<_CharT, _Traits, _Alloc>& __rhs)
+{return std::move(__lhs.append(__rhs));}
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+inline CStringT<_CharT, _Traits, _Alloc>
+	operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
+{return std::move(__rhs.insert(0, __lhs));}
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+inline CStringT<_CharT, _Traits, _Alloc>
+	operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
+{
+	const auto __size = __lhs.size() + __rhs.size();
+	const bool __cond = (__size > __lhs.capacity()
+		&& __size <= __rhs.capacity());
+	return __cond ? std::move(__rhs.insert(0, __lhs))
+		: std::move(__lhs.append(__rhs));
+}
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+inline CStringT<_CharT, _Traits, _Alloc>
+	operator+(const _CharT* __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
+{return std::move(__rhs.insert(0, __lhs));}
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+inline CStringT<_CharT, _Traits, _Alloc>
+	operator+(_CharT __lhs,	CStringT<_CharT, _Traits, _Alloc>&& __rhs)
+{return std::move(__rhs.insert(0, 1, __lhs));}
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+inline CStringT<_CharT, _Traits, _Alloc>
+	operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, const _CharT* __rhs)
+{return std::move(__lhs.append(__rhs));}
+
+template<typename _CharT, typename _Traits, typename _Alloc>
+inline CStringT<_CharT, _Traits, _Alloc>
+	operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, _CharT __rhs)
+{return std::move(__lhs.append(1, __rhs));}
+#endif
+
+using CStringA			= CStringT<char>;
+using CStringW			= CStringT<wchar_t>;
+
+using CStdStringA		= string;
+using CStdStringW		= wstring;
+
+#ifdef _UNICODE
+	using CString		= CStringW;
+	using CStdString	= CStdStringW;
+#else
+	using CString		= CStringA;
+	using CStdString	= CStdStringA;
+#endif
+
+#define _HASH_SEED	(size_t)0xdeadbeef
+
+template<class _Kty>
+inline size_t hash_value(const _Kty& _Keyval)
+{
+	return ((size_t)_Keyval ^ _HASH_SEED);
+}
+
+template <class _InIt>
+inline size_t _Hash_value(_InIt _Begin, _InIt _End)
+{
+	size_t _Val = 2166136261U;
+
+	while(_Begin != _End)
+		_Val = 16777619U * _Val ^ (size_t)*_Begin++;
+
+	return (_Val);
+}
+
+template<class _Elem, class _Traits, class _Alloc>
+inline size_t hash_value(const basic_string<_Elem, _Traits, _Alloc>& _Str)
+{
+	const _Elem *_Ptr = _Str.c_str();
+
+	return (_Hash_value(_Ptr, _Ptr + _Str.size()));
+}
+
+template<class _Elem>
+inline size_t hash_value(const CStringT<_Elem>& _Str)
+{
+	const _Elem *_Ptr = _Str.c_str();
+
+	return (_Hash_value(_Ptr, _Ptr + _Str.size()));
+}
+
+inline size_t hash_value(const char *_Str)
+{
+	return (_Hash_value(_Str, _Str + strlen(_Str)));
+}
+
+inline size_t hash_value(const wchar_t *_Str)
+{
+	return (_Hash_value(_Str, _Str + wcslen(_Str)));
+}

+ 66 - 0
common/SysHelper.cpp

@@ -0,0 +1,66 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "SysHelper.h"
+
+#include <stdio.h>
+#include <sys/utsname.h>
+
+DWORD _GetKernelVersion()
+{
+	utsname uts;
+	
+	if(uname(&uts) == RS_FAIL)
+		return 0;
+
+	char c;
+	int major, minor, revise;
+
+	if(sscanf(uts.release, "%d.%d.%d%c", &major, &minor, &revise, &c) < 3)
+		return 0;
+
+	return (DWORD)((major << 16) | (minor << 8) | revise);
+}
+
+DWORD GetSysPageSize()
+{
+	static const DWORD _s_page_size = (DWORD)SysGetPageSize();
+	return _s_page_size;
+}
+
+DWORD GetKernelVersion()
+{
+	static const DWORD _s_kernel_version = _GetKernelVersion();
+	return _s_kernel_version;
+}
+
+BOOL IsKernelVersionAbove(BYTE major, BYTE minor, BYTE revise)
+{
+	return GetKernelVersion() >= (DWORD)((major << 16) | (minor << 8) | revise);
+}
+
+DWORD GetDefaultWorkerThreadCount()
+{
+	static const DWORD _s_dwtc = MIN((PROCESSOR_COUNT * 2 + 2), MAX_WORKER_THREAD_COUNT);
+	return _s_dwtc;
+}

+ 186 - 0
common/SysHelper.h

@@ -0,0 +1,186 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+
+#include <unistd.h>
+#include <sched.h>
+#include <pthread.h>
+
+#include <sys/syscall.h>
+#include <sys/sysinfo.h>
+
+using namespace std;
+
+/* 最大工作线程数 */
+#define MAX_WORKER_THREAD_COUNT			512
+/* 默认对象缓存锁定时间 */
+#define DEFAULT_OBJECT_CACHE_LOCK_TIME	(30 * 1000)
+/* 默认对象缓存池大小 */
+#define DEFAULT_OBJECT_CACHE_POOL_SIZE	600
+/* 默认对象缓存池回收阀值 */
+#define DEFAULT_OBJECT_CACHE_POOL_HOLD	600
+/* 默认内存块缓存容量 */
+#define DEFAULT_BUFFER_CACHE_CAPACITY	4096
+/* 默认内存块缓存池大小 */
+#define DEFAULT_BUFFER_CACHE_POOL_SIZE	1024
+/* 默认内存块缓存池回收阀值 */
+#define DEFAULT_BUFFER_CACHE_POOL_HOLD	1024
+
+/* 使用外部垃圾回收 */
+#define USE_EXTERNAL_GC					1
+
+
+#define SysGetSystemConfig				sysconf
+#define SysGetSystemInfo				sysinfo
+
+#if !defined(__ANDROID__)
+	#define SysGetPageSize				getpagesize
+	#define SysGetNumberOfProcessors	get_nprocs
+#else
+	#define SysGetPageSize()			sysconf(_SC_PAGESIZE)
+	#define SysGetNumberOfProcessors()	sysconf(_SC_NPROCESSORS_ONLN)
+#endif
+
+#define SYS_PAGE_SIZE					GetSysPageSize()
+
+#define PROCESSOR_COUNT					(::SysGetNumberOfProcessors())
+#define GetCurrentProcessId				getpid
+#define SELF_PROCESS_ID					(::GetCurrentProcessId())
+#define gettid()						syscall(__NR_gettid)
+#define GetCurrentNativeThreadId()		gettid()
+#define SELF_NATIVE_THREAD_ID			(::GetCurrentNativeThreadId())
+#define GetCurrentThreadId				pthread_self
+#define SELF_THREAD_ID					(::GetCurrentThreadId())
+#define IsSameThread(tid1, tid2)		pthread_equal((tid1), (tid2))
+#define IsSelfThread(tid)				IsSameThread((tid), SELF_THREAD_ID)
+inline BOOL IsSameNativeThread(pid_t pid1, pid_t pid2)
+										{return (pid1 == pid2);}
+#define IsSelfNativeThread(pid)			IsSameNativeThread((pid), SELF_PROCESS_ID)
+#define DEFAULT_WORKER_THREAD_COUNT		GetDefaultWorkerThreadCount()
+
+// Yield
+#if defined(__cplusplus)
+#include <thread>
+static inline void __atomic_yield()
+{
+	std::this_thread::yield();
+}
+#elif defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+static inline void __atomic_yield()
+{
+	YieldProcessor();
+}
+#elif defined(__SSE2__)
+#include <emmintrin.h>
+static inline void __atomic_yield()
+{
+	_mm_pause();
+}
+#elif   (defined(__GNUC__) || defined(__clang__)) && \
+		(defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__armel__) || defined(__ARMEL__) || \
+		defined(__aarch64__) || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__))
+#if defined(__x86_64__) || defined(__i386__)
+static inline void __atomic_yield()
+{
+	__asm__ volatile ("pause" ::: "memory");
+}
+#elif defined(__aarch64__)
+static inline void __atomic_yield()
+{
+	__asm__ volatile("wfe");
+}
+#elif (defined(__arm__) && __ARM_ARCH__ >= 7)
+static inline void __atomic_yield()
+{
+	__asm__ volatile("yield" ::: "memory");
+}
+#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)
+static inline void __atomic_yield()
+{
+	__asm__ __volatile__ ("or 27,27,27" ::: "memory");
+}
+#elif defined(__armel__) || defined(__ARMEL__)
+static inline void __atomic_yield()
+{
+	__asm__ volatile ("nop" ::: "memory");
+}
+#endif
+#elif defined(__sun)
+// Fallback for other archs
+#include <synch.h>
+static inline void __atomic_yield()
+{
+	smt_pause();
+}
+#elif defined(__wasi__)
+#include <sched.h>
+static inline void __atomic_yield()
+{
+	sched_yield();
+}
+#else
+#include <unistd.h>
+static inline void __atomic_yield()
+{
+  sleep(0);
+}
+#endif	// Yield
+
+#define YieldProcessor					__atomic_yield
+#define SwitchToThread					sched_yield
+
+inline void __asm_nop()					{__asm__ __volatile__("nop" : : : "memory");}
+inline void __asm_rep_nop()				{__asm__ __volatile__("rep; nop" : : : "memory");}
+
+DWORD GetSysPageSize();
+DWORD GetKernelVersion();
+BOOL IsKernelVersionAbove(BYTE major, BYTE minor, BYTE revise);
+DWORD GetDefaultWorkerThreadCount();
+
+
+#if defined(__ANDROID__)
+
+#include<android/api-level.h>
+
+#if !defined(EFD_SEMAPHORE)
+	#define EFD_SEMAPHORE	00000001
+#endif
+
+#define pthread_cancel(t)
+
+#if defined(__ANDROID_API__)
+#if (__ANDROID_API__ < 21)
+
+	#define ppoll(fd, nfds, ptmspec, sig)							poll((fd), (nfds), ((ptmspec) == nullptr) ? -1 : ((ptmspec)->tv_sec * 1000 + (ptmspec)->tv_nsec / 1000000))
+	#define epoll_create1(flag)										epoll_create(32)
+	#define epoll_pwait(epfd, events, maxevents, timeout, sigmask)	epoll_wait((epfd), (events), (maxevents), (timeout))
+
+#endif
+#endif
+
+#endif

+ 50 - 0
common/Thread.cpp

@@ -0,0 +1,50 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "Thread.h"
+
+static __thread BOOL s_tlsInterrupt		= FALSE;
+BOOL __CThread_Interrupt_::sm_bInitFlag	= __CThread_Interrupt_::InitSigAction();
+
+__CThread_Interrupt_::~__CThread_Interrupt_	() {s_tlsInterrupt = FALSE;}
+BOOL __CThread_Interrupt_::IsInterrupted	() {return s_tlsInterrupt;}
+
+BOOL __CThread_Interrupt_::InitSigAction()
+{
+	struct sigaction act;
+	sigemptyset(&act.sa_mask);
+
+	act.sa_handler	= SignalHandler;
+	act.sa_flags	= 0;
+
+	if(IS_HAS_ERROR(sigaction(SIG_NO_INTERRUPT, &act, nullptr)))
+		ERROR_ABORT();
+
+	return TRUE;
+}
+
+void __CThread_Interrupt_::SignalHandler(int sig)
+{
+	if(sig == SIG_NO_INTERRUPT)
+		s_tlsInterrupt = TRUE;
+}

+ 612 - 0
common/Thread.h

@@ -0,0 +1,612 @@
+/*
+* Copyright: JessMA Open Source (ldcsaa@gmail.com)
+*
+* Author	: Bruce Liang
+* Website	: https://github.com/ldcsaa
+* Project	: https://github.com/ldcsaa/HP-Socket
+* Blog		: http://www.cnblogs.com/ldcsaa
+* Wiki		: http://www.oschina.net/p/hp-socket
+* QQ Group	: 44636872, 75375912
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "hpsocket/GlobalDef.h"
+#include "hpsocket/GlobalErrno.h"
+#include "RWLock.h"
+#include "STLHelper.h"
+
+#include <pthread.h>
+#include <signal.h>
+#include <utility>
+
+using namespace std;
+
+/* Used to retry syscalls that can return EINTR. */
+#define NO_EINTR_EXCEPT_THR_INTR(exp) ({							\
+	long int _rc;													\
+	do {_rc = (long int)(exp);}										\
+	while (IS_HAS_ERROR(_rc) && IS_INTR_ERROR()						\
+			&& !::IsThreadInterrupted());							\
+    _rc; })
+
+#define NO_EINTR_EXCEPT_THR_INTR_INT(exp)	((int)NO_EINTR_EXCEPT_THR_INTR(exp))
+
+class __CThread_Interrupt_
+{
+public:
+	static const int SIG_NO_INTERRUPT = (_NSIG - 5);
+
+private:
+	friend BOOL IsThreadInterrupted();
+	template<typename T, typename P, typename R> friend class CThread;
+
+private:
+	static BOOL IsInterrupted();
+	static BOOL InitSigAction();
+	static void SignalHandler(int sig);
+
+private:
+	~__CThread_Interrupt_();
+
+private:
+	static BOOL sm_bInitFlag;
+};
+
+inline BOOL IsThreadInterrupted() {return __CThread_Interrupt_::IsInterrupted();}
+
+class __CFakeRunnerClass_ {};
+
+template<class T, class P = VOID, class R = UINT_PTR> class CThread
+{
+public:
+
+	using F	 = R (T::*)(P*);
+	using SF = R (*)(P*);
+
+	struct TWorker
+	{
+		CThread* m_pThread;
+		BOOL	 m_bDetach;
+
+		T*	m_pRunner;
+		F	m_pFunc;
+		P*	m_pArg;
+
+	public:
+
+		TWorker(CThread* pThread, BOOL bDetach = FALSE, T* pRunner = nullptr, F pFunc = nullptr, P* pArg = nullptr)
+		: m_pThread(pThread)
+		{
+			Reset(bDetach, pRunner, pFunc, pArg);
+		}
+
+		void Reset(BOOL bDetach = FALSE, T* pRunner = nullptr, F pFunc = nullptr, P* pArg = nullptr)
+		{
+			m_bDetach	= bDetach;
+			m_pRunner	= pRunner;
+			m_pFunc		= pFunc;
+			m_pArg		= pArg;
+		}
+
+	public:
+
+		template<typename T_, typename R_, typename = enable_if_t<!is_same<T_, __CFakeRunnerClass_>::value && !is_void<R_>::value>>
+		PVOID Run(T_*, R_*)
+		{
+			return (PVOID)(UINT_PTR)((m_pRunner->*m_pFunc)(m_pArg));
+		}
+
+		template<typename T_, typename = enable_if_t<!is_same<T_, __CFakeRunnerClass_>::value>>
+		PVOID Run(T_*, PVOID)
+		{
+			(m_pRunner->*m_pFunc)(m_pArg);
+
+			return nullptr;
+		}
+
+		template<typename R_, typename = enable_if_t<!is_void<R_>::value>>
+		PVOID Run(__CFakeRunnerClass_*, R_*)
+		{
+			return (PVOID)(UINT_PTR)(*(SF*)&m_pFunc)(m_pArg);
+		}
+
+		PVOID Run(__CFakeRunnerClass_*, VOID*)
+		{
+			(*(SF*)&m_pFunc)(m_pArg);
+
+			return nullptr;
+		}
+	};
+
+	friend struct TWorker;
+
+public:
+
+	BOOL Start(SF pFunc, P* pArg = nullptr, BOOL bDetach = FALSE, const pthread_attr_t* pAttr = nullptr)
+	{
+		return Start((__CFakeRunnerClass_*)nullptr, *(F*)&pFunc, pArg, bDetach, pAttr);
+	}
+
+	BOOL Start(T* pRunner, F pFunc, P* pArg = nullptr, BOOL bDetach = FALSE, const pthread_attr_t* pAttr = nullptr)
+	{
+		int rs = ERROR_INVALID_STATE;
+
+		if(IsRunning())
+			::SetLastError(rs);
+		else
+		{
+			m_Worker.Reset(bDetach, pRunner, pFunc, pArg);
+
+			SetRunning(TRUE);
+
+			rs = pthread_create(&m_ulThreadID, pAttr, ThreadProc, (PVOID)(&m_Worker));
+
+			if(rs != NO_ERROR)
+			{
+				Reset();
+				::SetLastError(rs);
+			}
+		}
+
+		return (rs == NO_ERROR);
+	}
+
+#if !defined(__ANDROID__)
+
+	BOOL Cancel()
+	{
+		int rs = NO_ERROR;
+
+		if(!IsRunning() || ::IsSelfThread(m_ulThreadID))
+			rs = ERROR_INVALID_STATE;
+		else
+			rs = pthread_cancel(m_ulThreadID);
+
+		if(rs != NO_ERROR)
+			::SetLastError(rs);
+
+		return (rs == NO_ERROR);
+	}
+
+	BOOL Join(R* pResult = nullptr, BOOL bWait = TRUE, LONG lWaitMillsec = INFINITE)
+	{
+		int rs = NO_ERROR;
+
+		if(!IsRunning() || ::IsSelfThread(m_ulThreadID))
+			rs = ERROR_INVALID_STATE;
+		else
+		{
+			if(!bWait)
+				rs = pthread_tryjoin_np(m_ulThreadID, (PVOID*)pResult);
+			else if(IS_INFINITE(lWaitMillsec))
+				rs = pthread_join(m_ulThreadID, (PVOID*)pResult);
+			else
+			{
+				timespec ts;
+				::GetFutureTimespec(lWaitMillsec, ts, CLOCK_REALTIME);
+
+				rs = pthread_timedjoin_np(m_ulThreadID, (PVOID*)pResult, &ts);
+			}
+		}
+
+		if(rs == NO_ERROR)
+			SetRunning(FALSE);
+		else
+			::SetLastError(rs);
+
+		return (rs == NO_ERROR);
+	}
+
+#else
+
+	BOOL Cancel()
+	{
+		SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+		return FALSE;
+	}
+
+	BOOL Join(R* pResult = nullptr)
+	{
+		int rs = NO_ERROR;
+
+		if(!IsRunning() || ::IsSelfThread(m_ulThreadID))
+			rs = ERROR_INVALID_STATE;
+		else
+			rs = pthread_join(m_ulThreadID, (PVOID*)pResult);
+
+		if(rs == NO_ERROR)
+			SetRunning(FALSE);
+		else
+			::SetLastError(rs);
+
+		return (rs == NO_ERROR);
+	}
+
+#endif
+
+	BOOL Detach()
+	{
+		int rs = NO_ERROR;
+
+		if(!IsRunning())
+			rs = ERROR_INVALID_STATE;
+		else
+			rs = pthread_detach(m_ulThreadID);
+
+		if(rs == NO_ERROR)
+			Reset();
+		else
+			::SetLastError(rs);
+
+		return (rs == NO_ERROR);
+	}
+
+	void Reset()
+	{
+		SetRunning(FALSE);
+
+		m_ulThreadID	= 0;
+		m_lNativeID		= 0;
+
+		m_Worker.Reset();
+	}
+
+	BOOL Interrupt()
+	{
+		if(!IsRunning())
+		{
+			SetLastError(ERROR_INVALID_STATE);
+			return FALSE;
+		}
+
+		return (IS_NO_ERROR(pthread_kill(m_ulThreadID, __CThread_Interrupt_::SIG_NO_INTERRUPT)));
+	}
+
+	void SetRunning(BOOL bRunning) {m_bRunning = bRunning;}
+
+	BOOL IsRunning		() const {return m_bRunning;}
+	T* GetRunner		() const {return m_Worker.m_pRunner;}
+	F GetFunc			() const {return m_Worker.m_pFunc;}
+	SF GetSFunc			() const {return *(SF*)&m_Worker.m_pFunc;}
+	P* GetArg			() const {return m_Worker.m_pArg;}
+	THR_ID GetThreadID	() const {return m_ulThreadID;}
+	NTHR_ID GetNativeID	() const {return m_lNativeID;}
+
+	BOOL IsInMyThread		()					const {return IsMyThreadID(SELF_THREAD_ID);}
+	BOOL IsMyThreadID		(THR_ID ulThreadID)	const {return ::IsSameThread(ulThreadID, m_ulThreadID);}
+	BOOL IsMyNativeThreadID	(NTHR_ID lNativeID)	const {return ::IsSameNativeThread(lNativeID, m_lNativeID);}
+
+private:
+
+	static PVOID ThreadProc(LPVOID pv)
+	{
+		UnmaskInterruptSignal();
+
+		__CThread_Interrupt_ tlsInterrupt;
+
+		TWorker* pWorker = (TWorker*)pv;
+
+		if(pWorker->m_bDetach)
+			pWorker->m_pThread->Detach();
+		else
+			pWorker->m_pThread->m_lNativeID = SELF_NATIVE_THREAD_ID;
+
+		PVOID pResult = pWorker->Run((T*)nullptr, (R*)nullptr);
+
+		return pResult;
+	}
+
+	static void UnmaskInterruptSignal()
+	{
+		sigset_t ss;
+
+		sigemptyset(&ss);
+		sigaddset(&ss, __CThread_Interrupt_::SIG_NO_INTERRUPT);
+
+		pthread_sigmask(SIG_UNBLOCK, &ss, nullptr);
+	}
+
+public:
+
+	CThread()
+	: m_Worker(this)
+	{
+		Reset();
+	}
+
+	virtual ~CThread()
+	{
+		if(IsRunning())
+		{
+			Interrupt();
+			Join(nullptr);
+		}
+
+		ASSERT(!IsRunning());
+	}
+
+	DECLARE_NO_COPY_CLASS(CThread)
+
+private:
+
+	THR_ID	m_ulThreadID;
+	NTHR_ID	m_lNativeID;
+	BOOL	m_bRunning;
+
+	TWorker	m_Worker;
+};
+
+template<class P = VOID, class R = UINT_PTR> using CStaticThread = CThread<__CFakeRunnerClass_, P, R>;
+
+template<class T> class CTlsObj
+{
+	using TLocalMap = unordered_map<THR_ID, T*>;
+
+public:
+
+	T* TryGet()
+	{
+		T* pValue = nullptr;
+
+		{
+			CReadLock locallock(m_lock);
+			auto it = m_map.find(SELF_THREAD_ID);
+
+			if(it != m_map.end())
+				pValue = it->second;
+		}
+
+		return pValue;
+	}
+
+	template<typename ... _Con_Param> T* Get(_Con_Param&& ... construct_args)
+	{
+		T* pValue = TryGet();
+
+		if(pValue == nullptr)
+		{
+			pValue = Construct(forward<_Con_Param>(construct_args) ...);
+
+			CWriteLock locallock(m_lock);
+			m_map[SELF_THREAD_ID] = pValue;
+		}
+
+		return pValue;
+	}
+
+	template<typename ... _Con_Param> T& GetRef(_Con_Param&& ... construct_args)
+	{
+		return *Get(forward<_Con_Param>(construct_args) ...);
+	}
+
+	T* SetNewAndGetOld(T* pValue)
+	{
+		T* pOldValue = TryGet();
+
+		if(pValue != pOldValue)
+		{
+			if(pValue == nullptr)
+				DoRemove();
+			else
+			{
+				CWriteLock locallock(m_lock);
+				m_map[SELF_THREAD_ID] = pValue;
+			}
+		}
+
+		return pOldValue;
+	}
+
+	void Set(T* pValue)
+	{
+		T* pOldValue = SetNewAndGetOld(pValue);
+
+		if(pValue != pOldValue)
+			DoDelete(pOldValue);
+	}
+
+	void Remove()
+	{
+		T* pValue = TryGet();
+
+		if(pValue != nullptr)
+		{
+			DoDelete(pValue);
+			DoRemove();
+		}
+	}
+
+	void Clear()
+	{
+		CWriteLock locallock(m_lock);
+
+		if(!IsEmpty())
+		{
+			for(auto it = m_map.begin(), end = m_map.end(); it != end; ++it)
+				DoDelete(it->second);
+
+			m_map.clear();
+		}
+	}
+
+	TLocalMap&			GetLocalMap()			{return m_map;}
+	const TLocalMap&	GetLocalMap()	const	{return m_map;}
+
+	CTlsObj&	operator = (T* p)		{Set(p); return *this;}
+	T*			operator ->	()			{return Get();}
+	const T*	operator ->	()	const	{return Get();}
+	T&			operator *	()			{return GetRef();}
+	const T&	operator *	()	const	{return GetRef();}
+	size_t Size	()				const	{return m_map.size();}
+	bool IsEmpty()				const	{return m_map.empty();}
+
+private:
+
+	inline void DoRemove()
+	{
+		CWriteLock locallock(m_lock);
+		m_map.erase(SELF_THREAD_ID);
+	}
+
+	static inline void DoDelete(T* pValue)
+	{
+		if(pValue != nullptr)
+			delete pValue;
+	}
+
+	template<typename ... _Con_Param> static inline T* Construct(_Con_Param&& ... construct_args)
+	{
+		return new T(forward<_Con_Param>(construct_args) ...);
+	}
+
+public:
+
+	CTlsObj()
+	{
+
+	}
+
+	CTlsObj(T* pValue)
+	{
+		Set(pValue);
+	}
+
+	~CTlsObj()
+	{
+		Clear();
+	}
+
+private:
+
+	CSimpleRWLock	m_lock;
+	TLocalMap		m_map;
+
+	DECLARE_NO_COPY_CLASS(CTlsObj)
+};
+
+template<class T> class CTlsSimple
+{
+	using TLocalMap = unordered_map<THR_ID, T>;
+
+	static const T DEFAULT = (T)(0);
+
+public:
+
+	BOOL TryGet(T& tValue)
+	{
+		BOOL isOK = FALSE;
+
+		{
+			CReadLock locallock(m_lock);
+			auto it = m_map.find(SELF_THREAD_ID);
+
+			if(it != m_map.end())
+			{
+				tValue	= it->second;
+				isOK	= TRUE;
+			}
+		}
+
+		return isOK;
+	}
+
+	T Get(T tDefault = DEFAULT)
+	{
+		T tValue;
+
+		if(TryGet(tValue))
+			return tValue;
+
+		Set(tDefault);
+
+		return tDefault;
+	}
+
+	T SetNewAndGetOld(T tValue)
+	{
+		T tOldValue;
+
+		if(!TryGet(tOldValue))
+			tOldValue = DEFAULT;
+		else if(tValue != tOldValue)
+			Set(tValue);
+
+		return tOldValue;
+	}
+
+	void Set(T tValue)
+	{
+		CWriteLock locallock(m_lock);
+		m_map[SELF_THREAD_ID] = tValue;
+	}
+
+	void Remove()
+	{
+		T tValue;
+
+		if(TryGet(tValue))
+		{
+			CWriteLock locallock(m_lock);
+			m_map.erase(SELF_THREAD_ID);
+		}
+	}
+
+	void Clear()
+	{
+		CWriteLock locallock(m_lock);
+
+		if(!IsEmpty())
+			m_map.clear();
+	}
+
+	TLocalMap&			GetLocalMap()			{return m_map;}
+	const TLocalMap&	GetLocalMap()	const	{return m_map;}
+
+	CTlsSimple& operator = (T t) {Set(t); return *this;}
+	BOOL operator		== (T t) {return Get() == t;}
+	BOOL operator		!= (T t) {return Get() != t;}
+	BOOL operator		>= (T t) {return Get() >= t;}
+	BOOL operator		<= (T t) {return Get() <= t;}
+	BOOL operator		 > (T t) {return Get() > t;}
+	BOOL operator		 < (T t) {return Get() < t;}
+
+	size_t Size	()	const	{return m_map.size();}
+	bool IsEmpty()	const	{return m_map.empty();}
+
+public:
+
+	CTlsSimple()
+	{
+
+	}
+
+	CTlsSimple(T tValue)
+	{
+		Set(tValue);
+	}
+
+	~CTlsSimple()
+	{
+		Clear();
+	}
+
+	DECLARE_NO_COPY_CLASS(CTlsSimple)
+
+private:
+
+	CSimpleRWLock	m_lock;
+	TLocalMap		m_map;
+};

+ 11 - 0
common/http/Readme.txt

@@ -0,0 +1,11 @@
+http_parser Modifications
+--------------------
+1. move 'enum state' from http_parser.c to http_parser.h
+3. http_parser.c ignore warning: "-Wconversion", "-Wsign-conversion"
+
+llhttp Modifications
+--------------------
+1. llhttp_api.c ignore warning: "-Wconversion", "-Wsign-conversion"
+1. llhttp_url.c ignore warning: "-Wconversion", "-Wsign-conversion"
+2. llhttp_internal.c ignore warning: "-Wconversion", "-Wsign-conversion" "-Wunused-variable" "-Wunreachable-code"
+3. llhttp.h, llhttp_url.h : LLHTTP_STRICT_MODE set default value 1

+ 903 - 0
common/http/llhttp.h

@@ -0,0 +1,903 @@
+
+#ifndef INCLUDE_LLHTTP_H_
+#define INCLUDE_LLHTTP_H_
+
+#define LLHTTP_VERSION_MAJOR 9
+#define LLHTTP_VERSION_MINOR 2
+#define LLHTTP_VERSION_PATCH 1
+
+#ifndef INCLUDE_LLHTTP_ITSELF_H_
+#define INCLUDE_LLHTTP_ITSELF_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+typedef struct llhttp__internal_s llhttp__internal_t;
+struct llhttp__internal_s {
+  int32_t _index;
+  void* _span_pos0;
+  void* _span_cb0;
+  int32_t error;
+  const char* reason;
+  const char* error_pos;
+  void* data;
+  void* _current;
+  uint64_t content_length;
+  uint8_t type;
+  uint8_t method;
+  uint8_t http_major;
+  uint8_t http_minor;
+  uint8_t header_state;
+  uint16_t lenient_flags;
+  uint8_t upgrade;
+  uint8_t finish;
+  uint16_t flags;
+  uint16_t status_code;
+  uint8_t initial_message_completed;
+  void* settings;
+};
+
+int llhttp__internal_init(llhttp__internal_t* s);
+int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+#endif  /* INCLUDE_LLHTTP_ITSELF_H_ */
+
+
+#ifndef LLLLHTTP_C_HEADERS_
+#define LLLLHTTP_C_HEADERS_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum llhttp_errno {
+  HPE_OK = 0,
+  HPE_INTERNAL = 1,
+  HPE_STRICT = 2,
+  HPE_CR_EXPECTED = 25,
+  HPE_LF_EXPECTED = 3,
+  HPE_UNEXPECTED_CONTENT_LENGTH = 4,
+  HPE_UNEXPECTED_SPACE = 30,
+  HPE_CLOSED_CONNECTION = 5,
+  HPE_INVALID_METHOD = 6,
+  HPE_INVALID_URL = 7,
+  HPE_INVALID_CONSTANT = 8,
+  HPE_INVALID_VERSION = 9,
+  HPE_INVALID_HEADER_TOKEN = 10,
+  HPE_INVALID_CONTENT_LENGTH = 11,
+  HPE_INVALID_CHUNK_SIZE = 12,
+  HPE_INVALID_STATUS = 13,
+  HPE_INVALID_EOF_STATE = 14,
+  HPE_INVALID_TRANSFER_ENCODING = 15,
+  HPE_CB_MESSAGE_BEGIN = 16,
+  HPE_CB_HEADERS_COMPLETE = 17,
+  HPE_CB_MESSAGE_COMPLETE = 18,
+  HPE_CB_CHUNK_HEADER = 19,
+  HPE_CB_CHUNK_COMPLETE = 20,
+  HPE_PAUSED = 21,
+  HPE_PAUSED_UPGRADE = 22,
+  HPE_PAUSED_H2_UPGRADE = 23,
+  HPE_USER = 24,
+  HPE_CB_URL_COMPLETE = 26,
+  HPE_CB_STATUS_COMPLETE = 27,
+  HPE_CB_METHOD_COMPLETE = 32,
+  HPE_CB_VERSION_COMPLETE = 33,
+  HPE_CB_HEADER_FIELD_COMPLETE = 28,
+  HPE_CB_HEADER_VALUE_COMPLETE = 29,
+  HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34,
+  HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35,
+  HPE_CB_RESET = 31
+};
+typedef enum llhttp_errno llhttp_errno_t;
+
+enum llhttp_flags {
+  F_CONNECTION_KEEP_ALIVE = 0x1,
+  F_CONNECTION_CLOSE = 0x2,
+  F_CONNECTION_UPGRADE = 0x4,
+  F_CHUNKED = 0x8,
+  F_UPGRADE = 0x10,
+  F_CONTENT_LENGTH = 0x20,
+  F_SKIPBODY = 0x40,
+  F_TRAILING = 0x80,
+  F_TRANSFER_ENCODING = 0x200
+};
+typedef enum llhttp_flags llhttp_flags_t;
+
+enum llhttp_lenient_flags {
+  LENIENT_HEADERS = 0x1,
+  LENIENT_CHUNKED_LENGTH = 0x2,
+  LENIENT_KEEP_ALIVE = 0x4,
+  LENIENT_TRANSFER_ENCODING = 0x8,
+  LENIENT_VERSION = 0x10,
+  LENIENT_DATA_AFTER_CLOSE = 0x20,
+  LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,
+  LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80,
+  LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100,
+  LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200
+};
+typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
+
+enum llhttp_type {
+  HTTP_BOTH = 0,
+  HTTP_REQUEST = 1,
+  HTTP_RESPONSE = 2
+};
+typedef enum llhttp_type llhttp_type_t;
+
+enum llhttp_finish {
+  HTTP_FINISH_SAFE = 0,
+  HTTP_FINISH_SAFE_WITH_CB = 1,
+  HTTP_FINISH_UNSAFE = 2
+};
+typedef enum llhttp_finish llhttp_finish_t;
+
+enum llhttp_method {
+  HTTP_DELETE = 0,
+  HTTP_GET = 1,
+  HTTP_HEAD = 2,
+  HTTP_POST = 3,
+  HTTP_PUT = 4,
+  HTTP_CONNECT = 5,
+  HTTP_OPTIONS = 6,
+  HTTP_TRACE = 7,
+  HTTP_COPY = 8,
+  HTTP_LOCK = 9,
+  HTTP_MKCOL = 10,
+  HTTP_MOVE = 11,
+  HTTP_PROPFIND = 12,
+  HTTP_PROPPATCH = 13,
+  HTTP_SEARCH = 14,
+  HTTP_UNLOCK = 15,
+  HTTP_BIND = 16,
+  HTTP_REBIND = 17,
+  HTTP_UNBIND = 18,
+  HTTP_ACL = 19,
+  HTTP_REPORT = 20,
+  HTTP_MKACTIVITY = 21,
+  HTTP_CHECKOUT = 22,
+  HTTP_MERGE = 23,
+  HTTP_MSEARCH = 24,
+  HTTP_NOTIFY = 25,
+  HTTP_SUBSCRIBE = 26,
+  HTTP_UNSUBSCRIBE = 27,
+  HTTP_PATCH = 28,
+  HTTP_PURGE = 29,
+  HTTP_MKCALENDAR = 30,
+  HTTP_LINK = 31,
+  HTTP_UNLINK = 32,
+  HTTP_SOURCE = 33,
+  HTTP_PRI = 34,
+  HTTP_DESCRIBE = 35,
+  HTTP_ANNOUNCE = 36,
+  HTTP_SETUP = 37,
+  HTTP_PLAY = 38,
+  HTTP_PAUSE = 39,
+  HTTP_TEARDOWN = 40,
+  HTTP_GET_PARAMETER = 41,
+  HTTP_SET_PARAMETER = 42,
+  HTTP_REDIRECT = 43,
+  HTTP_RECORD = 44,
+  HTTP_FLUSH = 45,
+  HTTP_QUERY = 46
+};
+typedef enum llhttp_method llhttp_method_t;
+
+enum llhttp_status {
+  HTTP_STATUS_CONTINUE = 100,
+  HTTP_STATUS_SWITCHING_PROTOCOLS = 101,
+  HTTP_STATUS_PROCESSING = 102,
+  HTTP_STATUS_EARLY_HINTS = 103,
+  HTTP_STATUS_RESPONSE_IS_STALE = 110,
+  HTTP_STATUS_REVALIDATION_FAILED = 111,
+  HTTP_STATUS_DISCONNECTED_OPERATION = 112,
+  HTTP_STATUS_HEURISTIC_EXPIRATION = 113,
+  HTTP_STATUS_MISCELLANEOUS_WARNING = 199,
+  HTTP_STATUS_OK = 200,
+  HTTP_STATUS_CREATED = 201,
+  HTTP_STATUS_ACCEPTED = 202,
+  HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
+  HTTP_STATUS_NO_CONTENT = 204,
+  HTTP_STATUS_RESET_CONTENT = 205,
+  HTTP_STATUS_PARTIAL_CONTENT = 206,
+  HTTP_STATUS_MULTI_STATUS = 207,
+  HTTP_STATUS_ALREADY_REPORTED = 208,
+  HTTP_STATUS_TRANSFORMATION_APPLIED = 214,
+  HTTP_STATUS_IM_USED = 226,
+  HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299,
+  HTTP_STATUS_MULTIPLE_CHOICES = 300,
+  HTTP_STATUS_MOVED_PERMANENTLY = 301,
+  HTTP_STATUS_FOUND = 302,
+  HTTP_STATUS_SEE_OTHER = 303,
+  HTTP_STATUS_NOT_MODIFIED = 304,
+  HTTP_STATUS_USE_PROXY = 305,
+  HTTP_STATUS_SWITCH_PROXY = 306,
+  HTTP_STATUS_TEMPORARY_REDIRECT = 307,
+  HTTP_STATUS_PERMANENT_REDIRECT = 308,
+  HTTP_STATUS_BAD_REQUEST = 400,
+  HTTP_STATUS_UNAUTHORIZED = 401,
+  HTTP_STATUS_PAYMENT_REQUIRED = 402,
+  HTTP_STATUS_FORBIDDEN = 403,
+  HTTP_STATUS_NOT_FOUND = 404,
+  HTTP_STATUS_METHOD_NOT_ALLOWED = 405,
+  HTTP_STATUS_NOT_ACCEPTABLE = 406,
+  HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
+  HTTP_STATUS_REQUEST_TIMEOUT = 408,
+  HTTP_STATUS_CONFLICT = 409,
+  HTTP_STATUS_GONE = 410,
+  HTTP_STATUS_LENGTH_REQUIRED = 411,
+  HTTP_STATUS_PRECONDITION_FAILED = 412,
+  HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
+  HTTP_STATUS_URI_TOO_LONG = 414,
+  HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
+  HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,
+  HTTP_STATUS_EXPECTATION_FAILED = 417,
+  HTTP_STATUS_IM_A_TEAPOT = 418,
+  HTTP_STATUS_PAGE_EXPIRED = 419,
+  HTTP_STATUS_ENHANCE_YOUR_CALM = 420,
+  HTTP_STATUS_MISDIRECTED_REQUEST = 421,
+  HTTP_STATUS_UNPROCESSABLE_ENTITY = 422,
+  HTTP_STATUS_LOCKED = 423,
+  HTTP_STATUS_FAILED_DEPENDENCY = 424,
+  HTTP_STATUS_TOO_EARLY = 425,
+  HTTP_STATUS_UPGRADE_REQUIRED = 426,
+  HTTP_STATUS_PRECONDITION_REQUIRED = 428,
+  HTTP_STATUS_TOO_MANY_REQUESTS = 429,
+  HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430,
+  HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
+  HTTP_STATUS_LOGIN_TIMEOUT = 440,
+  HTTP_STATUS_NO_RESPONSE = 444,
+  HTTP_STATUS_RETRY_WITH = 449,
+  HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450,
+  HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
+  HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460,
+  HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463,
+  HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494,
+  HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495,
+  HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496,
+  HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497,
+  HTTP_STATUS_INVALID_TOKEN = 498,
+  HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499,
+  HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
+  HTTP_STATUS_NOT_IMPLEMENTED = 501,
+  HTTP_STATUS_BAD_GATEWAY = 502,
+  HTTP_STATUS_SERVICE_UNAVAILABLE = 503,
+  HTTP_STATUS_GATEWAY_TIMEOUT = 504,
+  HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
+  HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
+  HTTP_STATUS_INSUFFICIENT_STORAGE = 507,
+  HTTP_STATUS_LOOP_DETECTED = 508,
+  HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509,
+  HTTP_STATUS_NOT_EXTENDED = 510,
+  HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511,
+  HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520,
+  HTTP_STATUS_WEB_SERVER_IS_DOWN = 521,
+  HTTP_STATUS_CONNECTION_TIMEOUT = 522,
+  HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523,
+  HTTP_STATUS_TIMEOUT_OCCURED = 524,
+  HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525,
+  HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526,
+  HTTP_STATUS_RAILGUN_ERROR = 527,
+  HTTP_STATUS_SITE_IS_OVERLOADED = 529,
+  HTTP_STATUS_SITE_IS_FROZEN = 530,
+  HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561,
+  HTTP_STATUS_NETWORK_READ_TIMEOUT = 598,
+  HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599
+};
+typedef enum llhttp_status llhttp_status_t;
+
+#define HTTP_ERRNO_MAP(XX) \
+  XX(0, OK, OK) \
+  XX(1, INTERNAL, INTERNAL) \
+  XX(2, STRICT, STRICT) \
+  XX(25, CR_EXPECTED, CR_EXPECTED) \
+  XX(3, LF_EXPECTED, LF_EXPECTED) \
+  XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
+  XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \
+  XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
+  XX(6, INVALID_METHOD, INVALID_METHOD) \
+  XX(7, INVALID_URL, INVALID_URL) \
+  XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
+  XX(9, INVALID_VERSION, INVALID_VERSION) \
+  XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
+  XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
+  XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
+  XX(13, INVALID_STATUS, INVALID_STATUS) \
+  XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
+  XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
+  XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
+  XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
+  XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
+  XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
+  XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
+  XX(21, PAUSED, PAUSED) \
+  XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
+  XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
+  XX(24, USER, USER) \
+  XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \
+  XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \
+  XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \
+  XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \
+  XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \
+  XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \
+  XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \
+  XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \
+  XX(31, CB_RESET, CB_RESET) \
+
+
+#define HTTP_METHOD_MAP(XX) \
+  XX(0, DELETE, DELETE) \
+  XX(1, GET, GET) \
+  XX(2, HEAD, HEAD) \
+  XX(3, POST, POST) \
+  XX(4, PUT, PUT) \
+  XX(5, CONNECT, CONNECT) \
+  XX(6, OPTIONS, OPTIONS) \
+  XX(7, TRACE, TRACE) \
+  XX(8, COPY, COPY) \
+  XX(9, LOCK, LOCK) \
+  XX(10, MKCOL, MKCOL) \
+  XX(11, MOVE, MOVE) \
+  XX(12, PROPFIND, PROPFIND) \
+  XX(13, PROPPATCH, PROPPATCH) \
+  XX(14, SEARCH, SEARCH) \
+  XX(15, UNLOCK, UNLOCK) \
+  XX(16, BIND, BIND) \
+  XX(17, REBIND, REBIND) \
+  XX(18, UNBIND, UNBIND) \
+  XX(19, ACL, ACL) \
+  XX(20, REPORT, REPORT) \
+  XX(21, MKACTIVITY, MKACTIVITY) \
+  XX(22, CHECKOUT, CHECKOUT) \
+  XX(23, MERGE, MERGE) \
+  XX(24, MSEARCH, M-SEARCH) \
+  XX(25, NOTIFY, NOTIFY) \
+  XX(26, SUBSCRIBE, SUBSCRIBE) \
+  XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+  XX(28, PATCH, PATCH) \
+  XX(29, PURGE, PURGE) \
+  XX(30, MKCALENDAR, MKCALENDAR) \
+  XX(31, LINK, LINK) \
+  XX(32, UNLINK, UNLINK) \
+  XX(33, SOURCE, SOURCE) \
+  XX(46, QUERY, QUERY) \
+
+
+#define RTSP_METHOD_MAP(XX) \
+  XX(1, GET, GET) \
+  XX(3, POST, POST) \
+  XX(6, OPTIONS, OPTIONS) \
+  XX(35, DESCRIBE, DESCRIBE) \
+  XX(36, ANNOUNCE, ANNOUNCE) \
+  XX(37, SETUP, SETUP) \
+  XX(38, PLAY, PLAY) \
+  XX(39, PAUSE, PAUSE) \
+  XX(40, TEARDOWN, TEARDOWN) \
+  XX(41, GET_PARAMETER, GET_PARAMETER) \
+  XX(42, SET_PARAMETER, SET_PARAMETER) \
+  XX(43, REDIRECT, REDIRECT) \
+  XX(44, RECORD, RECORD) \
+  XX(45, FLUSH, FLUSH) \
+
+
+#define HTTP_ALL_METHOD_MAP(XX) \
+  XX(0, DELETE, DELETE) \
+  XX(1, GET, GET) \
+  XX(2, HEAD, HEAD) \
+  XX(3, POST, POST) \
+  XX(4, PUT, PUT) \
+  XX(5, CONNECT, CONNECT) \
+  XX(6, OPTIONS, OPTIONS) \
+  XX(7, TRACE, TRACE) \
+  XX(8, COPY, COPY) \
+  XX(9, LOCK, LOCK) \
+  XX(10, MKCOL, MKCOL) \
+  XX(11, MOVE, MOVE) \
+  XX(12, PROPFIND, PROPFIND) \
+  XX(13, PROPPATCH, PROPPATCH) \
+  XX(14, SEARCH, SEARCH) \
+  XX(15, UNLOCK, UNLOCK) \
+  XX(16, BIND, BIND) \
+  XX(17, REBIND, REBIND) \
+  XX(18, UNBIND, UNBIND) \
+  XX(19, ACL, ACL) \
+  XX(20, REPORT, REPORT) \
+  XX(21, MKACTIVITY, MKACTIVITY) \
+  XX(22, CHECKOUT, CHECKOUT) \
+  XX(23, MERGE, MERGE) \
+  XX(24, MSEARCH, M-SEARCH) \
+  XX(25, NOTIFY, NOTIFY) \
+  XX(26, SUBSCRIBE, SUBSCRIBE) \
+  XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+  XX(28, PATCH, PATCH) \
+  XX(29, PURGE, PURGE) \
+  XX(30, MKCALENDAR, MKCALENDAR) \
+  XX(31, LINK, LINK) \
+  XX(32, UNLINK, UNLINK) \
+  XX(33, SOURCE, SOURCE) \
+  XX(34, PRI, PRI) \
+  XX(35, DESCRIBE, DESCRIBE) \
+  XX(36, ANNOUNCE, ANNOUNCE) \
+  XX(37, SETUP, SETUP) \
+  XX(38, PLAY, PLAY) \
+  XX(39, PAUSE, PAUSE) \
+  XX(40, TEARDOWN, TEARDOWN) \
+  XX(41, GET_PARAMETER, GET_PARAMETER) \
+  XX(42, SET_PARAMETER, SET_PARAMETER) \
+  XX(43, REDIRECT, REDIRECT) \
+  XX(44, RECORD, RECORD) \
+  XX(45, FLUSH, FLUSH) \
+  XX(46, QUERY, QUERY) \
+
+
+#define HTTP_STATUS_MAP(XX) \
+  XX(100, CONTINUE, CONTINUE) \
+  XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \
+  XX(102, PROCESSING, PROCESSING) \
+  XX(103, EARLY_HINTS, EARLY_HINTS) \
+  XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \
+  XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \
+  XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \
+  XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \
+  XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \
+  XX(200, OK, OK) \
+  XX(201, CREATED, CREATED) \
+  XX(202, ACCEPTED, ACCEPTED) \
+  XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \
+  XX(204, NO_CONTENT, NO_CONTENT) \
+  XX(205, RESET_CONTENT, RESET_CONTENT) \
+  XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \
+  XX(207, MULTI_STATUS, MULTI_STATUS) \
+  XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \
+  XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \
+  XX(226, IM_USED, IM_USED) \
+  XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \
+  XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \
+  XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \
+  XX(302, FOUND, FOUND) \
+  XX(303, SEE_OTHER, SEE_OTHER) \
+  XX(304, NOT_MODIFIED, NOT_MODIFIED) \
+  XX(305, USE_PROXY, USE_PROXY) \
+  XX(306, SWITCH_PROXY, SWITCH_PROXY) \
+  XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \
+  XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \
+  XX(400, BAD_REQUEST, BAD_REQUEST) \
+  XX(401, UNAUTHORIZED, UNAUTHORIZED) \
+  XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \
+  XX(403, FORBIDDEN, FORBIDDEN) \
+  XX(404, NOT_FOUND, NOT_FOUND) \
+  XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \
+  XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \
+  XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \
+  XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \
+  XX(409, CONFLICT, CONFLICT) \
+  XX(410, GONE, GONE) \
+  XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \
+  XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \
+  XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \
+  XX(414, URI_TOO_LONG, URI_TOO_LONG) \
+  XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \
+  XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \
+  XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \
+  XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \
+  XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \
+  XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \
+  XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \
+  XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \
+  XX(423, LOCKED, LOCKED) \
+  XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \
+  XX(425, TOO_EARLY, TOO_EARLY) \
+  XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \
+  XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \
+  XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \
+  XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \
+  XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \
+  XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \
+  XX(444, NO_RESPONSE, NO_RESPONSE) \
+  XX(449, RETRY_WITH, RETRY_WITH) \
+  XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \
+  XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \
+  XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \
+  XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \
+  XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \
+  XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \
+  XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \
+  XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \
+  XX(498, INVALID_TOKEN, INVALID_TOKEN) \
+  XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \
+  XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \
+  XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \
+  XX(502, BAD_GATEWAY, BAD_GATEWAY) \
+  XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \
+  XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \
+  XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \
+  XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \
+  XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \
+  XX(508, LOOP_DETECTED, LOOP_DETECTED) \
+  XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \
+  XX(510, NOT_EXTENDED, NOT_EXTENDED) \
+  XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \
+  XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \
+  XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \
+  XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \
+  XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \
+  XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \
+  XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \
+  XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \
+  XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \
+  XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \
+  XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \
+  XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \
+  XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \
+  XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \
+
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+#endif  /* LLLLHTTP_C_HEADERS_ */
+
+
+#ifndef INCLUDE_LLHTTP_API_H_
+#define INCLUDE_LLHTTP_API_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <stddef.h>
+
+#if defined(__wasm__)
+#define LLHTTP_EXPORT __attribute__((visibility("default")))
+//#elif defined(_WIN32)
+//#define LLHTTP_EXPORT __declspec(dllexport)
+#else
+#define LLHTTP_EXPORT
+#endif
+
+typedef llhttp__internal_t llhttp_t;
+typedef struct llhttp_settings_s llhttp_settings_t;
+
+typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
+typedef int (*llhttp_cb)(llhttp_t*);
+
+struct llhttp_settings_s {
+  /* Possible return values 0, -1, `HPE_PAUSED` */
+  llhttp_cb      on_message_begin;
+
+  /* Possible return values 0, -1, HPE_USER */
+  llhttp_data_cb on_url;
+  llhttp_data_cb on_status;
+  llhttp_data_cb on_method;
+  llhttp_data_cb on_version;
+  llhttp_data_cb on_header_field;
+  llhttp_data_cb on_header_value;
+  llhttp_data_cb      on_chunk_extension_name;
+  llhttp_data_cb      on_chunk_extension_value;
+
+  /* Possible return values:
+   * 0  - Proceed normally
+   * 1  - Assume that request/response has no body, and proceed to parsing the
+   *      next message
+   * 2  - Assume absence of body (as above) and make `llhttp_execute()` return
+   *      `HPE_PAUSED_UPGRADE`
+   * -1 - Error
+   * `HPE_PAUSED`
+   */
+  llhttp_cb      on_headers_complete;
+
+  /* Possible return values 0, -1, HPE_USER */
+  llhttp_data_cb on_body;
+
+  /* Possible return values 0, -1, `HPE_PAUSED` */
+  llhttp_cb      on_message_complete;
+  llhttp_cb      on_url_complete;
+  llhttp_cb      on_status_complete;
+  llhttp_cb      on_method_complete;
+  llhttp_cb      on_version_complete;
+  llhttp_cb      on_header_field_complete;
+  llhttp_cb      on_header_value_complete;
+  llhttp_cb      on_chunk_extension_name_complete;
+  llhttp_cb      on_chunk_extension_value_complete;
+
+  /* When on_chunk_header is called, the current chunk length is stored
+   * in parser->content_length.
+   * Possible return values 0, -1, `HPE_PAUSED`
+   */
+  llhttp_cb      on_chunk_header;
+  llhttp_cb      on_chunk_complete;
+  llhttp_cb      on_reset;
+};
+
+/* Initialize the parser with specific type and user settings.
+ *
+ * NOTE: lifetime of `settings` has to be at least the same as the lifetime of
+ * the `parser` here. In practice, `settings` has to be either a static
+ * variable or be allocated with `malloc`, `new`, etc.
+ */
+LLHTTP_EXPORT
+void llhttp_init(llhttp_t* parser, llhttp_type_t type,
+                 const llhttp_settings_t* settings);
+
+LLHTTP_EXPORT
+llhttp_t* llhttp_alloc(llhttp_type_t type);
+
+LLHTTP_EXPORT
+void llhttp_free(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_type(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_http_major(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_http_minor(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_method(llhttp_t* parser);
+
+LLHTTP_EXPORT
+int llhttp_get_status_code(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_upgrade(llhttp_t* parser);
+
+/* Reset an already initialized parser back to the start state, preserving the
+ * existing parser type, callback settings, user data, and lenient flags.
+ */
+LLHTTP_EXPORT
+void llhttp_reset(llhttp_t* parser);
+
+/* Initialize the settings object */
+LLHTTP_EXPORT
+void llhttp_settings_init(llhttp_settings_t* settings);
+
+/* Parse full or partial request/response, invoking user callbacks along the
+ * way.
+ *
+ * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
+ * interrupts, and such errno is returned from `llhttp_execute()`. If
+ * `HPE_PAUSED` was used as a errno, the execution can be resumed with
+ * `llhttp_resume()` call.
+ *
+ * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
+ * is returned after fully parsing the request/response. If the user wishes to
+ * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
+ *
+ * NOTE: if this function ever returns a non-pause type error, it will continue
+ * to return the same error upon each successive call up until `llhttp_init()`
+ * is called.
+ */
+LLHTTP_EXPORT
+llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
+
+/* This method should be called when the other side has no further bytes to
+ * send (e.g. shutdown of readable side of the TCP connection.)
+ *
+ * Requests without `Content-Length` and other messages might require treating
+ * all incoming bytes as the part of the body, up to the last byte of the
+ * connection. This method will invoke `on_message_complete()` callback if the
+ * request was terminated safely. Otherwise a error code would be returned.
+ */
+LLHTTP_EXPORT
+llhttp_errno_t llhttp_finish(llhttp_t* parser);
+
+/* Returns `1` if the incoming message is parsed until the last byte, and has
+ * to be completed by calling `llhttp_finish()` on EOF
+ */
+LLHTTP_EXPORT
+int llhttp_message_needs_eof(const llhttp_t* parser);
+
+/* Returns `1` if there might be any other messages following the last that was
+ * successfully parsed.
+ */
+LLHTTP_EXPORT
+int llhttp_should_keep_alive(const llhttp_t* parser);
+
+/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
+ * appropriate error reason.
+ *
+ * Important: do not call this from user callbacks! User callbacks must return
+ * `HPE_PAUSED` if pausing is required.
+ */
+LLHTTP_EXPORT
+void llhttp_pause(llhttp_t* parser);
+
+/* Might be called to resume the execution after the pause in user's callback.
+ * See `llhttp_execute()` above for details.
+ *
+ * Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
+ */
+LLHTTP_EXPORT
+void llhttp_resume(llhttp_t* parser);
+
+/* Might be called to resume the execution after the pause in user's callback.
+ * See `llhttp_execute()` above for details.
+ *
+ * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
+ */
+LLHTTP_EXPORT
+void llhttp_resume_after_upgrade(llhttp_t* parser);
+
+/* Returns the latest return error */
+LLHTTP_EXPORT
+llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
+
+/* Returns the verbal explanation of the latest returned error.
+ *
+ * Note: User callback should set error reason when returning the error. See
+ * `llhttp_set_error_reason()` for details.
+ */
+LLHTTP_EXPORT
+const char* llhttp_get_error_reason(const llhttp_t* parser);
+
+/* Assign verbal description to the returned error. Must be called in user
+ * callbacks right before returning the errno.
+ *
+ * Note: `HPE_USER` error code might be useful in user callbacks.
+ */
+LLHTTP_EXPORT
+void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
+
+/* Returns the pointer to the last parsed byte before the returned error. The
+ * pointer is relative to the `data` argument of `llhttp_execute()`.
+ *
+ * Note: this method might be useful for counting the number of parsed bytes.
+ */
+LLHTTP_EXPORT
+const char* llhttp_get_error_pos(const llhttp_t* parser);
+
+/* Returns textual name of error code */
+LLHTTP_EXPORT
+const char* llhttp_errno_name(llhttp_errno_t err);
+
+/* Returns textual name of HTTP method */
+LLHTTP_EXPORT
+const char* llhttp_method_name(llhttp_method_t method);
+
+/* Returns textual name of HTTP status */
+LLHTTP_EXPORT
+const char* llhttp_status_name(llhttp_status_t status);
+
+/* Enables/disables lenient header value parsing (disabled by default).
+ *
+ * Lenient parsing disables header value token checks, extending llhttp's
+ * protocol support to highly non-compliant clients/server. No
+ * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
+ * lenient parsing is "on".
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
+
+
+/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
+ * `Content-Length` headers (disabled by default).
+ *
+ * Normally `llhttp` would error when `Transfer-Encoding` is present in
+ * conjunction with `Content-Length`. This error is important to prevent HTTP
+ * request smuggling, but may be less desirable for small number of cases
+ * involving legacy servers.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
+
+
+/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
+ * requests responses.
+ *
+ * Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
+ * the HTTP request/response after the request/response with `Connection: close`
+ * and `Content-Length`. This is important to prevent cache poisoning attacks,
+ * but might interact badly with outdated and insecure clients. With this flag
+ * the extra request/response will be parsed normally.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * poisoning attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of `Transfer-Encoding` header.
+ *
+ * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value
+ * and another value after it (either in a single header or in multiple
+ * headers whose value are internally joined using `, `).
+ * This is mandated by the spec to reliably determine request body size and thus
+ * avoid request smuggling.
+ * With this flag the extra value will be parsed normally.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of HTTP version.
+ *
+ * Normally `llhttp` would error when the HTTP version in the request or status line
+ * is not `0.9`, `1.0`, `1.1` or `2.0`.
+ * With this flag the invalid value will be parsed normally.
+ *
+ * **Enabling this flag can pose a security issue since you will allow unsupported
+ * HTTP versions. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_version(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of additional data received after a message ends
+ * and keep-alive is disabled.
+ *
+ * Normally `llhttp` would error when additional unexpected data is received if the message
+ * contains the `Connection` header with `close` value.
+ * With this flag the extra data will discarded without throwing an error.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * poisoning attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of incomplete CRLF sequences.
+ *
+ * Normally `llhttp` would error when a CR is not followed by LF when terminating the
+ * request line, the status line, the headers or a chunk header.
+ * With this flag only a CR is required to terminate such sections.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled);
+
+/*
+ * Enables/disables lenient handling of line separators.
+ *
+ * Normally `llhttp` would error when a LF is not preceded by CR when terminating the
+ * request line, the status line, the headers, a chunk header or a chunk data.
+ * With this flag only a LF is required to terminate such sections.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of chunks not separated via CRLF.
+ *
+ * Normally `llhttp` would error when after a chunk data a CRLF is missing before
+ * starting a new chunk.
+ * With this flag the new chunk can start immediately after the previous one.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of spaces after chunk size.
+ *
+ * Normally `llhttp` would error when after a chunk size is followed by one or more
+ * spaces are present instead of a CRLF or `;`.
+ * With this flag this check is disabled.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+#endif  /* INCLUDE_LLHTTP_API_H_ */
+
+
+#endif  /* INCLUDE_LLHTTP_H_ */

+ 520 - 0
common/http/llhttp_api.c

@@ -0,0 +1,520 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "llhttp.h"
+
+#ifdef __GNUC__
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wconversion"
+	#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+
+#define CALLBACK_MAYBE(PARSER, NAME)                                          \
+  do {                                                                        \
+    const llhttp_settings_t* settings;                                        \
+    settings = (const llhttp_settings_t*) (PARSER)->settings;                 \
+    if (settings == NULL || settings->NAME == NULL) {                         \
+      err = 0;                                                                \
+      break;                                                                  \
+    }                                                                         \
+    err = settings->NAME((PARSER));                                           \
+  } while (0)
+
+#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN)                         \
+  do {                                                                        \
+    const llhttp_settings_t* settings;                                        \
+    settings = (const llhttp_settings_t*) (PARSER)->settings;                 \
+    if (settings == NULL || settings->NAME == NULL) {                         \
+      err = 0;                                                                \
+      break;                                                                  \
+    }                                                                         \
+    err = settings->NAME((PARSER), (START), (LEN));                           \
+    if (err == -1) {                                                          \
+      err = HPE_USER;                                                         \
+      llhttp_set_error_reason((PARSER), "Span callback error in " #NAME);     \
+    }                                                                         \
+  } while (0)
+
+void llhttp_init(llhttp_t* parser, llhttp_type_t type,
+                 const llhttp_settings_t* settings) {
+  llhttp__internal_init(parser);
+
+  parser->type = type;
+  parser->settings = (void*) settings;
+}
+
+
+#if defined(__wasm__)
+
+extern int wasm_on_message_begin(llhttp_t * p);
+extern int wasm_on_url(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_status(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_headers_complete(llhttp_t * p, int status_code,
+                                    uint8_t upgrade, int should_keep_alive);
+extern int wasm_on_body(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_message_complete(llhttp_t * p);
+
+static int wasm_on_headers_complete_wrap(llhttp_t* p) {
+  return wasm_on_headers_complete(p, p->status_code, p->upgrade,
+                                  llhttp_should_keep_alive(p));
+}
+
+const llhttp_settings_t wasm_settings = {
+  wasm_on_message_begin,
+  wasm_on_url,
+  wasm_on_status,
+  NULL,
+  NULL,
+  wasm_on_header_field,
+  wasm_on_header_value,
+  NULL,
+  NULL,
+  wasm_on_headers_complete_wrap,
+  wasm_on_body,
+  wasm_on_message_complete,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+
+
+llhttp_t* llhttp_alloc(llhttp_type_t type) {
+  llhttp_t* parser = malloc(sizeof(llhttp_t));
+  llhttp_init(parser, type, &wasm_settings);
+  return parser;
+}
+
+void llhttp_free(llhttp_t* parser) {
+  free(parser);
+}
+
+#endif  // defined(__wasm__)
+
+/* Some getters required to get stuff from the parser */
+
+uint8_t llhttp_get_type(llhttp_t* parser) {
+  return parser->type;
+}
+
+uint8_t llhttp_get_http_major(llhttp_t* parser) {
+  return parser->http_major;
+}
+
+uint8_t llhttp_get_http_minor(llhttp_t* parser) {
+  return parser->http_minor;
+}
+
+uint8_t llhttp_get_method(llhttp_t* parser) {
+  return parser->method;
+}
+
+int llhttp_get_status_code(llhttp_t* parser) {
+  return parser->status_code;
+}
+
+uint8_t llhttp_get_upgrade(llhttp_t* parser) {
+  return parser->upgrade;
+}
+
+
+void llhttp_reset(llhttp_t* parser) {
+  llhttp_type_t type = parser->type;
+  const llhttp_settings_t* settings = parser->settings;
+  void* data = parser->data;
+  uint16_t lenient_flags = parser->lenient_flags;
+
+  llhttp__internal_init(parser);
+
+  parser->type = type;
+  parser->settings = (void*) settings;
+  parser->data = data;
+  parser->lenient_flags = lenient_flags;
+}
+
+
+llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
+  return llhttp__internal_execute(parser, data, data + len);
+}
+
+
+void llhttp_settings_init(llhttp_settings_t* settings) {
+  memset(settings, 0, sizeof(*settings));
+}
+
+
+llhttp_errno_t llhttp_finish(llhttp_t* parser) {
+  int err;
+
+  /* We're in an error state. Don't bother doing anything. */
+  if (parser->error != 0) {
+    return 0;
+  }
+
+  switch (parser->finish) {
+    case HTTP_FINISH_SAFE_WITH_CB:
+      CALLBACK_MAYBE(parser, on_message_complete);
+      if (err != HPE_OK) return err;
+
+    /* FALLTHROUGH */
+    case HTTP_FINISH_SAFE:
+      return HPE_OK;
+    case HTTP_FINISH_UNSAFE:
+      parser->reason = "Invalid EOF state";
+      return HPE_INVALID_EOF_STATE;
+    default:
+      abort();
+  }
+}
+
+
+void llhttp_pause(llhttp_t* parser) {
+  if (parser->error != HPE_OK) {
+    return;
+  }
+
+  parser->error = HPE_PAUSED;
+  parser->reason = "Paused";
+}
+
+
+void llhttp_resume(llhttp_t* parser) {
+  if (parser->error != HPE_PAUSED) {
+    return;
+  }
+
+  parser->error = 0;
+}
+
+
+void llhttp_resume_after_upgrade(llhttp_t* parser) {
+  if (parser->error != HPE_PAUSED_UPGRADE) {
+    return;
+  }
+
+  parser->error = 0;
+}
+
+
+llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
+  return parser->error;
+}
+
+
+const char* llhttp_get_error_reason(const llhttp_t* parser) {
+  return parser->reason;
+}
+
+
+void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
+  parser->reason = reason;
+}
+
+
+const char* llhttp_get_error_pos(const llhttp_t* parser) {
+  return parser->error_pos;
+}
+
+
+const char* llhttp_errno_name(llhttp_errno_t err) {
+#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
+  switch (err) {
+    HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+    default: abort();
+  }
+#undef HTTP_ERRNO_GEN
+}
+
+
+const char* llhttp_method_name(llhttp_method_t method) {
+#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
+  switch (method) {
+    HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)
+    default: abort();
+  }
+#undef HTTP_METHOD_GEN
+}
+
+const char* llhttp_status_name(llhttp_status_t status) {
+#define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING;
+  switch (status) {
+    HTTP_STATUS_MAP(HTTP_STATUS_GEN)
+    default: abort();
+  }
+#undef HTTP_STATUS_GEN
+}
+
+
+void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_HEADERS;
+  } else {
+    parser->lenient_flags &= ~LENIENT_HEADERS;
+  }
+}
+
+
+void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
+  } else {
+    parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
+  }
+}
+
+
+void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_KEEP_ALIVE;
+  } else {
+    parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
+  }
+}
+
+void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;
+  } else {
+    parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;
+  }
+}
+
+void llhttp_set_lenient_version(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_VERSION;
+  } else {
+    parser->lenient_flags &= ~LENIENT_VERSION;
+  }
+}
+
+void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;
+  } else {
+    parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;
+  }
+}
+
+void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;
+  } else {
+    parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;
+  }
+}
+
+void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
+  } else {
+    parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
+  }
+}
+
+void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF;
+  } else {
+    parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF;
+  }
+}
+
+void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) {
+  if (enabled) {
+    parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
+  } else {
+    parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;
+  }
+}
+
+/* Callbacks */
+
+
+int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_message_begin);
+  return err;
+}
+
+
+int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_url_complete);
+  return err;
+}
+
+
+int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_status_complete);
+  return err;
+}
+
+
+int llhttp__on_method(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_method_complete);
+  return err;
+}
+
+
+int llhttp__on_version(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_version_complete);
+  return err;
+}
+
+
+int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_header_field_complete);
+  return err;
+}
+
+
+int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_header_value_complete);
+  return err;
+}
+
+
+int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_headers_complete);
+  return err;
+}
+
+
+int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_message_complete);
+  return err;
+}
+
+
+int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_chunk_header);
+  return err;
+}
+
+
+int llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_chunk_extension_name_complete);
+  return err;
+}
+
+
+int llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_chunk_extension_value_complete);
+  return err;
+}
+
+
+int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_chunk_complete);
+  return err;
+}
+
+
+int llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_reset);
+  return err;
+}
+
+
+/* Private */
+
+
+void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
+                   const char* msg) {
+  if (p == endp) {
+    fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
+            s->flags, msg);
+  } else {
+    fprintf(stderr, "p=%p type=%d flags=%02x next=%02x   debug=%s\n", s,
+            s->type, s->flags, *p, msg);
+  }
+}
+
+#ifdef __GNUC__
+	#pragma GCC diagnostic pop
+#endif

Some files were not shown because too many files changed in this diff