首页
社区
课程
招聘
[原创]物联网安全himqtt防火墙数据结构之红黑树源码分析
发表于: 2019-11-19 09:50 9380

[原创]物联网安全himqtt防火墙数据结构之红黑树源码分析

2019-11-19 09:50
9380

物联网安全himqtt防火墙数据结构之红黑树源码分析

    随着5G的发展,物联网安全显得特别重要,himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWall,C语言编写,采用epoll模式支持IoT数十万的高并发连接,并且兼容ModSecurity部分规则。 代码非常优秀,非常值得收藏和学习。

    红黑树是一种特殊的自平衡二叉树数据结构,其自旋和左旋乃天才的设计,linux内核中大量使用。阿里巴巴面试非常看重红黑树,因为从几十万的数据中,用短短的几步就可能查到所要的数据,其原理教程网上很多,今天我们从首款物联网防火墙himqtt里面上分析红黑树源码。

    首先在github上下载himqtt的源码:https://github.com/qq4108863/himqtt

找到src目录下的mqtt_rbtree.c和mqtt_rbtree.h文件,结构图如下:

 

typedef ngx_uint_t  ngx_rbtree_key_t;

typedef ngx_int_t   ngx_rbtree_key_int_t;

/* 红黑树节点结构 */

typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;

struct ngx_rbtree_node_s {

    ngx_rbtree_key_t       key;     /* 节点的键值 */

    ngx_rbtree_node_t     *left;    /* 节点的左孩子 */

    ngx_rbtree_node_t     *right;   /* 节点的右孩子 */

    ngx_rbtree_node_t     *parent;  /* 节点的父亲 */

    u_char                 color;   /* 节点的颜色 */

    u_char                 data;    /* */};

typedef struct ngx_rbtree_s  ngx_rbtree_t;

typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

/* 红黑树结构 */

struct ngx_rbtree_s {

    ngx_rbtree_node_t     *root;    /* 指向树的根节点 */

    ngx_rbtree_node_t     *sentinel;/* 指向树的叶子节点NIL */

    ngx_rbtree_insert_pt   insert;  /* 添加元素节点的函数指针,解决具有相同键值,但不同颜色节点的冲突问题;

                                     * 该函数指针决定新节点的行为是新增还是替换原始某个节点*/};

/* 给节点着色,1表示红色,0表示黑色  */

#define ngx_rbt_red(node)               ((node)->color = 1)

#define ngx_rbt_black(node)             ((node)->color = 0)/* 判断节点的颜色 */

#define ngx_rbt_is_red(node)            ((node)->color)

#define ngx_rbt_is_black(node)          (!ngx_rbt_is_red(node))/* 复制某个节点的颜色 */

#define ngx_rbt_copy_color(n1, n2)      (n1->color = n2->color)

/* 节点着黑色的宏定义 *//* a sentinel must be black */

#define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)

/* 初始化红黑树,即为空的红黑树 *//* tree 是指向红黑树的指针,

 * s 是红黑树的一个NIL节点,

 * i 表示函数指针,决定节点是新增还是替换

 */

#define ngx_rbtree_init(tree, s, i)                                           \

    ngx_rbtree_sentinel_init(s);                                              \

    (tree)->root = s;                                                         \

    (tree)->sentinel = s;                                                     \

    (tree)->insert = i

/* 左旋转操作 */

static ngx_inline voidngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,

    ngx_rbtree_node_t *node){

    ngx_rbtree_node_t  *temp;

 /*

       |                       |

       x                       y

      / \                     / \

     a   y     ---left--->   x   c

        / \                 / \

       b   c               a   b

     */

/*node = x*/

    temp = node->right;/* temp为node节点的右孩子 */

    node->right = temp->left;/* 设置node节点的右孩子为temp的左孩子 */

    if (temp->left != sentinel) {

        temp->left->parent = node;

    }

    temp->parent = node->parent;

    if (node == *root) {

        *root = temp;

    } else if (node == node->parent->left) {

        node->parent->left = temp;

    } else {

        node->parent->right = temp;

    }

    temp->left = node;

node->parent = temp;}

/* 右旋转操作 */

static ngx_inline voidngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,

    ngx_rbtree_node_t *node){

    ngx_rbtree_node_t  *temp;

   /*

       |                       |

       x                       y

      / \                     / \

     a   y     <---right--   x   c

        / \                 / \

       b   c               a   b

     */

    /*node = y*/

    temp = node->left;

    node->left = temp->right;

    if (temp->right != sentinel) {

        temp->right->parent = node;

    }

    temp->parent = node->parent;

    if (node == *root) {

        *root = temp;

    } else if (node == node->parent->right) {

        node->parent->right = temp;

    } else {

        node->parent->left = temp;

    }

    temp->right = node;

    node->parent = temp;}

/* 获取红黑树键值最小的节点 */

static ngx_inline ngx_rbtree_node_t *ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel){

    while (node->left != sentinel) {

        node = node->left;

    }

    return node;}

/* 插入节点 *//* 插入节点的步骤:

 * 1、首先按照二叉查找树的插入操作插入新节点;

 * 2、然后把新节点着色为红色(避免破坏红黑树性质5);

 * 3、为维持红黑树的性质,调整红黑树的节点(着色并旋转),使其满足红黑树的性质;

 */

voidngx_rbtree_insert(ngx_thread_volatile ngx_rbtree_t *tree,

    ngx_rbtree_node_t *node){

    ngx_rbtree_node_t  **root, *temp, *sentinel;

    /* a binary tree insert */

    root = (ngx_rbtree_node_t **) &tree->root;

    sentinel = tree->sentinel;

    /* 若红黑树为空,则比较简单,把新节点作为根节点,

     * 并初始化该节点使其满足红黑树性质

     */

    if (*root == sentinel) {

        node->parent = NULL;

        node->left = sentinel;

        node->right = sentinel;

        ngx_rbt_black(node);

        *root = node;

        return;

    }

    /* 若红黑树不为空,则按照二叉查找树的插入操作进行

     * 该操作由函数指针提供

     */

    tree->insert(*root, node, sentinel);

    /* re-balance tree */

    /* 调整红黑树,使其满足性质,

     * 其实这里只是破坏了性质4:若一个节点是红色,则孩子节点都为黑色;

     * 若破坏了性质4,则新节点 node 及其父亲节点 node->parent 都为红色;

     */

    while (node != *root && ngx_rbt_is_red(node->parent)) {

        /* 若node的父亲节点是其祖父节点的左孩子 */

        if (node->parent == node->parent->parent->left) {

            temp = node->parent->parent->right;/* temp节点为node的叔叔节点 */

            /* case1:node的叔叔节点是红色 */

            /* 此时,node的父亲及叔叔节点都为红色;

             * 解决办法:将node的父亲及叔叔节点着色为黑色,将node祖父节点着色为红色;

             * 然后沿着祖父节点向上判断是否会破会红黑树的性质;

             */

            if (ngx_rbt_is_red(temp)) {

                ngx_rbt_black(node->parent);

                ngx_rbt_black(temp);

                ngx_rbt_red(node->parent->parent);

                node = node->parent->parent;

            } else {

                /* case2:node的叔叔节点是黑色且node是父亲节点的右孩子 */

                /* 则此时,以node父亲节点进行左旋转,使case2转变为case3;

                 */

                if (node == node->parent->right) {

                    node = node->parent;

                    ngx_rbtree_left_rotate(root, sentinel, node);

                }

                /* case3:node的叔叔节点是黑色且node是父亲节点的左孩子 */

                /* 首先,将node的父亲节点着色为黑色,祖父节点着色为红色;

                 * 然后以祖父节点进行一次右旋转;

                 */

                ngx_rbt_black(node->parent);

                ngx_rbt_red(node->parent->parent);

                ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);

            }

        } else {/* 若node的父亲节点是其祖父节点的右孩子 */

            /* 这里跟上面的情况是对称的,就不再进行讲解了

             */

            temp = node->parent->parent->left;

            if (ngx_rbt_is_red(temp)) {

                ngx_rbt_black(node->parent);

                ngx_rbt_black(temp);

                ngx_rbt_red(node->parent->parent);

                node = node->parent->parent;

            } else {

                if (node == node->parent->left) {

                    node = node->parent;

                    ngx_rbtree_right_rotate(root, sentinel, node);

                }

                ngx_rbt_black(node->parent);

                ngx_rbt_red(node->parent->parent);

                ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);

            }

        }

    }

    /* 根节点必须为黑色 */

    ngx_rbt_black(*root);}

/* 这里只是将节点插入到红黑树中,并没有判断是否满足红黑树的性质;

 * 类似于二叉查找树的插入操作,这个函数为红黑树插入操作的函数指针;

 */

voidngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,

    ngx_rbtree_node_t *sentinel){

    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        /* 判断node节点键值与temp节点键值的大小,以决定node插入到temp节点的左子树还是右子树 */

        p = (node->key < temp->key) ? &temp->left : &temp->right;

        if (*p == sentinel) {


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
最新回复 (4)
雪    币: 3359
活跃值: (10992)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
2
为什么要把一个算法,和这么多名词堆在一起,搞了一个很大的标题,在代码里加了一大堆注释,不懂的人看了还是不懂,看看别人写的:https://blog.csdn.net/msdnwolaile/article/details/51985842
2019-11-19 10:09
0
雪    币: 6790
活跃值: (4441)
能力值: (RANK:600 )
在线值:
发帖
回帖
粉丝
3
虽然文章内容讲算法,也和物联网没有多大关系,但还是感谢楼主的无私付出。
2019-11-19 11:33
0
雪    币: 270
活跃值: (1662)
能力值: ( LV5,RANK:75 )
在线值:
发帖
回帖
粉丝
4
提点小建议,我感觉楼主可以说说这个算法有什么优点或者为什么物联网要用到这个算法,更贴合设计方面的思想
2019-11-19 15:29
0
雪    币: 652
活跃值: (106)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
下周文章,讲述哪些地方用红黑树,哪些情况用hash。
2019-11-19 17:03
0
游客
登录 | 注册 方可回帖
返回
//