一篇较简单的介绍CS模式的文章
Symbian中的Client/Server机制:
Server和Client一般在不同的进程中运行,因此它们之间的通讯需要使用内核提供的某种IPC机制来进行通讯。Symbian提供一些封装好了的RSessionBase/CServer/CSession类供Client来建立和Server之间的连接,C/S之间通过消息(RMessage类)来进行通信,这样将内核的IPC实现细节隐藏在C++的函数调用中。
可以将Symbian中的C/S结构类似到一个基于TCP的Socket实现中。在一个典型的TCP Socket连接中,大概有如下的步骤:
1. Server启动,阻塞在Accept中等待客户请求
2. Client创建一个Socket,调用Connect尝试进行连接
3. Server从Accept返回,创建一个新的Socket和该Client进行通信
Symbian的C/S结构为:
1. Client创建一个RSessionBase,调用Connect方法连接Server
2. CServer当时阻塞在NewSession中,收到客户连接请求后,创建一个CSession该Client进行通信
3. 然后Client通过RSessionBase::SendReceive发送请求,Server端的CSession收到该请求并执行之
因此,RSessionBase类似于TCP Socket中的Client端的套接字,CServer就是Server端那个Listen的套接字,CSession就是Server端用来和client通信的套接字。当然它们实际上有太多的不同。
稍微具体一点的基本流程是:
首先,Server开发者一般会提供给Client一个RSessionBase的派生类,例如我们常见的RFs。在这个RSessionBase的派生类中,封装了一些函数,例如如何建立一个到Server的连接(按照惯例,该函数命名为Connect),如何将request进行封装等。
Client于Server之间的典型通讯步骤:
1. Client调用一个RSessionBase派生类的Connect函数,例如 RMyServerSession::Connect
2. 该函数内部实现将寻找当前系统运行的Server(通过TFindServer),如果没有发现该Server运行,则;
3. 启动该Server(通过RThread::Create函数在另一个线程中启动,或者通过RProcess::Create函数在一个独立的进程中启动该Server,后面这种比较常见)。
Server被启动后,Server内部的实现大概如下:
在Server线程中,首先安装一个CActiveScheduler,然后创建一个具体的CServer对象,CServer本身继承自CActive,这样就使得Server可以被CActiveScheduler调度。然后启动该Server(调用CServer::StartL,大概CServer的内部实现是调用基类CActive::SetActive,表明自己发出了一个请求,希望该请求完成时被调度,对于CServer,它发出的请求就是希望收到Client的连接),然后进入wait loop(调用CActiveScheduler::Start函数)。当有客户通过CreateSession发送连接请求时,Scheduler将调用CServer::NewSessionL函数。
4. client调用RSessionBase::CreateSession函数建立一个到Server的连接(通过Server的名字)。(估计内核根据Server名字,找到对应该名字的线程RThread,然后就可以建立一个通道,估计RSessionBase中的iHandler成员变量用来代表这个连接通道,同时估计Server端的CSession中的RThread iClient成员变量也被Server用来表示这个连接通道)。
5. 通过某种IPC机制,我们的CServer派生类的NewSesssion函数被调用,(即Client端的CreateSession会对应到Server端的NewSession函数中,当然这是由内核完成的。于是我们创建一个CSession派生类来和Client进行联系,该派生类继承来的CSession对象中包含了一个表示Client所在线程的RThread对象iClient。
6. Client通过RSessionBase的SendReceive发送一个请求并期待获得返回信息。系统内部会将之转换成一个RMessage并调用Server端CSession的一个虚拟函数。
7. Server端代表该client连接的CSession对象(在第5步时被创建)的ServiceL(const RMessage& aMessage)虚拟函数被调用,其中的aMessage对象包含了Client的请求信息。
8. Server根据该aMessage的内容,调用合适的处理函数。
9. Server调用Message().Complete()通知Client该命令处理完毕。 (任何时候都可以调用Message()来获得当前Session中的消息)。内部实现可能是给Client所在的Thread中的信号量加一(CSession中有个成员变量RThread iClient,而RThread中有一个函数可以用来给于该线程关联的信号量加一)。
大致就是如此。
一个简单的C/S端的对应关系如下:
Client Server
RSessionBase::Connect <---> Launch server.exe
RSessionBase::CreateSession <---> CServer::NewSession
RSessionBase::SendReceive <---> CSession::ServiceL