Symbian CS结构主要的类关系如下:
其中重要的几个函数:
一般在client端会有connect(),这个函数是自己写的,名字随便,但是功能是建立server进程(如果有的话就通过名字find),然后调用RSessionBase的 CreateSession()函数,这里面建立了一个session的内核对象。个人感觉凡事R打头的类在symbian里都封装了相应的系统调用。通过这些个操作进入内核。
要注意的是,在建立server进程的时候,一般有一个wait来等server那边start起来,再signal回来。当这个wait开启,server那边进线程函数,e32main里面会自构造一个cserver2的派生类(注意cserver2是一个活动对象)。
这么看server线程里面实际就是一个活动对象套在schedule里面,schedule在不断的循环看看是否有消息从client那边过来。
在构造cserver2的派生类对象的时候,会调用cserver2的startL函数,这个函数很关键。
CServer2::StartL()
{
CreateKernelServerObject; // via system call
default add itself into schedule loop//because in symbian example "acyncclientserver",i can not find schedule::add
//(CServer2),so add AO must be implemented by server2 itself.
SetActive();
set kernel object buff for server.
}
再看一下CServer2的结构
class CServer2::CAtive
{
data:
RMessage2 iMessage;
RServer2 iServer;
}
please note that creating server kernel object use RServer handler which is similar to RSessionBase on client side.
在StartL的最后会在server内核对象里面 把server usermode 的数据结构RMessage2的引用付给server 内核数据结构。这样有数据从client那边过来的时候,内核可以通过先accept 再delivery把数据从client那边拿到server的内核空间,然后再通过startl建立的usermode和kernelmode的关系付给rmessage2.
startl运行完成后,server的内核对象DServer已经建立了,并且DServer buff已经包含了RMessage2的引用。这就是server准备就绪了。
那么下边的工作就是client那边createsession。
CreateSession()
{
CreateSessionKernelObject; //DSession
Add DSession obejct to DServer // via the first param ServerName to loop find in all servers list.
Version match and send AO Status.
}
please note that the first communication between server and client happened at the stage "version match".
版本匹配是第一次通信的发生地,第一次的send recieve就是在这里发生的。这个里面在cserver2收到version之后会根据version建立server端session的内核对象,也是DSession,通过一个虚函数NewSessionL建立的,server usermode的代码必须有一个派生于CSession2的类来实现。这样就真正建立了,一个DServer控制两个DSession。每个 RMessage2 对象里面关联了一个CSession2的指针,这样的话虽然server的buf就一块。但是每个数据都能找到对应的session。RMessage2 有很多份。
后面讲述client的server通信的细节:
client 通过RSessionBase的sendrecieve函数来和server通信。
要注意通信client使用的参数TIpcArgs,实际是个指针数组,用模板构造函数将自己构造成存放最多4个地址的数据结构。
如果这么写 TIpcArgs args( &aPtr ); 那么args的值就是tptr对象的首地址,传到server那边的rmessage2的也是这个地址。
这个数据结构在内核中也有一个结构与之对应,但是内核中的结构里面保留了更多关于client的信息,比如status和 sessionlink。,这些数据最终会在server accept的时候付给server的数据buf,并且reqeustcomplete服务器线程,至此client发数据结束。
在RequestComplte里面给信号量的时候会强制将server的status制成KErrNone,这点是和client那边的区别。
server在收到信号量之后会通过callback的形式将messagedata的ptr付给messageptr。messageptr最终存放的就是RMessage2的引用。至此数据以传送给CServer2。
此时信号量有了,status是kErrNone了,CServer被激活进入runl处理数据,处理完通过Complete写回client,这会把client的status强制弄成KErrNone,这就是前面讲到的不一样的地方,server这边置client status的方法在客户代码里面,而client置server status的代码在内核。
complete将RMessgae2的数据通过sessionlink找到原来的那个client session的messagedata地址,在返回去。 同样里面会给client thread 信号量,至此一次数据传递结束。
这里在写程序的时候最好要保证你用server WriteL的时候,你client的那边的数据载体的生命周期够长,否则就会一直WriteL的leave, bad desciriptor错误 -38. 建议在client那边弄个成员变量Tbuf什么的,直接把TBuf package and send 过去。
Notes: 在client createprocess的时候, 可以不用RSamepore.wait 来等待服务器那边 RSemapore.signal, symbian 提供了现在client那边把你的服务器这样
RProcess server;
server.Logon();
User::WaitForRequest();
然后等server初始化好了在server那边 用RProcess::::Rendezvous() 去request 所有logon的线程。
从CS架构的示例代码中看到两种方式.
1. 信号量。来自S60Ex/ClientServerSync
//=================client process========
RSemaphore semaphore;
result = semaphore.CreateGlobal( KTimeServerSemaphoreName, 0 );
if ( result != KErrNone )
{
return result;
}
result = CreateServerProcess();
if ( result != KErrNone )
{
return result;
}
semaphore.Wait();
semaphore.Close();
//===========server process========
RSemaphore semaphore;
User::LeaveIfError( semaphore.OpenGlobal( KTimeServerSemaphoreName ) );
// Semaphore opened ok
semaphore.Signal();
semaphore.Close();
2. RProcess::Rendezvous 这个更简单
//============client process================
TRequestStatus stat;
server.Rendezvous(stat);
if(stat != KRequestPending)
{
server.Kill(0); // abort startup
}
else
{
server.Resume(); // logon OK - start the server
}
User::WaitForRequest(stat); // wait for start or death
//===========server process========
RProcess::Rendezvous(KErrNone);