设为首页 加入收藏

TOP

跨平台长连接组件设计及可插拔改造(四)
2018-04-10 06:06:45 】 浏览:894
Tags:台长 连接 组件 设计 改造
func[])(dul_node_t **) = { ws_alloc, tls_alloc, uv_alloc, }; char const *loaders[] = { "ws", "tls", "uv" }; dul_node_t *processor_loader(tokenizer_t *tokenizer, const char *loader, dul_node_t *pre_loader) { char *p = loader; char *inner_ptr = NULL; /* params 提取组装 */ p = strtok_r(p, "?", &inner_ptr); dul_node_t *node = NULL; map_t params = hashmap_new(); params_parser(inner_ptr, params); /* 这里采用转移表,进行插件初始化 */ while (strcmp(loaders[sqe], p) != 0) { sqe++; } oper_func[sqe](&node); if (node == NULL) { return NULL; } node->init(node, params); hashmap_free(params); // 双向链表前后关系绑定 pre_loader->next = node; node->pre = pre_loader; return node; } /* params string 解析 */ void params_parser(char *query, map_t params) { char *outer_ptr = NULL; char *p = strtok_r(query, "&", &outer_ptr); while (p) { char *inner_ptr = NULL; char *key = strtok_r(p, "=", &inner_ptr); hashmap_put(params, key, inner_ptr); p = strtok_r(NULL, "&", &outer_ptr); } }

Tips:随着插件的增加,对应初始化的代码也会越来越多,而且都是重复代码,为了减少这部分工作,我们可以采取宏来定义函数。后续如果增加一个插件,只需要在底下加一行 LOADER_ALLOC(zim_xx, xx) 即可。

#define LOADER_ALLOC(type, name)                    \
    void name##_alloc(dul_node_t **ctx) {           \
        type##_t **loader = (type##_t **)ctx;       \
        (*loader) = malloc(sizeof(type##_t));       \
        (*loader)->init = &name##_init;             \
        (*loader)->next = NULL;                     \
        (*loader)->pre = NULL;                      \
    }                       

LOADER_ALLOC(websocket, ws);
LOADER_ALLOC(zim_tls, tls);
LOADER_ALLOC(zim_uv, uv);

接口调用

再回到一开始我们思考接口调用的问题,由于有了函数指针变量,我们就需要在插件的初始化中把函数的地址存储在这些变量中:

int ws_init(dul_node_t *ctx, map_t params) {
    websocket_t *ws = (websocket_t *)ctx;
    bzero(ws, sizeof(websocket_t));
    
    // 省略中间初始化过程
  
    ws->init = &ws_init;
    ws->conn = &ws_connect;
    ws->close = &ws_close;
    ws->destroy = &ws_destroy;
    ws->reset = &ws_reset;
    ws->write_data = &ws_send;
    ws->read_data = &ws_read;
    ws->conn_cb = &ws_conn_cb;
    ws->write_cb = &ws_send_cb;
    ws->recv_cb = &ws_recv_cb;
    ws->close_cb = &ws_close_cb;
    return OK;
}

对比接口前后调用的方式,前者需要知道下一个 connect 函数,并进行显式调用,如果在 TLS 和 TCP 中新增一层,就需要改动 connect 函数的调用。但后者完全没有这个顾虑,不论是新增还是删除插件,它都可以通过指针找到对应的结构体,调用其 connect 函数,插件内部无需任何改动,岂不妙哉。

/* 改造前 */
int tls_ws_connect(tls_ws_t *handle,
                   tls_ws_conn_cb conn_cb,
                   tls_ws_close_cb close_cb) {
	...

    return uv_tls_connect(tls,
                          handle->host,
                          handle->port,
                          on__tls_connect);
}

/* 改造后 */
static void tls_connect(dul_node_t *ctx) {
    zim_tls_t *tls = (zim_tls_t *)ctx;
    
    ...

    if (tls->next && tls->next->conn) {
        tls->next->host = tls->host;
        tls->next->port = tls->port;
        tls->next->conn(tls->next);
    }
}

新增插件

基于改造后组件,新增插件只需要改动三处,以日志插件为例:

  • 增加日志文件

在头文件中定义 zim_log_s 结构体(这里没有额外的成员):

typedef struct zim_log_s zim_log_t;

struct zim_log_s {
    dul_node_t *pre;
    dul_node_t *next;

    char *host;
    int port;
    map_t params;

    node_init init;
    node_conn conn;
    node_write_data write_data;
    node_read_data read_data;
    node_close close;
    node_destroy destroy;
    node_reset reset;

    node_conn_cb conn_cb;
    node_write_cb write_cb
首页 上一页 1 2 3 4 5 下一页 尾页 4/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Spring 中获取 request 的几种方.. 下一篇Java日志框架:logback详解

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目