往期知识点记录:
一、前言
本文分析的相关代码实现位于distributedschedule_samgr_lite\interfaces\kits\communication\broadcast\broadcast_interface.h
和distributedschedule_samgr_lite\communication\broadcast\source\pub_sub_implement.c
。 在本系列中将对以下数据结构及函数进行详解。
struct Consumer:消息的接收者
struct Provider:消息的发布者
struct Subscriber:消息的订阅管理者
struct PubSubInterface:发布和订阅的接口
struct PubSubImplement:广播子功能实例
BCE_CreateInstance:创建广播子功能实例
AddTopic:添加主题信息
Subscribe:订阅主题
ModifyConsumer:更改主题的消费者
Unsubscribe:取消主题的订阅
Publish:发布消息
ImmediatelyPublish:立即发布
DefaultHandle:消息处理函数
二、宏定义及数据结构
//当前服务名称
#define BROADCAST_SERVICE "Broadcast"
//当前功能名称
#define PUB_SUB_FEATURE "Provider and subscriber"
//表示事件或数据的主题,用于区分不同类型的事件或数据
typedef uint32 Topic;
//枚举广播服务特有的错误代码
enum BroadcastErrCode {
//当前topic已被订阅,即重复订阅
EC_ALREADY_SUBSCRIBED = EC_SUCCESS + 1
};
//定义topic的消费者,用于接收事件和推送数据
typedef struct Consumer Consumer;
struct Consumer {
//消费者的身份标识
const Identity *identity;
/*
@consumer:数据的消费者
@topic:将被处理的topic
@origin:指向要处理的数据的指针
定义消费者如何处理已发布的topic中的事件或数据。
*/
void (*Notify)(Consumer *consumer, const Topic *topic, const Request *origin);
//检查两个消费者是否相等,防止重复订阅
BOOL (*Equal)(const Consumer *current, const Consumer *other);
};
// 定义topic的事件和数据的提供者,负责将消息发布到指定的topic中
typedef struct Provider Provider;
struct Provider {
//向指定topic发布事件或数据。
BOOL (*Publish)(IUnknown *iUnknown, const Topic *topic, uint8 *data, int16 len);
};
//定义外部接口的订阅者,以订阅主题的事件和数据。
typedef struct Subscriber Subscriber;
//订阅器负责消费者和topic之间的交互以及topic到broadcast的交互
//即负责将topic关联到broadcast中,将消费者关联到topic上
//修改topic的使用者,为消费者退订topic
struct Subscriber {
//将指定的主题添加到Broadcast服务
int (*AddTopic)(IUnknown *iUnknown, const Topic *topic);
//为consumers订阅指定的主题。
int (*Subscribe)(IUnknown *iUnknown, const Topic *topic, Consumer *consumer);
//修改指定主题的使用者。
Consumer *(*ModifyConsumer)(IUnknown *iUnknown, const Topic *topic, Consumer *old, Consumer *current);
//退订指定主题。
Consumer *(*Unsubscribe)(IUnknown *iUnknown, const Topic *topic, const Consumer *consumer);
};
typedef struct PubSubInterface PubSubInterface;
struct PubSubInterface {
INHERIT_IUNKNOWN; //继承iunknown
Subscriber subscriber; //订阅者
Provider provider; //发布者
};
//消息类型
enum MessageID {
MSG_PUBLISH = 1,
MSG_BUTT
};
//继承,并封装PubSubFeature,是广播功能的实例对象
typedef struct PubSubImplement {
INHERIT_IUNKNOWNENTRY(PubSubInterface);//继承接口
PubSubFeature *feature; //功能对象成员
} PubSubImplement;
//广播服务子功能的全局实例
static PubSubImplement g_pubSubImplement = {
DEFAULT_IUNKNOWN_ENTRY_BEGIN,
.subscriber.AddTopic = AddTopic,
.subscriber.Subscribe = Subscribe,
.subscriber.ModifyConsumer = ModifyConsumer,
.subscriber.Unsubscribe = Unsubscribe,
.provider.Publish = Publish,
DEFAULT_IUNKNOWN_ENTRY_END,
.feature = NULL
};
三、函数实现详解
创建广播功能实例
//根据feature创建实例
PubSubImplement *BCE_CreateInstance(Feature *feature)
{
//为PubSubFeature配置实例
g_pubSubImplement.feature = (PubSubFeature *)feature;
return &g_pubSubImplement;
}
为广播功能添加topic
/*
函数功能:添加topic
函数参数:@iUnknown:接口对象
@topic:待添加的topic
函数返回:添加成功 返回EC_SUCCESS,添加失败 返回EC_INVALID or EC_FAILURE or EC_NOMEMORY
函数描述:topic的信息是通过relation来维护的。所以添加topic本质上就是在PubSubFeature功能中添加新的关系,关系包含了topic信息和消费者。
将topic保存到新的关系结点中,然后将它以头插法的方式插入到链表中
*/
static int AddTopic(IUnknown *iUnknown, const Topic *topic)
{
//参数检查
if (iUnknown == NULL || topic == NULL) {
return EC_INVALID;
}
//获取接口的PubSubImplement成员
PubSubImplement *broadcast = GET_OBJECT(iUnknown, PubSubImplement, iUnknown);
if (broadcast->feature == NULL || broadcast->feature->GetRelation == NULL) {
//实例对象未初始化,返回EC_FAILURE
return EC_FAILURE;
}
//查询feature与指定topic的关系
if (broadcast->feature->GetRelation(broadcast->feature, topic) != NULL) {
//关系已存在,无需添加,则返回EC_FAILURE
return EC_FAILURE;
}
//关系不存在,添加topic
//获取指定功能下的关系链表的头节点
Relation *head = &broadcast->feature->relations;
//构建新的关系结点
Relation *newRelation = (Relation *)SAMGR_Malloc(sizeof(Relation));
if (newRelation == NULL) {
//内存分配失败
return EC_NOMEMORY;
}
//初始化relation
newRelation->topic = *topic; //关联topic
newRelation->callbacks.consumer = NULL; //初始化消费者结点
//链表结点指针初始化
UtilsListInit(&newRelation->callbacks.node);
MUTEX_Lock(broadcast->feature->mutex);
//将新的关系结点插入双向链表,头插法
UtilsListAdd(&head->node, &(newRelation->node));
MUTEX_Unlock(broadcast->feature->mutex);
return EC_SUCCESS;
}
订阅topic
/*
函数功能:为消费者订阅指定的topic
函数参数:@iUnknown:统一的接口对象,屏蔽对象细节,通过GET_OBJECT获取真正的对象
@topic:待订阅的topic
@consumer:topic中数据的消费者
函数返回:订阅成功 返回EC_SUCCESS,订阅失败 返回EC_INVALID or EC_FAILURE or EC_ALREADY_SUBSCRIBED or EC_NOMEMORY
函数描述:在指定的功能下创建订阅关系。在订阅前先判断topic是否有效以及是否重复订阅。
若满足订阅条件,则创建新的消费者结点,保存消费者信息,然后将该结点插入到与topic关联的消费者链表中。
*/
static int Subscribe(IUnknown *iUnknown, const Topic *topic, Consumer *consumer)
{
//参数检查
if (iUnknown == NULL || topic == NULL || consumer == NULL) {
return EC_INVALID;
}
//获取PubSubImplement成员
PubSubImplement *broadcast = GET_OBJECT(iUnknown, PubSubImplement, iUnknown);
if (broadcast->feature == NULL || broadcast->feature->GetRelation == NULL) {
//未初始化,返回EC_FAILURE
return EC_FAILURE;
}
//在创建订阅关系前,先查询指定的topic是否存在
Relation *relation = broadcast->feature->GetRelation(broadcast->feature, topic);
if (relation == NULL) {
//topic不存在,返回EC_FAILURE
return EC_FAILURE;
}
//链表中存在包含当前topic的relation,添加消费者
MUTEX_Lock(broadcast->feature->mutex);
ConsumerNode *item = NULL;
//遍历关系链表
UTILS_DL_LIST_FOR_EACH_ENTRY(item, &relation->callbacks.node, ConsumerNode, node) {
//遍历链表,检查当前消费者是否已订阅该topic
if (item->consumer->Equal(item->consumer, consumer)) {
//已订阅,返回EC_ALREADY_SUBSCRIBED,不允许重复订阅
MUTEX_Unlock(broadcast->feature->mutex);
return EC_ALREADY_SUBSCRIBED;
}
}
MUTEX_Unlock(broadcast->feature->mutex);//这里解锁,线程安全?疑惑
//当前消费者未订阅该topic,建立订阅关系
//创建消费者结点
ConsumerNode *consumerNode = (ConsumerNode *)SAMGR_Malloc(sizeof(ConsumerNode));
if (consumerNode == NULL) {
return EC_NOMEMORY;
}
//链表结点指针初始化
UtilsListInit(&consumerNode->node);
//更新消费者信息
consumerNode->consumer = consumer;
MUTEX_Lock(broadcast->feature->mutex);
//获取存储消费者的链表
ConsumerNode *head = &relation->callbacks;
//将关系结点插入双向链表,头插法
UtilsListAdd(&head->node, &consumerNode->node);
MUTEX_Unlock(broadcast->feature->mutex);
return EC_SUCCESS;
}
写在最后
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请看下图提示: