Symbian C/S 机制分析

以下分析基于:

 

Platform :S60 3rd Edition, Feature Pack 2 SDK

 

Operating System :Symbian OS v9.3

 

一,为什么要使用Client/Server架构
在Symibian OS中所有的异步服务都是Server通过C/S架构来实现的。Client是利用Server提供的特定服务的程序,Server接受来至Client的请求消息并同步或异步的处理他们。C/S架构有如下的优点:

1,可扩展性
2,有效性:相同的Server可以服务多个Client。
3,安全性:Server和Client存在于单独的进程中,并且通过消息传递进行通信。具有错误行为Client不会使他的Server崩溃(但是,Server可以通过客户线程的句柄来是具有错误行为的Client产生严重错误)。
4,异步性:在服务器完成请求的时候使用AO机制来通知他的Client。通过AO来挂起线程而不是轮询请求的状态,SymbianOS减少了处理该请求的处理器周期,从而节约了电源,这对于移动设备来说是非常重要的。

二,Client/Server架构的处理流程
l: Clinet和Server处于不同的进程中,他们无法访问彼此的虚地址空间,所以他们使用消息传递协议来通信,这种通信的渠道就称为会话。会话由内核创建,同时内核还在所有的Client/Server通信中充当媒介。
2 :服务,特别是系统提供的服务,比如:文件服务,窗口服务和字体和位图服务等都是在系统启动的时候就启动了。当然如果是自己做的server可以在需要的时候,即当有client发出请求的时候再启动。然后服务器阻塞在某个点上,等待client请求的到来。在Client发出一个请求后,服务器会new一 个子会话来处理这个client的请求,然后自己又继续阻塞在监听请求的点上,以满足其他Client的请求。每个Client和Server的后续交互都是通过连接Server时创建的Session来完成的。

三,R类介绍
C/S架构免不了要使用R类,那么什么是R类呢?这里的"R"是Resource的第一个字母,是资源的意思。
1,简介
具有"R"前缀的类表示某个资源的客户端句柄。应用程序实际上并不拥有资源,资源由设备上一个Symbian OS 服务器所拥有,服务器管理资源的使用。客户可以使用这些句柄访问服务器管理的资源,并请求使用它的功能。
R类没有任何公共的基类,有些分块的派系类,比如,有些类是从RHandleBase派生—RFs,而有的什么都不是—RWindow等。一般来说,R类是 在栈上实例化或是嵌套在C类中,然后通过某种方式打开他们(通常通过一个方法调用来打开,例如调用Open()或Connect())。结束使用它们时, 有必要使用适当的方法来处理这些类(通常使用Close()函数)。如果在完成这项工作时失败,则R类所连接的服务器内存和其他资源泄漏。

2,深入
Symbian 里的R类到底是啥?基本上所有R类都包含一个指向kernel-side object的handle,这个handle是一个整数!通过这个整数,在DObjectIx这个容器中可以索引到R类指 向的对象,这些对象都是从DObject派生出来的,具有引用计数的功能!当user-side的R类调用Close方法后,对应的计数减1,到0时,就会自动析构内核的对象。
那么内核是如何把一个handle的32位整数解析成一个指向内核对象的指针的呢?
struct SDObjectIxRec
{
TInt16 instance;
TInt16 uniqueID;
DObject* obj;
}
这些指针通过SDObjectIxRec包装,放到DObjectIx的数组中,每一个DProcess或者DThread都有一个这样一个数组!(所以,当你创建一个R类的时候需要你指明是thread范围的还是process范围的,他们会被放到不同的地方)。
1.根据BIT30看这个handle是存在thread里还是process的DObjectIx里
2.根据这个handle的BIT0-14来判断这个handle真的在DObjectIx里,数组不越界
3.根据BIT0-14获取这个index对应的SDObjectIxRec
4.根据BIT16-29和获得SDObjectIxRec的instance比对
5.如果一致就无措,这些操作可能会需要NKern::LockSystem()
6.获得SDObjectIxRec的obj指针

四,相关类的分析
CServer2  是一个AO,从客户线程接受请求,然后把这些请求分发到相应的服务器端的客户session。它可以利用客户线程请求来创建服务器端的客户线程。
/*
Abstract base class for servers (version 2). A server must define and implement a derived class. (Note that this class should be used instead of CServer)
*/
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();
/*
Gets a handle to the server.Note that the RServer2 object is classified as Symbian internal, and its member functions cannot be acessed. However, the handle can be passed to the RSessionBase::CreateSession() variants that take a server handle.
*/
inline RServer2 Server() const { return iServer; }
protected:  
inline const RMessage2 Message() const;
IMPORT_C CServer2(TInt aPriority, TServerType aType=EUnsharableSessions);
IMPORT_C void DoCancel(); 4 j( _7 ]- G) G! }5 R
IMPORT_C void RunL();
IMPORT_C TInt RunError(TInt aError);
private:
/*
Creates a server-side session object.
The session represents a communication link between a client and a server, and its creation is initiated by the client through a call to one of the RSessionBase::CreateSession() variants.
A server must provide an implementation, which as a minimum should:
check that the version of the server is compatible with the client by comparing the client supplied version number against the server's version number; it should leave if there is incompatibility.
construct and return the server side client session object.

@param aVersion The version information supplied by the client.
@param aMessage Represents the details of the client request that is requesting the creation of the session.
@return A pointer to the newly created server-side session object.
@see User::QueryVersionSupported()
*/
IMPORT_C virtual CSession2* NewSessionL(const TVersion aVersion,const RMessage2& aMessage) const =0;
};

有两个API需要我们注意一下:
1、IMPORT_C void StartL(const TDesC aName);
SDK中描述是把指定名称的Server加入到AS中,并且触发一个请求。一般这个函数会在我们自己写的类的ConstructL()中调用。因为 CServer2是从CActive派生的,所以我们可以猜测在StartL中,会触发一个请求,然后调用CActive的方法SetActive来表明AO启动了请求。
一般,系统的服务器,比如:文件服务器,字体和位图服务器和窗口服务器等都是在系统启动的时候就启动了。但是,如果我们自己的服务器不希望如此,只是在必要的时候,即当有Client发出request的时候再启动server,我们就需要在ConstructL()中手动调用这个函数了。

2、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就会被调用。
猜测下Server的实现如下:当Client端调用了RSessionBase::CreateSession (),内核找到相应的Server,然后Server线程的AS会check这个Client在Server端有没有对应的Session,如果没有就调用NewSessionL来创建一个Session。Client和Server后续的数据操作都通过这个Session来完成。
当我们在设计自己的Server的时候,我们需要从CServer2类继承,CServer2是一个AO是从CActive派生的,他实现了CActive 的几个纯虚/虚函数,我们的Server类必须实现NewSessionL这个纯虚函数,当然如果你仅仅实现这个也是useless的。一般我们会重写CServer2的RunError的实现,但是不会去重写RunL和DoCancel方法。
通常,我们给Server传递一个请求,让Server做某些修改后再回传给我们,所以在SendReceive函数中不要传递局部变量的描述符,通常是用一个data member来做。

CSession2 服务器端的客户Session, 充当 Client和Server 的通信信道,一个Client线程能和一个Server并发多个线程。
/*
A session can be:
- restricted to the creating thread
- can be shared with other threads in the same process
- can be shared by all threads in the system.
A server must define and implement a derived class. In particular, it must provide an implementation for the ServiceL() virtual function.
Note that this class should be used instead of CSession
*/
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();

/*
Handles the servicing of a client request that has been passed to the server.
This function must be implemented in a derived class. The details of the request are contained within the message. 
@param aMessage The message containing the details of the client request.
*/
  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请求的完成。

既然,Server端有个Session,那么Client端也有个相应的Session了,答案是肯定的。它就是RSessionBase,它是Client端的Session句柄,通过这个Client端的接口就可以和Server通信了。
/*
Clients normally define and implement a derived class to provide a richer interface.
*/
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是同步的,一个Client的Session对Server同时只能有一个当前的同步请求,异步请求可以有多个。

RMessage2 这个类封装了Client请求的细节,在Client和Server端传递。
/*
An object that encapsulates the details of a client request.
*/
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:
/*
 The request type.
 */
    TInt iFunction;
}
这里的 Function ()对应于RSessionbase:: SendReceive (TInt aFunction,const TIpcArgs aArgs,TRequestStatus& aStatus) 中的aFunction。Client端通过TIpcArgs这个结构包装数据向Server发送请求,TIpcArgs 支持0到4个参数,最多只能是4个,如果参数是简单的整型值我们可以通过RMessage2的Int0-3这些个API得到他们的值,如果是其他类型,比如是描述符,那么就的用Ptr0-3()来获得指针了。系统内部会把TIpcArgs的内容封装成RMessage2,并在Server端相应 Session的ServiceL()中进行解析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值