www.pudn.com > IOCPNet_Demo.zip > IOCPNet.h


//////////////////////////////////////////////////////////////////////////////////////////////////// 
//	Module Name: 
//		IOCPNet.h 
//	Author: 
//		Chun-Hyok, Chong. 
//	Description: 
//		It was desined for high performance and large size data flow. 
//////////////////////////////////////////////////////////////////////////////////////////////////// 
 
#ifndef __IOCPNET_H__ 
#define __IOCPNET_H__ 
 
#include "workframe.h" 
#include "WorkframeReturnCode.h" 
#include "errlog.h" 
#include "preallocator.h" 
#include "SafeLocker.h" 
#include "SafeDynMem.h" 
#include "SafeStaticMemMulti.h" 
#include "LinkedListStatic.h" 
#include  
#include  
 
#define THREAD_NUM_FACTOR_PER_CPU							2 // experimental value, revise it on considering the specification of running system. 
 
#define IP_STR_LEN											(15 + 1) 
#define BUFFER_UNIT_SIZE									512 
#define BUFFER_TO_SLOT_FACTOR								4 
#define IO_READ_BUFFER_SIZE									BUFFER_UNIT_SIZE // for IO Read. 
#define NET_SIZE_INFO_LEN									4 
 
#ifdef CLIENT_SIDE 
#define MAX_ACCEPTABLE_SOCKET_NUM							20 
#else 
//#define MAX_ACCEPTABLE_SOCKET_NUM							65000 // Win2003/2K Server. 
#define MAX_ACCEPTABLE_SOCKET_NUM							30000 // WinXP. 
#endif 
 
#define DEFAULT_READQ_SLOT_NUM								50000 
#define DEFAULT_WRITEQ_BLOCK_NUM							50000 
#define DEFAULT_SOCKET_EVENT_NUM							50000 
#define BACK_LOG_SIZE										50000 
 
#define SOCKET_EVENT_TYPE_CLOSE								1 
#define SOCKET_EVENT_TYPE_READ								2 
 
#define STATUS_WRITE_FROM_Q									1 // It is not the result of IO, just internal command. 0x00000001 is in exceptional memory range. 
#define STATUS_BEFORE_ACCEPT								2 
#define STATUS_ACCEPT										3 
#define STATUS_WAITING_FOR_EVENT							4 
#define IO_TYPE_READ										5 
#define IO_TYPE_WRITE										6 
#define IO_TYPE_WRITE_LAST									7 
 
#define BUFFER_SIZE_TO_ACCEPT								((sizeof (sockaddr_in) + 16) * 2) 
 
#define PREPARE_SOCKET_INIT									1 
#define PREPARE_SOCKET_REASSIGN								2 
 
// The ways of operation of read Q and write Q are different. 
// So I implemented the two different Q class. 
 
class OIOCPNet; 
struct OBufferedSocket; 
struct OTemporaryWriteData; 
struct OVERLAPPEDExt; 
 
struct OReadSlotElement 
{ 
	DWORD bUsed; 
	OBufferedSocket *pBuffSock; 
	DWORD TotalSize; 
	DWORD ReadSize; 
	BYTE *pData; 
}; // OReadQSlotElement 
 
class OIOCPNetReadSlot 
{ 
private: 
	OErrLog *m_pEL; 
	OSafeDynMem m_DMemSlot; // Used(Allocate -> Free) just one time, when this class is created. 
	OReadSlotElement *m_pSlot; 
	DWORD m_SlotIndexToPut; 
	DWORD m_SlotNum; 
	OPreAllocator m_PAReadQ; 
	OSafeStaticMemMulti m_SMMReadQ; 
	CRITICAL_SECTION m_SlotLock; 
	OIOCPNet *m_pIOCPNet; 
 
private: 
	void Constructor(OErrLog *pEL, OIOCPNet *pIOCPNet); 
	void Destructor(); 
	int SetNextIndexToPut(); 
public: 
	OIOCPNetReadSlot(OErrLog *pEL, OIOCPNet *pIOCPNet) 
	{Constructor(pEL, pIOCPNet);} 
	~OIOCPNetReadSlot() 
	{Destructor();} 
	int SetSlots(DWORD SlotNumber); 
	int Put(int EventType, OBufferedSocket *pBuffSock, BYTE *pDataPartial, DWORD IOSize); 
	void ReleaseSlot(OReadSlotElement *pSlot); 
}; // OIOCPNetReadSlot 
 
struct OWriteBlockElement 
{ 
	BOOL bFilled; 
	DWORD PacketNumTotal; 
	DWORD PacketNumSerial; 
	OBufferedSocket *pBuffSock; 
	DWORD BlockDataSize; 
	BYTE BlockData[BUFFER_UNIT_SIZE]; 
}; // OWriteBlockElement 
 
class OIOCPNetWriteBlock 
{ 
private: 
	OErrLog *m_pEL; 
	OIOCPNet *m_pIOCPNet; 
	DWORD m_BlockNum; 
	CRITICAL_SECTION m_BlockLock; 
	OWriteBlockElement *m_pBlock; 
	OSafeDynMem m_DMemBlock; // Used(Allocate -> Free) just one time, when this class is created. 
	DWORD m_BlockIndexToPut; 
	DWORD m_BlockIndexToGet; 
 
private: 
	void Constructor(OErrLog *pEL, OIOCPNet *pIOCPNet); 
	void Destructor(); 
public: 
	OIOCPNetWriteBlock(OErrLog *pEL, OIOCPNet *pIOCPNet) 
	{Constructor(pEL, pIOCPNet);} 
	~OIOCPNetWriteBlock() 
	{Destructor();} 
	int SetBlock(DWORD BlockNumber); 
	int Put(OBufferedSocket *pBuffSock, BYTE *pDataTotal, DWORD TotalSize); 
	// Externally locked by m_BlockLock, because of sequential calling to this function. 
	// ppData will be freed by the external function. 
	int GetBlockNeedsExternalLock(OBufferedSocket **ppBuffSock, BYTE *pData, DWORD *pReadSize, BOOL *pDoesItHaveMoreSequence); 
	CRITICAL_SECTION *GetBlockLock(); 
}; // OIOCPNetWriteBlock 
 
struct OVERLAPPEDExt 
{ 
	OVERLAPPED OL; 
	int IOType; 
	OBufferedSocket *pBuffSock; 
	OTemporaryWriteData *pTempWriteData; 
}; // OVERLAPPEDExt 
 
struct OBufferedSocket 
{ 
	DWORD SocketUnique; 
	SOCKET Socket; 
	 
	BYTE BufferForAccept[BUFFER_SIZE_TO_ACCEPT]; 
	DWORD AcceptBytes; 
 
	OVERLAPPEDExt OLExt; 
 
	WSABUF BufferReadIO; 
	BYTE BufferReadIORealData[IO_READ_BUFFER_SIZE]; 
 
	OReadSlotElement *pFillingSlot; // used by IOCPReadQ, only. 
	 
	void *pCustomData; // This is the second advantage of using IOCP. After socket event -> no need for finding player data loop. The first thing is that no select loop needed. 
 
	int NetSizeInfoTempLen; 
	BYTE NetSizeInfoTemp[NET_SIZE_INFO_LEN]; 
}; // OBufferedSocket 
 
struct OSocketEvent 
{ 
	DWORD SocketUnique; 
	int EventType; 
	OBufferedSocket *pBuffSock; 
	OReadSlotElement *pSlot; 
	BYTE *pData; 
}; // OSocketEvent 
 
struct OThreadParam 
{ 
	OIOCPNet *pIOCPNet; 
	HANDLE hEvent; 
}; // OThreadParam 
 
struct OTemporaryWriteData 
{ 
	SOCKET Socket; 
	BYTE Data[BUFFER_UNIT_SIZE]; 
	WSABUF DataBuf; 
	OVERLAPPEDExt OLExt; 
}; // OTemporaryWriteData 
 
class OIOCPNet 
{ 
private: 
	OErrLog *m_pEL;	 
	 
	OIOCPNetReadSlot *m_pReadSlot; 
	OIOCPNetWriteBlock *m_pWriteBlock; 
	 
	DWORD m_SocketUniqueGen; // SOCKET or the memory content of pBuffSock are not reliable for checking the uniqueness. 
	HANDLE m_hCompletionPort; 
	DWORD m_WorkerThreadNum; 
	DWORD m_dRunning; 
	SOCKET m_ListeningSocket; 
	OPreAllocator m_PABuffSock; 
	OSafeStaticMemMulti m_SMMBuffSock; 
 
	CRITICAL_SECTION m_SocketEventLock; 
	OPreAllocator m_PASocketEvent; 
	OLinkedListStatic m_SocketEventList; 
	HANDLE m_hSocketEvent; 
 
	CRITICAL_SECTION m_BuffSockLock; // Create, CloseSocket, WriteData may be in different threads. 
 
	OSafeDynMem m_DMemForHandle; 
	OThreadParam *m_pThreadParam; 
	DWORD m_ThreadParamNum; 
 
	OPreAllocator m_PATempWriteData; 
	OSafeStaticMemMulti m_SMMTempWriteData; 
 
	DWORD m_ActiveConnectionNum; 
 
	LPFN_ACCEPTEX m_lpfnAcceptEx; 
 
private: 
	void Constructor(OErrLog *pEL); 
	void Destructor(); 
	OBufferedSocket *CreateBufferedSocket(int Mode, OBufferedSocket *pBuffSockOld); 
	BOOL AssociateSocketWithCompletionPort(SOCKET Socket, HANDLE hCompletionPort, DWORD dwCompletionKey); 
	int CloseSocket(DWORD SocketUnique, OBufferedSocket *pBuffSock, OReadSlotElement *pSlot); 
	int PrepareAcceptableSockets(char *AcceptIP, unsigned short AcceptPort); 
	int PrepareSocket(int Mode, OBufferedSocket *pBuffSock); 
	int SetInitialAcceptMode(OBufferedSocket *pBuffSock); 
	int SetAcceptMode(OBufferedSocket *pBuffSock); 
	int ChangeSocketModeAfterAccept(SOCKET Socket); 
public: 
	OIOCPNet(OErrLog *pEL) 
	{Constructor(pEL);} 
	~OIOCPNet() 
	{Destructor();} 
	int PutSocketEvent(int EventType, OBufferedSocket *pBuffSock, OReadSlotElement *pSlotEle, BYTE *pData); // It is called by ReadQ. 
	int PutSocketEvent(DWORD SocketUnique, int EventType, OBufferedSocket *pBuffSock, OReadSlotElement *pSlotEle, BYTE *pData); // by CloseSocketExternal. 
	int Start(char *AcceptIP, unsigned short AcceptPort); 
	void Stop(); 
	void WorkerThread(HANDLE hEvent); 
	// It might be called by a logic process thread. 
	// pSlotEle(and it's pData) must be released, when the event type is not close-socket. 
	// SocketUnique, pBuffSock and pSlot are needed when it releases data or closes the socket. 
	int GetSocketEventData(int WaitTimeMilli, int *pEventType, DWORD *pSocketUnique, BYTE **ppReadData, DWORD *pReadSize, OBufferedSocket **ppBuffSock, OReadSlotElement **ppSlot, void **ppCustData); // GetSocketEventData -> ReleaseSocketEvent! 
	void ReleaseSocketEvent(OReadSlotElement *pSlot); // GetSocketEventData -> ReleaseSocketEvent! 
	int WriteData(DWORD SocketUnique, OBufferedSocket *pBuffSock, BYTE *pDataTotal, DWORD TotalSize); 
	void *AttachCustomDataWithBufferedSocket(void *pData, OBufferedSocket *pBufferedSocket); 
	void *DetachCustomDataFromBufferedSocket(OBufferedSocket *pBufferedSocket); 
	void *GetCustomData(OBufferedSocket *pBufferedSocket); 
	SOCKET GetNomalSocketFromBufferedSocket(OBufferedSocket *pBufferedSocket); 
	void CloseSocketExternal(DWORD SocketUnique, OBufferedSocket *pBuffSock, OReadSlotElement *pSlot); // when you need to close a socket externally. 
}; // OIOCPNet 
 
DWORD WINAPI WorkerThreadDummy(LPVOID pParam); 
 
#endif // __IOCPNET_H__