libiop网络库数据结构和基础知识

最近朋友推荐,学习了libiop这个网络库,作者封装的很全面,代码很简洁

适合初学者学习基于事件驱动的网络io

先看看iop_def.h, 这里面定义了常用的数据结构

tag_iop_base_t 主要用于管理所有事件,每个事件是一个iop_t,

maxio表示最大的文件描述符,

free_list_head 表示可用的空闲列表头部id,一般用iops + free_list_head

取出iop_t 的元素

同理free_list_tail,最后一个可用iop,

iop_op_t 是封装了几个函数指针的结构体,

包括网络模型的名字,事件的添加,事件的删除,事件的更改,事件的派发

剩下的如注释所示

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.*/
};

看一下iop_t结构体,id是从0开始到n的数,这个是在tag_iop_base_t 中初始化队列时做的,

io_handle_t是这个结构存储的socket id, iop_type分三种0表示释放,1表示io读写,2表示

定时器事件, iop_event_cb表示事件回调函数指针,每一个iop_t绑定了不同的回调函数,

比如accept,比如read,比如write,但是这些回调函数要封装成iop_event_cb类型,

dbuf_t 是作者封装的一个管理发送和接受数据的结构

/*
*tag_iop_t:iop结构,每一个iop对象都会对应一个tag_iop_t结构
*/
struct tag_iop_t
{
    int id;                    /*对应的id*/
    io_handle_t handle;        /*关联的句柄*/
    int iop_type;            /*对象类型:0:free,1:io,2:timer*/
    int prev;                /*上一个对象*/
    int next;                /*下一个对象*/
    unsigned int events;                /*关注的事件*/
    int timeout;            /*超时值*/
    iop_event_cb evcb;        /*事件回调*/
    void *arg;                /*用户指定的参数,由用户负责释放资源*/
    void *sys_arg;            /*系统指定的参数,系统自动释放资源*/
    /*以下字段对定时器无用*/
    dbuf_t *sbuf;        /*发送缓存区*/
    dbuf_t *rbuf;        /*接收缓存区*/
    iop_time_t last_dispatch_time;    /*上次调度的时间*/
};

iop_event_cb 定义如下

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

dbuf_t结构如下

struct tag_dbuf
{
    unsigned int capacity;
    unsigned int size;
    void *data;
};

至于dbuf_t如何开辟空间释放空间,读写偏移的都不做赘述

iop_base_t中iop_op_t 结构很重要,是事件调度的核心

结构如下

struct tag_iop_op_t
{
    const char *name;                               //模型名称
    void (*base_free)(iop_base_t *);        //资源释放的接口
    int (*base_dispatch)(iop_base_t *, int);  //模型调度接口
    //添加事件
    int (*base_add)(iop_base_t *, int, io_handle_t, unsigned int);
    //删除事件
    int (*base_del)(iop_base_t *, int,io_handle_t);
    //修改事件
    int (*base_mod)(iop_base_t *, int, io_handle_t, unsigned int);
};

因为对应不同的平台,我们要应用不同的网络模型,比如epoll,select,iocp等等。

但是对于异步通信IO我们采取事件回调机制,也就是说提前绑定好读事件,写事件等,

在循环中调用base_dispatch函数指针,就可以实现对于不同模型的派发。

上面就是libiop模型的基本结构和框架

我们看下epoll模型的封装

tag_epoll_data 是封装的epoll基本结构,

这个结构存在iop_base_t的model_data里

struct tag_epoll_data {
    struct epoll_event *events; //监听的epoll_events 队列
    int nevents; //epoll_events 事件大小
    int epfd; //epoll_create 产生的epoll表句柄
};

两个函数,iop_t应用层的读写宏

EV_TYPE_READ和
EV_TYPE_WRITEepoll的读写宏
EPOLLIN和EPOLLOUT互相转换
static uint32_t to_epoll_events(uint32_t what)
{
    uint32_t events=0;
    if(what & EV_TYPE_READ)
    {
        events = EPOLLIN;
    }
    if(what & EV_TYPE_WRITE)
    {
        events |= EPOLLOUT;
    }
    return events;
}

static uint32_t from_epoll_events(uint32_t events)
{
    uint32_t what=0;
    if(events & (EPOLLHUP|EPOLLERR))
    {
        what = EV_TYPE_READ | EV_TYPE_WRITE;
    }
    else
    {
        if(events & EPOLLIN){what |= EV_TYPE_READ;}
        if(events & EPOLLOUT){what |= EV_TYPE_WRITE;}
    }
    return what;
}

初始化epoll结构和数据

int iop_init_epoll(void *iop_base, int maxev)
{
    iop_base_t *base = (iop_base_t *)iop_base;
    //iop_base  事 件 操作结构体

    //iop_base_t中op_imp取出模型抽象的结构体
    iop_op_t *iop_op = &(base->op_imp);
    //开辟epoll_data空间
    iop_epoll_data_t *iop_data = (iop_epoll_data_t *)(malloc(sizeof(iop_epoll_data_t)));
    if(!iop_data)
    {
        return -1;
    }
    //监听的队列大小为maxev
    iop_data->nevents = maxev;
    //为epll_data里监听事件队列开辟连续空间
    iop_data->events = (struct epoll_event *)(malloc(sizeof(struct epoll_event) * maxev));
    if(!iop_data)
    {
        free(iop_data);
        return -1;
    }

    //模型内部实现,不同模型不同的函数指针和名字
    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表句柄
    int epfd = epoll_create(1024);
    if(epfd < 0)
    {
        free(iop_data->events);
        free(iop_data);
        free(iop_op);
        return -1;
    }
    iop_data->epfd = epfd;

    //iop_epoll_data_t类型的数据存在base的model_data里
    //方便回调
    base->model_data = iop_data;

    return 0;
}

对应的释放epoll开辟的空间和数据

//epoll 释放
static void epoll_free(iop_base_t *base)
{
    //model_data里存放了epoll数据的指针
    iop_epoll_data_t *iop_data = (iop_epoll_data_t *)(base->model_data);
    if(!iop_data){return;}
    //释放events队列
    if(iop_data->events)
    {
        free(iop_data->events);
    }
    //关闭iop_data->epfd
    if(iop_data->epfd >= 0)
    {
        close(iop_data->epfd);
    }
    free(iop_data);
    base->model_data = (void *)0;
}

epoll 添加事件

//epoll添加事件
//base 为iop_base回传指针
//id为iop的id
//io_handle_t 为socket
//events 为事件类型(EV_TYPE_READ或者EV_TYPE_WRITE)
static int epoll_add(iop_base_t *base, int id, io_handle_t handle, unsigned int events)
{
    iop_epoll_data_t *iop_data = (iop_epoll_data_t *)(base->model_data);
    struct epoll_event ev;
    ev.data.u32 = id;
    //转换为EPOLLIN或者EPOLLOUT
    ev.events = to_epoll_events(events);
    //iop_set_nonblock(handle);
    return epoll_ctl(iop_data->epfd, EPOLL_CTL_ADD, (int)handle, &ev);
}

epoll删除事件

//epoll删除事件
//base 为iop_base回传指针
//id为iop的id
//io_handle_t 为socket
static int epoll_del(iop_base_t *base, int id,io_handle_t handle)
{
    iop_epoll_data_t *iop_data = (iop_epoll_data_t *)(base->model_data);
    struct epoll_event ev;
    ev.data.u32 = id;
    ev.events = 0;
    //ev回传进去,删除epoll_events中socket为handle的注册事件
    return epoll_ctl(iop_data->epfd, EPOLL_CTL_DEL, (int)handle, &ev);
}

epoll事件更改

//epoll 模式更改(读写更改)
static int epoll_mod(iop_base_t *base, int id, io_handle_t handle, unsigned int events)
{
    iop_epoll_data_t *iop_data = (iop_epoll_data_t *)(base->model_data);
    struct epoll_event ev;
    ev.data.u32 = id;
    ev.events = to_epoll_events(events);
    return epoll_ctl(iop_data->epfd, EPOLL_CTL_MOD, (int)handle, &ev);
}

epoll事件派发

//epoll 事件派发
static int epoll_dispatch(iop_base_t * base, int timeout)
{
    int i;
    int id = 0;
    iop_t *iop = NULL;
    //iop_base中取出模型数据
    iop_epoll_data_t *iop_data = (iop_epoll_data_t *)(base->model_data);
    int n = 0;
    do{
        n = epoll_wait(iop_data->epfd, iop_data->events, iop_data->nevents, timeout);
    }while((n < 0) && (errno == EINTR));
    base->cur_time = time(NULL);
    for(i = 0; i < n; i++)
    {
        //取出iop的id
        id = (int)((iop_data->events)[i].data.u32);
        if(id >= 0 && 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;
}

以上就是libiop事件驱动的核心结构和设计,做个简单的总结,如果我们要设计一个多路复用的事件驱动

基本结构是这样的

//eventEle是应用层管理的最小单元

int (*WRAFuc  )(eventLoop* eventLoopP, int id, int mask, ...); 

//mask为应用层自己定义的读写标记

struct eventEle

{

   int socket; //关联的socket

   WRAFuc  mPfunc;  //读写接受等功能回调的函数

   //读写缓冲区可自己封装

   char  readBuf[];   //读缓冲区

   char writeBuff[];  //写缓冲区

};

//事件轮询的基本结构

struct eventLoop

{

   eventEle * eventList;

   int maxfd;

   int lastActiveTime;

   iop_op_t op_imp;    /*事件模型的内部实现*/

   void * model_data;  /*void 指针指向开辟的不同模型的数据*/

};

不同模型的操作进行封装成一个结构体,

结构体里面有添加,删除,更改,派发,释放的函数指针

struct tag_iop_op_t
{
const char *name; //模型名称
void (*base_free)(iop_base_t *); //资源释放的接口
int (*base_dispatch)(iop_base_t *, int); //模型调度接口
//添加事件
int (*base_add)(iop_base_t *, int, io_handle_t, unsigned int);
//删除事件
int (*base_del)(iop_base_t *, int,io_handle_t);
//修改事件
int (*base_mod)(iop_base_t *, int, io_handle_t, unsigned int);
};

这就是设计一个基本的事件驱动网络库的基本思路,

源代码下载地址:http://download.csdn.net/detail/secondtonone1/9517689

我的公众号:

时间: 2024-10-10 10:41:21

libiop网络库数据结构和基础知识的相关文章

socket网络编程的一些基础知识

源地址:http://blog.csdn.net/roger_77/article/details/1453049 目录: 1) 什么是套接字? 2) Internet 套接字的两种类型 3) 网络理论 4) 结构体 5) 本机转换 6) IP 地址和如何处理它们 7) socket()函数 8) bind()函数 9) connect()函数 10) listen()函数 11) accept()函数 12) send()和recv()函数 13) sendto()和recvfrom()函数 

Java网络编程一:基础知识详解

网络基础知识 1.OSI分层模型和TCP/IP分层模型的对应关系 这里对于7层模型不展开来讲,只选择跟这次系列主题相关的知识点介绍. 2.七层模型与协议的对应关系 网络层   ------------     IP(网络之间的互联协议) 传输层   ------------     TCP(传输控制协议).UDP(用户数据报协议) 应用层   ------------     Telnet(Internet远程登录服务的标准协议和主要方式).FTP(文本传输协议).HTTP(超文本传送协议) 3

大话数据结构&ndash;1.基础知识

  2.算法:算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每个指令表现为一个或多个操作. 特性:输入.输出.有穷性.确定性.可行性.

TCP/IP网络协议基础知识集锦[转]

引言 本篇属于TCP/IP协议的基础知识,重点介绍了TCP/IP协议簇的内容.作用以及TCP.UDP.IP三种常见网络协议相关的基础知识. 内容 TCP/IP协议簇是由OSI七层模型发展而来的,之所以存在OSI互联参考模型主要有以下几点好处: 1.用于解决不同厂商的设备互联问题:即兼容性考虑; 2.层次化结构独立性强;可扩展性强; 3.统一标准可以方便学习; TCP/IP模型 TCP/IP模型与OSI模型的相互关系如下图所示: 注释 1.应用层:与OSI的应用层.表示层.会话层相对应,主要作用是

软考之路(1)——浅解网络基础知识

对网络这一块的基础知识理解如下,以图文并茂的形式展出,便于分析和理解.解析与图如下: 物理层: 功能: 提供为建立.维护和拆除物理链路所需的机械.电气.功能和规程的特性: 提供有关在传输介质上传输非结构的位流及物理链路故障检测指示: 为数据链路层提供一个物理连接,以及他们的机械.电气.功能和过程性.如规定使用电缆和接头的类型,传送信号的电压.在这一层,数据还没有被组织,仅作为原始的位流或电气电压处理,单位是位. 物理链路: 这里多次出现物理链路,简单解释物理链路:一条无源的点到点的物理线路段,中

【搜索引擎基础知识2】网络爬虫

部分内容参考<这就是搜索引擎> 通用搜索引擎的处理对象是互联网网页,目前网页数量以百亿计,搜索引擎的网络爬虫能够高效地将海量的网页数据传下载到本地,在本地 形成互联网网页的镜像备份.它是搜索引擎系统中很关键也很基础的构件. (一)网络爬虫本质就是浏览器http请求 浏览器和网络爬虫是两种不同的网络客户端,都以相同的方式来获取网页: 1.首先, 客户端程序连接到域名系统(DNS)服务器上,DNS服务器将主机 名转换成ip 地址. 2.接下来,客户端试着连接具有该IP地址的服务器.服务器上可能有多

网络连接相关基础知识笔记

一.常说的TCP/IP的含义 TCP/IP协议簇并不仅仅指TCP协议和IP协议,实际它包括了一系列协议组成的集合,如:TCP,IP,UDP,FTP,SMTP,DNS,ARP,PPP等 TCP与UDP协议都属于传输层协议,但有很大不同,TCP是面向连接的协议,提供的是可靠的数据流服务,TCP采用"带重传的肯定确认"机制来实现传输的可靠性,实现了一种"虚电路",因为从物理上来说,并不是真正在两台主机间建立了连接,这种连接只是存在于逻辑上的.最大的开销出现在通信前建立连接

Python网络爬虫基础知识学习

对Python有一些简单了解的朋友都知识Python编程语言有个很强大的功能,那就是Python网络爬虫(http://www.maiziedu.com/course/python/645-9570/),一提到Python,就会想到相关的Python爬虫和scrapy等等,今天就来简单认识学习Python爬虫的基础知识,有了一定的相关爬虫知识,以后学习scrapy.urllib等等知识时,会相对轻松些. 爬虫: 网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组

TCP/IP协议(一)网络基础知识

参考书籍为<图解tcp/ip>-第五版.这篇随笔,主要内容还是TCP/IP所必备的基础知识,包括计算机与网络发展的历史及标准化过程(简述).OSI参考模型.网络概念的本质.网络构建的设备等 下面是协议层从底层至顶层的一个模型图: 一.计算机网络的背景 1.1 计算机的发展 有人说:"20世纪最伟大的发明就是计算机",自诞生伊始,计算机经历了一系列发展,从大型通用计算机.超级计算机.小型机.个人电脑.工作站以及现如今笔记本.平板.智能手机等, 计算机已经彻底融入了我们的生活