#include "StdAfx.h"
#include "PipeService.h"
//#include "MainDlg.h"
#include "Protocol.h"

#define PIPENAME _T("\\\\.\\pipe\\OGCAssist")
// 每一个处理器上产生多少个线程(为了最大限度的提升服务器性能，详见配套文档)
#define WORKER_THREADS_PER_PROCESSOR 2
// 同时投递的Accept请求的数量(这个要根据实际的情况灵活设置)
#define MAX_POST_ACCEPT              2
// 传递给Worker线程的退出信号
#define EXIT_CODE                    NULL


// 释放指针和句柄资源的宏

// 释放指针宏
#define RELEASE(x)                      {if(x != NULL ){delete x;x=NULL;}}
// 释放句柄宏
#define RELEASE_HANDLE(x)               {if(x != NULL && x!=INVALID_HANDLE_VALUE){ CloseHandle(x);x = NULL;}}
// 释放Socket宏
#define RELEASE_SOCKET(x)               {if(x !=INVALID_SOCKET) { closesocket(x);x=INVALID_SOCKET;}}

OnConnectCallback CIOCPPipe::lpOnConnectCallback = NULL;
OnDisconnectCallback CIOCPPipe::lpOnDisconnectCallback = NULL;
OnReciveCallback CIOCPPipe::lpOnReciveCallback = NULL;

CIOCPPipe::CIOCPPipe(void)
{
    m_nThreads = 0;
    m_hShutdownEvent = NULL;
    m_hIOCompletionPort = NULL;
    m_phWorkerThreads= NULL;
    m_pMain = NULL;
}

CIOCPPipe::~CIOCPPipe(void)
{
	// 确保资源彻底释放
	this->Stop();
}

DWORD WINAPI CIOCPPipe::_WorkerThread(LPVOID lpParam)
{    
	THREADPARAMS_WORKER* pParam = (THREADPARAMS_WORKER*)lpParam;
	CIOCPPipe* pIOCPModel = (CIOCPPipe*)pParam->pIOCPModel;
	int nThreadNo = (int)pParam->nThreadNo;

	pIOCPModel->ShowMessage(_T("工作者线程启动，ID: %d."),nThreadNo);

	OVERLAPPED           *pOverlapped = NULL;
	ULONG_PTR            pCompletionKey  = NULL;
	DWORD                dwBytesTransfered = 0;
	PER_PIPE_CONTEXT	 *pPipeContext = NULL;

	// 循环处理请求，知道接收到Shutdown信息为止
	while (WAIT_OBJECT_0 != WaitForSingleObject(pIOCPModel->m_hShutdownEvent, 0))
	{
		BOOL bReturn = GetQueuedCompletionStatus(
			pIOCPModel->m_hIOCompletionPort,
			&dwBytesTransfered,
			(PULONG_PTR)&pPipeContext,
			&pOverlapped,
			INFINITE);

        dprintf(_T("IOCP有消息"));

		// 如果收到的是退出标志，则直接退出
		if ( EXIT_CODE==(DWORD)pPipeContext )
		{
			//break;
			dprintf(_T("IOCP: 退出消息"));
            continue;
		}

		// 判断是否出现了错误
		if( !bReturn )  
		{  
			DWORD dwErr = GetLastError();

			// 显示一下提示信息
			if( !pIOCPModel->HandleError( pPipeContext, dwErr ) )
			{
				//break;
                continue;
			}

			continue;  
		}  
		else  
		{  	
			// 读取传入的参数
			PER_IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_CONTEXT, m_Overlapped);  

			dprintf(_T("IOCP: 读取传入的参数=%ld"), pIoContext->m_OpType);
			// 判断是否有客户端断开了
			if((0 == dwBytesTransfered) && ( OP_RECV==pIoContext->m_OpType || OP_SEND==pIoContext->m_OpType))  
			{  
				//pIOCPModel->_ShowMessage( _T("客户端 %s:%d 断开连接."),pIoContext->szClientName, pIoContext->dwProcessId );
				if ( lpOnDisconnectCallback )
				{
					lpOnDisconnectCallback(pIoContext);
				}
				// 释放掉对应的资源
				pIOCPModel->RemoveContext( pPipeContext );

 				continue;  
			}  
			else
			{
				switch( pIoContext->m_OpType )  
				{  
				case OP_ACCEPT:
					{
						dprintf(_T("客户端连接成功"));
						pIOCPModel->DoAccpet( pPipeContext, pIoContext );
                        
						// 发送第一个请求:回调处理;
						if ( lpOnConnectCallback )
						{
							lpOnConnectCallback(pIoContext);
						}
					}
					break;
				case OP_RECV:
					{
						dprintf(_T("客户端消息到达"));
						pIOCPModel->DoRecv( pPipeContext, pIoContext );
					}
					break;
				case OP_SEND:
					{
						// 发送成功后,此处有消息到达;
						dprintf(_T("消息送达客户端"));
					}
					break;
				default:
					// 不应该执行到这里
					dprintf(_T("_WorkThread中的 pIoContext->m_OpType 参数异常.\n"));
					break;
				}
			}
		}
	}

	TRACE(_T("工作者线程 %d 号退出.\n"),nThreadNo);

	// 释放线程参数
	RELEASE(lpParam);	

	return 0;
}

bool CIOCPPipe::Start()
{
	// 初始化线程互斥量
	InitializeCriticalSection(&m_csContextList);

	// 建立系统退出的事件通知
	m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	// 初始化IOCP
	if (false == InitIOCP())
	{
		ShowMessage(_T("初始化IOCP失败！\n"));
		return false;
	}
	else
	{
		ShowMessage(_T("\nIOCP初始化完毕\n."));
	}


	ShowMessage(_T("系统准备就绪，等候连接....\n"));

	return true;
}

void CIOCPPipe::Stop()
{
    // 激活关闭消息通知
    SetEvent(m_hShutdownEvent);

    for (int i = 0; i < m_nThreads; i++)
    {
        // 通知所有的完成端口操作退出
        PostQueuedCompletionStatus(m_hIOCompletionPort, 0, (DWORD)EXIT_CODE, NULL);
    }

    // 等待所有的客户端资源退出
    WaitForMultipleObjects(m_nThreads, m_phWorkerThreads, TRUE, INFINITE);

    // 清除客户端列表信息
    ClearContextList();

    // 释放其他资源
    DeInitialize();

    ShowMessage(_T("停止监听\n"));
}

bool CIOCPPipe::InitIOCP()
{
	// 建立第一个完成端口
	m_hIOCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 );

	if ( NULL == m_hIOCompletionPort)
	{
		ShowMessage(_T("建立完成端口失败！错误代码: %d!\n"), WSAGetLastError());
		return false;
	}

	// 根据本机中的处理器数量，建立对应的线程数
#ifdef _DEBUG
    m_nThreads = 2;
#else
	m_nThreads = WORKER_THREADS_PER_PROCESSOR * _GetNoOfProcessors();
#endif

	// 为工作者线程初始化句柄
	m_phWorkerThreads = new HANDLE[m_nThreads];
	
	// 根据计算出来的数量建立工作者线程
	DWORD nThreadID;
	for (int i = 0; i < m_nThreads; i++)
	{
		THREADPARAMS_WORKER* pThreadParams = new THREADPARAMS_WORKER;
		pThreadParams->pIOCPModel = this;
		pThreadParams->nThreadNo  = i+1;
		m_phWorkerThreads[i] = ::CreateThread(0, 0, _WorkerThread, (void *)pThreadParams, 0, &nThreadID);
	}

	for ( int i = 0; i < MAX_POST_ACCEPT; i++ )
	{
		PostAccept();
	}
    
	TRACE(" 建立 _WorkerThread %d 个.\n", m_nThreads );

	return true;
}

void CIOCPPipe::DeInitialize()
{
	// 删除客户端列表的互斥量
	DeleteCriticalSection(&m_csContextList);

	// 关闭系统退出事件句柄
	RELEASE_HANDLE(m_hShutdownEvent);

	// 释放工作者线程句柄指针
	for( int i=0;i<m_nThreads;i++ )
	{
		RELEASE_HANDLE(m_phWorkerThreads[i]);
	}
	
	RELEASE(m_phWorkerThreads);

	// 关闭IOCP句柄
	RELEASE_HANDLE(m_hIOCompletionPort);

	ShowMessage(_T("释放资源完毕.\n"));
}

bool CIOCPPipe::PostAccept()
{
	PER_PIPE_CONTEXT* pPipeContext = new PER_PIPE_CONTEXT;
    pPipeContext->m_hPipe = CreateNamedPipe(
        PIPENAME,                // pipe name 
        PIPE_ACCESS_DUPLEX |     // read/write access 
        FILE_FLAG_OVERLAPPED,    // overlapped mode 
        PIPE_TYPE_MESSAGE |      // message-type pipe 
        PIPE_READMODE_MESSAGE |  // message-read mode 
        PIPE_WAIT,               // blocking mode 
        PIPE_UNLIMITED_INSTANCES,               // number of instances 
        0,   // output buffer size 
        0,   // input buffer size 
        NMPWAIT_WAIT_FOREVER,     // client time-out 
        NULL);                   // default security attributes

    if ( pPipeContext->m_hPipe == INVALID_HANDLE_VALUE )
	{
		delete pPipeContext;
		return false;
	}

	// 绑定到完成端口中;
	HANDLE hIOCP = CreateIoCompletionPort((HANDLE)pPipeContext->m_hPipe, m_hIOCompletionPort, (DWORD)pPipeContext, 0);
    if ( hIOCP == NULL )
    {
		delete pPipeContext;
        return false;
    }

	// 异步等待客户端连接;
    PER_IO_CONTEXT *pNewIoContext = pPipeContext->GetNewIoContext();
    memset(pNewIoContext, 0, sizeof(PER_IO_CONTEXT));
    pNewIoContext->m_OpType = OP_ACCEPT;
    pNewIoContext->m_PipeAccept = pPipeContext->m_hPipe;
    BOOL bConnect = ConnectNamedPipe(pPipeContext->m_hPipe, (LPOVERLAPPED)pNewIoContext);

	if( bConnect )  
	{// 重叠IO或完成端口,返回的是Fase;
        CloseHandle(hIOCP);
		//pPipeContext->RemoveContext(pNewIoContext);
		ShowMessage(_T("创建用于Accept的Pipe失败！错误代码: %d"), GetLastError()); 
		dprintf(_T("创建用于Accept的Pipe失败！错误代码: %d"), GetLastError()); 
		delete pPipeContext;

		return false;  
	} 

#if 0 // 线程中处理？;
	BOOL fPendingIO = FALSE;
	switch(GetLastError())
	{
		// The overlapped connection in progress.
	case ERROR_IO_PENDING:
		fPendingIO = TRUE;
		break;
		// Client is already connected, so signal an event
	case ERROR_PIPE_CONNECTED:
		break;
		// If an error occurs during the connect operation...
	default:
		break;
	}

	return fPendingIO;
#else

	dprintf(_T("Post Accept <%lp> 成功！"), &pNewIoContext->m_Overlapped); 
	AddToContextList(pPipeContext);

	return true;
#endif
}

bool CIOCPPipe::DoAccpet( PER_PIPE_CONTEXT *pPipeContext, PER_IO_CONTEXT* pIoContext )
{
	if( !PostRecv(pIoContext) )
	{
		return false;
	}

	//dprintf(_T("DoAccpet:%p"), &pIoContext->m_Overlapped);
	//pIoContext->ResetBuffer();//此处ResetBuffer导致接收时数据空;
	return PostAccept(); 	
}

bool CIOCPPipe::PostRecv( PER_IO_CONTEXT* pIoContext )
{
	// 初始化变量
	DWORD dwFlags = 0;
	DWORD dwBytes = 0;
	//WSABUF *p_wbuf   = &pIoContext->chRequest;
	OVERLAPPED *p_ol = &pIoContext->m_Overlapped;

	dprintf(_T("PostRecv:%p"), &pIoContext->m_Overlapped);
	pIoContext->ResetBuffer();
	pIoContext->m_OpType = OP_RECV;

	dprintf(_T("等待客户端消息"));
	// 初始化完成后，投递WSARecv请求
	int nBytesRecv = ReadFile( pIoContext->m_PipeAccept, pIoContext->szBuffer, BUFSIZE, &dwBytes, p_ol );

	// 如果返回值错误，并且错误的代码并非是Pending的话，那就说明这个重叠请求失败了
	if ((-1 == nBytesRecv) && (ERROR_IO_PENDING != GetLastError()))
	{
		ShowMessage(_T("投递第一个WSARecv失败！"));
		dprintf(_T("投递第一个WSARecv失败"));
		return false;
	}

	return true;
}

bool CIOCPPipe::DoRecv( PER_PIPE_CONTEXT *pPipeContext, PER_IO_CONTEXT* pIoContext )
{
	RecvProcess(pPipeContext, pIoContext);

	// 然后开始投递下一个WSARecv请求
	return PostRecv( pIoContext );
}

bool CIOCPPipe::AssociateWithIOCP( PER_IO_CONTEXT* pIoContext )
{
	// 将用于和客户端通信的SOCKET绑定到完成端口中
	
	return true;
}

void CIOCPPipe::AddToContextList( PER_PIPE_CONTEXT* pPipeContext )
{
	EnterCriticalSection(&m_csContextList);

	m_arrayClientContext.Add(pPipeContext);	
	
	LeaveCriticalSection(&m_csContextList);
}

void CIOCPPipe::RemoveContext( PER_PIPE_CONTEXT* pPipeContext )
{
	EnterCriticalSection(&m_csContextList);

	for( int i=0; i<m_arrayClientContext.GetCount(); i++ )
	{
		if( pPipeContext == m_arrayClientContext.GetAt(i) )
		{
			RELEASE( pPipeContext );			
			m_arrayClientContext.RemoveAt(i);			
			break;
		}
	}

	LeaveCriticalSection(&m_csContextList);
}

void CIOCPPipe::ClearContextList()
{
	EnterCriticalSection(&m_csContextList);

	for( int i=0;i<m_arrayClientContext.GetCount();i++ )
	{
		delete m_arrayClientContext.GetAt(i);
	}

	m_arrayClientContext.RemoveAll();

	LeaveCriticalSection(&m_csContextList);
}

int CIOCPPipe::GetNoOfProcessors()
{
	SYSTEM_INFO si;

	GetSystemInfo(&si);

	return si.dwNumberOfProcessors;
}

void CIOCPPipe::ShowMessage(const CString szFormat,...) const
{
	// 根据传入的参数格式化字符串
	CString   strMessage;
	va_list   arglist;

	// 处理变长参数
	va_start(arglist, szFormat);
	strMessage.FormatV(szFormat,arglist);
	va_end(arglist);
}

void CIOCPPipe::RecvProcess(PER_PIPE_CONTEXT* pPipeContext, PER_IO_CONTEXT* pIoContext)
{
	if ( pIoContext )
	{
		// 数据长度;
		pIoContext->m_Overlapped.InternalHigh;
		
		/* 
			PACKAGE pak;
			memcpy整个chRequest到其pak后, pIoContext空;原因：是因为pak结构大小只有DATAHEADER+buf，buf并不能代替MSG_INFO。
			所以将chRequest整个内存复制到pak时,越界了。

			memcpy(&pak, pIoContext->chRequest, pIoContext->m_Overlapped.InternalHigh);
		*/
		DATAHEADER header;
		MSG_INFO msg_info;
		memcpy(&header, pIoContext->szBuffer, sizeof(DATAHEADER));
		memcpy(&msg_info, pIoContext->szBuffer+sizeof(DATAHEADER), sizeof(MSG_INFO));

		dprintf(_T("接收到客户端消息：Result=%d, Tpye=%d, Proctocl=%d, Len=%ld, Name=%s, Id=%ld, Data=%s"), 
			msg_info.byResult,
			header.byMsgType, 
			header.byProtocol, 
			header.dwPackageLen, 
			msg_info.szClientName,
			msg_info.dwClientId,
			msg_info.byData
			);

		DWORD lpNumberOfBytesWritten = 0;
		TCHAR szMsg[MAX_PATH] = {0};
		_stprintf(szMsg, _T("%ld：%ld"), GetCurrentThreadId(), GetTickCount());
#if 0
		// WriteFile时,不需要异步处理,所以重叠IO参数NUULL;
		BOOL bRet = WriteFile(pIoContext->m_PipeAccept, pIoContext->szBuffer, pIoContext->m_Overlapped.InternalHigh, &lpNumberOfBytesWritten, NULL);
		if ( !bRet )
		{
			dprintf(_T("发送消息失败：%s, %ld, %ld"), pIoContext->szBuffer, GetLastError(), GetCurrentThreadId());
		}
#else
		if ( pIoContext->pWriteIoContext == NULL )
		{
			pIoContext->pWriteIoContext = new PER_IO_CONTEXT();
			pIoContext->pWriteIoContext->m_OpType = OP_SEND;
			pIoContext->pWriteIoContext->m_PipeAccept = pIoContext->m_PipeAccept;
		}
		memcpy(pIoContext->pWriteIoContext->szBuffer, pIoContext->szBuffer, pIoContext->m_Overlapped.InternalHigh);
		BOOL bRet = WriteFile(pIoContext->m_PipeAccept, pIoContext->pWriteIoContext->szBuffer, pIoContext->m_Overlapped.InternalHigh, &lpNumberOfBytesWritten, &pIoContext->pWriteIoContext->m_Overlapped);
		if ( !bRet )
		{
			dprintf(_T("发送消息失败：%s, %ld, %ld"), pIoContext->szBuffer, GetLastError(), GetCurrentThreadId());
		}
#endif
	}
}

/////////////////////////////////////////////////////////////////////
// 判断客户端Socket是否已经断开，否则在一个无效的Socket上投递WSARecv操作会出现异常
// 使用的方法是尝试向这个socket发送数据，判断这个socket调用的返回值
// 因为如果客户端网络异常断开(例如客户端崩溃或者拔掉网线等)的时候，服务器端是无法收到客户端断开的通知的

bool CIOCPPipe::HandleError( PER_PIPE_CONTEXT *pPipeContext, const DWORD& dwErr )
{
	// 如果是超时了，就再继续等吧  
	if( WAIT_TIMEOUT == dwErr )  
	{  	
		dprintf(_T("HandleError: 超时"));
		return true;
	}
	// 可能是客户端异常退出了
	else if( ERROR_BROKEN_PIPE == dwErr )
	{
		RemoveContext(pPipeContext);
		//ShowMessage( _T("检测到客户端异常退出！") );
		dprintf(_T("检测到客户端异常退出\n\n"));
		return true;
	}

	dprintf(_T("完成端口操作出现错误，线程退出。错误代码：%d"),dwErr);
	ShowMessage( _T("完成端口操作出现错误，线程退出。错误代码：%d"),dwErr );

	return false;
}




