libevent粘包分包解决方案:bufferevent + evbuffer

本文详细介绍了libevent库的使用,包括event_base的创建、销毁及事件循环,以及event的创建、注册和管理。重点讲解了libevent如何处理socket的粘包分包问题,通过bufferevent提供了事件驱动的网络编程解决方案,包括创建、释放、设置回调函数和水位。同时,还介绍了Evbuffer作为IO缓冲的作用,如何进行数据的添加、删除、移动和搜索。通过示例展示了如何使用bufferevent和Evbuffer优化socket网络编程,解决TCP粘包拆包问题。
摘要由CSDN通过智能技术生成

转自:http://blog.sina.com.cn/s/blog_9f1496990102vshz.html

 

原文:http://www.lvtao.net/c/631.html

Libevent介绍

libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。著名分布式缓存软件memcached也是libevent based,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。

libevent官方网站 http://libevent.org/

英文文档 http://www.wangafu.net/~nickm/libevent-book/

中文文档 http://www.cppblog.com/mysileng/category/20374.html

Libevent是基于事件的网络库。说的通俗点,例如我的客户端连接到服务端属于一个连接的事件,当这个事件触发的时候就会去处理。

该文章阅读过程中,请结合下面的socket例子,可能会更加清晰的理解每一个接口的用法。

event_base

1. 创建event_base

event_base是event(事件,后面会讲event)的一个集合。event_base中存放你是监听是否就绪的event。一般情况下一个线程一个event_base,多个线程的情况下需要开多个event_base。

event_base主要是用来管理和实现事件的监听循环。

一般情况下直接new一个event_base就可以满足大部分需求了,如果需要配置参数的,可以参见libevent官网。

创建方法:

struct event_base *event_base_new(void);

销毁方法:

void event_base_free(struct event_base *base);

重新初始化:

int event_reinit(struct event_base *base);

2. 查看IO模型

IO多路复用模型中,有多种方法可以供我们选择,但是这些模型是在不同的平台下面的: select  poll  epoll  kqueue  devpoll  evport  win32

当我们创建一个event_base的时候,libevent会自动为我们选择最快的IO多路复用模型,Linux下一般会用epoll模型。

下面这个方法主要是用来获取IO模型的名称。

const char *event_base_get_method(const struct event_base *base);

3. 销毁event_base

void event_base_free(struct event_base *base);

4. 事件循环 event loop

我们上面说到 event_base是一组event的集合,我们也可以将event事件注册到这个集合中。当需要事件监听的时候,我们就需要对这个event_base进行循环。

下面这个函数非常重要,会在内部不断的循环监听注册上来的事件。

int event_base_dispatch(struct event_base *base);

返回值:0 表示成功退出  -1 表示存在错误信息。

还可以用这个方法:

#define EVLOOP_ONCE             0x01  #define EVLOOP_NONBLOCK         0x02  #define EVLOOP_NO_EXIT_ON_EMPTY 0x04  
  int event_base_loop(struct event_base *base, int flags);

event_base_loop这个方法会比event_base_dispatch这个方法更加灵活一些。

EVLOOP_ONCE: 阻塞直到有一个活跃的event,然后执行完活跃事件的回调就退出。

EVLOOP_NONBLOCK : 不阻塞,检查哪个事件准备好,调用优先级最高的那一个,然后退出。

0:如果参数填了0,则只有事件进来的时候才会调用一次事件的回调函数,比较常用

事件循环停止的情况:

1. event_base中没有事件event

2. 调用event_base_loopbreak(),那么事件循环将停止

3. 调用event_base_loopexit(),那么事件循环将停止

4. 程序错误,异常退出

两个退出的方法:

// 这两个函数成功返回 0 失败返回 -1  // 指定在 tv 时间后停止事件循环  // 如果 tv == NULL 那么将无延时的停止事件循环  int event_base_loopexit(struct event_base *base,const struct timeval *tv);  
// 立即停止事件循环(而不是无延时的停止)  int event_base_loopbreak(struct event_base *base);

两个方法区别:

1. event_base_loopexit(base, NULL) 如果当前正在为多个活跃事件调用回调函数,那么不会立即退出,而是等到所有的活跃事件的回调函数都执行完成后才退出事件循环

2. event_base_loopbreak(base) 如果当前正在为多个活跃事件调用回调函数,那么当前正在调用的回调函数会被执行,然后马上退出事件循环,而并不处理其他的活跃事件了

5. event_base的例子:

#include <<span>stdio.h>  #include <<span>stdlib.h>  #include <<span>unistd.h>  #include <<span>sys/types.h>   #include <<span>sys/socket.h>          #include <<span>netinet/in.h>          #include <<span>arpa/inet.h>  #include <<span>string.h>  #include <<span>fcntl.h>   #include <<span>event2/event.h>  #include <<span>event2/bufferevent.h>  int main() {  
puts("init a event_base!");  
struct event_base *base; //定义一个event_base  
base = event_base_new(); //初始化一个event_base  
const char *x =  event_base_get_method(base); //查看用了哪个IO多路复用模型,linux一下用epoll  
printf("METHOD:%s\n", x);  
int y = event_base_dispatch(base); //事件循环。因为我们这边没有注册事件,所以会直接退出  
event_base_free(base);  //销毁libevent  
return 1;  
}

返回:

event 事件

event_base是事件的集合,负责事件的循环,以及集合的销毁。而event就是event_base中的基本单元:事件。

我们举一个简单的例子来理解事件。例如我们的socket来进行网络开发的时候,都会使用accept这个方法来阻塞监听是否有客户端socket连接上来,如果客户端连接上来,则会创建一个线程用于服务端与客户端进行数据的交互操作,而服务端会继续阻塞等待下一个客户端socket连接上来。客户端连接到服务端实际就是一种事件。

1. 创建一个事件event

struct event *event_new(struct event_base *base, evutil_socket_t fd,short what, event_callback_fn cb,void *arg);

参数:

1. base:即event_base

2. fd:文件描述符。

3. what:event关心的各种条件。

4. cb:回调函数。

5. arg:用户自定义的数据,可以传递到回调函数中去。

libevent是基于事件的,也就是说只有在事件到来的这种条件下才会触发当前的事件。例如:

1. fd文件描述符已准备好可写或者可读

2. fd马上就准备好可写和可读。

3. 超时的情况 timeout

4. 信号中断

5. 用户触发的事件

what参数 event各种条件:

// 超时  #define EV_TIMEOUT 0x01  // event 相关的文件描述符可以读了  #define EV_READ 0x02  // event 相关的文件描述符可以写了  #define EV_WRITE 0x04  // 被用于信号检测(详见下文)  #define EV_SIGNAL 0x08  // 用于指定 event 为 persistent 持久类型。当事件执行完毕后,不会被删除,继续保持pending等待状态;  // 如果是非持久类型,则回调函数执行完毕后,事件就会被删除,想要重新使用这个事件,就必须将这个事件继续添加event_add   #define EV_PERSIST 0x10  // 用于指定 event 会被边缘触发  #define EV_ET 0x20

2. 释放event_free

真正的释放event的内存。

void event_free(struct event *event);

event_del 清理event的内存。这个方法并不是真正意义上的释放内存。

当函数会将事件转为 非pending和非activing的状态。

int event_del(struct event *event);

3. 注册event

该方法将用于向event_base注册事件。

参数:ev 为事件指针;tv 为时间指针。当tv = NULL的时候则无超时时间。

函数返回:0表示成功 -1 表示失败。

int event_add(struct event *ev, const struct timeval *tv);

tv时间结构例子:

struct timeval five_seconds = {5, 0};  
event_add(ev1, &five_seconds);

4.event_assign

event_new每次都会在堆上分配内存。有些场景下并不是每次都需要在堆上分配内存的,这个时候我们就可以用到event_assign方法。

已经初始化或者处于 pending 的 event,首先需要调用 event_del() 后再调用 event_assign()。这个时候就可以重用这个event了。

// 此函数用于初始化 event(包括可以初始化栈上和静态存储区中的 event)  // event_assign() 和 event_new() 除了 event 参数之外,使用了一样的参数  // event 参数用于指定一个未初始化的且需要初始化的 event  // 函数成功返回 0 失败返回 -1  int event_assign(struct event *event, struct event_base *base,evutil_socket_t fd, short what,void (*callback)(evutil_socket_t, short, void *), void *arg);  
       
// 类似上面的函数,此函数被信号 event 使用  event_assign(event, base, signum, EV_SIGNAL|EV_PERSIST, callback, arg)

5. 信号事件

信号事件也可以对信号进行事件的处理。用法和event_new类似。只不过处理的是信号而已

// base --- event_base  // signum --- 信号,例如 SIGHUP  // callback --- 信号出现时调用的回调函数  // arg --- 用户自定义数据  evsignal_new(base, signum, cb, arg)  
       
//将信号 event 注册到 event_base  evsignal_add(ev, tv)   
       
// 清理信号 event  evsignal
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值