发布与订阅设计模式(C语言)

总述

设计模式有很多,发布与订阅模式便是其中之一,具有很好的解耦作用,程序开发量一大,如果程序框架没有设计好,杂糅在一起,后期的维护成本将是巨大的。

模式介绍

发布与订阅模式,由一个消息代理中心基于消息主题进行数据的传输与转发,完全隔离发送者与接收者之间的联系,实现高度的解耦,当然该模式也存在缺点,例如由于存在消息代理,所以效率没有直接传输的高等。

C语言实现

/**
 * @file msg_com.h
 * @author your name (you@domain.com)
 * @brief pub and sub communication mode
 * @version 0.1
 * @date 2023-05-28
 * 
 * @copyright Copyright (c) 2023
 * 
 */

#ifndef MSG_COM_H
#define MSG_COM_H

#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "log.h"

#ifdef __cplusplus
extern "C"
{
#endif

#define  MSG_CENTER_LOG(fmt, ...)  COMLOG_DEBUG_LOG(fmt, ##__VA_ARGS__)

#define  ANY_ID (0xFFFFFFFF)

typedef  void(*HandleEvent)(char *event_name, void *data, int srcId);
typedef  void(*MutexLock)(bool lock);

/* 订阅事件节点 */
typedef struct EventSubscriberNode
{
    char  event[32];
    HandleEvent func;
	int needId;
    struct EventSubscriberNode *next;
}EventSubscriberNode_t;


/* 发布事件节点 */ 
typedef struct EventPublishNode
{
    char  event[32]; 
    void  *data; //内部会拷贝一份,并自动释放
	int   srcId;
    struct EventPublishNode *next;
}EventPublishNode_t;

typedef struct MsgCenterHandle
{
	MutexLock       mutexSubscriber;
    MutexLock       mutexPublish;
	EventSubscriberNode_t *eventSubscriberHead;
	EventPublishNode_t    *eventPublishHead;
	volatile int                   exit_flg;
}MsgCenterHandle_t;

/**
 * @brief init msg center
 * 
 * @param mutexSubscriber mutex lock func
 * @param mutexPublish    mutex lock func
 * @return MsgCenterHandle_t* handle
 */
MsgCenterHandle_t *MsgCenterInit(MutexLock mutexSubscriber, MutexLock mutexPublish);

/**
 * @brief deinit handle and set it null
 * 
 * @param msg_handle MsgCenterHandle_t **
 */
void MsgCenterDeinit(MsgCenterHandle_t **msg_handle);

/**
 * @brief loop call this func, 
 * the shorter the interval, the faster the event execution
 * @param msg_handle MsgCenterHandle_t *
 * @return int -1 error 0 success
 */
int MsgCenterLoop(MsgCenterHandle_t *msg_handle);

/**
 * @brief Subscriber one event
 * 
 * @param handle 
 * @param event event topic name
 * @param func 
 * @param needId only listen the event with the needId(Event sender ID),
 *        ANY_ID not judged needId(Event sender ID)
 * @return int  -1 error 0 success
 */
int SubscriberEvent(MsgCenterHandle_t *handle, char *event, HandleEvent func, int needId);

/**
 * @brief cancel listen one event
 * 
 * @param handle 
 * @param event 
 * @param func 
 * @return int  -1 error 0 success
 */
int ReleaseSubscriberEvent(MsgCenterHandle_t *handle, char *event, HandleEvent func);

/**
 * @brief publish one event
 * 
 * @param handle 
 * @param event event topic name
 * @param data 
 * @param datalen 
 * @param srcId Event sender ID
 * @return int -1 error 0 success
 */
int PublishEvent(MsgCenterHandle_t *handle, char *event, void *data, int datalen, int srcId);

#ifdef __cplusplus
}
#endif


#endif
/**
 * @file msg_com.c
 * @author your name (you@domain.com)
 * @brief pub and sub communication mode
 * @version 0.1
 * @date 2023-05-28
 * 
 * @copyright Copyright (c) 2023
 * 
 */

#include "msg_com.h"


static void MsgMutexLock(MutexLock lock, bool is_lock)
{
    if(lock)
    {
       lock(is_lock);
    }
}

int MsgCenterLoop(MsgCenterHandle_t *msg_handle)
{ 
    //MSG_CENTER_LOG("EventProcess start \r\n");
    MsgCenterHandle_t *handle = (MsgCenterHandle_t *)msg_handle;
    if(handle == NULL)
    {
       MSG_CENTER_LOG("handle is null"); 
       return -1;
    }

	if (!handle->exit_flg)
    {
        EventPublishNode_t cur = {0};
        /*find last valid publish event*/
        MsgMutexLock(handle->mutexPublish, true);
        EventPublishNode_t **curPublish = &handle->eventPublishHead;
        EventPublishNode_t *tempPublish = NULL;
        /*curPublish = last_node->next pointer address, so modify *curPublish = modify last_node->next obj*/
        while ((*curPublish) != NULL )
        {
            if(strlen((*curPublish)->event) > 0)
            {
                if((*curPublish)->next == NULL)
                {
                    memcpy(&cur, *curPublish, sizeof(EventPublishNode_t));
                    break;
                }
 
                memcpy(&cur, *curPublish, sizeof(EventPublishNode_t));
                /*save the next node of valid cur node*/
                tempPublish = (*curPublish)->next;
                break;
            }
            curPublish = &((*curPublish)->next);
        }
        
        if(curPublish != NULL && *curPublish != NULL)
        {
            memset((*curPublish)->event, 0, sizeof((*curPublish)->event));
            free((*curPublish));
            *curPublish = NULL;
            //delete cur node, and use the next node replace
            if(tempPublish)
            {
               *curPublish = tempPublish;
            }
        }   
        MsgMutexLock(handle->mutexPublish, false);
		
        if (strlen(cur.event) <= 0) 
        {
            return 0;
        }
		
        MsgMutexLock(handle->mutexSubscriber, true);
        EventSubscriberNode_t *curSubscriber = (EventSubscriberNode_t *)handle->eventSubscriberHead;
        while (curSubscriber != NULL)
        {
            if(curSubscriber->needId == cur.srcId && strcmp(curSubscriber->event , cur.event) == 0)
            {
                curSubscriber->func(cur.event, cur.data, cur.srcId);
            }
			else if(curSubscriber->needId == ANY_ID && strcmp(curSubscriber->event , cur.event) == 0)
			{
				curSubscriber->func(cur.event, cur.data, ANY_ID);
			}
            curSubscriber = curSubscriber->next;
        }
        MsgMutexLock(handle->mutexSubscriber, false);

        if(cur.data)
        { 
            free(cur.data);
        }   
    }

    //MSG_CENTER_LOG("EventProcess end \r\n");
	return 0;
}


MsgCenterHandle_t *MsgCenterInit(MutexLock mutexSubscriber, MutexLock mutexPublish)
{
	MsgCenterHandle_t *handle = (MsgCenterHandle_t *)malloc(sizeof(MsgCenterHandle_t));
	if(handle == NULL)
	{
		MSG_CENTER_LOG("hanlde malloc fail\r\n");
		return NULL;
	}

	handle->exit_flg = 0;
	
    handle->eventSubscriberHead = (EventSubscriberNode_t *)malloc(sizeof(EventSubscriberNode_t));
	if(handle->eventSubscriberHead == NULL)
	{
	    MSG_CENTER_LOG("eventSubscriberHead malloc fail\r\n");
		goto FAIL;
	}
	
    memset(handle->eventSubscriberHead->event, 0, sizeof(handle->eventSubscriberHead->event));
    handle->eventSubscriberHead->func = NULL;
    handle->eventSubscriberHead->next = NULL;
    
    handle->eventPublishHead = (EventPublishNode_t *)malloc(sizeof(EventPublishNode_t));
	if(handle->eventPublishHead == NULL)
	{
	    MSG_CENTER_LOG("eventPublishHead malloc fail\r\n");
		goto FAIL;
	}
	
    memset(handle->eventPublishHead->event, 0, sizeof(handle->eventPublishHead->event));
    handle->eventPublishHead->next = NULL;
    handle->eventPublishHead->data = NULL;
    
    handle->mutexSubscriber = mutexSubscriber;
    handle->mutexPublish = mutexPublish;
	
	return handle;
    FAIL:
	if(handle->eventSubscriberHead)
	{
		free(handle->eventSubscriberHead);
		handle->eventSubscriberHead = NULL;
	}
	
	if(handle->eventPublishHead)
	{
		free(handle->eventPublishHead);
		handle->eventPublishHead = NULL;
	}
	
	return NULL;
}


void MsgCenterDeinit(MsgCenterHandle_t **msg_handle)
{

	MsgCenterHandle_t *handle = *msg_handle;
	if(handle == NULL)
	{
	   MSG_CENTER_LOG("handle is null\r\n");
	   return;
	}
	
	MsgMutexLock(handle->mutexSubscriber, true);
	EventSubscriberNode_t *subCur =  handle->eventSubscriberHead;
	EventSubscriberNode_t *subNext = subCur->next;
	while(subCur)
	{
		free(subCur);
		subCur = subNext;
		if(subCur)
		   subNext = subCur->next;
	}
	handle->eventSubscriberHead = NULL;
	MsgMutexLock(handle->mutexSubscriber, false);


	MsgMutexLock(handle->mutexPublish, true);
	EventPublishNode_t *pubCur =  handle->eventPublishHead;
	EventPublishNode_t *pubNext = pubCur->next;
	while(pubCur)
	{
		if(pubCur->data)
		{
			free(pubCur->data);
		}
		free(pubCur);
		pubCur = pubNext;
		if(pubCur)
		   pubNext = pubCur->next;
	}
	handle->eventPublishHead = NULL;
	MsgMutexLock(handle->mutexPublish, false);
    handle->exit_flg = 1;
    free(*msg_handle);
	*msg_handle = NULL;
	return;
}


int SubscriberEvent(MsgCenterHandle_t *handle, char * event, HandleEvent func, int needId)
{
	if(handle == NULL || func == NULL || event == NULL)
	{
	   MSG_CENTER_LOG("handle|func|event is null\r\n");
	   return -1;
	}
	
	EventSubscriberNode_t *node = (EventSubscriberNode_t *)malloc(sizeof(EventSubscriberNode_t));
	if(node == NULL)
	{
	   MSG_CENTER_LOG("node is malloc fail\r\n");
	   return -1;
	}
	
	node->func = func;
	node->needId = needId;
	strncpy(node->event, event, sizeof(node->event) - 1);
    node->next = NULL;
	 
	MsgMutexLock(handle->mutexSubscriber, true);
    EventSubscriberNode_t *current = handle->eventSubscriberHead;
	if(current != NULL)
	{
	    while (current->next != NULL)
	    {
	        current = current->next;
			if(strcmp(current->event, event) == 0 && current->func == func && current->needId == needId)
		    {
		       MSG_CENTER_LOG("exist same node %s\r\n", event);
		       free(node);
			   MsgMutexLock(handle->mutexSubscriber, false);
			   return -1;
			}
	    }
	    current->next = node;
	}
	else
	{
        free(node);
	    MsgMutexLock(handle->mutexSubscriber, false);
	    return -1;
	}
    MsgMutexLock(handle->mutexSubscriber, false);
	return 0;
}


int ReleaseSubscriberEvent(MsgCenterHandle_t *handle, char *event, HandleEvent func)
{
	if(handle == NULL || event == NULL || func == NULL)
	{
	   MSG_CENTER_LOG("handle|func|event is null\r\n");
	   return -1;
	}
	
	MsgMutexLock(handle->mutexSubscriber, true);
    EventSubscriberNode_t **current = &handle->eventSubscriberHead;
    while ((*current) != NULL)
    {
        if(strcmp((*current)->event, event) == 0 && (*current)->func == func)
        {
            if((*current)->next == NULL)
            {
                (*current)->func = NULL;
				free(*current);
                (*current) = NULL;
                break;
            }
            EventSubscriberNode_t *de = *current;
            *current = (*current)->next;
            free(de);
            break;
        }
        current = &(*current)->next;
    }
    MsgMutexLock(handle->mutexSubscriber, false);
	
	return 0;
	
}


int PublishEvent(MsgCenterHandle_t *handle, char * event, void *data, int datalen, int srcId)
{
    if(handle == NULL || event == NULL)
	{
	   MSG_CENTER_LOG("handle|event is null\r\n");
	   return -1;
	}
	
	EventPublishNode_t *node = (EventPublishNode_t *)malloc(sizeof(EventPublishNode_t));
	if(node == NULL)
	{
	   MSG_CENTER_LOG("node is malloc fail\r\n");
	   return -1;
	}
	
	node->srcId = srcId;
	strncpy(node->event, event, sizeof(node->event) - 1);


    node->data = malloc(datalen);
    if(node->data == NULL)
    {
        MSG_CENTER_LOG("node->data is malloc fail\r\n");
        free(node);
        return -1;
    }
    

	memcpy(node->data, data, datalen);
    node->next = NULL;
	
	MsgMutexLock(handle->mutexPublish, true);
    EventPublishNode_t *current = handle->eventPublishHead;
	if(current != NULL)
	{
	    while (current->next != NULL)
	    {
	        current = current->next;
	    }
	    current->next = node;
	}
	else
	{
        free(node);
		MsgMutexLock(handle->mutexPublish, false);
		return -1;
	}
    MsgMutexLock(handle->mutexPublish, false);
	return 0;
} 


  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
订阅分发框架是一种常见的设计模式,它用于实现事件驱动的系统。在该框架中,订阅者可以订阅一个或多个事件,发布者可以发布事件,订阅者接收到事件后可以执行相应的操作。下面是一个用C语言实现订阅分发框架的示例代码: ``` #include <stdio.h> #include <stdlib.h> // 定义事件类型 typedef enum { EVENT_TYPE_1, EVENT_TYPE_2, EVENT_TYPE_3 } event_type_t; // 定义事件数据结构 typedef struct { event_type_t type; int data; } event_t; // 定义订阅者数据结构 typedef struct subscriber_t { void (*callback)(event_t event); // 回调函数 struct subscriber_t* next; // 下一个订阅者 } subscriber_t; // 定义发布者数据结构 typedef struct { subscriber_t* subscribers[3]; // 订阅者链表 } publisher_t; // 初始化发布者 void publisher_init(publisher_t* publisher) { for (int i = 0; i < 3; i++) { publisher->subscribers[i] = NULL; } } // 添加订阅者 void publisher_subscribe(publisher_t* publisher, event_type_t type, void (*callback)(event_t event)) { // 创建新的订阅者 subscriber_t* new_subscriber = (subscriber_t*)malloc(sizeof(subscriber_t)); new_subscriber->callback = callback; new_subscriber->next = NULL; // 将订阅者添加到链表中 subscriber_t* last_subscriber = publisher->subscribers[type]; if (last_subscriber == NULL) { publisher->subscribers[type] = new_subscriber; } else { while (last_subscriber->next != NULL) { last_subscriber = last_subscriber->next; } last_subscriber->next = new_subscriber; } } // 发布事件 void publisher_publish(publisher_t* publisher, event_t event) { // 找到该事件的订阅者链表 subscriber_t* subscriber = publisher->subscribers[event.type]; // 遍历链表,调用每个订阅者的回调函数 while (subscriber != NULL) { subscriber->callback(event); subscriber = subscriber->next; } } // 定义回调函数 void callback1(event_t event) { printf("Event type 1 received with data %d\n", event.data); } void callback2(event_t event) { printf("Event type 2 received with data %d\n", event.data); } void callback3(event_t event) { printf("Event type 3 received with data %d\n", event.data); } int main() { // 初始化发布者 publisher_t publisher; publisher_init(&publisher); // 添加订阅者 publisher_subscribe(&publisher, EVENT_TYPE_1, callback1); publisher_subscribe(&publisher, EVENT_TYPE_2, callback2); publisher_subscribe(&publisher, EVENT_TYPE_3, callback3); // 发布事件 event_t event1 = { EVENT_TYPE_1, 123 }; event_t event2 = { EVENT_TYPE_2, 456 }; event_t event3 = { EVENT_TYPE_3, 789 }; publisher_publish(&publisher, event1); publisher_publish(&publisher, event2); publisher_publish(&publisher, event3); return 0; } ``` 在上面的代码中,我们定义了事件类型和事件数据结构,订阅者数据结构和发布者数据结构。发布者通过添加订阅者和发布事件来实现订阅分发框架。我们还定义了三个回调函数,用于处理不同类型的事件。在main函数中,我们创建了一个发布者,添加三个订阅者,并发布了三个事件。当订阅者收到事件时,它们会执行相应的回调函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值