/ IOCPServer.h: interface for the CIOCPServer class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_IOCPSERVER_H__1EC64F02_5939_46BF_B121_DEDD88DB2D1B__INCLUDED_)
#define AFX_IOCPSERVER_H__1EC64F02_5939_46BF_B121_DEDD88DB2D1B__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define BUFSIZE 1024
#pragma comment(lib, "Ws2_32.lib")
#include <stdio.h>
#include <stdlib.h>
#include <Winsock2.h>
#include "buffer.h"
// a前面如果不使用#号,就会报Conversion from integral type to pointer type requires reinterpret_cast,
// C-style cast or function-style cast的错误
#define PRINTDEBUG(a) PrintError(#a,__FILE__,__LINE__,GetLastError())
__inline int PrintError(LPSTR linedesc, LPSTR filename, int lineno, DWORD errnum)
{
LPSTR lpBuffer;
char errbuf[256];
DWORD numread;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errnum,
LANG_NEUTRAL,
(LPTSTR)&lpBuffer,
0,
NULL );
wsprintf(errbuf, "/nThe following call failed at line %d in %s:/n/n"
" %s/n/nReason: %s/n", lineno, filename, linedesc, lpBuffer);
WriteFile(GetStdHandle(STD_ERROR_HANDLE), errbuf, strlen(errbuf), &numread, FALSE );
return errnum;
}
//定义操作类型
enum IOType
{
OP_IOInitialize,
OP_IORead,
OP_IOWrite,
OP_IOIdle
};
//定义completion key
typedef struct _CLIENTCONTEXT
{
SOCKET m_Socket; // 服务器建立的对应于客户端通信的socket
CHAR m_ip[32]; // 客户端的ip
UINT m_nPort; // 客户端的端口
//存储接/发送数据的缓冲区
/*
为什么要用到这两个缓冲区呢,因为有的时候发送数据的时候不能一次性全部发送完毕
这就需要使用一个类对缓冲区进行管理,比如delete掉前面发送的200k数据等等,
发送数据时写道writebuffer中,接收数据保存到readbuffer内,对读到数据的处理
也需要进行管理,所以使用了一个ReadBuffer
*/
CBuffer m_ReadBuffer;
CBuffer m_WriteBuffer;
//winsock取得数据的缓冲区
//wsaInBuffer这个结构体的buf属性,也就是char*就是m_byInBuffer!!
WSABUF m_wsaInBuffer;
CHAR m_byInBuffer[BUFSIZE];
//winsock发送数据的缓冲区,发送得数据就放在m_WriteBuffer
//WSASend函数需要这个参数
WSABUF m_wsaOutBuffer;
//为了防止写数据覆盖发生,设定此事件,差不多相当于互斥写吧
HANDLE m_hWriteComplete;
} CLIENTCONTEXT, *LPCLIENTCONTEXT;
typedef struct _OVERLAPPEDPLUS
{
WSAOVERLAPPED ol;
//操作类型
IOType opCode;
} OVERLAPPEDPLUS, *LPOVERLAPPEDPLUS;
class CIOCPServer
{
public:
CIOCPServer();
virtual ~CIOCPServer();
void Accept();
BOOL Initialize(DWORD conns, int port);
//绑定在端口上工作线程
static DWORD WINAPI CompletionWorkerThread( void * lpParam);
protected:
//处理消息
virtual void ProcessReceiveData(LPCLIENTCONTEXT lpContext, CBuffer &buffer) = 0;
//用户退出,或者连接中断,返回TRUE:已经处理过该对出事件,FALSE,没有处理过该退出事件
//子类函数必须写
virtual BOOL ClientExit(LPCLIENTCONTEXT lpContext) = 0 ;
//写数据
void Send(LPCLIENTCONTEXT lpContext, CString strData);
private:
//根据消息overlapped的类型,处理消息
BOOL ProcessIOMessage(IOType opCode, LPCLIENTCONTEXT lpContext , DWORD dwIoSize);
//通知客户连接时初始化成功
BOOL OnClientInitializing(LPCLIENTCONTEXT lpContext, DWORD dwIoSize);
//通知读客户数据成功
BOOL OnClientReading(LPCLIENTCONTEXT lpContext, DWORD dwIoSize);
//通知用户写数据成功
BOOL OnClientWriting(LPCLIENTCONTEXT lpContext, DWORD dwIoSize);
//通知用户退出或连接中断
void FreeClientContext(LPCLIENTCONTEXT lpContext);
//在端口上产生线程
void CreateWorkerThread();
//关闭完成端口
void CloseCompletionPort();
//分配连接overlappedplus
LPOVERLAPPEDPLUS AllocateOverlappedPlus(IOType ioType);
//分配连接进入的客户的相关信息
LPCLIENTCONTEXT AllocateContext();
//释放overlappedplus
void FreeOverlappedPlus(LPOVERLAPPEDPLUS lpOlp);
private:
//结束端口完成事件通过设置和查询来实现互斥效果
HANDLE m_hKillEvent;
//完成端口句柄
HANDLE m_hIocp;
//线程数
DWORD m_dwThreads;
//产生监听socket
SOCKET m_sListen;
};
#endif // !defined(AFX_IOCPSERVER_H__1EC64F02_5939_46BF_B121_DEDD88DB2D1B__INCLUDED_)
// IOCPServer.cpp: implementation of the CIOCPServer class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "IOCPServer.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CIOCPServer::CIOCPServer()
{
//socket初始化
WSADATA wsd;
WORD wVersionRequested = MAKEWORD(2, 2);
int nResult = WSAStartup(wVersionRequested, &wsd);
if ( nResult == SOCKET_ERROR )
{
WSACleanup();
PRINTDEBUG(WSAGetLastError());
}
//LOBYTE 就是 (byte)wsd.wVersion,取参数的后8位
//HIBYTE 把参数右移8位,转化为byte,取后8位
if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2)
{
WSACleanup();
}
m_hKillEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
printf("socket init successful... /n");
}
CIOCPServer::~CIOCPServer()
{
WSACleanup();
}
//分配连接overlappedplus
//创建一个类型为ioType的OVERLAPPED结构,内容为全0
LPOVERLAPPEDPLUS CIOCPServer::AllocateOverlappedPlus(IOType ioType)
{
OVERLAPPEDPLUS* pOlp = NULL;
pOlp = new OVERLAPPEDPLUS;
ZeroMemory(pOlp, sizeof(OVERLAPPEDPLUS));
pOlp->opCode = ioType;
return pOlp;
}
//分配连接进入的客户的相关信息
LPCLIENTCONTEXT CIOCPServer::AllocateContext()
{
LPCLIENTCONTEXT lpContext = NULL;
lpContext = new CLIENTCONTEXT;
ZeroMemory(lpContext, sizeof(CLIENTCONTEXT));
//自己指向自己,我倒!!
lpContext->m_wsaInBuffer.buf = lpContext->m_byInBuffer;
lpContext->m_wsaInBuffer.len = BUFSIZE; // 大小默认为1024
//创建了一个Event,每个lpclientcontext都有一个event!!!
lpContext->m_hWriteComplete = CreateEvent(NULL, FALSE, TRUE, NULL);
return lpContext;
}
//释放overlappedplus
void CIOCPServer::FreeOverlappedPlus(LPOVERLAPPEDPLUS lpOlp)
{
delete lpOlp;
}
//根据消息overlapped的类型,处理消息,返回值TRUE:继续读,FALSE,不读
//一般写事件就不让他都返回FALSE,没有必要再读了!
BOOL CIOCPServer::ProcessIOMessage(IOType opCode, LPCLIENTCONTEXT lpContext , DWORD dwIoSize)
{
BOOL bRet = FALSE;
//根据opCode确定操作
switch (opCode)
{
case OP_IOInitialize:
//打印显示出一句话:玩家自ip 。。端口号连入
bRet = OnClientInitializing(lpContext, dwIoSize);
break;
case OP_IORead:
//如果需要读端口,则读之
bRet = OnClientReading(lpContext, dwIoSize);
break;
case OP_IOWrite:
//如果需要写端口,则尝试写之
bRet = OnClientWriting(lpContext, dwIoSize);
break;
default:
printf("worker thread:unknown operation.../n");
}
return bRet;
}
//关闭完成端口
void CIOCPServer::CloseCompletionPort( )
{
//发送0字节表示关闭完成端口,completion packet的lp
PostQueuedCompletionStatus(m_hIocp, 0, (DWORD