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;
}