Symbian之client-server架构

1. Client/Server架构
Symibian OS中所有的异步服务都是Server通过C/S架构来实现的。Client是利用Server提供的特定服务的程序,Server接受来至Client 的请求消息并同步或异步的处理他们。C/S架构有如下的优点:
1,可扩展性 
2,有效性:相同的Server可以服务多个Client
3,安全性:ServerClient存在于单独的进程中,并且通过消息传递进行通信。具有错误行为Client 不会使他的Server崩溃(但是,Server可以通过客户线程的句柄来是具有错误行为的Client产生严重错误)。 
4,异步性:在服务器完成请求的时候使用AO机制来通知他的Client。通过AO来挂起线程而不是轮询请求的状 态,Symbian OS减少了处理该请求的处理器周期,从而节约了电源,这对于移动设备来说是非常重要的。
2. Client/Server架构的处理流程
ClinetServer处于不同的进程中,他们无法访问彼此的虚地址空间,所以他们使用消息传递协议来通信,这 种通信的渠道就称为会话。会话由内核创建,同时内核还在所有的Client/Server通信中充当媒介。
服务,特别是系统提供的服务,比如:文件服务,窗口服务和字体和位图服务等都是在系统启动的时候就启动了。当然如果 是自己做的server可以在需要的时候,即当有client发出请求的时候再启动。然后服务器阻塞在某个点上,等待client请求的到来。在 Client发出一个请求后,服务器会new一个子会话来处理这个client的请求,然后自己又继续阻塞在监听请求的点上,以满足其他Client的请 求。每个ClientServer的后续交互都是通过连接Server时创建的Session来完成的。

 

Server主要是用来管理一些共享的资源,如File Server-管理文件系统;Window Server-管理对屏幕、键盘等的共享访问;通常服务和客户端工作在不同的线程或进程,互相之间不干扰。每个服务在启动时候都有一个名字,系统服务的名 字以!开头,每一个服务有对应的API,他们知道对应服务的名字。客户端和服务端通过session进行通信,消息以TIpcArgs的形式进行传递,包 括要访问的服务类型和参数。客户端的RSessionBase和服务端的一个CSession2对应,前者发起连接后,RServer2会从内核负责协调通信的 DServer哪里获得消息,然后CServer2会通过NewSessionL()创建对应CSession2
3. 相关类的分析
跟client-server Framework相关的类有8个: RServer2,CServer2,CSession2,RMessenge2,RMessengePtr2,RSessionBase,RSubsessionBase,TIpcArgs.
CServer2 是一个AO,从客户线程接受请求,然后把这些请求分发到相应的服务器端的客户session。它可以利用客户线程请求来创建服务器端的客户线程。

以下摘自Symbian os源代码
/*
Abstract base class for servers (version 2).
 
This is an active object. It accepts requests from client threads and forwards
them to the relevant server-side client session. It also handles the creation
of server-side client sessions as a result of requests from client threads.
 
A server must define and implement a derived class.
*/
class CServer2 : public CActive
    {
public:
        IMPORT_C virtual ~CServer2() =0;
        IMPORT_C TInt Start(const TDesC& aName);
        IMPORT_C void StartL(const TDesC& aName);
        IMPORT_C void ReStart();
        IMPORT_C void SetPinClientDescriptors(TBool aPin);

protected:
       
inline const RMessage2& Message() const;
        IMPORT_C CServer2(TInt aPriority, TServerType aType=EUnsharableSessions);
        IMPORT_C void DoCancel();
        IMPORT_C void RunL();
        IMPORT_C TInt RunError(TInt aError);
        IMPORT_C virtual void DoConnect(const RMessage2& aMessage);
        IMPORT_C virtual TInt Extension_(TUint aExtensionId, TAny*& a0, TAny* a1);
private:
   
        IMPORT_C virtual CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const =0;
...
};
有两个API需要我们注意一下:
IMPORT_C void StartL(const TDesC& aName);
SDK中描述是把指定名称的Server加入到active scheduler
中,并且触发一个请求。一般这个函数会在我们自己写的类的 ConstructL()中调用。一般,系统的服务器,比如:文件服务器,字体和位图服务器和窗口服务器等都是在系统启动的时候就启动了。但是,如果我们自己的服务器不希望如此,只是在必 要的时候,即当有Client发出request的时候再启动server,我们就需要在ConstructL()中手动调用这个函数了。

IMPORT_C virtual CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const =0;
server端的Session,这个Session代表一个Client和一个Server的通信连接,它可以通过 RSessionBase::CreateSession() 的调用来创建和初始化。也就是说,如果Client端调用了RSessionBase::CreateSession(),那么Server端的 NewSessionL就会被调用ClientServer后续的数据操作 都通过这个Session来完成。
当我们在设计自己的Server的时候,我们需要从CServer2类继承,CServer2是一个AO是从CActive派生的,他实现了 CActive的几个纯虚/虚函数,我们的Server类必须实现NewSessionL这个纯虚函数,当然如果你仅仅实现这个也是useless的。一 般我们会重写CServer2RunError的实现,但是不会去重写RunLDoCancel方法。 
CSession2* CPtpServer::NewSessionL(const TVersion& aVersion,
                                   const RMessage2& /*aMessage*/) const
    {
    ...
    CPtpSession* session = CPtpSession::NewL(const_cast<CPtpServer*>(this));
    return session;
    }
通常,我们给Server传递一个请求,让Server做某些修改后在回传给我们,所以在SendReceive 数中不要传递局部变量的描述符,通常是用一个data member来做。
CSession2服务器端的客户Session,充当ClientServer的通信信道,一个Client线程能和一个 Server并发多个线程。
class CSession2 : public CBase
{
public:
   IMPORT_C virtual ~CSession2() =0;
public:
   inline const CServer2* Server() const;
   IMPORT_C void ResourceCountMarkStart();
   IMPORT_C void ResourceCountMarkEnd(const RMessage2& aMessage);
   IMPORT_C virtual TInt CountResources();

  
virtual void ServiceL(const RMessage2& aMessage) =0;
   IMPORT_C virtual void ServiceError(const RMessage2& aMessage,TInt aError);
...
...
};

virtual void ServiceL(const RMessage2& aMessage) =0;
这个是纯虚函数,所以派生类必须做实现。当Client在建立了和Server的连接后,会通过某个接口向Server发送数据请求等操作,Server 端的Session的虚函数ServiceL()会被调用,在参数RMessage2中会包括Client的请求信息。根据 aMessage.Function()的值做相应的处理。处理完后调用aMessage.Complete( KErrNone );来告知Client处理结束了,通常是增加请求线程上的信号量来告诉Client请求的完成。
 
NONSHARABLE_CLASS(CPtpSession) : public CSession2, MServiceHandlerObserver
        {
public:
        static CPtpSession* NewL(CPtpServer* aServer);
        ~CPtpSession();
        void ServiceL( const RMessage2& aMessage );
}

既然,Server端有个Session,那么Client端也有个相应的Session了,答案是肯定的。它就是RSessionBase,它是Client端的Session 柄,通过这个Client端的接口就可以和Server通信了。

class RSessionBase : public RHandleBase
{
    friend class RSubSessionBase;
   
。。。
    IMPORT_C TInt Open(RMessagePtr2 aMessage,TInt aParam,TOwnerType aType=EOwnerProcess);
    IMPORT_C TInt Open(RMessagePtr2 aMessage,TInt aParam,const TSecurityPolicy& aServerPolicy,TOwnerType aType=EOwnerProcess);
    IMPORT_C TInt Open(TInt aArgumentIndex, TOwnerType aType=EOwnerProcess);
    IMPORT_C TInt Open(TInt aArgumentIndex, const TSecurityPolicy& aServerPolicy, TOwnerType aType=EOwnerProcess);
    inline TInt SetReturnedHandle(TInt aHandleOrError);
    IMPORT_C TInt SetReturnedHandle(TInt aHandleOrError,const TSecurityPolicy& aServerPolicy);
protected:
    inline TInt
CreateSession(const TDesC& aServer,const TVersion& aVersion);
    IMPORT_C TInt
CreateSession(const TDesC& aServer,const TVersion& aVersion,TInt aAsyncMessageSlots);
    IMPORT_C TInt
CreateSession(const TDesC& aServer,const TVersion& aVersion,TInt aAsyncMessageSlots,TIpcSessionType aType,const TSecurityPolicy* aPolicy=0, TRequestStatus* aStatus=0);
    inline TInt
CreateSession(RServer2 aServer,const TVersion& aVersion);
    IMPORT_C TInt
CreateSession(RServer2 aServer,const TVersion& aVersion,TInt aAsyncMessageSlots);
    IMPORT_C TInt
CreateSession(RServer2 aServer,const TVersion& aVersion,TInt aAsyncMessageSlots,TIpcSessionType aType,const TSecurityPolicy* aPolicy=0, TRequestStatus* aStatus=0);
    inline static TInt SetReturnedHandle(TInt aHandleOrError,RHandleBase& aHandle);

    inline TInt
Send(TInt aFunction,const TIpcArgs& aArgs) const;
    inline void
SendReceive(TInt aFunction,const TIpcArgs& aArgs,TRequestStatus& aStatus) const;
    inline TInt
SendReceive(TInt aFunction,const TIpcArgs& aArgs) const;
    inline TInt
Send(TInt aFunction) const;
    inline void
SendReceive(TInt aFunction,TRequestStatus& aStatus) const;
    inline TInt
SendReceive(TInt aFunction) const;
   
。。。
};

这个类提供了很多重载的CreateSession()方 法,根据需要进行选择,涉及到几个概念就是消息槽和IPC Session的类型,可以根据SDK的描述使用,SDK有详细的阐述,没有用到过,这里就不详述了。

SendReceive()也有很多重载的方法,主要是同/异步的选择,那些没有 TRequestStatus参数的API是同步的,一个ClientSessionServer同时只能有一个当前的同步请求,异步请求可以有多 个。


RMessage2 个类封装了Client请求的细节,在ClientServer端传递。

class RMessage2 : public RMessagePtr2
{
    friend class CServer2;
#endif
    inline TInt Function() const;
    inline TInt Int0() const;
    inline TInt Int1() const;
    inline TInt Int2() const;
    inline TInt Int3() const;
    inline const TAny* Ptr0() const;
    inline const TAny* Ptr1() const;
    inline const TAny* Ptr2() const;
    inline const TAny* Ptr3() const;
    inline CSession2* Session() const;
protected:
   
   
    TInt iFunction;

};

这里的Function()对应于RSessionbase::SendReceive(TInt aFunction,const TIpcArgs& aArgs,TRequestStatus& aStatus) 中的aFunctionClient端通过TIpcArgs这个结构包装数据向Server发送请求,TIpcArgs 04个参数,最多只能是4个,如果参数是简单的整型值我们可以通过RMessage2Int0-3这些个API得到他们的值,如果是其他类新,比如 是描述符,那么就的用Ptr0-3()来获得指针了。系统内部会把TIpcArgs的内容封装成RMessage2,并在Server端相应 SessionServiceL()中进行解析。
 

 

 

 

posted on 2010-05-26 22:04  魏方  阅读(1014)  评论(0编辑  收藏  举报

导航