上一篇讲到了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. Nginx特性验证-反向代理/负载均衡/页面缓存/URL重定向

    原文发表于cu:2016-08-25 参考文档: Nginx 反向代理.负载均衡.页面缓存.URL重写等:http://freeloda.blog.51cto.com/2033581/1288553 ...

  2. 你用 Python 做过什么有趣的数据挖掘项目?

    有网友在知乎提问:「你用 Python 做过什么有趣的数据挖掘项目?」 我最近刚开始学习 Python, numpy, scipy 等, 想做一些数据方面的项目,但是之前又没有这方面的经验.所以想知道 ...

  3. 欢迎来怼-----Beta冲刺贡献分数分配结果

    队名:欢迎来怼 小组成员 队长:田继平 成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文

  4. Thunder团队Final周贡献分分配结果

    小组名称:Thunder 项目名称:爱阅app 组长:王航 成员:李传康.翟宇豪.邹双黛.苗威.宋雨.胡佑蓉.杨梓瑞 分配规则 则1:基础分,拿出总分的20%(8分)进行均分,剩下的80%(32分)用 ...

  5. Java微笔记(8)

    Java 中的包装类 Java 为每个基本数据类型都提供了一个包装类,这样就可以像操作对象那样来操作基本数据类型 基本类型和包装类之间的对应关系: 包装类主要提供了两大类方法: 将本类型和其他基本类型 ...

  6. 周总结web未完成的代码

    <html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Con ...

  7. ASP.NET Core 中的 Razor 页面介绍

    标题:ASP.NET Core 中的 Razor 页面介绍 地址:https://docs.microsoft.com/zh-cn/aspnet/core/razor-pages/index?view ...

  8. Vue于React特性对比(二)

    一,关于响应式数据更新方式的实现 1)只有在data里面定义的数据才会有响应式更新 vue依赖的defineProperty的数据劫持加上依赖数据,实现数据的响应式更新.可以称之为依赖式的响应.因为依 ...

  9. GO语言教程(一)Linux( Centos)下Go的安装, 以及HelloWorld

    写在前面: 目前,Go语言已经发布了1.5的版本,已经有不少Go语言相关的书籍和教程了,但是看了一些后,觉得还是应该自己写一套Go语言的教程.给广大学习Go语言的朋友多一种选择.因为,咱写的教程,向来 ...

  10. Mware中CentOS设置静态IP

    Mware中CentOS设置静态IP   因为之前搭建的MongoDB分片没有采用副本集,最近现网压力较大,所以准备研究一下,于是在自己电脑的虚拟机中搭建环境,但是发现之前VMware设置的是DHCP ...