网络服务器的编写相对客户端的网络应用较为复杂,既要实现单个客户端的功能,也要实现其管理,包括应答,读/写数据的线程开启/回收,多线之间的数据互斥等.(管理方面是采用了一个非开源项目的代码,所以代码暂不便公开,将以LIB方式提供大家使用.)为方便后台服务的开发,因此采用了MFC框架.范例中代码较为多不一一进行说明,只从大体上简单介绍每个类的作,并针对网络相关部分进行相关说明.
范例是实现一个消息管理服务.提供群发或定向发送文本消息至客户端,并带有服务启动/关闭,日志记录的功能.
CServerDlg是服务器操作界面,界面及功能说明如下图所示:
CSystemOptionDlg是服务器的设置选项对话框.服务器方面同时提供了网络,数据库,计时器3种引擎的调用,因此可设置实际中是否应用,若要使用则必需设定相关的参数.
CInitParamter负责服务器参数的保存/读取,并允许由其它类直接访问内置的变量值.数据会自动保存在注册表的:HKEY_CURRENT_USER\\Software\\OGF\\GameServer
CGameService是服务器的流程执行的框架,负责按指定的流程对内核服务的初始化,可完全复杂使用.
CDataBaseSink是数据库处理的钩子类,负责实现服务器所需的数据库操作.(但不是本例的重点,且没有使用的成分,所以不作详解)
AttemperEnginerSink负责服务器管理的协调处理,同时,也是各种消息的回调接口.本例中用到的是网络的应答,读取和关闭事件.其相关代码如下:
//网络应答事件
bool __cdecl CAttemperEngineSink::OnEventSocketAccept(NTY_SocketAcceptEvent * pSocketAcceptEvent)
{
ASSERT(m_pServerDlgSink);
if(m_pServerDlgSink){
//转换为IP字串
in_addr SocketAddr;
SocketAddr.S_un.S_addr = pSocketAcceptEvent->dwClientIP;
CString szClientIP;
szClientIP.Format("%s - %d", inet_ntoa(SocketAddr), pSocketAcceptEvent->wIndex);
//添加至客户端列表
int nIndex = m_pServerDlgSink->m_ClientList.InsertString(-1, szClientIP);
m_pServerDlgSink->m_ClientList.SetItemData(nIndex, MAKELONG(pSocketAcceptEvent->wIndex, pSocketAcceptEvent->wRoundID));
//设置允许被群发
m_pITCPSocketEngine->AllowBatchSend(pSocketAcceptEvent->wIndex, pSocketAcceptEvent->wRoundID, true);
}
return true;
}
//网络读取事件
bool __cdecl CAttemperEngineSink::OnEventSocketRead(CMD_Command Command, void * pDataBuffer, WORD wDataSize, NTY_SocketReadEvent * pSocketReadEvent)
{
//网络消息处理
switch (Command.wMainCmdID)
{
//自定义的消息体
case 10049:
{
if(Command.wSubCmdID==2||Command.wSubCmdID==4){
//将读取的内容写入日志框
CString szTemp;
CString szRecv = (LPCTSTR)pDataBuffer;
CEdit* pEdit = (CEdit*)m_pServerDlgSink->GetDlgItem(IDC_EDIT_RECV);
pEdit->GetWindowText(szTemp);
szTemp += "\r\n";
szTemp += szRecv;
pEdit->SetWindowText(szTemp);
}
return true;
}
}
//if unknow command, will close the socket connect!!!
return false;
}
//网络关闭事件
bool __cdecl CAttemperEngineSink::OnEventSocketClose(NTY_SocketCloseEvent * pSocketCloseEvent)
{
LONG l = MAKELONG(pSocketCloseEvent->wIndex, pSocketCloseEvent->wRoundID);
//枚举所有列表项,找出相同网络标识,将其删除
for(int nIndex=0;nIndex<m_pServerDlgSink->m_ClientList.GetCount();nIndex++){
if(m_pServerDlgSink->m_ClientList.GetItemData(nIndex)==l){
m_pServerDlgSink->m_ClientList.DeleteString(nIndex);
break;
}
}
return true;
}
//发送文本
bool CAttemperEngineSink::SendText()
{
m_pServerDlgSink->UpdateData();
//获取接收客户端的标识,找不到则进行群发
//nIndex, nRoundID是客户端连上服务器时记录的标识,不能单一作判断使用
int nIndex = INDEX_ALL_SOCKET;
int nRoundID = 0;
if(m_pServerDlgSink->m_ClientList.GetCurSel()!=LB_ERR){
LONG l = m_pServerDlgSink->m_ClientList.GetItemData(m_pServerDlgSink->m_ClientList.GetCurSel());
nIndex = l & 0xFFFF;
nRoundID = l>>16;
}
//发送消息
m_pITCPSocketEngine->SendData(nIndex, nRoundID, 10049, 3, (LPVOID)((LPCTSTR)m_pServerDlgSink->m_SendText), m_pServerDlgSink->m_SendText.GetLength()+1);
return true;
}
运行结果:
操~~~,CSDN是什么垃圾,竟然文章保存后是显示柳传志,又要我重写,气死我了!
网络应用范例代码:
http://dl2.csdn.net/down4/20070725/25000917122.rar
网络应用范例运行程序:
http://dl2.csdn.net/down4/20070725/25000851623.rar