通过理解函数指针构建回调函数来实现mosquitto源码功能

Posted Shemesz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过理解函数指针构建回调函数来实现mosquitto源码功能相关的知识,希望对你有一定的参考价值。

1.指针(多级指针)

我们从人类起源时期讲起,说从前…hahahaha开玩笑了,但是理解这一切的基础就是先明白 指针到底是什么?
下面通过一段代码来理解复习一下指针

int main (int argc, char **argv)
{
	int				a = 0x12345678;
	int		   *    p1;     //一级指针
	int		   *   *p2;     //二级指针:指向p2指针 的指针
	int 	   *  **p3;     //三级指针:指向p3指针的指针 的指针

	p1 = &a;       //p1保存着a的地址:p1指向a  
	p2 = &p1;      //p2保存着p1的地址:p2指向p1
	p3 = &p2;      //p3保存着p2的地址:p3指向p2
	
	printf("a:%s\\n", a);                 //查看a的值
	printf("*p1:%s\\n", *p1);             //取值运算符*查看p1指向地址空间的值
	printf("**p2:%s\\n", **p2);           //查看p2指向的p1指向地址空间的值
	printf("***p3:%s\\n", ***p3);          //查看p3指向的p2指向的怕p1指向的地址空间的值

    return 0}

打印结果:(明白了吗?就是一级一级访问想要的数据)

小技巧:(当然个人见解)
想必大家都应该玩过开心消消乐吧,其实(取值运算符*)与(取址运算符&)可以看作消消乐,这俩碰一起,就能消掉,再来一对再消,最后剩下什么,结果就是什么

2.函数指针

基于前面多级指针的理解,那么当我们将一个指针指向一个变量名的时候就表达为:*p1 =a ;那么我们是否也可以理解,当我们想用指针(*ptr)指向一个函数( int abc(int a, int b) )的函数名(abc)的时候,是否也可以表达为: int (*ptr)(int a, int b),这就是函数指针的原理,(ptr)必须要用括号,否则星号会和返回值类型int结合起来,改变返回值的类型变为指针类型!

下面我们通过一段代码来理解一下:

#include <stdio.h>
  
int add(int a, int b)
{
    return a+b;
}

int mul(int a, int b)
{
    return a*b;
}


int main (int argc, char **argv)
{
    int     val;
    int (*ptr)(int,int);   //声明这是一个指针函数,形参只声明类型即可

    ptr = add;             //指针指向函数变量名
    val = ptr(4,5);        //现在ptr就是函数指针,通过声明的函数的参数形式传值
    printf("func add's val:%d\\n", val);


    ptr = mul;
    val = ptr(2,5);
    printf("func mul's val:%d\\n", val);


    return 0;
}

打印结果

3.回调函数

什么是回调函数呢?答案其实五花八门,还是需要自己写代码才能理解更深刻

个人理解:
  调用某一个函数,然后将另外一个函数作为参数传递进来叫回调函数,英文就叫callback函数,当然理解这句话可能看的明白,但不知道具体实现呢,废话不多说,上代码,保你一眼看会!

#include <stdio.h>
  
int add(int a, int b)
{
    return a+b;
}

int sub(int a, int b)
{
    return a-b;
}

int mul(int a, int b)
{
    return a*b;
}

void process_data(int a, int b, int (*callback)(int, int))   //回调函数就由函数指针来接收(想起上面函数指针写的代码了吗?就是为这个做准备)
{
    printf("process data : %d\\n", callback(a, b));
}

int main (int argc, char **argv)
{
    process_data(3, 4, add);    //将数值与回调函数作为参数传进去,add就是回调函数
    process_data(3, 4, sub);
    process_data(3, 4, mul);


    return 0;
}

打印结果:

4.注册函数

简答来说就是,注册这样一个函数,当条件满足的时候,就调用这个函数,我们来简单实现一下

#include <stdio.h>
#include <unistd.h>

void print_odd(void)
{
    printf("odd data\\n");
}


void print_even(void)
{
    printf("even data\\n");
}

void (*reg_func[1])(void);   //注册这样一个函数

int main (int argc, char **argv)
{
    int i;

    reg_func[0] = print_odd;    //奇数
    reg_func[1] = print_even;   //偶数

    while(1)
    {
        reg_func[i%2]();      //取余结果只有0和1,是偶数就打印偶数,是奇数就打印奇数
        i++;
        sleep(1);
    }

    return 0;
}

(其实Linux的最常见的注册函数是信号处理函数single() )

5.typedef的妙用

上面提到signal这个函数的实现,我们在Linux下来man观察一下

要实现signal()函数我们应该怎样写


看出返回值也是调用的回调函数类型,这样写又绕又难懂,可signal()就是这么实现的,那怎么好懂一点呢?这里就想到了typedef的妙用

再来看看官方man手册是怎么定义的(如出一辙),这样就十分简单易懂

那这样一来,我们也就可以把上面的注册函数改简单一点

#include <stdio.h>
#include <unistd.h>

void print_odd(void)
{
    printf("odd data\\n");
}


void print_even(void)
{
    printf("even data\\n");
}


typedef void (*func_t)(void);


int main (int argc, char **argv)
{
    int i;

    func_t      reg_func[1];   //直接定义func_t类型的数组

    reg_func[0] = print_odd;
    eeeeeeeeeee = print_even;



    while(1)
    {
        reg_func[i%2]();      //取余结果只有0和1,是偶数就打印偶数,是奇数就打印奇数
        i++;
        sleep(1);
    }

    return 0;
}

是的这样直接将一个复杂的函数指针定义为一个类型,用的时候直接用这种类型定义出一个变量即可,十分方便

6.实现mosquitto源码功能

实现mosquitto功能前,小伙伴们可以先了解一下mosquitto函数及实现,点击下方,可以对比观看,感受更深!
mosquitto库函数
mosquitto发布与订阅的实现

首先我们的mosquitto功能的核心就是就是 struct mosquitto *mosq这个结构体我们来设计一下

  • 首先,我们看mosquitto函数的时候发现所有的用户数据都是通过mosq这个结构体拿到的,所以我们肯定要设计一个指针来接收数据
struct mosqiutto
{
	void		*user_data;
}
  • 然后我们来定义一下回调函数
  • 完成回调函数定义,我们就将他加进结构体,这样设置的注册函数,当条件满足调用回调函数的时候,就可以从mosq结构体里面拿到要用的回调函数
  • 这样在头文件中定义,一前一后都互相包含了对方的定义,回调函数的定义里有mosq结构体的定义,mosq结构体又包含回调函数,鸡生蛋还是蛋生鸡的世纪难题,当然编译器会有解决方法,我们只需要这样定义即可解决

    下面就是我们的源码实现了
  • mosquitto.c (功能函数文件)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "mosquitto.h"

struct mosquitto *mosquitto_new(void *args)
{
    struct mosquitto    *mosq;

    mosq = malloc(sizeof(*mosq));      //开辟一段内存空间

    mosq->user_data = args;           //将传进来的用户数据,保存到结构体对应接口里
}

int mosquitto_connect_callback_set(struct mosquitto *mosq, mosq_callback_t cb)
{
    mosq->conn_cb = cb;       //传进来的回调函数保存到结构体的对应接口里去
}


int mosquitto_disconnect_callback_set(struct mosquitto *mosq, mosq_callback_t cb)
{
    mosq->disc_cb = cb;     传进来的回调函数保存到结构体的对应接口里去
}

int mosquitto_loop_forever(struct mosquitto *mosq)
{
    while(1)
    {
        mosq->conn_cb(mosq, mosq->user_data);  //调用回调函数
        sleep(1);

        mosq->disc_cb(mosq, mosq->user_data);  //调用回调函数
        sleep(1);
   }
}

  • mosquitto.h
#ifndef _MOSQUITTO_H_
#define _MOSQUITTO_H_

struct mosquitto;  //表示声明,我后面会定义这样一个结构体

typedef void (*mosq_callback_t)(struct mosquitto *, void *);

struct mosquitto
{
    void                *user_data;    //用一个指针来接受用户数据的地址

    mosq_callback_t     conn_cb;      //连接的回调
    mosq_callback_t     disc_cb;      //断开的回调

};

struct mosquitto *mosquitto_new(void *args);
int mosquitto_connect_callback_set(struct mosquitto *mosq, mosq_callback_t cb);
int mosquitto_disconnect_callback_set(struct mosquitto *mosq, mosq_callback_t cb);
int mosquitto_loop_forever(struct mosquitto *mosq);

#endif

  • main.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "mosquitto.h"

typedef struct mqtt_ctx_s
{
    char    devID[32];
    int     devType;

}mqtt_ctx_t;

void pub_connect_callback(struct mosquitto *mosq, void *args)       //连接回调函数
{
    mqtt_ctx_t              *mqtt_ctx = (mqtt_ctx_t*)args;    
    printf("Device [%s] got connect\\n", mqtt_ctx->devID);     
}

void pub_disconnect_callback(struct mosquitto *mosq, void *args)   //断开的回调函数
{
    mqtt_ctx_t              *mqtt_ctx = (mqtt_ctx_t*)args;
    printf("Device [%s] got disconnect\\n", mqtt_ctx->devID);
}

int main(int argc, char **argv)
{
    mqtt_ctx_t          mqtt_ctx;
    struct mosquitto    *mosq;

    strncpy(mqtt_ctx.devID, "abc", sizeof(mqtt_ctx.devID));   //初始化结构体
    mqtt_ctx.devType = 1;

    mosq = mosquitto_new(&mqtt_ctx);     //传入用户数据

    mosquitto_connect_callback_set(mosq, pub_connect_callback);   //设置回调函数,将上面设置好的回调函数传入mosq结构体,条件满足时调用回调
    mosquitto_disconnect_callback_set(mosq, pub_disconnect_callback); 


    mosquitto_loop_forever(mosq);    //在函数里循环调用mosq里设置好的回调函数

    free(mosq);  //释放结构体
    return 0;
}

打印结果:(类似于mosquitto的循环发布)

这就是一个简单的功能模块实现,基于一些所学知识的应用,谢谢阅读!

以上是关于通过理解函数指针构建回调函数来实现mosquitto源码功能的主要内容,如果未能解决你的问题,请参考以下文章

通过理解函数指针构建回调函数来实现mosquitto源码功能

通过理解函数指针构建回调函数来实现mosquitto源码功能

回调函数的简单理解与实现

如何快速理解函数指针与回调函数?

js 回调函数理解与应用

java里面,c里面都有回调函数,回调函数都是啥东西啊???