Bladeren bron

添加tcp服务器端代码

Flynn 8 jaren geleden
bovenliggende
commit
9db2af856e
2 gewijzigde bestanden met toevoegingen van 1638 en 0 verwijderingen
  1. 1340 0
      HP-Socket/Src/TcpServer.cpp
  2. 298 0
      HP-Socket/Src/TcpServer.h

+ 1340 - 0
HP-Socket/Src/TcpServer.cpp

@@ -0,0 +1,1340 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Version	: 4.2.1
+ * Author	: Bruce Liang
+ * Website	: http://www.jessma.org
+ * Project	: https://github.com/ldcsaa
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 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 "stdafx.h"
+#include "TcpServer.h"
+#include "../../Common/Src/WaitFor.h"
+
+#include <malloc.h>
+#include <process.h>
+
+EnHandleResult CTcpServer::TriggerFireAccept(TSocketObj* pSocketObj)
+{
+	CReentrantSpinLock locallock(pSocketObj->csRecv);
+	return FireAccept(pSocketObj);
+}
+
+EnHandleResult CTcpServer::TriggerFireReceive(TSocketObj* pSocketObj, TBufferObj* pBufferObj)
+{
+	EnHandleResult rs = (EnHandleResult)HR_CLOSED;
+
+	if(TSocketObj::IsValid(pSocketObj))
+	{
+		CReentrantSpinLock locallock(pSocketObj->csRecv);
+
+		if(TSocketObj::IsValid(pSocketObj))
+		{
+			rs = FireReceive(pSocketObj, (BYTE*)pBufferObj->buff.buf, pBufferObj->buff.len);
+		}
+	}
+
+	return rs;
+}
+
+EnHandleResult CTcpServer::TriggerFireSend(TSocketObj* pSocketObj, TBufferObj* pBufferObj)
+{
+	EnHandleResult rs = FireSend(pSocketObj, (BYTE*)pBufferObj->buff.buf, pBufferObj->buff.len);
+
+	if(rs == HR_ERROR)
+	{
+		TRACE("<S-CNNID: %Iu> OnSend() event should not return 'HR_ERROR' !!\n", pSocketObj->connID);
+		ASSERT(FALSE);
+	}
+
+	AddFreeBufferObj(pBufferObj);
+
+	return rs;
+}
+
+EnHandleResult CTcpServer::TriggerFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+{
+	CReentrantSpinLock locallock(pSocketObj->csRecv);
+	return FireClose(pSocketObj, enOperation, iErrorCode);
+}
+
+void CTcpServer::SetLastError(EnSocketError code, LPCSTR func, int ec)
+{
+	m_enLastError = code;
+
+	TRACE("%s --> Error: %d, EC: %d\n", func, code, ec);
+}
+
+BOOL CTcpServer::Start(LPCTSTR lpszBindAddress, USHORT usPort)
+{
+	if(!CheckParams() || !CheckStarting())
+		return FALSE;
+
+	PrepareStart();
+
+	if(CreateListenSocket(lpszBindAddress, usPort))
+		if(CreateCompletePort())
+			if(CreateWorkerThreads())
+				if(StartAccept())
+				{
+					m_enState = SS_STARTED;
+					return TRUE;
+				}
+
+	Stop();
+
+	return FALSE;
+}
+
+BOOL CTcpServer::CheckParams()
+{
+	if	((m_enSendPolicy >= SP_PACK && m_enSendPolicy <= SP_DIRECT)								&&
+		((int)m_dwMaxConnectionCount > 0)														&&
+		((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 >= 0)													&&
+		((int)m_dwFreeSocketObjPool >= 0)														&&
+		((int)m_dwFreeBufferObjPool >= 0)														&&
+		((int)m_dwFreeSocketObjHold >= m_dwFreeSocketObjPool)									&&
+		((int)m_dwFreeBufferObjHold >= m_dwFreeBufferObjPool)									&&
+		((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_dwFreeSocketObjHold);
+
+	m_bfObjPool.SetItemCapacity((int)m_dwSocketBufferSize);
+	m_bfObjPool.SetPoolSize((int)m_dwFreeBufferObjPool);
+	m_bfObjPool.SetPoolHold((int)m_dwFreeBufferObjHold);
+
+	m_bfObjPool.Prepare();
+}
+
+BOOL CTcpServer::CheckStarting()
+{
+	CSpinLock locallock(m_csState);
+
+	if(m_enState == SS_STOPPED)
+		m_enState = SS_STARTING;
+	else
+	{
+		SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_OPERATION);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CTcpServer::CheckStoping()
+{
+	if(m_enState == SS_STOPPED)
+		return FALSE;
+
+	CSpinLock locallock(m_csState);
+
+	if(HasStarted())
+	{
+		m_enState = SS_STOPPING;
+		return TRUE;
+	}
+	else if(m_enState == SS_STOPPING)
+	{
+		while(m_enState != SS_STOPPED)
+			::Sleep(30);
+
+		SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_OPERATION);
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpServer::CreateListenSocket(LPCTSTR lpszBindAddress, USHORT usPort)
+{
+	BOOL isOK = FALSE;
+
+	m_soListen	= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if(m_soListen != INVALID_SOCKET)
+	{
+		SOCKADDR_IN addr;
+		::sockaddr_A_2_IN(AF_INET, lpszBindAddress, usPort, addr);
+
+		BOOL bOnOff	= (m_dwKeepAliveTime > 0 && m_dwKeepAliveInterval > 0);
+		::SSO_KeepAliveVals(m_soListen, bOnOff, m_dwKeepAliveTime, m_dwKeepAliveInterval);
+
+		if(::bind(m_soListen, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN)) != SOCKET_ERROR)
+		{
+			if(FirePrepareListen(m_soListen) != HR_ERROR)
+			{
+				if(::listen(m_soListen, m_dwSocketListenQueue) != SOCKET_ERROR)
+				{
+					m_pfnAcceptEx				= ::Get_AcceptEx_FuncPtr(m_soListen);
+					m_pfnGetAcceptExSockaddrs	= ::Get_GetAcceptExSockaddrs_FuncPtr(m_soListen);
+					m_pfnDisconnectEx			= ::Get_DisconnectEx_FuncPtr(m_soListen);
+
+					ASSERT(m_pfnAcceptEx);
+					ASSERT(m_pfnGetAcceptExSockaddrs);
+					ASSERT(m_pfnDisconnectEx);
+
+					isOK = TRUE;
+				}
+				else
+					SetLastError(SE_SOCKET_LISTEN, __FUNCTION__, ::WSAGetLastError());
+			}
+			else
+				SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ERROR_CANCELLED);
+		}
+		else
+			SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
+	}
+	else
+		SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
+
+	return isOK;
+}
+
+BOOL CTcpServer::CreateCompletePort()
+{
+	m_hCompletePort	= ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
+	if(m_hCompletePort == nullptr)
+		SetLastError(SE_CP_CREATE, __FUNCTION__, ::GetLastError());
+
+	return (m_hCompletePort != nullptr);
+}
+
+BOOL CTcpServer::CreateWorkerThreads()
+{
+	BOOL isOK = TRUE;
+
+	for(DWORD i = 0; i < m_dwWorkerThreadCount; i++)
+	{
+		HANDLE hThread = (HANDLE)_beginthreadex(nullptr, 0, WorkerThreadProc, (LPVOID)this, 0, nullptr);
+		if(hThread)
+			m_vtWorkerThreads.push_back(hThread);
+		else
+		{
+			SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::GetLastError());
+			isOK = FALSE;
+			break;
+		}
+	}
+
+	return isOK;
+}
+
+BOOL CTcpServer::StartAccept()
+{
+	BOOL isOK = TRUE;
+
+	if(::CreateIoCompletionPort((HANDLE)m_soListen, m_hCompletePort, m_soListen, 0))
+	{
+		m_iRemainAcceptSockets = m_dwAcceptSocketCount;
+
+		for(DWORD i = 0; i < m_dwAcceptSocketCount; i++)
+			VERIFY(::PostIocpAccept(m_hCompletePort));
+	}
+	else
+	{
+		SetLastError(SE_SOCKE_ATTACH_TO_CP, __FUNCTION__, ::GetLastError());
+		isOK = FALSE;
+	}
+
+	return isOK;
+}
+
+BOOL CTcpServer::Stop()
+{
+	if(!CheckStoping())
+		return FALSE;
+
+	CloseListenSocket();
+
+	WaitForAcceptSocketClose();
+
+	DisconnectClientSocket();
+	WaitForClientSocketClose();
+	WaitForWorkerThreadEnd();
+	
+	ReleaseClientSocket();
+
+	FireShutdown();
+
+	ReleaseFreeSocket();
+	ReleaseFreeBuffer();
+
+	CloseCompletePort();
+
+	Reset();
+
+	return TRUE;
+}
+
+void CTcpServer::Reset()
+{
+	m_phSocket.Reset();
+
+	m_iRemainAcceptSockets		= 0;
+	m_pfnAcceptEx				= nullptr;
+	m_pfnGetAcceptExSockaddrs	= nullptr;
+	m_pfnDisconnectEx			= nullptr;
+	m_enState					= SS_STOPPED;
+}
+
+void CTcpServer::CloseListenSocket()
+{
+	if(m_soListen != INVALID_SOCKET)
+	{
+		::ManualCloseSocket(m_soListen);
+		m_soListen = INVALID_SOCKET;
+	}
+}
+
+void CTcpServer::DisconnectClientSocket()
+{
+	DWORD size					= 0;
+	unique_ptr<CONNID[]> ids	= m_bfActiveSockets.GetAllElementIndexes(size);
+
+	for(DWORD i = 0; i < size; i++)
+		Disconnect(ids[i]);
+}
+
+void CTcpServer::ReleaseClientSocket()
+{
+	VERIFY(m_bfActiveSockets.IsEmpty());
+	m_bfActiveSockets.Reset();
+}
+
+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;
+}
+
+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);
+
+	if(!m_lsFreeSocket.TryPut(pSocketObj))
+	{
+		m_lsGCSocket.PushBack(pSocketObj);
+
+		if(m_lsGCSocket.Size() > m_dwFreeSocketObjPool)
+			ReleaseGCSocketObj();
+	}
+}
+
+void CTcpServer::ReleaseGCSocketObj(BOOL bForce)
+{
+	TSocketObj* pSocketObj	= nullptr;
+	DWORD now				= ::TimeGetTime();
+
+	while(m_lsGCSocket.PopFront(&pSocketObj))
+	{
+		if(bForce || (int)(now - pSocketObj->freeTime) >= (int)m_dwFreeSocketObjLockTime)
+			DeleteSocketObj(pSocketObj);
+		else
+		{
+			m_lsGCSocket.PushBack(pSocketObj);
+			break;
+		}
+	}
+}
+
+BOOL CTcpServer::InvalidSocketObj(TSocketObj* pSocketObj)
+{
+	BOOL bDone = FALSE;
+
+	if(TSocketObj::IsValid(pSocketObj))
+	{
+		CReentrantSpinLock	locallock(pSocketObj->csRecv);
+		CCriSecLock			locallock2(pSocketObj->csSend);
+
+		if(TSocketObj::IsValid(pSocketObj))
+		{
+			TSocketObj::Invalid(pSocketObj);
+			bDone = TRUE;
+		}
+	}
+
+	return bDone;
+}
+
+void CTcpServer::AddClientSocketObj(CONNID dwConnID, TSocketObj* pSocketObj)
+{
+	ASSERT(FindSocketObj(dwConnID) == nullptr);
+
+	pSocketObj->connTime	= ::TimeGetTime();
+	pSocketObj->activeTime	= pSocketObj->connTime;
+
+	VERIFY(m_bfActiveSockets.ReleaseLock(dwConnID, pSocketObj));
+}
+
+void CTcpServer::ReleaseFreeSocket()
+{
+	TSocketObj* pSocketObj = nullptr;
+
+	while(m_lsFreeSocket.TryGet(&pSocketObj))
+		DeleteSocketObj(pSocketObj);
+
+	VERIFY(m_lsFreeSocket.IsEmpty());
+	m_lsFreeSocket.Reset();
+
+	ReleaseGCSocketObj(TRUE);
+	VERIFY(m_lsGCSocket.IsEmpty());
+}
+
+TSocketObj* CTcpServer::CreateSocketObj()
+{
+	TSocketObj* pSocketObj = (TSocketObj*)m_phSocket.Alloc(sizeof(TSocketObj));
+	ASSERT(pSocketObj);
+
+	pSocketObj->TSocketObj::TSocketObj(m_bfObjPool);
+	
+	return pSocketObj;
+}
+
+void CTcpServer::DeleteSocketObj(TSocketObj* pSocketObj)
+{
+	ASSERT(pSocketObj);
+
+	pSocketObj->TSocketObj::~TSocketObj();
+	m_phSocket.Free(pSocketObj);
+}
+
+TBufferObj* CTcpServer::GetFreeBufferObj(int iLen)
+{
+	ASSERT(iLen >= -1 && iLen <= (int)m_dwSocketBufferSize);
+
+	TBufferObj* pBufferObj	= m_bfObjPool.PickFreeItem();
+	if(iLen < 0) iLen		= m_dwSocketBufferSize;
+	pBufferObj->buff.len	= iLen;
+
+	return pBufferObj;
+}
+
+void CTcpServer::AddFreeBufferObj(TBufferObj* pBufferObj)
+{
+	m_bfObjPool.PutFreeItem(pBufferObj);
+}
+
+void CTcpServer::ReleaseFreeBuffer()
+{
+	m_bfObjPool.Clear();
+}
+
+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)
+		TriggerFireClose(pSocketObj, SO_CLOSE, SE_OK);
+	else if(enFlag == SCF_ERROR)
+		TriggerFireClose(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);
+
+	return ::GetSocketLocalAddress(m_soListen, 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))
+		return ::GetSocketLocalAddress(pSocketObj->socket, lpszAddress, iAddressLen, usPort);
+
+	return FALSE;
+}
+
+BOOL CTcpServer::GetRemoteAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
+{
+	ASSERT(lpszAddress != nullptr && iAddressLen > 0);
+
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(TSocketObj::IsExist(pSocketObj))
+	{
+		ADDRESS_FAMILY usFamily;
+		return ::sockaddr_IN_2_A(pSocketObj->remoteAddr, usFamily, lpszAddress, iAddressLen, usPort);
+	}
+
+	return FALSE;
+}
+
+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))
+	{
+		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))
+	{
+		*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))
+	{
+		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))
+	{
+		*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))
+	{
+		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))
+	{
+		*ppReserved2 = pSocketObj->reserved2;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL CTcpServer::GetPendingDataLength(CONNID dwConnID, int& iPending)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(TSocketObj::IsValid(pSocketObj))
+	{
+		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
+		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
+		isOK = FALSE;
+
+	return isOK;
+}
+
+BOOL CTcpServer::Disconnect(CONNID dwConnID, BOOL bForce)
+{
+	BOOL isOK				= FALSE;
+	TSocketObj* pSocketObj	= FindSocketObj(dwConnID);
+
+	if(TSocketObj::IsValid(pSocketObj))
+	{
+		if(bForce)
+			isOK = ::PostIocpDisconnect(m_hCompletePort, dwConnID);
+		else
+			isOK = m_pfnDisconnectEx(pSocketObj->socket, nullptr, 0, 0);
+	}
+
+	return isOK;
+}
+
+BOOL CTcpServer::DisconnectLongConnections(DWORD dwPeriod, BOOL bForce)
+{
+	if(dwPeriod > MAX_CONNECTION_PERIOD)
+		return FALSE;
+
+	DWORD size					= 0;
+	unique_ptr<CONNID[]> ids	= m_bfActiveSockets.GetAllElementIndexes(size);
+	DWORD now					= ::TimeGetTime();
+
+	for(DWORD i = 0; i < size; i++)
+	{
+		CONNID connID			= ids[i];
+		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;
+
+	DWORD size					= 0;
+	unique_ptr<CONNID[]> ids	= m_bfActiveSockets.GetAllElementIndexes(size);
+	DWORD now					= ::TimeGetTime();
+
+	for(DWORD i = 0; i < size; i++)
+	{
+		CONNID connID			= ids[i];
+		TSocketObj* pSocketObj	= FindSocketObj(connID);
+
+		if(TSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->activeTime) >= (int)dwPeriod)
+			Disconnect(connID, bForce);
+	}
+
+	return TRUE;
+}
+
+void CTcpServer::WaitForAcceptSocketClose()
+{
+	while(m_iRemainAcceptSockets > 0)
+		::WaitWithMessageLoop(100);
+}
+
+void CTcpServer::WaitForClientSocketClose()
+{
+	while(m_bfActiveSockets.Elements() > 0)
+		::WaitWithMessageLoop(100);
+}
+
+void CTcpServer::WaitForWorkerThreadEnd()
+{
+	int count = (int)m_vtWorkerThreads.size();
+
+	for(int i = 0; i < count; i++)
+		::PostIocpExit(m_hCompletePort);
+
+	int remain	= count;
+	int index	= 0;
+
+	while(remain > 0)
+	{
+		int wait = min(remain, MAXIMUM_WAIT_OBJECTS);
+		HANDLE* pHandles = (HANDLE*)_alloca(sizeof(HANDLE) * wait);
+
+		for(int i = 0; i < wait; i++)
+			pHandles[i]	= m_vtWorkerThreads[i + index];
+
+		VERIFY(::WaitForMultipleObjects((DWORD)wait, pHandles, TRUE, INFINITE) == WAIT_OBJECT_0);
+
+		for(int i = 0; i < wait; i++)
+			::CloseHandle(pHandles[i]);
+
+		remain	-= wait;
+		index	+= wait;
+	}
+
+	m_vtWorkerThreads.clear();
+}
+
+void CTcpServer::CloseCompletePort()
+{
+	if(m_hCompletePort != nullptr)
+	{
+		::CloseHandle(m_hCompletePort);
+		m_hCompletePort = nullptr;
+	}
+}
+
+BOOL CTcpServer::DoAccept()
+{
+	BOOL isOK = FALSE;
+
+	if(HasStarted())
+	{
+		SOCKET		soClient	= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+		TBufferObj*	pBufferObj	= GetFreeBufferObj();
+
+		ASSERT(soClient != INVALID_SOCKET);
+
+		isOK = (::PostAccept(m_pfnAcceptEx, m_soListen, soClient, pBufferObj) == NO_ERROR);
+
+		if(!isOK)
+		{
+			VERIFY(!HasStarted());
+
+			::ManualCloseSocket(soClient);
+			AddFreeBufferObj(pBufferObj);
+		}
+	}
+
+	if(!isOK)
+	{
+		::InterlockedDecrement(&m_iRemainAcceptSockets);
+		ASSERT(m_iRemainAcceptSockets >= 0);
+	}
+
+	return isOK;
+}
+
+UINT WINAPI CTcpServer::WorkerThreadProc(LPVOID pv)
+{
+	CTcpServer* pServer = (CTcpServer*)pv;
+
+	while(TRUE)
+	{
+		DWORD dwErrorCode = NO_ERROR;
+
+		DWORD dwBytes;
+		OVERLAPPED* pOverlapped;
+		TSocketObj* pSocketObj;
+		
+		BOOL result = ::GetQueuedCompletionStatus
+												(
+													pServer->m_hCompletePort,
+													&dwBytes,
+													(PULONG_PTR)&pSocketObj,
+													&pOverlapped,
+													INFINITE
+												);
+
+		if(pOverlapped == nullptr)
+		{
+			EnIocpAction action = pServer->CheckIocpCommand(pOverlapped, dwBytes, (ULONG_PTR)pSocketObj);
+
+			if(action == IOCP_ACT_CONTINUE)
+				continue;
+			else if(action == IOCP_ACT_BREAK)
+				break;
+		}
+
+		TBufferObj* pBufferObj	= CONTAINING_RECORD(pOverlapped, TBufferObj, ov);
+		CONNID dwConnID			= pBufferObj->operation != SO_ACCEPT ? pSocketObj->connID : 0;
+
+		if (!result)
+		{
+			DWORD dwFlag	= 0;
+			DWORD dwSysCode = ::GetLastError();
+
+			if(pServer->HasStarted())
+			{
+				SOCKET sock	= pBufferObj->operation != SO_ACCEPT ? pBufferObj->client : (SOCKET)pSocketObj;
+				result		= ::WSAGetOverlappedResult(sock, &pBufferObj->ov, &dwBytes, FALSE, &dwFlag);
+
+				if (!result)
+				{
+					dwErrorCode = ::WSAGetLastError();
+					TRACE("GetQueuedCompletionStatus error (<S-CNNID: %Iu> SYS: %d, SOCK: %d, FLAG: %d)\n", dwConnID, dwSysCode, dwErrorCode, dwFlag);
+				}
+			}
+			else
+				dwErrorCode = dwSysCode;
+
+			ASSERT(dwSysCode != 0 && dwErrorCode != 0);
+		}
+
+		pServer->HandleIo(dwConnID, pSocketObj, pBufferObj, dwBytes, dwErrorCode);
+	}
+
+	pServer->OnWorkerThreadEnd(::GetCurrentThreadId());
+
+	return 0;
+}
+
+EnIocpAction CTcpServer::CheckIocpCommand(OVERLAPPED* pOverlapped, DWORD dwBytes, ULONG_PTR ulCompKey)
+{
+	ASSERT(pOverlapped == nullptr);
+
+	EnIocpAction action = IOCP_ACT_CONTINUE;
+	CONNID dwConnID		= (CONNID)ulCompKey;
+
+	switch(dwBytes)
+	{
+	case IOCP_CMD_SEND		: DoSend(dwConnID)			; break;
+	case IOCP_CMD_ACCEPT	: DoAccept()				; break;
+	case IOCP_CMD_DISCONNECT: ForceDisconnect(dwConnID)	; break;
+	case IOCP_CMD_EXIT		: action = IOCP_ACT_BREAK	; break;
+	default					: CheckError(FindSocketObj(dwConnID), SO_CLOSE, (int)dwBytes);
+	}
+
+	return action;
+}
+
+void CTcpServer::ForceDisconnect(CONNID dwConnID)
+{
+	AddFreeSocketObj(FindSocketObj(dwConnID), SCF_CLOSE);
+}
+
+void CTcpServer::HandleIo(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj, DWORD dwBytes, DWORD dwErrorCode)
+{
+	ASSERT(pBufferObj != nullptr);
+	ASSERT(pSocketObj != nullptr);
+
+	if(dwErrorCode != NO_ERROR)
+	{
+		HandleError(dwConnID, pSocketObj, pBufferObj, dwErrorCode);
+		return;
+	}
+
+	if(dwBytes == 0 && pBufferObj->operation != SO_ACCEPT)
+	{
+		AddFreeSocketObj(pSocketObj, SCF_CLOSE);
+		AddFreeBufferObj(pBufferObj);
+		return;
+	}
+
+	pBufferObj->buff.len = dwBytes;
+
+	switch(pBufferObj->operation)
+	{
+	case SO_ACCEPT:
+		HandleAccept((SOCKET)pSocketObj, pBufferObj);
+		break;
+	case SO_SEND:
+		HandleSend(dwConnID, pSocketObj, pBufferObj);
+		break;
+	case SO_RECEIVE:
+		HandleReceive(dwConnID, pSocketObj, pBufferObj);
+		break;
+	default:
+		ASSERT(FALSE);
+	}
+}
+
+void CTcpServer::HandleError(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj, DWORD dwErrorCode)
+{
+	if(pBufferObj->operation != SO_ACCEPT)
+		CheckError(pSocketObj, pBufferObj->operation, dwErrorCode);
+	else
+	{
+		::ManualCloseSocket(pBufferObj->client);
+		VERIFY(::PostIocpAccept(m_hCompletePort));
+	}
+
+	AddFreeBufferObj(pBufferObj);
+}
+
+void CTcpServer::HandleAccept(SOCKET soListen, TBufferObj* pBufferObj)
+{
+	VERIFY(::PostIocpAccept(m_hCompletePort));
+
+	int iLocalSockaddrLen;
+	int iRemoteSockaddrLen;
+	SOCKADDR* pLocalSockAddr;
+	SOCKADDR* pRemoteSockAddr;
+
+	m_pfnGetAcceptExSockaddrs
+							(
+								pBufferObj->buff.buf,
+								0,
+								sizeof(SOCKADDR_IN) + 16,
+								sizeof(SOCKADDR_IN) + 16,
+								(SOCKADDR **)&pLocalSockAddr,
+								&iLocalSockaddrLen,
+								(SOCKADDR **)&pRemoteSockAddr,
+								&iRemoteSockaddrLen
+							);
+
+	CONNID dwConnID = 0;
+	SOCKET socket	= pBufferObj->client;
+
+	if(!HasStarted() || !m_bfActiveSockets.AcquireLock(dwConnID))
+	{
+		::ManualCloseSocket(socket, SD_BOTH);
+		AddFreeBufferObj(pBufferObj);
+
+		return;
+	}
+
+	TSocketObj* pSocketObj = GetFreeSocketObj(dwConnID, socket);
+
+	memcpy(&pSocketObj->remoteAddr, pRemoteSockAddr, sizeof(SOCKADDR_IN));
+	AddClientSocketObj(dwConnID, pSocketObj);
+
+	::SSO_UpdateAcceptContext(socket, soListen);
+	::CreateIoCompletionPort((HANDLE)socket, m_hCompletePort, (ULONG_PTR)pSocketObj, 0);
+
+	if(TriggerFireAccept(pSocketObj) != HR_ERROR)
+		DoReceive(dwConnID, pSocketObj, pBufferObj);
+	else
+	{
+		AddFreeSocketObj(pSocketObj, SCF_NONE);
+		AddFreeBufferObj(pBufferObj);
+	}
+}
+
+void CTcpServer::HandleSend(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj)
+{
+	switch(m_enSendPolicy)
+	{
+	case SP_PACK:
+		{
+			long sndCount = ::InterlockedDecrement(&pSocketObj->sndCount);
+
+			TriggerFireSend(pSocketObj, pBufferObj);
+			if(sndCount == 0) DoSendPack(pSocketObj);
+		}
+
+		break;
+	case SP_SAFE:
+		{
+			long sndCount = ::InterlockedDecrement(&pSocketObj->sndCount);
+
+			if(sndCount == 0 && !pSocketObj->smooth)
+			{
+				CCriSecLock locallock(pSocketObj->csSend);
+
+				if((sndCount = pSocketObj->sndCount) == 0)
+					pSocketObj->smooth = TRUE;
+			}
+
+			TriggerFireSend(pSocketObj, pBufferObj);
+			if(sndCount == 0) DoSendSafe(pSocketObj);
+		}
+
+		break;
+	case SP_DIRECT:
+		{
+			TriggerFireSend(pSocketObj, pBufferObj);
+		}
+
+		break;
+	default:
+		ASSERT(FALSE);
+	}
+}
+
+void CTcpServer::HandleReceive(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj)
+{
+	if(m_bMarkSilence) pSocketObj->activeTime = ::TimeGetTime();
+	EnHandleResult hr = TriggerFireReceive(pSocketObj, pBufferObj);
+
+	if(hr == HR_OK || hr == HR_IGNORE)
+		DoReceive(dwConnID, pSocketObj, pBufferObj);
+	else if(hr == HR_CLOSED)
+	{
+		AddFreeBufferObj(pBufferObj);
+	}
+	else
+	{
+		AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ERROR_CANCELLED);
+		AddFreeBufferObj(pBufferObj);
+	}
+}
+
+int CTcpServer::DoReceive(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj)
+{
+	pBufferObj->buff.len = m_dwSocketBufferSize;
+	int result =::PostReceive(pSocketObj, pBufferObj);
+
+	if(result != NO_ERROR)
+	{
+		CheckError(pSocketObj, SO_RECEIVE, result);
+		AddFreeBufferObj(pBufferObj);
+	}
+
+	return result;
+}
+
+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 = (char*)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)
+	{
+		CCriSecLock 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)
+	{
+		if(m_enSendPolicy == SP_DIRECT && TSocketObj::IsValid(pSocketObj))
+			::PostIocpClose(m_hCompletePort, pSocketObj->connID, result);
+
+		::SetLastError(result);
+	}
+
+	return (result == NO_ERROR);
+}
+
+int CTcpServer::SendInternal(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount)
+{
+	int result = NO_ERROR;
+
+	for(int i = 0; i < iCount; i++)
+	{
+		int iBufLen = pBuffers[i].len;
+
+		if(iBufLen > 0)
+		{
+			BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
+			ASSERT(pBuffer);
+
+			switch(m_enSendPolicy)
+			{
+			case SP_PACK:	result = SendPack(pSocketObj, pBuffer, iBufLen);	break;
+			case SP_SAFE:	result = SendSafe(pSocketObj, pBuffer, iBufLen);	break;
+			case SP_DIRECT:	result = SendDirect(pSocketObj, pBuffer, iBufLen);	break;
+			default: ASSERT(FALSE);	result = ERROR_INVALID_INDEX;				break;
+			}
+
+			if(result != NO_ERROR)
+				break;
+		}
+	}
+
+	return result;
+}
+
+int CTcpServer::SendPack(TSocketObj* pSocketObj, const BYTE* pBuffer, int iLength)
+{
+	BOOL isPostSend = !TSocketObj::IsPending(pSocketObj);
+	return CatAndPost(pSocketObj, pBuffer, iLength, isPostSend);
+}
+
+int CTcpServer::SendSafe(TSocketObj* pSocketObj, const BYTE* pBuffer, int iLength)
+{
+	BOOL isPostSend = !TSocketObj::IsPending(pSocketObj) && TSocketObj::IsSmooth(pSocketObj);
+	return CatAndPost(pSocketObj, pBuffer, iLength, isPostSend);
+}
+
+int CTcpServer::SendDirect(TSocketObj* pSocketObj, const BYTE* pBuffer, int iLength)
+{
+	int result	= NO_ERROR;
+	int iRemain	= iLength;
+
+	while(iRemain > 0)
+	{
+		int iBufferSize = min(iRemain, (int)m_dwSocketBufferSize);
+		TBufferObj* pBufferObj = GetFreeBufferObj(iBufferSize);
+		memcpy(pBufferObj->buff.buf, pBuffer, iBufferSize);
+
+		result = ::PostSend(pSocketObj, pBufferObj);
+
+		if(result != NO_ERROR)
+		{
+			AddFreeBufferObj(pBufferObj);
+			break;
+		}
+
+		iRemain -= iBufferSize;
+		pBuffer += iBufferSize;
+	}
+
+	return result;
+}
+
+int CTcpServer::CatAndPost(TSocketObj* pSocketObj, const BYTE* pBuffer, int iLength, BOOL isPostSend)
+{
+	int result = NO_ERROR;
+
+	pSocketObj->sndBuff.Cat(pBuffer, iLength);
+	pSocketObj->pending += iLength;
+
+	if(isPostSend && !::PostIocpSend(m_hCompletePort, pSocketObj->connID))
+		result = ::GetLastError();
+
+	return result;
+}
+
+int CTcpServer::DoSend(CONNID dwConnID)
+{
+	TSocketObj* pSocketObj = FindSocketObj(dwConnID);
+
+	if(TSocketObj::IsValid(pSocketObj))
+		return DoSend(pSocketObj);
+
+	return ERROR_OBJECT_NOT_FOUND;
+}
+
+int CTcpServer::DoSend(TSocketObj* pSocketObj)
+{
+	switch(m_enSendPolicy)
+	{
+	case SP_PACK:			return DoSendPack(pSocketObj);
+	case SP_SAFE:			return DoSendSafe(pSocketObj);
+	default: ASSERT(FALSE);	return ERROR_INVALID_INDEX;
+	}
+}
+
+int CTcpServer::DoSendPack(TSocketObj* pSocketObj)
+{
+	int result = NO_ERROR;
+
+	if(TSocketObj::IsPending(pSocketObj))
+	{
+		CCriSecLock locallock(pSocketObj->csSend);
+
+		if(TSocketObj::IsValid(pSocketObj))
+			result = SendItem(pSocketObj);
+	}
+
+	if(!IOCP_SUCCESS(result))
+		CheckError(pSocketObj, SO_SEND, result);
+
+	return result;
+}
+
+int CTcpServer::DoSendSafe(TSocketObj* pSocketObj)
+{
+	int result = NO_ERROR;
+
+	if(TSocketObj::IsPending(pSocketObj) && TSocketObj::IsSmooth(pSocketObj))
+	{
+		CCriSecLock locallock(pSocketObj->csSend);
+
+		if(TSocketObj::IsPending(pSocketObj) && TSocketObj::IsSmooth(pSocketObj))
+		{
+			pSocketObj->smooth = FALSE;
+
+			result = SendItem(pSocketObj);
+
+			if(result == NO_ERROR)
+				pSocketObj->smooth = TRUE;
+		}
+	}
+
+	if(!IOCP_SUCCESS(result))
+		CheckError(pSocketObj, SO_SEND, result);
+
+	return result;
+}
+
+int CTcpServer::SendItem(TSocketObj* pSocketObj)
+{
+	int result = NO_ERROR;
+
+	while(pSocketObj->sndBuff.Size() > 0)
+	{
+		::InterlockedIncrement(&pSocketObj->sndCount);
+
+		TBufferObj* pBufferObj	= pSocketObj->sndBuff.PopFront();
+		int iBufferSize			= pBufferObj->buff.len;
+
+		ASSERT(iBufferSize > 0 && iBufferSize <= (int)m_dwSocketBufferSize);
+
+		pSocketObj->pending	   -= iBufferSize;
+
+		result = ::PostSendNotCheck(pSocketObj, pBufferObj);
+
+		if(result != NO_ERROR)
+		{
+			if(result != WSA_IO_PENDING)
+				AddFreeBufferObj(pBufferObj);
+
+			break;
+		}
+	}
+
+	return result;
+}
+
+BOOL CTcpServer::SendSmallFile(CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail)
+{
+	CAtlFile file;
+	CAtlFileMapping<> fmap;
+	WSABUF szBuf[3];
+
+	HRESULT hr = ::MakeSmallFilePackage(lpszFileName, file, fmap, szBuf, pHead, pTail);
+
+	if(FAILED(hr))
+	{
+		::SetLastError(HRESULT_CODE(hr));
+		return FALSE;
+	}
+
+	return SendPackets(dwConnID, szBuf, 3);
+}
+
+void CTcpServer::CheckError(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
+{
+	if(iErrorCode != WSAENOTSOCK && iErrorCode != ERROR_OPERATION_ABORTED)
+		AddFreeSocketObj(pSocketObj, SCF_ERROR, enOperation, iErrorCode);
+}

+ 298 - 0
HP-Socket/Src/TcpServer.h

@@ -0,0 +1,298 @@
+/*
+ * Copyright: JessMA Open Source (ldcsaa@gmail.com)
+ *
+ * Version	: 4.2.1
+ * Author	: Bruce Liang
+ * Website	: http://www.jessma.org
+ * Project	: https://github.com/ldcsaa
+ * Blog		: http://www.cnblogs.com/ldcsaa
+ * Wiki		: http://www.oschina.net/p/hp-socket
+ * QQ Group	: 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/Src/Event.h"
+#include "../../Common/Src/STLHelper.h"
+#include "../../Common/Src/RingBuffer.h"
+#include "../../Common/Src/PrivateHeap.h"
+
+class CTcpServer : public ITcpServer
+{
+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			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 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);}
+
+public:
+	virtual BOOL IsSecure				() {return FALSE;}
+
+	virtual BOOL SetConnectionExtra(CONNID dwConnID, PVOID pExtra);
+	virtual BOOL GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra);
+
+	virtual void SetSendPolicy				(EnSendPolicy enSendPolicy)		{m_enSendPolicy				= enSendPolicy;}
+	virtual void SetMaxConnectionCount		(DWORD dwMaxConnectionCount)	{m_dwMaxConnectionCount		= dwMaxConnectionCount;}
+	virtual void SetWorkerThreadCount		(DWORD dwWorkerThreadCount)		{m_dwWorkerThreadCount		= dwWorkerThreadCount;}
+	virtual void SetSocketListenQueue		(DWORD dwSocketListenQueue)		{m_dwSocketListenQueue		= dwSocketListenQueue;}
+	virtual void SetAcceptSocketCount		(DWORD dwAcceptSocketCount)		{m_dwAcceptSocketCount		= dwAcceptSocketCount;}
+	virtual void SetSocketBufferSize		(DWORD dwSocketBufferSize)		{m_dwSocketBufferSize		= dwSocketBufferSize;}
+	virtual void SetFreeSocketObjLockTime	(DWORD dwFreeSocketObjLockTime)	{m_dwFreeSocketObjLockTime	= dwFreeSocketObjLockTime;}
+	virtual void SetFreeSocketObjPool		(DWORD dwFreeSocketObjPool)		{m_dwFreeSocketObjPool		= dwFreeSocketObjPool;}
+	virtual void SetFreeBufferObjPool		(DWORD dwFreeBufferObjPool)		{m_dwFreeBufferObjPool		= dwFreeBufferObjPool;}
+	virtual void SetFreeSocketObjHold		(DWORD dwFreeSocketObjHold)		{m_dwFreeSocketObjHold		= dwFreeSocketObjHold;}
+	virtual void SetFreeBufferObjHold		(DWORD dwFreeBufferObjHold)		{m_dwFreeBufferObjHold		= dwFreeBufferObjHold;}
+	virtual void SetKeepAliveTime			(DWORD dwKeepAliveTime)			{m_dwKeepAliveTime			= dwKeepAliveTime;}
+	virtual void SetKeepAliveInterval		(DWORD dwKeepAliveInterval)		{m_dwKeepAliveInterval		= dwKeepAliveInterval;}
+	virtual void SetMarkSilence				(BOOL bMarkSilence)				{m_bMarkSilence				= bMarkSilence;}
+
+	virtual EnSendPolicy GetSendPolicy		()	{return m_enSendPolicy;}
+	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;}
+
+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 void OnWorkerThreadEnd(DWORD dwThreadID) {}
+
+	BOOL DoSendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
+	BOOL DoSendPackets(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
+	TSocketObj* FindSocketObj(CONNID dwConnID);
+
+private:
+	EnHandleResult TriggerFireAccept(TSocketObj* pSocketObj);
+	EnHandleResult TriggerFireReceive(TSocketObj* pSocketObj, TBufferObj* pBufferObj);
+	EnHandleResult TriggerFireSend(TSocketObj* pSocketObj, TBufferObj* pBufferObj);
+	EnHandleResult TriggerFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
+
+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 CreateCompletePort();
+	BOOL CreateWorkerThreads();
+	BOOL StartAccept();
+
+	void CloseListenSocket();
+	void WaitForAcceptSocketClose();
+	void DisconnectClientSocket();
+	void WaitForClientSocketClose();
+	void ReleaseClientSocket();
+	void ReleaseFreeSocket();
+	void ReleaseFreeBuffer();
+	void WaitForWorkerThreadEnd();
+	void CloseCompletePort();
+
+	TBufferObj*	GetFreeBufferObj(int iLen = -1);
+	TSocketObj*	GetFreeSocketObj(CONNID dwConnID, SOCKET soClient);
+	void		AddFreeBufferObj(TBufferObj* pBufferObj);
+	void		AddFreeSocketObj(TSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0);
+	TSocketObj*	CreateSocketObj();
+	void		DeleteSocketObj(TSocketObj* pSocketObj);
+	BOOL		InvalidSocketObj(TSocketObj* pSocketObj);
+	void		ReleaseGCSocketObj(BOOL bForce = FALSE);
+
+	void		AddClientSocketObj(CONNID dwConnID, TSocketObj* pSocketObj);
+	void		CloseClientSocketObj(TSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0, int iShutdownFlag = SD_SEND);
+
+private:
+	static UINT WINAPI WorkerThreadProc(LPVOID pv);
+
+	EnIocpAction CheckIocpCommand(OVERLAPPED* pOverlapped, DWORD dwBytes, ULONG_PTR ulCompKey);
+
+	void ForceDisconnect(CONNID dwConnID);
+	void HandleIo		(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj, DWORD dwBytes, DWORD dwErrorCode);
+	void HandleError	(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj, DWORD dwErrorCode);
+	void HandleAccept	(SOCKET soListen, TBufferObj* pBufferObj);
+	void HandleSend		(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj);
+	void HandleReceive	(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj);
+
+	int SendInternal(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
+	int SendPack	(TSocketObj* pSocketObj, const BYTE* pBuffer, int iLength);
+	int SendSafe	(TSocketObj* pSocketObj, const BYTE* pBuffer, int iLength);
+	int SendDirect	(TSocketObj* pSocketObj, const BYTE* pBuffer, int iLength);
+	int CatAndPost	(TSocketObj* pSocketObj, const BYTE* pBuffer, int iLength, BOOL isPostSend);
+
+	BOOL DoAccept	();
+	int DoReceive	(CONNID dwConnID, TSocketObj* pSocketObj, TBufferObj* pBufferObj);
+
+	int DoSend		(CONNID dwConnID);
+	int DoSend		(TSocketObj* pSocketObj);
+	int DoSendPack	(TSocketObj* pSocketObj);
+	int DoSendSafe	(TSocketObj* pSocketObj);
+	int SendItem	(TSocketObj* pSocketObj);
+
+	void CheckError	(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
+
+public:
+	CTcpServer(ITcpServerListener* pListener)
+	: m_pListener				(pListener)
+	, m_hCompletePort			(nullptr)
+	, m_soListen				(INVALID_SOCKET)
+	, m_iRemainAcceptSockets	(0)
+	, m_pfnAcceptEx				(nullptr)
+	, m_pfnGetAcceptExSockaddrs	(nullptr)
+	, m_pfnDisconnectEx			(nullptr)
+	, m_enLastError				(SE_OK)
+	, m_enState					(SS_STOPPED)
+	, m_enSendPolicy			(SP_PACK)
+	, m_dwMaxConnectionCount	(DEFAULT_MAX_CONNECTION_COUNT)
+	, m_dwWorkerThreadCount		(DEFAULT_WORKER_THREAD_COUNT)
+	, m_dwSocketListenQueue		(DEFAULT_TCP_SERVER_SOCKET_LISTEN_QUEUE)
+	, m_dwAcceptSocketCount		(DEFAULT_TCP_SERVER_ACCEPT_SOCKET_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)
+	{
+		ASSERT(m_wsSocket.IsValid());
+		ASSERT(m_pListener);
+	}
+
+	virtual ~CTcpServer()
+	{
+		Stop();
+	}
+
+private:
+	EnSendPolicy m_enSendPolicy;
+	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;
+
+private:
+	CInitSocket					m_wsSocket;
+	LPFN_ACCEPTEX				m_pfnAcceptEx;
+	LPFN_GETACCEPTEXSOCKADDRS	m_pfnGetAcceptExSockaddrs;
+	LPFN_DISCONNECTEX			m_pfnDisconnectEx;
+
+private:
+	ITcpServerListener*	m_pListener;
+	SOCKET				m_soListen;
+	HANDLE				m_hCompletePort;
+	EnServiceState		m_enState;
+	EnSocketError		m_enLastError;
+
+	vector<HANDLE>		m_vtWorkerThreads;
+
+	CPrivateHeap		m_phSocket;
+	CBufferObjPool		m_bfObjPool;
+
+	CSpinGuard			m_csState;
+
+	TSocketObjPtrPool	m_bfActiveSockets;
+
+	TSocketObjPtrList	m_lsFreeSocket;
+	TSocketObjPtrQueue	m_lsGCSocket;
+
+	volatile long		m_iRemainAcceptSockets;
+};