OpenHarmony轻量系统服务管理|进程间通信的客户端代理详解(一)

100 篇文章 1 订阅
100 篇文章 0 订阅

往期知识点记录:

一、前言

上一篇文章讲解了高效、便携的工厂模式,并分析了如何使用工厂方式创建及销毁客户端代理的相关代码。在本文中进一步分析客户端代理的系列函数。相关代码实现位于distributedschedule_samgr_lite\samgr_endpoint\source\default_client.c

二、数据结构分析

//标识服务名称和功能名称
struct SaName {
    const char *service;        //service的name
    const char *feature;        //feature的name
};
//客户端代理的头部信息
struct IClientHeader {
    SaName key;             //sa的标识,包含service的name和feature的name
    SvcIdentity target;     //目标(服务端)的访问地址
    uint32 deadId;
    const IpcContext *context;  //IPC通信上下文
};
//客户端代理入口
struct IClientEntry {
    //接口继承
    INHERIT_IUNKNOWNENTRY(IClientProxy);
};
#pragma pack(1)
struct IDefaultClient {
    IClientHeader header;   //客户端代理头部
    IClientEntry entry;     //客户端代理入口
};
#pragma pack()
//默认代理入口
static const IClientEntry DEFAULT_ENTRY = {CLIENT_IPROXY_BEGIN, .Invoke = ProxyInvoke, IPROXY_END};

函数实现详解

创建客户端代理
/*
    函数功能:创建客户端代理并获取代理接口
    函数参数:@context:IPC通信上下文
             @service:服务名称
             @feature:功能名称
    函数返回:创建成功 返回代理接口,创建失败 返回NULL
    函数描述:首先向主endpoint查询指定服务和功能的访问地址,若返回无效值,则该服务和功能不存在或未注册。
             若查询成功,则作为当前客户端代理的目标地址。
             然后使用工厂方法创建相应的客户端代理,并更新代理的成员。
             最后注册死亡回调函数,并返回代理接口。
*/
IUnknown *SAMGR_CreateIProxy(const IpcContext *context, const char *service, const char *feature)
{
    //通过当前进程的endpoint向主endpoint发送查询指定服务和功能的访问地址的请求
    SvcIdentity identity = QueryIdentity(context, service, feature);
    if (identity.handle == INVALID_INDEX) {
        //返回的访问地址是无效值
        return NULL;
    }
    //使用工厂方法为服务和功能创建客户端代理
    IDefaultClient *client = SAMGR_CreateIClient(service, feature, sizeof(IClientHeader));
    if (client == NULL) {
        //通过创建器创建失败,自行申请内存创建
        client = SAMGR_Malloc(sizeof(IDefaultClient));
        if (client == NULL) {
            return NULL;
        }
        //为客户端代理配置默认入口
        client->entry = DEFAULT_ENTRY;
    }
    //对client的头部赋值,这里额外定义一个IClientHeader指针只是为了方便赋值
    IClientHeader *header = &client->header;
    header->target = identity;      //指定客户端代理所面向的目标的访问地址
    //key值用于标识当前代理是与哪个服务和功能关联的
    header->key.service = service;  //服务名称
    header->key.feature = feature;  //功能名称
    header->context = context;      //当前进程的IPC上下文,标识IPC通信访问的入口
    //注册死亡回调函数,当本进程与远程服务断开连接时触发
    (void)RegisterDeathCallback(context, identity, OnServiceExit, client, &header->deadId);
    IClientEntry *entry = &client->entry;
    //对client的入口中的函数指针赋值
    entry->iUnknown.Invoke = ProxyInvoke;   //代理的功能函数
    entry->iUnknown.AddRef = AddRef;        //增加引用计数
    entry->iUnknown.Release = Release;      //减少引用计数
    //从entry中获取IUnknown接口对象
    return GET_IUNKNOWN(*entry);
}
获取服务和功能的访问地址
/*
    函数功能:获取指定服务和功能的访问地址
    函数参数:@service:服务名称
             @feature:功能名称
    函数返回:获取成功 返回真实的访问地址,获取失败 返回无效的访问地址
    函数描述:1.根据服务名和功能名查询客户端代理接口,该代理接口关联了服务和功能的访问地址。
             2.获取接口中的IDefaultClient成员,最后从这个成员头部字段中得到服务和功能的访问地址。
*/
SvcIdentity SAMGR_GetRemoteIdentity(const char *service, const char *feature)
{
    SvcIdentity identity = {INVALID_INDEX, INVALID_INDEX, INVALID_INDEX};
    //根据服务名称和功能名称查找指定的代理接口。存在则返回,不存在则创建它并返回
    IUnknown *iUnknown = SAMGR_FindServiceApi(service, feature);
    if (iUnknown == NULL) {
        return identity;
    }
    IClientProxy *proxy = NULL;
    //查询指定版本的IUnknown接口的子类对象,结果保存在proxy中
    if (iUnknown->QueryInterface(iUnknown, CLIENT_PROXY_VER, (void **)&proxy) != EC_SUCCESS || proxy == NULL) {
        return identity;
    }
    //获取代理的IDefaultClient成员
    struct IDefaultClient *client = GET_OBJECT(proxy, struct IDefaultClient, entry.iUnknown);
    //获取头部中的目标地址信息
    identity = client->header.target;
    //释放引用
    proxy->Release((IUnknown *)proxy);
    return identity;
}
获取saname
/*
    函数功能:从代理接口中获取saname
    函数描述:获取指定代理接口关联的saname信息,它唯一标识了服务和功能
*/
SaName *SAMGR_GetSAName(const IUnknown *proxy)
{
    //获取代理接口的IDefaultClient成员
    IDefaultClient *client = GET_OBJECT(proxy, IDefaultClient, entry.iUnknown);
    //返回头部中的SaName信息
    return &(client->header.key);
}
键值比较函数
/*
    函数功能:键值比较函数,用于比较SaName类型的数据
    函数参数:@key:saname类型的数据,唯一标识服务和功能
    函数返回:相等 返回0,小于 返回-1,大于 返回1
    函数描述:由于saname的两个成员均为指针,所以先比较指针保存的地址是否相同。
             若不相同,则继续比较指向的值是否相同。
*/
int SAMGR_CompareSAName(const SaName *key1, const SaName *key2)
{
    //若相等返回0
    if (key1 == key2) {
        return 0;
    }
    //判断key1和key2的service指向的地址是否相同
    if (key1->service != key2->service) {
        //不相同,则比较指向的内容
        int ret = strcmp(key1->service, key2->service);
        if (ret != 0) {
            //若不相等则返回
            return ret;
        }
    }
    //key1和key2的service指向的值相同
    //比较feature指向的地址是否相同
    if (key1->feature == key2->feature) {
        return 0;
    }
    //服务不能为NULL,但是服务下属的功能可以为NULL。所以这里需要判断feature是否指向NULL
    if (key1->feature == NULL) {
        return -1;
    }
    if (key2->feature == NULL) {
        return 1;
    }
    //比较feature指针指向的内容
    return strcmp(key1->feature, key2->feature);
}
增加引用
//为iUnknown接口增加引用
static int AddRef(IUnknown *iUnknown)
{
    MUTEX_Lock(g_mutex);
    //为iUnknown接口增加引用,返回引用值
    int ref = IUNKNOWN_AddRef(iUnknown);
    MUTEX_Unlock(g_mutex);
    return ref;
}

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请看下图提示:
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值