上一篇讲到了libiop基本结构,这次根据libiop提供的test跟踪下消息和运行流程

void echo_server_test()
{
int keepalive_timeout = ;
iop_base_t *base = iop_base_new();
printf("create a new iop_base_t object.\n");
iop_add_tcp_server(base,"0.0.0.0",,
my_echo_parser,my_echo_processor,
my_echo_on_connect,my_echo_on_destroy,my_echo_on_error,
keepalive_timeout);
printf("create a new tcp server on port 7777.\n");
printf("start iop run loop.\n");
iop_run(base); }

echo_server_test 函数内部添加了一个tcpserver,将函数一层一层展开

展开iop_add_tcp_server

int iop_add_tcp_server(iop_base_t *base, const char *host, unsigned short port,
iop_parser parser, iop_processor processor,
iop_cb on_connect,iop_cb on_destroy,
iop_err_cb on_error, int keepalive_timeout)
{ iop_tcp_server_arg_t *sarg = ;
io_handle_t h = INVALID_HANDLE;
sarg = (iop_tcp_server_arg_t *)malloc(sizeof(iop_tcp_server_arg_t));
if(!sarg){return -;}
memset(sarg,,sizeof(iop_tcp_server_arg_t));
h = iop_tcp_server(host,port);
if(h == INVALID_HANDLE){return -;}
sarg = (iop_tcp_server_arg_t *)malloc(sizeof(struct tag_iop_tcp_server_arg_t));
if(!sarg)
{
iop_close_handle(h);
return -;
}
#ifdef WIN32
strcpy_s(sarg->host,sizeof(sarg->host)-, host);
#else
strcpy(sarg->host,host);
#endif
sarg->port = port;
sarg->timeout = keepalive_timeout;
sarg->on_connect = on_connect;
sarg->on_destroy = on_destroy;
sarg->on_error = on_error;
sarg->parser = parser;
sarg->processor = processor;
_list_add_before(base->tcp_protocol_list_head, _list_node_new(sarg));
return iop_add(base,h,EV_TYPE_READ,_iop_tcp_server_cb,(void *)sarg,-);
}

解读iop_add_tcp_server

函数参数iop_base_t 是iop基本事件结构,前面有说过,

struct tag_iop_base_t
{
iop_t *iops; /*所有iop*/
int maxio; /*最大并发io数,包括定时器在内*/
int maxbuf; /*单个发送或接收缓存的最大值*/
int free_list_head; /*可用iop列表*/
int free_list_tail; /*最后一个可用iop*/
int io_list_head; /*已用io类型的iop列表*/
int timer_list_head; /*已用timer类型的iop列表*/
int connect_list_head; /*异步连接的iop列表*/
volatile int exit_flag; /*退出标志*/ int dispatch_interval; /*高度的间隔时间*/
iop_op_t op_imp; /*事件模型的内部实现*/
void *model_data; /*事件模型特定的数据*/ iop_time_t cur_time; /*当前调度时间*/
iop_time_t last_time; /*上次调度时间*/
iop_time_t last_keepalive_time; /*上次检查keepalive的时间*/ _list_node_t * tcp_protocol_list_head; /*use for advance tcp server model.*/
};

第二个参数host是主机地址,port是端口号,剩下的 parser为解析函数指针

processor为处理函数指针,on_connect为连接的回调函数指针,on_destroy,为

销毁功能函数指针,on_error为错误情况下函数指针,keepalive_timeout为超时时间

这几个函数指针的类型如下,基本都类似的

/*tcp连接事件回调函数*/
typedef void (*iop_cb)(iop_base_t *,int,void *);
void iop_default_cb(iop_base_t *base, int id, void *arg); /*
* 返回-1代表要删除事件,返回0代表不删除
*/
typedef int (*iop_err_cb)(iop_base_t *,int,int,void *); int iop_default_err_cb(iop_base_t *base, int id, int err, void *arg); /************************************
*协议解析器,
* parameters:
* char *buf:数据
* int len:数据长度
*return:
* 返回0代表还要收更多数据以代解析,-1代表协议错误,>0代表解析成功一个数据包
***********************************/
typedef int (*iop_parser)(char *, int);
int iop_default_parser(char *buf, int len); /*
*数据处理器
*parameters:
* base:iop_base_t 指针
* id:iop对象的id
* buf:数据包起始点
* len:数据包长度
* arg:自带的参数
*return:
-1: 代表要关闭连接,0代表正常
*/
typedef int (*iop_processor)(iop_base_t *,int,char *,int,void *);

回到iop_add_tcp_server函数里

开辟arg的空间

sarg = (iop_tcp_server_arg_t *)malloc(sizeof(iop_tcp_server_arg_t));

绑定端口和地址

h = iop_tcp_server(host,port);
对arg赋值
sarg->port = port;
sarg->timeout = keepalive_timeout;
sarg->on_connect = on_connect;
sarg->on_destroy = on_destroy;
sarg->on_error = on_error;
sarg->parser = parser;
sarg->processor = processor;

下面这句代码最重要

iop_add(base,h,EV_TYPE_READ,_iop_tcp_server_cb,(void *)sarg,-);

这句代码将socket h绑定了一个读事件,当有读事件就绪时会触发iop_tcp_server_cb这个函数。

如何将h和iop_tcp_server_cb绑定的,展开iop_add

int iop_add(iop_base_t *base,io_handle_t handle,unsigned int events,iop_event_cb evcb,void *arg,int timeout)
{
int r = ;
iop_t *iop = _iop_base_get_free_node(base);
if(!iop){return -;}
iop->handle = handle;
iop->events = events;
iop->timeout = timeout;
iop->evcb = evcb;
iop->last_dispatch_time = base->cur_time;
iop->arg = arg;
//io 事件
if(handle != INVALID_HANDLE)
{
//LOG_DBG("iop_add io, id=%d.\n", iop->id);
iop->prev = -;
iop->next = base->io_list_head;
base->io_list_head = iop->id;
iop->iop_type = IOP_TYPE_IO;
iop_set_nonblock(handle);
r = (*(base->op_imp.base_add))(base, iop->id, handle, events);
if(r != )
{
iop_del(base,iop->id);
return -;
}
}
else
{
/*timer*/
//LOG_DBG("iop_add timer, id=%d.\n", iop->id);
iop->prev = -;
iop->next = base->timer_list_head;
base->timer_list_head = iop->id;
iop->iop_type = IOP_TYPE_TIMER;
}
return iop->id;
}

iop_add 形参不做解释,其中形参evcb也是函数指针

/*事件回调函数,返回-1代表要删除对象,返回0代表正常*/
typedef int (*iop_event_cb)(iop_base_t *,int,unsigned int,void *);

在iop_add内部完成iop回调函数evcb的绑定和基本参数赋值

然后判断是io事件还是定时器事件

对于IO事件,要通知网络层(epoll,select等不同模型)进行绑定,

调用base中op_imp成员的base_add函数指针完成绑定。

r = (*(base->op_imp.base_add))(base, iop->id, handle, events);

之所以能调用是因为之前op_imp.base_add被赋值了。

回到

void echo_server_test()
{
int keepalive_timeout = ;
iop_base_t *base = iop_base_new();
...
}
iop_base_t* iop_base_new(int maxio)
{
#ifdef _HAVE_EVENT_PORTS_
#endif
#ifdef _HAVE_WORKING_KQUEUE_
#endif
#ifdef _HAVE_EPOLL_
return iop_base_new_special(maxio,"epoll");
#endif
#ifdef _HAVE_DEVPOLL_
#endif
#ifdef _HAVE_POLL_
return iop_base_new_special(maxio,"poll");
#endif
#ifdef _HAVE_SELECT_
return iop_base_new_special(maxio,"select");
#endif
return NULL;
}

一层一层看

iop_base_t* iop_base_new_special(int maxio,const char *model)
{
int r = -;
iop_base_t *base = NULL;
if(strcmp(model,"epoll")==)
{
base = _iop_base_new(maxio);
if(base)
{
r = iop_init_epoll(base, maxio);
}
}
......
}
int iop_init_epoll(void *iop_base, int maxev)
{
...
//模型内部实现,不同模型不同的函数指针和名字
iop_op->name = "epoll";
iop_op->base_free = epoll_free;
iop_op->base_dispatch = epoll_dispatch;
iop_op->base_add = epoll_add;
iop_op->base_del = epoll_del;
iop_op->base_mod = epoll_mod; //1024 is not the max events limit.
//创建epoll表句柄
...
//iop_epoll_data_t类型的数据存在base的model_data里
//方便回调
base->model_data = iop_data; return ;
}

上面就是在new函数里实现的一层一层函数指针的绑定,所以之后才可以调用对应的函数指针。

在iop_add 函数绑定成功后,整个iop_add_tcp_server流程走完了。

我们下一步看看如何派发消息

void echo_server_test()
{
int keepalive_timeout = ;
iop_base_t *base = iop_base_new();
...
iop_add_tcp_server(...,...);
...
iop_run(base); }

iop_run函数完成消息轮询和派发

void iop_run(iop_base_t *base)
{
while(base->exit_flag == )
{
iop_dispatch(base);
}
iop_base_free(base);
}

iop_dispatch消息派发函数

iop_base_free iop_base释放

int iop_dispatch(iop_base_t *base)
{
int cur_id = ;
int next_id = ;
int r = ;
iop_t *iop = (iop_t *);
//调用不同模型的函数指针实现消息派发
dispatch_imp_cb dispatch_cb = base->op_imp.base_dispatch;
r = (*dispatch_cb)(base,base->dispatch_interval);
if( r == -)
{
return -;
} //检测定时器时间,定时调用
if(base->cur_time > base->last_time)
{
//check timers...
cur_id = base->timer_list_head;
while(cur_id != -)
{
iop = base->iops + cur_id;
next_id = iop->next;
if(base->cur_time > iop->last_dispatch_time + iop->timeout)
{
IOP_CB(base,iop,EV_TYPE_TIMER);
}
cur_id = next_id;
} /*********check for connect list.*********************/ cur_id = base->connect_list_head;
while(cur_id != -)
{
iop = base->iops + cur_id;
next_id = iop->next;
if(base->cur_time > iop->last_dispatch_time + iop->timeout)
{
IOP_CB(base,iop,EV_TYPE_TIMEOUT);
}
cur_id = next_id;
} //超时检测
/*********clear keepalive, 60 seconds per times***********************/
if(base->cur_time > base->last_keepalive_time+)
{
base->last_keepalive_time = base->cur_time;
cur_id = base->io_list_head;
while(cur_id != -)
{
iop = base->iops+cur_id;
next_id = iop->next;
if(iop->timeout > && iop->last_dispatch_time + iop->timeout < base->cur_time)
{
IOP_CB(base,iop,EV_TYPE_TIMEOUT);
}
cur_id = next_id;
}
} base->last_time = base->cur_time;
} return r;
}

这句代码是消息派发的关键

  //调用不同模型的函数指针实现消息派发
dispatch_imp_cb dispatch_cb = base->op_imp.base_dispatch;
r = (*dispatch_cb)(base,base->dispatch_interval);

base->op_imp.base_dispatch之前在epoll_init里完成过初始化

其实调用的是epoll的dispatch

static int epoll_dispatch(iop_base_t * base, int timeout)
{
int i;
int id = ;
iop_t *iop = NULL;
//iop_base中取出模型数据
iop_epoll_data_t *iop_data = (iop_epoll_data_t *)(base->model_data);
int n = ;
do{
n = epoll_wait(iop_data->epfd, iop_data->events, iop_data->nevents, timeout);
}while((n < ) && (errno == EINTR));
base->cur_time = time(NULL);
for(i = ; i < n; i++)
{
//取出iop的id
id = (int)((iop_data->events)[i].data.u32);
if(id >= && id < base->maxio)
{
iop = (base->iops)+id;
//这个宏是调用绑定在iop的事件回调函数(accept,read,write等)
IOP_CB(base,iop,from_epoll_events(iop_data->events[i].events));
}
}
return n;
}

这句话完成绑定在iop的回调函数调用,基本功能就是accept,read或者write等

//这个宏是调用绑定在iop的事件回调函数(accept,read,write等)
IOP_CB(base,iop,from_epoll_events(iop_data->events[i].events));

这样就是整个libiop通讯流程和事件驱动机制

我的公众号

libiop通讯流程和api讲解的更多相关文章

  1. 【jquery】 API讲解 内部培训资料

    资料在百度云盘 一.jquery  API讲解 1.jquery  api如何使用 jquery  api http://www.hemin.cn/jq/ 2.常用api讲解 选择器: 通过$()获取 ...

  2. zigbee学习:示例程序SampleApp中通讯流程

    zigbee学习:示例程序SampleApp中通讯流程 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 参考链接: http://wjf88223.bl ...

  3. IdentityServer4【QuickStart】之使用ClientCredentials流程保护API

    使用ClientCredentials流程保护API 这个示例展示了使用IdentityServer中保护APIs的最基本的场景. 在这个场景中我们会定义一个API和一个想要访问它的客户端.客户端会在 ...

  4. 项目实践之工作流引擎基本文档!Activiti工作流框架中流程引擎API和服务详解

    流程引擎的API和服务 流程引擎API(ProcessEngine API)是与Activiti打交道的最常用方式 Activiti从ProcessEngine开始.在ProcessEngine中,可 ...

  5. WCF开发的流程-服务端和客户端之间的通讯(内含demo讲解)

    讲解技术之前,恳请博友让我说几句废话.今天是我第一在博客园发布属于自己原创的博文(如有雷同,那是绝对不可能的事,嘿嘿).之前一直是拜读各位博友的大作,受益匪浅的我在这对博友们说声谢谢,谢谢你们的共享! ...

  6. smartJS 0.1 API 讲解 - FlowController

    本篇介绍0.1版中最后一个特性,FlowController:同时也对第一版总结一下,因为近两年全部都是在搞前端,都是做一些js框架类的东西,也做了不少有意思的功能,做smartjs对我来说一个是对自 ...

  7. PHP与API讲解(一)

    了解API: 在使用与创建自己的API之前我们需要先了解什么是API! API代表应用程序编程接口,而接口指的是一个特定的服务.一个应用程序或者其他程序的公共模块. 理解SOA(面向服务的架构):SO ...

  8. DIOCP网络通讯流程

    DIOCP 运作核心探密   原文连接: http://blog.qdac.cc/?p=2362 原作者: BB 天地弦的DIOCP早已经广为人知了,有很多的同学都用上了它,甚至各种变异.修改版本也出 ...

  9. Hibernate第二篇【API讲解、执行流程图】

    前言 从上一篇中已经大致介绍了Hibernate并且有了一个快速入门案例的基础了,-.本博文主要讲解Hibernate API 我们看看快速入门案例的代码用到了什么对象吧,然后一个一个讲解 publi ...

随机推荐

  1. 与面试官谈笑风生 | Python面向对象之访问控制

    Python从设计之初就是一门面向对象的语言,面向对象思想的第一个要素就是封装.所谓封装,通俗的讲就是类中的属性和方法,分为公有和私有,公有可以被外界访问,私有不能被外界访问,这就是封装中最关键的概念 ...

  2. 谈谈javascript中的变量提升还有函数提升

    在很多面试题中,经常会看到关于变量提升,还有函数提升的题目,所以我就写一篇自己理解之后的随笔,方便之后的查阅和复习. 首先举个例子 foo();//undefined function foo(){ ...

  3. [CF] Sasha and One More Name

    题目大意 就是给一个回文串,然后进行k次分割,产生k+1个字符子串,通过重新组合这k+1个字符字串,是否会出现新的不同的回文串,且最少需要分割几段.无法产生新的回文串则输出"Impossib ...

  4. Java中按值传递与按引用传递的区别

    值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响 ...

  5. lilntcode-508-摆动排序

    508-摆动排序 给你一个没有排序的数组,请将原数组就地重新排列满足如下性质 nums[0] <= nums[1] >= nums[2] <= nums[3].... 注意事项 请就 ...

  6. lintcode-383-装最多水的容器

    383-装最多水的容器 给定 n 个非负整数 a1, a2, ..., an, 每个数代表了坐标中的一个点 (i, ai).画 n 条垂直线,使得 i 垂直线的两个端点分别为(i, ai)和(i, 0 ...

  7. Java compiler level does not match the version of the installed Java project facet. map解决方法

    右键项目"Properties",在弹出的"Properties"窗口左侧,单击"Project Facets",打开"Proje ...

  8. (一)MySQL基础篇

    1.mysql简介 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库. 主流的数据库有:sqlserver,mysql,Oracle.SQLite.Access.MS SQL Se ...

  9. C#控件之Repeater控件使用

    歡迎大家來討論,修改,一定虛心接受. 1.為什麼使用Repeater控件? 關於把從數據庫讀取的數據綁定到前台頁面,我們可以使用DataGrid.DataGridView以及Repeater來佈局,三 ...

  10. 【第九周】beta-review阶段贡献分分配

    组名: 新蜂 组长: 武志远 组员: 宫成荣 谢孝淼 杨柳 李峤 项目名称: java俄罗斯方块NEO 武志远 武志远 武志远 武志远 武志远 宫成荣 宫成荣 杨柳 宫成荣 宫成荣 李峤 杨柳 李峤 ...