聊天室服务器端程的主要功能是管理在线用户、转发用户聊天消息。使用三种通讯消息结构:聊天内容消息结构、用户事件消息结构(用户登陆、验证用户登陆、请求用户列表、用户上线、用户离线)、用户信息列表消息结构。程序的主要逻辑由三个线程完成:监听线程、工作线程、银光策略文件监听线程。
这是一个简单的聊天服务器端程序,目的在于介绍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;
}
以上就是整个聊天服务器端的代码。(完)

浙公网安备 33010602011771号