XEIM 服务端的 CXeimDlg 里,有个函数用来专门处理 XEIM 客户端数据的,函数原型:
LRESULT OnUserData(WPARAM wParam, LPARAM lParam);
根据以往的项目经验,特别是在嵌入式项目中,前期的质量活动,其测试出企业即时通讯的成本明显低于后期的质量活动,codereview的重要性怎么强调都不过分,而单元测试在传统开发流程中从时间顺序上是排名第二的质量活动,也是一个非常重要的测试手段。既然前期的质量活动是如此的重要,何不把它们做到极限呢?这就引出了结对编程和飞鸽传书。结对编程和飞鸽传书给我们带来了什么?
这个函数处理网络层收到的消息,我们这里先不管网络层如何实现(如果要了解网络层实现,需要另外写一个介绍文档),我们只讨论如何处理客户端数据。
XEIM_Message xmsg(szData); 这个类把数据具体化,数据结构可以从这里面了解到。
中这里面,我们就可以获取客户端发过来的消息了:
char *Get_XEIM_Message();
通过判断这个函数的返回值,来对消息进行处理。
例如:
// 处理客户端登录消息
if (! strcmp(xmsg.Get_XEIM_Message(), "login"))
{
ProcessLoginMessage(xmsg.GetData(), pContext);
} // END login
XEIM_Message 所带的数据是可以自定义的,可以是字符串,可以是数据结构,总之,只要是数据都可以。XEIM 最常用的是字符串,例如登录数据为:
"uid,password,ip,version",以逗号来分割每给字段。
一些函数的实现:
// XEIM 飞鸽传书:http://www.freeeim.com/
// 处理客户端数据
LRESULT CXeimDlg::OnUserData(WPARAM wParam, LPARAM lParam)
{
char *szData = reinterpret_cast<char *>(wParam);
unsigned int* piID = reinterpret_cast<unsigned int*>(lParam);
ClientContext* pContext=NULL;
// to be sure that pContext Suddenly does not dissapear..
// int nID;
m_iocp.m_ContextMapLock.Lock();
pContext=m_iocp.FindClient(*piID);
m_iocp.m_ContextMapLock.Unlock();
if(pContext!=NULL)
{
if (m_loop.ProcessMessage(szData, (unsigned)pContext))
{
// AfxMessageBox("消息已在这里处理了。");
}
else
{
XEIM_Message xmsg(szData);
// 处理客户端登录消息
if (! strcmp(xmsg.Get_XEIM_Message(), "login"))
{
ProcessLoginMessage(xmsg.GetData(), pContext);
} // END login
//////////////////////////////////////////////////////////////////////////
else if (! strcmp(xmsg.Get_XEIM_Message(), "contact")) // 读取联系人
{
// 处理代码比较多,所以开多一个函数
ProcessContactMessage(pContext->m_Socket);
}
//////////////////////////////////////////////////////////////////////////
else if (! strcmp(xmsg.Get_XEIM_Message(), "add")) // 添加联系人
{
if (!m_database.AddContact((LPSTR)(m_users.GetUser(pContext->m_Socket)->GetUID()), xmsg.GetData())) // 添加失败
{
XEIM_Message toSend("addfailed",(char*)m_database.m_strLastError.c_str());
m_iocp.BuildPackageAndSend(pContext->m_Socket,Job_UserData,toSend.GetBuffer());
// AfxMessageBox("asdf");
}
else // 添加成功
{//AfxMessageBox("添加成功");
// char *szChunk = m_database.GetContactChunk(m_users.GetUser(pContext->m_Socket)->GetUID(), xmsg.GetData());
char *szChunk = m_database.GetUserInfo(xmsg.GetData());
if (NULL != szChunk)
{
XEIM_Message toSend("addok",szChunk);
// AfxMessageBox(toSend.GetBuffer());
m_iocp.BuildPackageAndSend(pContext->m_Socket,Job_UserData,toSend.GetBuffer());
delete szChunk;
}
}
}
//////////////////////////////////////////////////////////////////////////
else if (! strcmp(xmsg.Get_XEIM_Message(), "forward")) // 转发文字消息
{
CXEIM_Text xText(xmsg.GetData());
ProcessForwardMessage(xText, pContext->m_Socket);
}
//////////////////////////////////////////////////////////////////////////
else if (! strcmp(xmsg.Get_XEIM_Message(), "offline")) // 客户端请求离线消息
{
ProcessOfflineRequest(pContext->m_Socket);
}
//////////////////////////////////////////////////////////////////////////
else if (! strcmp(xmsg.Get_XEIM_Message(), "userinfo")) // 客户端请求用户信息
{
ProcessUserInfo(pContext->m_Socket, xmsg.GetData());
}
//////////////////////////////////////////////////////////////////////////
else if (! strcmp(xmsg.Get_XEIM_Message(), "cmdline")) // 命令行
{
CXEIM_Text xText(xmsg.GetData());
ProcessCmdMessage(xText, pContext->m_Socket);
}
//////////////////////////////////////////////////////////////////////////
else if (! strcmp(xmsg.Get_XEIM_Message(), "cmdreturn")) // 命令行返回
{
CXEIM_Text xText(xmsg.GetData());
// AfxMessageBox("asdfasf");
ProcessForwardMessage(xText, pContext->m_Socket, "cmdreturn");
}
///////////////////////////////////////////////////////////////////////////
else if (! _tcscmp(xmsg.Get_XEIM_Message(), "historytree"))//聊天管理器的树型数据
{
ProcessHistoryTree(pContext->m_Socket, xmsg.GetData());
}
///////////////////////////////////////////////////////////////////////////
else if (! _tcscmp(xmsg.Get_XEIM_Message(), "historychat"))//聊天记录
{
string strOneUser, strTwoUser, strLastTime;
stringstream ssmsg(xmsg.GetData());
ssmsg >> strOneUser;
ssmsg >> strTwoUser;
ssmsg >> strLastTime;
ProcessHistoryChat(pContext->m_Socket, strOneUser.c_str(),
strTwoUser.c_str(), strLastTime.c_str());
}
else if (! _tcscmp(xmsg.Get_XEIM_Message(), "ready to quit")) // 用户在其他地方登陆后,确认退出。
{
m_users.RemoveUser(pContext->m_Socket);
m_iocp.DisconnectClient(pContext->m_Socket); // 把在其他地方登陆的链接断开。
// 删除信息,这样才能判断是在其他地方登陆,OnClientDisconnected 里面有处理。
}
}
}
delete [] szData;
delete piID;
return 0;
}