//---------------------------------------------------------------------------

#ifndef IOCP_H
#define IOCP_H
//---------------------------------------------------------------------------
#include <winsock2.h>
#include <windows.h>
#include <process.h>

#pragma comment(lib,"ws2_32.lib")

#define BUFFER_SIZE 1024

typedef struct _PER_HANDLE_DATA		// per-handle数据
{
	SOCKET s;			// 对应的套节字句柄
	sockaddr_in addr;	// 客户方地址
} PER_HANDLE_DATA, *PPER_HANDLE_DATA;


typedef struct _PER_IO_DATA			// per-I/O数据
{
	OVERLAPPED ol;			// 重叠结构
	char buf[BUFFER_SIZE];	// 数据缓冲区
	int nOperationType;		// 操作类型
        _PER_IO_DATA *perSend; 
#define OP_READ   1
#define OP_WRITE  2
#define OP_ACCEPT 3
} PER_IO_DATA, *PPER_IO_DATA;



class InterLock
{
CRITICAL_SECTION cs;
public:
  InterLock()
  {
    InitializeCriticalSectionAndSpinCount(&cs,1024);
  }
  ~InterLock()
  {
    DeleteCriticalSection(&cs);
  }
  void Enter()
  {
    EnterCriticalSection(&cs);
  }
  void Leave()
  {
    LeaveCriticalSection(&cs);
  }
};
class LockManager
{
InterLock *pLock;
public:
   LockManager(InterLock* lock):pLock(lock)
   {
     if(pLock)
     pLock->Enter();
   }
   ~LockManager()
   {
     if(pLock)
     pLock->Leave();
   }
};
struct THREAD_DATA
{
  InterLock lock;
  SOCKET sListen;
  bool bExit;
};
class InitSock
{
  WSADATA wsaData;
public:
  InitSock()
  {
    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
    exit(1);
  }
  ~InitSock()
  {
    WSACleanup();
  }
};

UINT WINAPI BeginThread(LPVOID param);

bool PostRecv(PPER_IO_DATA pPerIO,SOCKET s);
bool PostSend(PPER_IO_DATA pPerIO,SOCKET s,const char* buffer,int bufferlen);
#endif
//---------------------------------------------------------------------------


#pragma hdrstop

#include "IOCP.h"
#include <list>
using std::list;
#include <cassert>
//---------------------------------------------------------------------------

#pragma package(smart_init)




//-------------
// 初始化Winsock库
InitSock theSock;

class PerDataManager
{
   static list<PPER_HANDLE_DATA> handlelist;
   static list<PPER_IO_DATA>    iolist;
public:
  static InterLock lock;
  
  static void Add(const PPER_HANDLE_DATA &pd)
  {
     LockManager manager(&lock);
     handlelist.push_back(pd);
  }
  static void Delete(const PPER_HANDLE_DATA &pd)
  {
    LockManager manager(&lock);
    list<PPER_HANDLE_DATA>::iterator it=find(handlelist.begin(),handlelist.end(),pd);
    if(it!=handlelist.end())
    {
        ::closesocket((*it)->s);
        ::GlobalFree((*it));
        handlelist.erase(it);
    }
  }
  static void Clear()
  {
    for(list<PPER_HANDLE_DATA>::iterator it=handlelist.begin();it!=handlelist.end();++it)
    {
        ::closesocket((*it)->s);
        ::GlobalFree((*it));
    }
    handlelist.clear();

    for(list<PPER_IO_DATA>::iterator it=iolist.begin();it!=iolist.end();++it)
    {
        ::GlobalFree((*it));
    }
    iolist.clear();
  }
  static void Add(const PPER_IO_DATA &pd)
  {
     LockManager manager(&lock);
     iolist.push_back(pd);
  }
  static void Delete(const PPER_IO_DATA &pd)
  {
    LockManager manager(&lock);
    list<PPER_IO_DATA>::iterator it=find(iolist.begin(),iolist.end(),pd);
    if(it!=iolist.end())
    {
        ::GlobalFree((*it));
        iolist.erase(it);
    }
  }
};
list<PPER_HANDLE_DATA> PerDataManager::handlelist;
list<PPER_IO_DATA> PerDataManager::iolist;
InterLock PerDataManager::lock;
PerDataManager pdatamanager;

bool PostRecv(PPER_IO_DATA pPerIO,SOCKET s)
{
    WSABUF buf;
    buf.buf = pPerIO->buf ;
    buf.len = BUFFER_SIZE;
    pPerIO->nOperationType = OP_READ;

    DWORD nFlags = 0;
    DWORD dwTrans = 0;
    int ret=::WSARecv(s, &buf, 1, &dwTrans, &nFlags, &pPerIO->ol, NULL);
    if((ret==SOCKET_ERROR)&&(WSAGetLastError()!=WSA_IO_PENDING))
    return false;
    return true;
}
bool PostSend(PPER_IO_DATA pPerIO,SOCKET s,const char* buffer,int bufferlen)
{
    memset(pPerIO->buf,0,BUFFER_SIZE);
    if(bufferlen>BUFFER_SIZE)
    bufferlen=BUFFER_SIZE;
    
    memcpy(pPerIO->buf,buffer,bufferlen);
    
    pPerIO->nOperationType = OP_WRITE;
    WSABUF buf_w;
    buf_w.buf = pPerIO->buf;
    buf_w.len = bufferlen;	
    DWORD dwRecv_w;
    DWORD dwFlags_w = 0;
    int ret=::WSASend(s, &buf_w, 1, &dwRecv_w, dwFlags_w, &pPerIO->ol, NULL);
    if((ret==SOCKET_ERROR)&&(WSAGetLastError()!=WSA_IO_PENDING))
    return false;
    return true;
}

UINT WINAPI ServerThread(LPVOID lpParam)
{
	// 得到完成端口对象句柄
	HANDLE hCompletion = (HANDLE)lpParam;

        InitCOM initcom;
	DWORD dwTrans;
	PPER_HANDLE_DATA pPerHandle;
	PPER_IO_DATA pPerIO;
	while(TRUE)
	{
		// 在关联到此完成端口的所有套节字上等待I/O完成
		BOOL bOK = ::GetQueuedCompletionStatus(hCompletion, 
			&dwTrans, (LPDWORD)&pPerHandle, (LPOVERLAPPED*)&pPerIO, WSA_INFINITE);

                if(!pPerHandle)//结束
                break;

		if(!bOK)						// 在此套节字上有错误发生
		{
                        pdatamanager.Delete(pPerHandle);
                        pdatamanager.Delete(pPerIO);
			continue;
		}
		
		if(dwTrans == 0 &&				// 套节字被对方关闭
			(pPerIO->nOperationType == OP_READ || pPerIO->nOperationType == OP_WRITE))	
			
		{
                        pdatamanager.Delete(pPerHandle);
                        pdatamanager.Delete(pPerIO);
			continue;
		}

		switch(pPerIO->nOperationType)	// 通过per-I/O数据中的nOperationType域查看什么I/O请求完成了
		{
		case OP_READ:	// 完成一个接收请求
			{
				pPerIO->buf[dwTrans] = '\0';

                                
                                memset(pPerIO->buf,0,BUFFER_SIZE);
                                PostRecv(pPerIO,pPerHandle->s);
			}
			break;
		case OP_WRITE: // 应该等待通知再继续Post(因为使用同一块缓存区),我这里没有作处理,因为我的数据量很少(不超过128字节)。
                        {
                           memset(pPerIO->buf,0,BUFFER_SIZE);
                        }
                        break;
		case OP_ACCEPT:
			break;
		}
	}
	return 0;
}

UINT WINAPI BeginThread(LPVOID param)
{
        THREAD_DATA *pThreaddata=((THREAD_DATA*)param);
	SOCKET sListen = pThreaddata->sListen;

	// 创建完成端口对象,创建工作线程处理完成端口对象中事件
	HANDLE hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
        assert(hCompletion);
	HANDLE hWorker=(HANDLE)_beginthreadex(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0);
        assert(hWorker);
	// 循环处理到来的连接

	while(TRUE)
	{
                {
                   LockManager manager(&(pThreaddata->lock));
                   if(pThreaddata->bExit)
                   break;
                }
		// 等待接受未决的连接请求
		SOCKADDR_IN saRemote;
		int nRemoteLen = sizeof(saRemote);
		SOCKET sNew = ::accept(sListen, (sockaddr*)&saRemote, &nRemoteLen);
                

		// 接受到新连接之后,为它创建一个per-handle数据,并将它们关联到完成端口对象。
		PPER_HANDLE_DATA pPerHandle =(PPER_HANDLE_DATA)::GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
                assert(pPerHandle);
		pPerHandle->s = sNew;
		memcpy(&pPerHandle->addr, &saRemote, nRemoteLen);

		::CreateIoCompletionPort((HANDLE)pPerHandle->s, hCompletion, (DWORD)pPerHandle, 0);

                pdatamanager.Add(pPerHandle);
                
		// 投递一个接收请求
		PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
                assert(pPerIO);
                
		pPerIO->nOperationType = OP_READ;
		WSABUF buf;
		buf.buf = pPerIO->buf;
		buf.len = BUFFER_SIZE;	
		DWORD dwRecv;
		DWORD dwFlags = 0;
		::WSARecv(pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL);

                pdatamanager.Add(pPerIO);

		pPerIO->perSend = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
		assert(pPerIO->perSend);
                pdatamanager.Add(pPerIO->perSend);
	}
        PostQueuedCompletionStatus(hCompletion, 0, 0, NULL);
        WaitForSingleObject(hWorker,INFINITE);
        CloseHandle(hWorker);
        CloseHandle(hCompletion);
        
        pdatamanager.Clear();
        return 0;
}



HANDLE hBegin=NULL;
SOCKET sListen=INVALID_SOCKET;
THREAD_DATA tdata;

bool Listen(int nPort)
{
  if(sListen==INVALID_SOCKET)
  sListen=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if(sListen==INVALID_SOCKET)
  return false;
  sockaddr_in addr;
  addr.sin_family=AF_INET;
  addr.sin_addr.S_un.S_addr=INADDR_ANY;
  addr.sin_port=::htons(nPort);

  if(::bind(sListen,(PSOCKADDR)&addr,sizeof(addr))==SOCKET_ERROR)
  return false;

  return ::listen(sListen,5)!=SOCKET_ERROR;
}
void Close()
{
  ::closesocket(sListen);
  sListen=INVALID_SOCKET;
}