關(guān)于RMsgQueue類的使用
RMsgQueue類是Symbian OS EKA2才提供的一個(gè)類,最近因?yàn)轫?xiàng)目中要使用,為此對(duì)使用進(jìn)行如下小結(jié)。
因?yàn)?/span>RMsgQueue類只是一個(gè)封裝好的內(nèi)核資源類,類似于RSocket和RTimer類,要想使用它進(jìn)行異步操作就必須對(duì)其用AO類來(lái)封裝,從而來(lái)實(shí)現(xiàn)監(jiān)聽消息,在有消息過(guò)來(lái)時(shí)得到通知并根據(jù)消息內(nèi)容進(jìn)行相對(duì)應(yīng)的處理。
那這個(gè)消息內(nèi)容又該如何定義呢?我們可以參看下RMsgQueue類定義
template <typename T>
class RMsgQueue : public RMsgQueueBase
{
public:
TInt CreateLocal(TInt aSize, TOwnerType aType=EOwnerProcess);
TInt CreateGlobal(const TDesC& aName, TInt aSize, TOwnerType aType=EOwnerProcess);
TInt Send(const T& aMsg);
void SendBlocking(const T& aMsg);
TInt Receive(T& aMsg);
void ReceiveBlocking(T& aMsg);
};
顯然該類RMsgQueue也是Symbian的廋模板類,具體消息內(nèi)容可以根據(jù)需求自定義類型,即如果傳遞的消息足夠簡(jiǎn)單則可以采用Tint類型來(lái)定義,考慮到我們的實(shí)際應(yīng)用對(duì)消息內(nèi)容進(jìn)行如下定義
typedef enum _NetWork_Msg_Type
{
NetworkSocketCreate,
NetworkSocketConnect,
NetworkSocketSend,
NetworkSocketRecv,
NetworkSocketClose,
}NetWorkMsgType;
typedef struct _NetWork_Msg
{
NetWorkMsgType m_msgType;
TInt32 m_msgFd;
}NetworkMsg;
其中的NetworkMsg就是我們?cè)谶@里使用的消息內(nèi)容,其中的成員m_msgType 是NetWorkMsgType定義的枚舉值,表示消息類型,另一個(gè)成員m_msgFd是我們跨線程傳遞一個(gè)Id值。
有了以上消息內(nèi)容,我們就可以簡(jiǎn)單的來(lái)定義消息隊(duì)列了
RMsgQueue<NetworkMsg> g_NetWorkMsgQue;
如上已經(jīng)實(shí)例化了一個(gè)RMsgQueue對(duì)象,但是RMsgQueue對(duì)象必須要像RTimer一樣需要調(diào)用CreateLocal或者CreateGlobal才能創(chuàng)建起來(lái),那我們?cè)谀睦飫?chuàng)建它呢?這兩個(gè)創(chuàng)建又有什么區(qū)別呢?由于RMsgQueue可以是全局的也可以是局部的,假如使用局部的,那么類的封裝性較好,而且可以很好的跨進(jìn)程使用,但是缺點(diǎn)是需要傳遞對(duì)象,此時(shí)創(chuàng)建就可以使用CreateGlobal。考慮到我們只在一個(gè)進(jìn)程的不同線程間使用,同一進(jìn)程的線程間可以共享資源(內(nèi)存),所以這里我們就采用簡(jiǎn)單的全局變量來(lái)實(shí)現(xiàn)它,為此創(chuàng)建就用了簡(jiǎn)單的CreateLocal函數(shù),而且我們將其放在了AO的二階段構(gòu)造中,詳見示例代碼。
至于AO的其它封裝,主要是利用RMsgQueue如下三個(gè)函數(shù)
void RMsgQueue::NotifyDataAvailable(TRequestStatus& aStatus);
TInt RMsgQueue::Send(const T& aMsg);
TInt RMsgQueue::Receive(T& aMsg);
先在封裝的AO中調(diào)用NotifyDataAvailable開啟消息的監(jiān)聽,然后就開始等待外部的Send函數(shù)調(diào)用發(fā)消息進(jìn)來(lái),一旦有消息Send進(jìn)來(lái)就進(jìn)入AO的Runl中,我們通過(guò)調(diào)用Receive函數(shù)來(lái)對(duì)傳遞進(jìn)來(lái)的消息內(nèi)容進(jìn)行解析和相應(yīng)處理。邏輯就這么簡(jiǎn)單,下面給出源代碼供參考。
頭文件內(nèi)容:
/*
* MessageQueueAO.h
*
* Created on: 2010-3-30
* Author: frank
*/
#ifndef MESSAGEQUEUEAO_H_
#define MESSAGEQUEUEAO_H_
#include <e32base.h>
#include <e32msgqueue.h>
typedef enum _NetWork_Msg_Type
{
NetworkConnect,
NetworkSocketCreate,
NetworkSocketConnect,
NetworkSocketSend,
NetworkSocketRecv,
NetworkSocketClose,
NetWorkDisConnect
}NetWorkMsgType;
typedef struct _NetWork_Msg
{
NetWorkMsgType m_msgType;
TInt32 m_msgFd;
}NetworkMsg;
const TInt KNumberOfMsgs = 10;
extern RMsgQueue<NetworkMsg> g_NetWorkMsgQue;
class CMessageQueueAO : public CActive
{
public:
// Cancel and destroy
virtual ~CMessageQueueAO();
// Two-phased constructor.
static CMessageQueueAO* NewL();
// Two-phased constructor.
static CMessageQueueAO* NewLC();
public:
// New functions
// Function for making the initial request
TInt StartMessageGet(const TDesC& aText=KNullDesC);
private:
// C++ constructor
CMessageQueueAO();
// Second-phase constructor
void ConstructL();
private:
// From CActive
// Handle completion
void RunL();
// How to cancel me
void DoCancel();
// Override to handle leaves from RunL(). Default implementation causes
// the active scheduler to panic.
TInt RunError(TInt aError);
};
#endif /* MESSAGEQUEUEAO_H_ */
實(shí)現(xiàn)文件內(nèi)容
/*
* MessageQueueAO.cpp
*
* Created on: 2010-3-30
* Author: frank
*/
#include "MessageQueueAO.h"
RMsgQueue<NetworkMsg> g_NetWorkMsgQue;
CMessageQueueAO::CMessageQueueAO()
:CActive(EPriorityHigh)
{
// TODO Auto-generated constructor stub
}
CMessageQueueAO::~CMessageQueueAO()
{
// TODO Auto-generated destructor stub
Cancel();
}
CMessageQueueAO* CMessageQueueAO::NewL()
{
CMessageQueueAO* self = CMessageQueueAO::NewLC();
CleanupStack::Pop(); // self;
return self;
}
CMessageQueueAO* CMessageQueueAO::NewLC()
{
CMessageQueueAO* self = new (ELeave) CMessageQueueAO();
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
void CMessageQueueAO::ConstructL()
{
CActiveScheduler::Add(this); // Add to scheduler
g_NetWorkMsgQue.CreateLocal(KNumberOfMsgs);
}
TInt CMessageQueueAO::StartMessageGet(const TDesC& aText)
{
g_NetWorkMsgQue.NotifyDataAvailable(iStatus);
SetActive(); // Tell scheduler a request is active
}
void SocketCreate(TInt32 aFd)
{
}
void SocketRecv(TInt32 aFd)
{
}
void SocketConnect(TInt32 aFd)
{
}
void CMessageQueueAO::RunL()
{
if (iStatus.Int() == KErrNone)
{
//接收數(shù)據(jù)
NetworkMsg msgfrmq;
g_NetWorkMsgQue.Receive(msgfrmq);
switch(msgfrmq.m_msgType)
{
case NetworkSocketCreate:
LockMutexRaw(g_NetMgrMutex);
SocketCreate(msgfrmq.m_msgFd);
UnlockMutexRaw(g_NetMgrMutex);
break;
case NetworkSocketConnect:
LockMutexRaw(g_NetMgrMutex);
SocketConnect(msgfrmq.m_msgFd);
UnlockMutexRaw(g_NetMgrMutex);
break;
case NetworkSocketRecv:
LockMutexRaw(g_NetMgrMutex);
SocketRecv(msgfrmq.m_msgFd);
UnlockMutexRaw(g_NetMgrMutex);
break;
case NetworkSocketClose:
LockMutexRaw(g_NetMgrMutex);
SocketClose(msgfrmq.m_msgFd);
UnlockMutexRaw(g_NetMgrMutex);
break;
default:
break;
}
//Using ReceiveBlocking()
// msgqueue.ReceiveBlocking(msgfrmq);// 如果使用blocking 可能那邊要發(fā)送兩次消息這邊才會(huì)跑哦
}
g_NetWorkMsgQue.NotifyDataAvailable(iStatus);//開啟下一個(gè)消息的接收
SetActive();
}
void CMessageQueueAO::DoCancel()
{
if( IsActive() )
{
g_NetWorkMsgQue.CancelDataAvailable();
}
}
TInt CMessageQueueAO::RunError(TInt aError)
{
return aError;
}
封裝完這個(gè)AO,大功就可以完成了,將這個(gè)AO放在一個(gè)獨(dú)立的線程中創(chuàng)建起來(lái),并調(diào)用StartMessageGet讓這個(gè)線程始終監(jiān)聽消息。外界線程通過(guò)調(diào)用g_NetWorkMsgQue.Send(msgfrmq)來(lái)給這個(gè)AO線程發(fā)消息并得到相應(yīng)的處理。在這里我就不過(guò)多展開了,因?yàn)橐婕皟蓚€(gè)線程間的操作,篇幅就太大了。
在結(jié)束小結(jié)的時(shí)候,我想著如何實(shí)現(xiàn)IPC,這樣的話,這個(gè)小結(jié)就完整了,碰巧,在逛博客的時(shí)候,發(fā)現(xiàn)一篇博文總結(jié)得很好,而且言簡(jiǎn)意賅,為此轉(zhuǎn)載與下面,跟大家一起分享,博文原址http://blog.sina.com.cn/s/blog_63b4ee0d0100g3xc.html
如何理解進(jìn)程間通信?
先用一個(gè)通俗的例子來(lái)解釋:比如我們需要實(shí)現(xiàn)這樣一種模式,應(yīng)用由進(jìn)程A與進(jìn)程B兩部分組成,A有UI,負(fù)責(zé)用戶交互;B沒(méi)有UI,完全后臺(tái)運(yùn)行,A與B之間可以相互通信。就如彩信的模式,彩信到達(dá)后后臺(tái)下載,下載完給出提示信息(即用戶界面),閱讀彩信再激活“信息”程序。
進(jìn)程通信即兩個(gè)并行進(jìn)程可以通過(guò)互相發(fā)送消息進(jìn)行合作,消息是通過(guò)消息緩沖而在進(jìn)程之間相互傳遞的。
RMsgQueue是3版提供的比較好的與事件機(jī)制融合的技術(shù), 2版只能使用其它傳統(tǒng)的, 信號(hào)量, 共享內(nèi)存等技術(shù).
解決方案:
讓我們來(lái)看代碼:
Server
void CP2PServer::ConstructL()
{
//創(chuàng)建
iMsgQueue.CreateGlobal(KGLobalName, KNumberOfSlots, KMessageLength, EOwnerProcess);
CActiveScheduler::Add( this); // Add to scheduler
iMsgQueue.NotifyDataAvailable( iStatus );//開始監(jiān)聽消息
SetActive(); // Tell scheduler a request is active
}
void CP2PServer::RunL()
{
if (iStatus.Int() == KErrNone)
{
//接收數(shù)據(jù)
TRAPD(error,iMsgQueue.Receive( &str_SendData, KMessageLength));
if(error==KErrNone)
{
iObserver->HandleMessageReceiveL(str_SendData.DataBuf.Left(str_SendData.DataLength));
}
}
}
Client
void CP2PClient::ConstructL()
{
//創(chuàng)建
iMsgQueue.OpenGlobal(KGLobalName, EOwnerProcess);
}
//發(fā)送消息
void CP2PClient::SendMessageL(const TDesC8& aSendMessage)
{
STR_SENDDATA temp;
temp.DataLength = aSendMessage.Length();
temp.DataBuf.Copy(aSendMessage);
iMsgQueue.Send(&temp, KMessageLength);
}
就一些疑難問(wèn)題解決如下:
B線程創(chuàng)建一個(gè)“東東”,名字為KGLobalName,并且B線程開啟監(jiān)控消息, A線程在需要時(shí)使用帶KGLobalName參數(shù)的OpenGlobal函數(shù)打開同名, 然后發(fā)送消息出來(lái), 之后直接關(guān)閉,B會(huì)收到此消息解析后開始進(jìn)行相應(yīng)的處理。
上面的這個(gè)“東東”就是一個(gè)消息隊(duì)列, 只要?jiǎng)?chuàng)建者線程B存在, 就可以使用, 一般這個(gè)設(shè)計(jì)都是B始終運(yùn)行, 并且建立RMsgQueue, 其它進(jìn)程需要時(shí)打開, 發(fā)送消息,然后關(guān)閉。
如果雙方都可以作為發(fā)送方與接受方,則每方都創(chuàng)建RMsgQueue,注意使用不同的名字。
最后除了感謝上面這位牛人給出的總結(jié)外,我再補(bǔ)充一下,如果不是為了避免傳遞消息時(shí)的內(nèi)存拷貝操作,單向操作用C/S架構(gòu)更為可取,至于雙向操作,目前看來(lái)的確還是RMsgQueue好些,不知道大牛們覺(jué)得我這種猜想是否成立。
posted on 2010-04-09 21:40
frank.sunny 閱讀(2026)
評(píng)論(1) 編輯 收藏 引用 所屬分類:
symbian 開發(fā)