导航

支持富客户端的MFC聊天室服务器端程序

Posted on 2011-02-23 14:02  田园小蛙  阅读(1190)  评论(0)    收藏  举报

  聊天室服务器端程的主要功能是管理在线用户、转发用户聊天消息。使用三种通讯消息结构:聊天内容消息结构、用户事件消息结构(用户登陆、验证用户登陆、请求用户列表、用户上线、用户离线)、用户信息列表消息结构。程序的主要逻辑由三个线程完成:监听线程、工作线程、银光策略文件监听线程。

  这是一个简单的聊天服务器端程序,目的在于介绍c++与富客户端之间通讯。程序在VS2010下开发完成,32位与64位环境均编译通过。采用“socket”与客户端进行字节流消息通讯,使用了“完成端口”。可以同时支持“silverlight4”和“flex4”客户端一起连接参与聊天。解决方案:

  

一、主逻辑类“CServer”

CServer类代码如下:                                                  

Server.h
/********************************************************************
file base: Server
file ext: h
author: 田园小蛙 (ourtree@live.cn)

purpose: 聊天服务器端逻辑
********************************************************************
*/
#pragma once
#include
"ConnectItem.h"
#include
"PubDefine.h"
#include
"NetMsg.h"
#include
"NetMsgChatInfo.h"
#include
"NetMsgRoleEvent.h"
#include
"NetMsgRoleInfoList.h"
#include
<map>
#include
<fstream>
#include
<string>
using namespace std;


typedef map
<UINT, CConnectItem*> MAP_CONNECTITEM;//!<连接元素队列
typedef map<string, UINT> MAP_ROLELIST;//!<用户队列

class CServer
{
public:
CServer(
void);
virtual ~CServer(void);

private:
bool Init();
bool InitListen();
bool InitWork();
bool Init943();

public:
/// \brief 运行监听
bool RunListen();

/// \brief 运行工作,处理完成的接收和发送
bool RunWork();

/// \brief 运行943监听
bool Run943();

/// \brief 开始服务
void Start(string strIP, int nPort);

/// \brief 停止服务
void ShutDown();

private:
/// \brief 向所有连接对象广播消息,除了参数内的连接对象
inline bool BoardCaseMsg(CConnectItem* pConItem, CNetMsg* pnetMsg);

/// \brief 向所有连接对象广播消息
inline bool BoardCaseMsg(CNetMsg* pnetMsg);

/// \brief 得空闲的连接对象
inline CConnectItem* GetConnectItemFree();

/// \brief 得空闲的943连接对象
inline CConnectItem* GetConnectItem943Free();

/// \brief 广播用户上下线事件
inline void RoleInOut(CConnectItem* pItem, int nType);
private:
HANDLE m_hCompPort;
//!< 完成端口
HANDLE m_hWrokThread;//!< 工作线程
HANDLE m_hListenThread;//!< 监听线程
HANDLE m_h943Thread;//!< 943监听线程

SOCKET m_ListenSocket;
//!< 监听socket
SOCKET m_ListenSocket943;//!< 943监听socket

string m_strIP;//!< 服务器IP
int m_nPort;//!< 服务器端口

MAP_CONNECTITEM
* m_pMapConnectItem;//!<工作用连接元素池
MAP_CONNECTITEM* m_pMapConnectItem943;//!<银光策略文件用连接元素池

MAP_ROLELIST m_MapRoleList;
//!< 登陆用户列表

UINT m_unNameIndex;
//!< 重名用户重命名索引
};

  

server.cpp
#include "StdAfx.h"
#include
"Server.h"

volatile LONG g_fShutdown;//关闭线程标志

unsigned
int WINAPI ListenThread(PVOID pvParam)
{
CServer
* pServer = (CServer*)pvParam;
while (!g_fShutdown)
{
pServer
->RunListen();
}
return(0);
}

unsigned
int WINAPI WorkThread(PVOID pvParam)
{
CServer
* pServer = (CServer*)pvParam;
while (!g_fShutdown)
{
pServer
->RunWork();
}
return(0);
}

unsigned
int WINAPI SL943Thread(PVOID pvParam)
{
CServer
* pServer = (CServer*)pvParam;
while (!g_fShutdown)
{
pServer
->Run943();
}
return(0);
}

CServer::CServer(
void) : m_hCompPort(NULL)
, m_hWrokThread(NULL)
, m_hListenThread(NULL)
, m_h943Thread(NULL)
, m_ListenSocket(NULL)
, m_pMapConnectItem(NULL)
, m_pMapConnectItem943(NULL)
, m_unNameIndex(
0)
{
m_pMapConnectItem
= new MAP_CONNECTITEM;

m_pMapConnectItem943
= new MAP_CONNECTITEM;

Init();
}


CServer::
~CServer(void)
{
if (m_hCompPort)
CloseHandle(m_hCompPort);

for (MAP_CONNECTITEM::iterator iter = m_pMapConnectItem->begin(); iter != m_pMapConnectItem->end(); iter++)
{
CConnectItem
* pItem = iter->second;
delete pItem;
}
m_pMapConnectItem
->clear();
delete m_pMapConnectItem;

for (MAP_CONNECTITEM::iterator iter = m_pMapConnectItem943->begin(); iter != m_pMapConnectItem943->end(); iter++)
{
CConnectItem
* pItem = iter->second;
delete pItem;
}
m_pMapConnectItem943
->clear();
delete m_pMapConnectItem943;

}

bool CServer::Init()
{
g_fShutdown
= 0;

//创建完成端口
if (m_hCompPort != NULL)
return false;

m_hCompPort
= CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (m_hCompPort == NULL)
return false;

return true;
}

bool CServer::InitListen()
{
WSADATA wsaData;
DWORD dwRet
= WSAStartup(0x0202, &wsaData);
if (dwRet != 0)
return false;

m_ListenSocket
= WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN ServerAddr;
ServerAddr.sin_family
= AF_INET;
ServerAddr.sin_addr.S_un.S_addr
= inet_addr(m_strIP.c_str());
ServerAddr.sin_port
= htons(m_nPort);
bind(m_ListenSocket, (LPSOCKADDR)
&ServerAddr, sizeof(ServerAddr));
listen(m_ListenSocket,
100);

m_hListenThread
= (HANDLE)_beginthreadex(NULL, NULL, &ListenThread, (PVOID)this, NULL, NULL);
return true;
}

bool CServer::RunListen()
{
SOCKADDR_IN ClientAddr;
int nAddrLength=sizeof(ClientAddr);
CConnectItem
* pconItem = NULL;
SOCKET acceptSocket
= INVALID_SOCKET;
acceptSocket
= WSAAccept(m_ListenSocket, (SOCKADDR *)&ClientAddr, &nAddrLength, NULL, NULL);
if (acceptSocket != INVALID_SOCKET)
{
pconItem
= GetConnectItemFree();
pconItem
->m_Socket = acceptSocket;
pconItem
->m_SocketAddr = ClientAddr;

//接收到连接,相关参数初始化,将连接加到队列中,并向队列中其它成员广播
if (CreateIoCompletionPort((HANDLE)pconItem->m_Socket, m_hCompPort, (ULONG_PTR)pconItem, 0) == NULL)
{
return false;
}

pconItem
->RecvMsg();
}

return true;
}

inline
bool CServer::BoardCaseMsg(CConnectItem* pConItem, CNetMsg* pnetMsg)
{
for (MAP_CONNECTITEM::iterator iter = m_pMapConnectItem->begin(); iter != m_pMapConnectItem->end(); iter++)
{
CConnectItem
* pItem = iter->second;
if (pItem != pConItem && pItem->m_bInUse)
{
pItem
->SendMsg((char*)pnetMsg->GetBuf(), pnetMsg->GetSize());
}
}
return true;
}

inline
bool CServer::BoardCaseMsg(CNetMsg* pnetMsg)
{
for (MAP_CONNECTITEM::iterator iter = m_pMapConnectItem->begin(); iter != m_pMapConnectItem->end(); iter++)
{
CConnectItem
* pItem = iter->second;
if (pItem->m_bInUse)
{
pItem
->SendMsg((char*)pnetMsg->GetBuf(), pnetMsg->GetSize());
}
}
return true;
}

bool CServer::InitWork()
{
m_hWrokThread
= (HANDLE)_beginthreadex(NULL, NULL, &WorkThread, (PVOID)this, NULL, NULL);//CREATE_SUSPENDED
return true;
}

bool CServer::RunWork()
{
DWORD BytesTransferred
= 0;
CConnectItem
* pConnectItem = NULL;
COverLapped
* pOverLapped = NULL;
//线程进入线程池,等待被唤醒LPDWORD
if (GetQueuedCompletionStatus(m_hCompPort, &BytesTransferred, (PULONG_PTR)&pConnectItem, (OVERLAPPED**)&pOverLapped, INFINITE))
{
if (pConnectItem == NULL)
return false;

if (pConnectItem->m_is943)
{
switch(pOverLapped->GetIoType())
{
case EN_IO_RECV:
{
//银光策略文件
char szSafeString[255] = "<?xml version=\"1.0\" encoding =\"utf-8\"?><access-policy><cross-domain-access><policy><allow-from><domain uri=\"*\" /></allow-from><grant-to><socket-resource port=\"4502-4506\" protocol=\"tcp\" /></grant-to></policy></cross-domain-access></access-policy>\0";

//策略文件长度
DWORD dwLen = (DWORD)strlen(szSafeString);

//发送策略文件
pConnectItem->SendMsg(szSafeString, dwLen);
}
case EN_IO_SEND:
{
pConnectItem
->Reset();
}
default:
break;
}

return true;
}

if (BytesTransferred == 0 || pOverLapped->GetIoType() == EN_IO_QUIT)
{
//连接断开,关闭连接
pConnectItem->Reset();

//广播离线信息
RoleInOut(pConnectItem, EN_NET_MSG_ROLEEVENT_ROLEOUT);

return true;
}

switch(pOverLapped->GetIoType())
{
case EN_IO_RECV:
{
//目前接收到的字节流就在这里处理吧!

//这里要功能是将收到的数据存到暂存缓冲中
//判断缓存中的数据是否可以组成一个ST_MSG_CHAT_INFO消息包
//将组成的ST_MSG_CHAT_INFO消息包向队列中其它连接广播

MoveMemory((BYTE
*)pConnectItem->m_pvBuffer + pConnectItem->m_lBuffSize , pOverLapped->m_buffer, BytesTransferred);
pConnectItem
->m_lBuffSize += BytesTransferred;

NET_MSG_HEAD
* pHead = (NET_MSG_HEAD*)&pConnectItem->m_pvBuffer;

if (pHead->nSize > NET_MSG_BUFFER_SIZE)
{
//包的长度不能超过NET_MSG_BUFFER_SIZE,显然不是正常包,断开连接

pConnectItem
->Reset();

//广播离线信息
RoleInOut(pConnectItem, EN_NET_MSG_ROLEEVENT_ROLEOUT);

return true;
}

if (pConnectItem->m_lBuffSize >= (ULONG)pHead->nSize)
{
//收到一个完整包,进行处理
switch (pHead->unType)
{
case EN_NET_MSG_ROLE_EVENT:
{
CNetMsgRoleEvent netmsg;
netmsg.InitMsg(pConnectItem
->m_unSocketIndex, (BYTE*)&pConnectItem->m_pvBuffer, pHead->nSize);

const ST_MSG_ROLE_EVENT* pInfo = netmsg.GetInfo();
switch (pInfo->nType)
{
case EN_NET_MSG_ROLEEVENT_LOGIN:
{
//收到客户端登陆消息后,验证用户名是否重复,向客户端发送登陆验证消息(包括修正的用户名)
string strRoleName = pInfo->szRoleName;

//检查是否有重名
MAP_ROLELIST::iterator iter = m_MapRoleList.find(strRoleName);
if (iter != m_MapRoleList.end())
{
char szNameIndex[10];
itoa(m_unNameIndex
++, szNameIndex, 10);

strRoleName
+= szNameIndex;
}

pConnectItem
->SetRoleName((char*)strRoleName.c_str());

//广播上线信息
RoleInOut(pConnectItem, EN_NET_MSG_ROLEEVENT_ROLEIN);

//向连接客户端发送确认消息
CNetMsgRoleEvent netmsgsend;
netmsgsend.Create(pInfo
->head.unMsgNumber, EN_NET_MSG_ROLEEVENT_VALIDELOGIN, (char*)pConnectItem->GetRoleName(), 0);
pConnectItem
->SendMsg((char*)netmsgsend.GetBuf(), netmsgsend.GetSize());
}
break;
case EN_NET_MSG_ROLEEVENT_REQROLELIST:
{
//收到客户端用户信息列表请求消息,返回在线用户信息列表消息

CNetMsgRoleInfoList netMsgRoleList;
netMsgRoleList.Create(pInfo
->head.unMsgNumber, EN_NET_MSG_END_FLAG_NORMAL);
int nInfoCount = MAX_ROLE_LIST_COUNT;

for (MAP_ROLELIST::iterator iter = m_MapRoleList.begin(); iter != m_MapRoleList.end(); iter++)
{
if (nInfoCount == 0)
{
pConnectItem
->SendMsg(&netMsgRoleList);
netMsgRoleList.Create(pInfo
->head.unMsgNumber, EN_NET_MSG_END_FLAG_NORMAL);
nInfoCount
= MAX_ROLE_LIST_COUNT;
}

string strName = iter->first;
UINT unSocketIndex
= iter->second;

ST_ROLE_INFO stRoleInfo;
memset(
&stRoleInfo, 0, sizeof(ST_ROLE_INFO));
stRoleInfo.nRoleIndex
= unSocketIndex;
memcpy(
&stRoleInfo.szRoleName, strName.c_str(), CONST_ROLE_NAME_LENGTH);

netMsgRoleList.AppendInfo(
&stRoleInfo);
nInfoCount
--;
}

netMsgRoleList.AddEndFlag();
pConnectItem
->SendMsg(&netMsgRoleList);
}
break;
default:
break;
}
}
break;
case EN_NET_MSG_CHAT_INFO:
{
//收到聊天内容消息后进行广播
CNetMsgChatInfo netmsg;
netmsg.InitMsg(pConnectItem
->m_unSocketIndex, (BYTE*)&pConnectItem->m_pvBuffer, pHead->nSize);

BoardCaseMsg(
&netmsg);
}
break;
default:
{
pConnectItem
->Reset();

return true;
}
break;
}

//将组包剩余的数据移动到缓存头
int nLeftSize = pConnectItem->m_lBuffSize - pHead->nSize;
MoveMemory((BYTE
*)pConnectItem->m_pvBuffer, (BYTE*)pConnectItem->m_pvBuffer + pHead->nSize, nLeftSize);
pConnectItem
->m_lBuffSize = nLeftSize;
}

//开始连接的下一次数据接收
pConnectItem->RecvMsg();
}
break;
case EN_IO_SEND:
{
//发送完成后将发送重叠结构归还空闲队列
pConnectItem->OnSendCompleted(pOverLapped, BytesTransferred);
}
break;
case EN_IO_QUIT:
{
//让线程安全退出
}
break;
default:
break;
}
}
else
{
if (pConnectItem != NULL)
{
pConnectItem
->Reset();

RoleInOut(pConnectItem, EN_NET_MSG_ROLEEVENT_ROLEOUT);
}
}

return true;
}

bool CServer::Init943()
{
WSADATA wsaData;
DWORD dwRet
= WSAStartup(0x0202, &wsaData);
if (dwRet != 0)
return false;

m_ListenSocket943
= WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN ServerAddr;
ServerAddr.sin_family
= AF_INET;
ServerAddr.sin_addr.S_un.S_addr
= inet_addr(m_strIP.c_str());
ServerAddr.sin_port
= htons(943);
bind(m_ListenSocket943, (LPSOCKADDR)
&ServerAddr, sizeof(ServerAddr));
listen(m_ListenSocket943,
100);

m_h943Thread
= (HANDLE)_beginthreadex(NULL, NULL, &SL943Thread, (PVOID)this, NULL, NULL);

return true;
}

bool CServer::Run943()
{
SOCKADDR_IN ClientAddr;
int nAddrLength=sizeof(ClientAddr);
CConnectItem
* pconItem = NULL;
SOCKET acceptSocket
= INVALID_SOCKET;
acceptSocket
= WSAAccept(m_ListenSocket943, (SOCKADDR *)&ClientAddr, &nAddrLength, NULL, NULL);
if (acceptSocket != INVALID_SOCKET)
{
pconItem
= GetConnectItem943Free();
pconItem
->m_Socket = acceptSocket;
pconItem
->m_SocketAddr = ClientAddr;

DWORD dwTransferred
= 0;

//接收到连接,相关参数初始化,将连接加到队列中,并向队列中其它成员广播
if (CreateIoCompletionPort((HANDLE)pconItem->m_Socket, m_hCompPort, (ULONG_PTR)pconItem, 0) == NULL)
{
return false;
}

pconItem
->RecvMsg();
}

return true;
}

void CServer::Start(string strIP, int nPort)
{
m_strIP
= strIP;
m_nPort
= nPort;

::InterlockedExchange(
&g_fShutdown, 0);

InitListen();

InitWork();

Init943();
}

void CServer::ShutDown()
{
::InterlockedExchange(
&g_fShutdown, 1);

closesocket(m_ListenSocket);

DWORD dwTransferred
= 0;
COverLapped overLapped(EN_IO_QUIT);
PostQueuedCompletionStatus(m_hCompPort, dwTransferred, NULL, (OVERLAPPED
*)&overLapped);

closesocket(m_ListenSocket943);

Sleep(
1000);

CloseHandle(m_hListenThread);
CloseHandle(m_hWrokThread);

for (MAP_CONNECTITEM::iterator iter = m_pMapConnectItem->begin(); iter != m_pMapConnectItem->end(); iter++)
{
CConnectItem
* pItem = iter->second;
if (pItem->m_bInUse = true)
pItem
->Reset();
}

}

inline CConnectItem
* CServer::GetConnectItemFree()
{
//这里应该加锁,但目前只有监听一个线程向队列中添加元素,且程序远行时不会删除元素,所以就不加了。

//这里用遍历的方法找空闲元素,速度慢,适合连接少的情况
for (MAP_CONNECTITEM::iterator iter = m_pMapConnectItem->begin(); iter != m_pMapConnectItem->end(); iter++)
{
CConnectItem
* pItem1 = iter->second;
if (pItem1->m_bInUse == false)
{
pItem1
->m_bInUse = true;
return pItem1;
}
}

//队列里没有空闲就生成一个新的返回并加入队列
CConnectItem* pItem = new CConnectItem;
pItem
->m_unSocketIndex = (UINT)(m_pMapConnectItem->size() + 1);
m_pMapConnectItem
->insert(make_pair(pItem->m_unSocketIndex, pItem));
pItem
->m_bInUse = true;

return pItem;
}

inline CConnectItem
* CServer::GetConnectItem943Free()
{
//目前只有943线程使用,暂不加锁。
for (MAP_CONNECTITEM::iterator iter = m_pMapConnectItem943->begin(); iter != m_pMapConnectItem943->end(); iter++)
{
CConnectItem
* pItem1 = iter->second;
if (pItem1->m_bInUse == false)
{
return pItem1;
}
}

CConnectItem
* pItem = new CConnectItem;
pItem
->m_is943 = true;
pItem
->m_unSocketIndex = (UINT)(m_pMapConnectItem943->size() + 1);
m_pMapConnectItem943
->insert(make_pair(pItem->m_unSocketIndex, pItem));

pItem
->m_bInUse = true;

return pItem;
}

inline
void CServer::RoleInOut(CConnectItem* pConnectItem, int nType)
{
string strName = pConnectItem->GetRoleName();

if (nType == EN_NET_MSG_ROLEEVENT_ROLEIN)
{
//加到用户队列
m_MapRoleList.insert(make_pair(strName, pConnectItem->m_unSocketIndex));
}
else if (nType == EN_NET_MSG_ROLEEVENT_ROLEOUT)
{
//从用户队列移除
MAP_ROLELIST::iterator iter = m_MapRoleList.find(strName);
if (iter != m_MapRoleList.end())
{
m_MapRoleList.erase(iter);
}
}
else
return;

CNetMsgRoleEvent netmsg;
netmsg.Create(
0, nType, (char*)pConnectItem->GetRoleName(), pConnectItem->m_unSocketIndex);
BoardCaseMsg(pConnectItem,
&netmsg);
}

  程序启动时将在“Init()”函数中初始化完成端口。调用“Start(string strIP, int nPort)”函数后将使三个线程开始服务。调用“ShutDown()”将停止三个线程的服务。

1、监听连接线程
    监听线程负责监听客户端的socket连接,如果有客户端连接,则创建一个与此连接相关的“CConnectItem”类,该类的网络接收和发送操作将由工作线程完成。
  

ConnectItem.h
/********************************************************************
file base: ConnectItem
file ext: h
author: 田园小蛙 (ourtree@live.cn)

purpose: 网络连接对象
********************************************************************
*/
#pragma once
#include
"OverLapped.h"
#include
"NetMsg.h"
#include
<deque>
#include
<map>
using namespace std;

typedef deque
<COverLapped*> DEQ_OVERLAPPED;
typedef map
<COverLapped*, COverLapped*> MAP_OVERLAPPED;

class CConnectItem
{
public:
CConnectItem(
void);
virtual ~CConnectItem(void);

public:
/// \brief 发送字节流消息
bool SendMsg(const char* pData, DWORD dwDataSize);

/// \brief 发送消息
bool SendMsg(CNetMsg* pnetMsg);

/// \brief 接收字节流消息
bool RecvMsg();

/// \brief 发送操作完成
bool OnSendCompleted(COverLapped* pOverLapped, DWORD dwThancferred);

/// \brief 初始化连接连
void Reset();

void SetRoleName(char* szName) {memcpy(_szRoleName, szName, CONST_ROLE_NAME_LENGTH);}
const char* GetRoleName() {return _szRoleName;}

private:
/// \brief 得一个空闲的发送重叠结构
inline COverLapped* PopSendLappedFree();

/// \brief 将使用完的重叠结构还原到空闲队列中
inline bool PushSendLappedFree(COverLapped* pOverLapped);

public:
COverLapped
* m_pOverlappedRecv;//!< 接收用重叠结构

bool m_is943;//!是否供银光策略文件用

SOCKET m_Socket;
//!<连接套接字
SOCKADDR_IN m_SocketAddr;//!<客户地址
bool m_bInUse;//!< 此连接对象是否在使用中
UINT m_unSocketIndex;//!< 此连接在本服的索引

ULONG m_lBuffSize;
//暂存了多少数据
BYTE m_pvBuffer[NET_MSG_BUFFER_SIZE * 8]; //暂存的数据

private:
MAP_OVERLAPPED
* m_pMapSendLappedActive;//!<使用中的发送结构
DEQ_OVERLAPPED* m_pDeqSendLappedFree;//!<空闲中的发送结构

char _szRoleName[CONST_ROLE_NAME_LENGTH];//用户聊天名
};
ConnectItem.cpp
#include "StdAfx.h"
#include
"ConnectItem.h"


CConnectItem::CConnectItem(
void) : m_pOverlappedRecv(NULL)
, m_pMapSendLappedActive(NULL)
, m_pDeqSendLappedFree(NULL)
, m_Socket(INVALID_SOCKET)
, m_bInUse(
false)
, m_lBuffSize(
0)
, m_is943(
false)
{
m_pOverlappedRecv
= new COverLapped(EN_IO_RECV);

m_pMapSendLappedActive
= new MAP_OVERLAPPED;
m_pDeqSendLappedFree
= new DEQ_OVERLAPPED;
}


CConnectItem::
~CConnectItem(void)
{
if (m_Socket != INVALID_SOCKET)
closesocket(m_Socket);

if (m_pOverlappedRecv)
delete m_pOverlappedRecv;

while (m_pDeqSendLappedFree->size() > 0)
{
delete m_pDeqSendLappedFree
->front();
m_pDeqSendLappedFree
->pop_front();
}
delete m_pDeqSendLappedFree;

for (MAP_OVERLAPPED::iterator iter = m_pMapSendLappedActive->begin(); iter != m_pMapSendLappedActive->end(); iter++)
{
COverLapped
* pOver = iter->second;

delete pOver;
}
m_pMapSendLappedActive
->clear();
delete m_pMapSendLappedActive;

}

bool CConnectItem::SendMsg(CNetMsg* pnetMsg)
{
return SendMsg((char*)pnetMsg->GetBuf(), pnetMsg->GetSize());
}

bool CConnectItem::SendMsg(const char* pData, DWORD dwDataSize)
{
if (m_Socket == INVALID_SOCKET)
return false;

COverLapped
* pOverLapped = PopSendLappedFree();

memcpy(
&pOverLapped->m_buffer, pData, dwDataSize);
pOverLapped
->m_DataBuf.len = dwDataSize;

DWORD dwThancferred
= 0;
int iRetCode = WSASend(m_Socket, &pOverLapped->m_DataBuf, 1, &dwThancferred, 0, (LPWSAOVERLAPPED)pOverLapped, NULL);
if ((iRetCode == SOCKET_ERROR) && (WSAGetLastError() != WSA_IO_PENDING))
{
OnSendCompleted(pOverLapped,
0);
return false;
}

return true;
}

bool CConnectItem::RecvMsg()
{
if (m_Socket == INVALID_SOCKET)
return false;

if (m_pOverlappedRecv == NULL)
return false;

DWORD dwThancferred
= 0;
DWORD dwFlags
= 0;

int iRetCode = WSARecv(m_Socket, &m_pOverlappedRecv->m_DataBuf, 1, &dwThancferred, &dwFlags, (LPWSAOVERLAPPED)m_pOverlappedRecv, NULL);
if ((iRetCode == SOCKET_ERROR) && (WSAGetLastError() != WSA_IO_PENDING))
{
return false;
}

return true;
}

bool CConnectItem::OnSendCompleted(COverLapped* pOverLapped, DWORD dwThancferred)
{
pOverLapped
->ReSet();

//将重叠结构还到空闲队列中
PushSendLappedFree(pOverLapped);

return true;
}

inline COverLapped
* CConnectItem::PopSendLappedFree()
{
COverLapped
* pOverLapped = NULL;
if (m_pDeqSendLappedFree->size() > 0)
{
pOverLapped
= m_pDeqSendLappedFree->front();
m_pDeqSendLappedFree
->pop_front();
}
else
{
pOverLapped
= new COverLapped(EN_IO_SEND);
}

m_pMapSendLappedActive
->insert(make_pair(pOverLapped, pOverLapped));

return pOverLapped;
}

inline
bool CConnectItem::PushSendLappedFree(COverLapped* pOverLapped)
{
bool bState = false;

MAP_OVERLAPPED::iterator iter
= m_pMapSendLappedActive->find(pOverLapped);
if (iter != m_pMapSendLappedActive->end())
{
//从活动队列中清除
m_pMapSendLappedActive->erase(iter);

bState
= true;
}

//加到空闲队列
m_pDeqSendLappedFree->push_back(pOverLapped);

return bState;
}

void CConnectItem::Reset()
{
m_pOverlappedRecv
->ReSet();

closesocket(m_Socket);
m_Socket
= INVALID_SOCKET;

m_bInUse
= false;

m_lBuffSize
= 0;

//将重叠结构都移到空闲队列中
for (MAP_OVERLAPPED::iterator iter = m_pMapSendLappedActive->begin(); iter != m_pMapSendLappedActive->end(); iter++)
{
COverLapped
* pOver = iter->second;
pOver
->ReSet();
m_pDeqSendLappedFree
->push_back(pOver);
}
m_pMapSendLappedActive
->clear();
}

  “ConnectItem”类可以代表客户端在服务器内的对象,包含连接的socket、数据缓冲、发送和接收用的重叠结构。

2.工作线程

  工作线程检查完成端口是否有完成的接收或发送操作,有完成的接收操作则进行相应的消息逻辑处理,有完成的发送操作则将发送重叠结构还到空闲发送重叠结构队列中。详见:CServer类的RunWork()函数。

  a.943端口接收或发送数据的完成操作(具体操作详见如下代码段的注释)

  

if (pConnectItem->m_is943)
{
switch(pOverLapped->GetIoType())
{
case EN_IO_RECV:
{
//银光策略文件
char szSafeString[255] = "<?xml version=\"1.0\" encoding =\"utf-8\"?><access-policy><cross-domain-access><policy><allow-from><domain uri=\"*\" /></allow-from><grant-to><socket-resource port=\"4502-4506\" protocol=\"tcp\" /></grant-to></policy></cross-domain-access></access-policy>\0";

//策略文件长度
DWORD dwLen = (DWORD)strlen(szSafeString);

//发送策略文件
pConnectItem->SendMsg(szSafeString, dwLen);
}
case EN_IO_SEND:
{
pConnectItem
->Reset();
}
default:
break;
}

return true;
}

  b.与客户端进行接收或发送数据的完成操作 (具体操作详见如下代码段的注释) 

与客户端的完成操作
if (BytesTransferred == 0 || pOverLapped->GetIoType() == EN_IO_QUIT)
{
//连接断开,关闭连接
pConnectItem->Reset();

//广播离线信息
RoleInOut(pConnectItem, EN_NET_MSG_ROLEEVENT_ROLEOUT);

return true;
}

switch(pOverLapped->GetIoType())
{
case EN_IO_RECV:
{
//目前接收到的字节流就在这里处理吧!

//这里要功能是将收到的数据存到暂存缓冲中
//判断缓存中的数据是否可以组成一个ST_MSG_CHAT_INFO消息包
//将组成的ST_MSG_CHAT_INFO消息包向队列中其它连接广播

MoveMemory((BYTE
*)pConnectItem->m_pvBuffer + pConnectItem->m_lBuffSize , pOverLapped->m_buffer, BytesTransferred);
pConnectItem
->m_lBuffSize += BytesTransferred;

NET_MSG_HEAD
* pHead = (NET_MSG_HEAD*)&pConnectItem->m_pvBuffer;

if (pHead->nSize > NET_MSG_BUFFER_SIZE)
{
//包的长度不能超过NET_MSG_BUFFER_SIZE,显然不是正常包,断开连接

pConnectItem
->Reset();

//广播离线信息
RoleInOut(pConnectItem, EN_NET_MSG_ROLEEVENT_ROLEOUT);

return true;
}

if (pConnectItem->m_lBuffSize >= (ULONG)pHead->nSize)
{
//收到一个完整包,进行处理
switch (pHead->unType)
{
case EN_NET_MSG_ROLE_EVENT:
{
CNetMsgRoleEvent netmsg;
netmsg.InitMsg(pConnectItem
->m_unSocketIndex, (BYTE*)&pConnectItem->m_pvBuffer, pHead->nSize);

const ST_MSG_ROLE_EVENT* pInfo = netmsg.GetInfo();
switch (pInfo->nType)
{
case EN_NET_MSG_ROLEEVENT_LOGIN:
{
//收到客户端登陆消息后,验证用户名是否重复,向客户端发送登陆验证消息(包括修正的用户名)
string strRoleName = pInfo->szRoleName;

//检查是否有重名
MAP_ROLELIST::iterator iter = m_MapRoleList.find(strRoleName);
if (iter != m_MapRoleList.end())
{
char szNameIndex[10];
itoa(m_unNameIndex
++, szNameIndex, 10);

strRoleName
+= szNameIndex;
}

pConnectItem
->SetRoleName((char*)strRoleName.c_str());

//广播上线信息
RoleInOut(pConnectItem, EN_NET_MSG_ROLEEVENT_ROLEIN);

//向连接客户端发送确认消息
CNetMsgRoleEvent netmsgsend;
netmsgsend.Create(pInfo
->head.unMsgNumber, EN_NET_MSG_ROLEEVENT_VALIDELOGIN, (char*)pConnectItem->GetRoleName(), 0);
pConnectItem
->SendMsg((char*)netmsgsend.GetBuf(), netmsgsend.GetSize());
}
break;
case EN_NET_MSG_ROLEEVENT_REQROLELIST:
{
//收到客户端用户信息列表请求消息,返回在线用户信息列表消息

CNetMsgRoleInfoList netMsgRoleList;
netMsgRoleList.Create(pInfo
->head.unMsgNumber, EN_NET_MSG_END_FLAG_NORMAL);
int nInfoCount = MAX_ROLE_LIST_COUNT;

for (MAP_ROLELIST::iterator iter = m_MapRoleList.begin(); iter != m_MapRoleList.end(); iter++)
{
if (nInfoCount == 0)
{
pConnectItem
->SendMsg(&netMsgRoleList);
netMsgRoleList.Create(pInfo
->head.unMsgNumber, EN_NET_MSG_END_FLAG_NORMAL);
nInfoCount
= MAX_ROLE_LIST_COUNT;
}

string strName = iter->first;
UINT unSocketIndex
= iter->second;

ST_ROLE_INFO stRoleInfo;
memset(
&stRoleInfo, 0, sizeof(ST_ROLE_INFO));
stRoleInfo.nRoleIndex
= unSocketIndex;
memcpy(
&stRoleInfo.szRoleName, strName.c_str(), CONST_ROLE_NAME_LENGTH);

netMsgRoleList.AppendInfo(
&stRoleInfo);
nInfoCount
--;
}

netMsgRoleList.AddEndFlag();
pConnectItem
->SendMsg(&netMsgRoleList);
}
break;
default:
break;
}
}
break;
case EN_NET_MSG_CHAT_INFO:
{
//收到聊天内容消息后进行广播
CNetMsgChatInfo netmsg;
netmsg.InitMsg(pConnectItem
->m_unSocketIndex, (BYTE*)&pConnectItem->m_pvBuffer, pHead->nSize);

BoardCaseMsg(
&netmsg);
}
break;
default:
{
pConnectItem
->Reset();

return true;
}
break;
}

//将组包剩余的数据移动到缓存头
int nLeftSize = pConnectItem->m_lBuffSize - pHead->nSize;
MoveMemory((BYTE
*)pConnectItem->m_pvBuffer, (BYTE*)pConnectItem->m_pvBuffer + pHead->nSize, nLeftSize);
pConnectItem
->m_lBuffSize = nLeftSize;
}

//开始连接的下一次数据接收
pConnectItem->RecvMsg();
}
break;
case EN_IO_SEND:
{
//发送完成后将发送重叠结构归还空闲队列
pConnectItem->OnSendCompleted(pOverLapped, BytesTransferred);
}
break;
case EN_IO_QUIT:
{
//让线程安全退出
}
break;
default:
break;
}

           

二、通讯消息结构

“PubDefine.h”文件:  

PubDefine.h
/********************************************************************
file base: PubDefine
file ext: h
author: 田园小蛙 (ourtree@live.cn)

purpose: 公共消息结构,常量
********************************************************************
*/
#pragma once

#define DATA_BUFSIZE_STORE 1024 * 4 //!< 发送缓冲区大小
#define NET_MSG_BUFFER_SIZE 1024 //!< 接收缓冲区大小

const int CONST_ROLE_NAME_LENGTH = 65; //!< 玩家账号名称占位字节长度
const int CHAT_CONTENT_LENGTH = 300; //!< 聊天内容长度(UTF8约100个汉字)
const int CONST_ACCESS_POLICY_LENGTH = 255; //!< 安全沙箱的长度

/// \brief 消息类型
typedef enum
{
EN_NET_MSG_ROLE_EVENT
= 1,
EN_NET_MSG_CHAT_INFO
= 2,
EN_NET_MSG_ROLE_LIST
= 3
} EN_NET_MSG_TYPE;

/// \brief 聊天内容消息类型
typedef enum
{
EN_NET_CHAT_ROLE_COMMUNITY
= 1, //用户群聊
EN_NET_CHAT_SYSTEM = 2//系统广播
} EN_NET_CHAT_TYPE;

/// \brief 消息结束标志
typedef enum
{
EN_NET_MSG_END_FLAG_NORMAL
= 0,
EN_NET_MSG_END_FLAG_END
= 1,
} EN_NET_MSG_END_FLAG;

/// \brief 用户事件消息类型
typedef enum
{
EN_NET_MSG_ROLEEVENT_LOGIN
= 1,//!< 用户登陆
EN_NET_MSG_ROLEEVENT_VALIDELOGIN = 2,//!< 验证用户登陆
EN_NET_MSG_ROLEEVENT_REQROLELIST = 3,//!< 请求用户列表
EN_NET_MSG_ROLEEVENT_ROLEIN = 4,//!< 用户连接
EN_NET_MSG_ROLEEVENT_ROLEOUT = 5//!< 用户退出
} EN_NET_MSG_ROLEEVENT;

/// \brief 消息头
#pragma pack(push)
#pragma pack(1)
typedef
struct _NET_MSG_HEAD
{
UINT nSize;
//!< 包大小(包括包头)
UINT unType; //!< 包类型
UINT unEndFlag; //!< 网络消息结束标志位,参见EM_NET_MSG_END_FLAG
UINT unMsgNumber; //!< 网络消息序号,回送给客户端的消息要和发送到服务器的消息的序号相同,广播消息序号为0
}NET_MSG_HEAD;
#pragma pack(pop)

/// \brief 聊天内容消息
#pragma pack(push)
#pragma pack(1)
typedef
struct _ST_MSG_CHAT_INFO
{
NET_MSG_HEAD head;
//!< 包头
int nChatType;//!< 聊天信息类型
char szFromName[CONST_ROLE_NAME_LENGTH];//!< 发送者名称
char szToName[CONST_ROLE_NAME_LENGTH];//!< 接收者名称
char szContent[CHAT_CONTENT_LENGTH]; //!< 聊天内容
}ST_MSG_CHAT_INFO;
#pragma pack(pop)

const int CN_SIZE_ST_MSG_CHAT_INFO = sizeof(ST_MSG_CHAT_INFO);

/// \brief 用户信息
#pragma pack(push)
#pragma pack(1)
typedef
struct _ST_ROLE_INFO
{
int nRoleIndex;//!<索引
char szRoleName[CONST_ROLE_NAME_LENGTH];//!< 发送者名称
}ST_ROLE_INFO;
#pragma pack(pop)

/// \brief 用户信息列表
#pragma pack(push)
#pragma pack(1)
typedef
struct _ST_ROLE_INFO_LIST
{
NET_MSG_HEAD head;
//!< 包头
int nCount; //!< 列表数量
ST_ROLE_INFO info[1]; //!< 用户列表
}ST_ROLE_INFO_LIST;
#pragma pack(pop)

const int MAX_ROLE_LIST_COUNT = (NET_MSG_BUFFER_SIZE - sizeof(ST_ROLE_INFO_LIST)) / sizeof(ST_ROLE_INFO) + 1;/// \brief 用户列表消息包容纳列表的最大个数

/// \brief 用户事件(登陆、验证登陆、请求用户列表、用户上线、用户下线)
#pragma pack(push)
#pragma pack(1)
typedef
struct _ST_MSG_ROLE_EVENT
{
NET_MSG_HEAD head;
//!< 包头
int nType;//!< 事件类型
char szRoleName[CONST_ROLE_NAME_LENGTH];//!< 用户名称
int nRoleIndex;//!< 索引
}ST_MSG_ROLE_EVENT;
#pragma pack(pop)

“PubDefine.h”里包含了一些常量及消息结构的定义。



“CNetMsg”类:消息基类 

NetMsg.h
/********************************************************************
file base: NetMsg
file ext: h
author: 田园小蛙 (ourtree@live.cn)

purpose: 消息基类
********************************************************************
*/
#pragma once
#include
"PubDefine.h"

class CNetMsg
{
public:
CNetMsg(
void);
virtual ~CNetMsg(void);

protected:
BYTE m_bufMsg[NET_MSG_BUFFER_SIZE];
//!< 缓冲区

UINT m_unSize;
//!< 大小
UINT m_unType; //!< 类型
UINT m_unSocketIndex; //!< 索引

public:
/// \brief 将收到的消息流拷贝到本地缓冲区中组成消息
bool InitMsg(UINT unSocketIndex, const BYTE* pData, const UINT unSize);

/// \brief 得本消息流缓冲
BYTE* GetBuf(void) {return m_bufMsg;}

/// \brief 得本消息流大小
UINT GetSize();

/// \brief 消息类型
UINT GetType();

/// \brief 连接索引
UINT GetSocketIndex() {return m_unSocketIndex;}

/// \brief 增中间包的标识(定义见EN_NET_MSG_END_FLAG枚举)
void AddEndFlag();

/// \brief 得客户端的消息标识
UINT GetMsgNumber();
};
NetMsg.cpp
#include "StdAfx.h"
#include
"NetMsg.h"


CNetMsg::CNetMsg(
void)
{
}


CNetMsg::
~CNetMsg(void)
{
}

bool CNetMsg::InitMsg(UINT unSocketIndex, const BYTE* pData, const UINT unSize)
{
m_unSocketIndex
= unSocketIndex;
memcpy(m_bufMsg, pData, unSize);

return true;
}

UINT CNetMsg::GetSize()
{
NET_MSG_HEAD
* pHead = (NET_MSG_HEAD*)m_bufMsg;
return pHead->nSize;
}

UINT CNetMsg::GetType()
{
NET_MSG_HEAD
* pHead = (NET_MSG_HEAD*)m_bufMsg;
return pHead->unType;
}

void CNetMsg::AddEndFlag()
{
NET_MSG_HEAD
* pHead = (NET_MSG_HEAD*)m_bufMsg;
pHead
->unEndFlag = EN_NET_MSG_END_FLAG_END;
}

UINT CNetMsg::GetMsgNumber()
{
NET_MSG_HEAD
* pHead = (NET_MSG_HEAD*)m_bufMsg;
return pHead->unMsgNumber;
}

“CNetMsgChatInfo”类:聊天内容消息 

NetMsgChatInfo.h
/********************************************************************
file base: NetMsgChatInfo
file ext: h
author: 田园小蛙 (ourtree@live.cn)

purpose: 聊天内容消息
********************************************************************
*/
#pragma once
#include
"netmsg.h"

class CNetMsgChatInfo :
public CNetMsg
{
public:
CNetMsgChatInfo(
void);
virtual ~CNetMsgChatInfo(void);

/// \brief 创建消息
bool Create(UINT unMsgNumber, int nType, char* pszContent, char* pszFromName, char* pszToName);
protected:
ST_MSG_CHAT_INFO
* m_pInfo;//!<消息结构
protected:
inline
bool Init(void);
};
NetMsgChatInfo.cpp
#include "StdAfx.h"
#include
"NetMsgChatInfo.h"


CNetMsgChatInfo::CNetMsgChatInfo(
void)
{
Init();
}


CNetMsgChatInfo::
~CNetMsgChatInfo(void)
{
}

bool CNetMsgChatInfo::Create(UINT unMsgNumber, int nType, char* pszContent, char* pszFromName, char* pszToName)
{
Init();

m_pInfo
->nChatType = nType;

memcpy(
&m_pInfo->szContent, pszContent, CHAT_CONTENT_LENGTH);
memcpy(
&m_pInfo->szFromName, pszFromName, CONST_ROLE_NAME_LENGTH);
memcpy(
&m_pInfo->szToName, pszToName, CONST_ROLE_NAME_LENGTH);

return true;
}

inline
bool CNetMsgChatInfo::Init(void)
{
memset(m_bufMsg,
0, sizeof(m_bufMsg));

m_unType
= EN_NET_MSG_CHAT_INFO;
m_unSize
= sizeof(ST_MSG_CHAT_INFO);

m_pInfo
= (ST_MSG_CHAT_INFO*)m_bufMsg;
m_pInfo
->head.nSize = m_unSize;
m_pInfo
->head.unType = m_unType;
m_pInfo
->head.unEndFlag = EN_NET_MSG_END_FLAG_NORMAL;
return true;
}

“CNetMsgRoleEvent”类:用户事件消息 

NetMsgRoleEvent.h
/********************************************************************
file base: NetMsgRoleEvent
file ext: h
author: 田园小蛙 (ourtree@live.cn)

purpose: 用户事件消息(登陆、验证登陆、请求用户列表、用户上线、用户下线)
********************************************************************
*/
#pragma once
#include
"netmsg.h"

class CNetMsgRoleEvent :
public CNetMsg
{
public:
CNetMsgRoleEvent(
void);
virtual ~CNetMsgRoleEvent(void);

/// \brief 创建消息(供发送用)
bool Create(UINT unMsgNumber, int nType, char* pszRoleName, UINT nRoleIndex);

/// \brief 取得消息结构
const ST_MSG_ROLE_EVENT* GetInfo() {return m_pInfo;}
protected:
ST_MSG_ROLE_EVENT
* m_pInfo;//!<消息结构

protected:
inline
bool Init(void);
};
NetMsgRoleEvent.cpp
#include "StdAfx.h"
#include
"NetMsgRoleEvent.h"


CNetMsgRoleEvent::CNetMsgRoleEvent(
void)
{
Init();
}


CNetMsgRoleEvent::
~CNetMsgRoleEvent(void)
{
}

bool CNetMsgRoleEvent::Create(UINT unMsgNumber, int nType, char* pszRoleName, UINT nRoleIndex)
{
Init();

m_pInfo
->head.unMsgNumber = unMsgNumber;

m_pInfo
->nType = nType;
m_pInfo
->nRoleIndex = nRoleIndex;

memcpy(
&m_pInfo->szRoleName, pszRoleName, CONST_ROLE_NAME_LENGTH);

return true;
}

inline
bool CNetMsgRoleEvent::Init(void)
{
memset(m_bufMsg,
0, sizeof(m_bufMsg));

m_unType
= EN_NET_MSG_ROLE_EVENT;
m_unSize
= sizeof(ST_MSG_ROLE_EVENT);

m_pInfo
= (ST_MSG_ROLE_EVENT*)m_bufMsg;
m_pInfo
->head.nSize = m_unSize;
m_pInfo
->head.unType = m_unType;
m_pInfo
->head.unEndFlag = EN_NET_MSG_END_FLAG_NORMAL;
return true;
}

“CNetMsgRoleInfoList”类:用户信息列表消息 

NetMsgRoleInfoList.h
/********************************************************************
file base: NetMsgRoleInfoList
file ext: h
author: 田园小蛙 (ourtree@live.cn)

purpose: 用户信息列表消息
********************************************************************
*/
#pragma once
#include
"netmsg.h"
#include
"PubDefine.h"


class CNetMsgRoleInfoList :
public CNetMsg
{
public:
CNetMsgRoleInfoList(
void);
virtual ~CNetMsgRoleInfoList(void);

protected:
ST_ROLE_INFO_LIST
* m_pInfo;//!<消息结构
int nCurrentCount;//!<用户信息子项的数量
public:
/// \brief 创建消息(供发送用)
bool Create(UINT unMsgNumber, UINT unEndFlag);

/// \brief 添加用户信息(列表项)
bool AppendInfo(ST_ROLE_INFO* pInfo);

private:
/// \brief 初始化消息
bool Init();
};
NetMsgRoleInfoList.cpp
#include "StdAfx.h"
#include
"NetMsgRoleInfoList.h"


CNetMsgRoleInfoList::CNetMsgRoleInfoList(
void)
{
Init();
}


CNetMsgRoleInfoList::
~CNetMsgRoleInfoList(void)
{
}

bool CNetMsgRoleInfoList::Create(UINT unMsgNumber, UINT unEndFlag)
{
Init();

m_pInfo
->nCount = 0;
m_pInfo
->head.unMsgNumber = unMsgNumber;
m_pInfo
->head.unEndFlag = unEndFlag;

return true;
}


bool CNetMsgRoleInfoList::AppendInfo(ST_ROLE_INFO* pInfo)
{
if(m_pInfo->nCount >= MAX_ROLE_LIST_COUNT)
return false;

memcpy(
&m_pInfo->info[m_pInfo->nCount], pInfo, sizeof(ST_ROLE_INFO));

m_pInfo
->nCount++;
m_pInfo
->head.nSize = sizeof(ST_ROLE_INFO_LIST) + (m_pInfo->nCount - 1) * sizeof(ST_ROLE_INFO);
m_unSize
= m_pInfo->head.nSize;
return true;
}

bool CNetMsgRoleInfoList::Init()
{
memset(m_bufMsg,
0, sizeof(m_bufMsg));

m_unType
= EN_NET_MSG_ROLE_LIST;
m_unSize
= sizeof(ST_ROLE_INFO_LIST);

m_pInfo
= (ST_ROLE_INFO_LIST*)m_bufMsg;
m_pInfo
->head.nSize = m_unSize;
m_pInfo
->head.unType = m_unType;

m_pInfo
->nCount = 0;

return true;
}

将消息封装成类方便代码书写,这些消息类的具体使用见“CServer”类的RunWork()函数。

 

三、使用完成端口的网络收发

  使用完成端口需要重叠结构,这里从“OVERLAPPED”结构扩展一个类“COverLapped",该类包含当前操作的类型(接收、发送、退出),操作数据的缓冲区。

  完成端口的初始化见“CServer”类的Init()函数,将socket连接绑定到完成端口见“CServer”类的RunListen()函数。

  

OverLapped.h
/********************************************************************
file base: OverLapped
file ext: h
author: 田园小蛙 (ourtree@live.cn)

purpose: 接收或发送用重叠结构(扩展自OVERLAPPED,包含一个数据缓冲区,当前网络操作状态)
********************************************************************
*/
#pragma once
#include
"PubDefine.h"

/// \brief 操作类型
typedef enum
{
EN_IO_SEND,
EN_IO_RECV,
EN_IO_QUIT
} EN_IO_TYPE;

class COverLapped : OVERLAPPED
{
public:
COverLapped(EN_IO_TYPE ioType);
COverLapped();
~COverLapped(void);

public:
char m_buffer[NET_MSG_BUFFER_SIZE];//!< 网络数据发送或接收缓冲区
WSABUF m_DataBuf; //!< 数据指针

private:
EN_IO_TYPE m_ioType;
//!< 操作类型

public:
/// \brief 重置此结构
void ReSet();

/// \brief 取当前结构类型
EN_IO_TYPE GetIoType() {return this->m_ioType;}

private:
/// \brief 初始化此结构
inline void InitOverLapped();
};
OverLapped.cpp
#include "StdAfx.h"
#include
"OverLapped.h"


COverLapped::COverLapped(EN_IO_TYPE ioType)
{
m_ioType
= ioType;

InitOverLapped();
}

COverLapped::COverLapped()
{

}

COverLapped::
~COverLapped(void)
{
}

void COverLapped::ReSet()
{
InitOverLapped();
}

inline
void COverLapped::InitOverLapped()
{
Internal
= InternalHigh = 0;
Offset
= OffsetHigh = 0;
hEvent
= NULL;

memset(m_buffer,
0, NET_MSG_BUFFER_SIZE);
m_DataBuf.buf
= m_buffer;
m_DataBuf.len
= NET_MSG_BUFFER_SIZE;
}

以上就是整个聊天服务器端的代码。(完)