libevent笔记 - API

libevent 网络编程需要初始化或者屏蔽的操作

#include 

#ifndef _WIN32
#include 
#endif

using namespace std;

int main()
{
#ifdef _WIN32//64 32
        WSADATA wsa;//init win socket
        WSAStartup(MAKEWORD(2,2), &wsa);
#else
        //ignore pipe signal, because send data to closed socket and dump
        if(signal(SIGPIPE, SIG_IGN)==SIG_ERR){
           return 0;
        }
#endif

...

#ifdef _WIN32
        getchar();
        WSAClentup();
#endif

 

网络模型说明

windows iocp只支持windows  //epoll的加强版,内部有线程池,epoll的话线程池需要自己实现

               select

linux支持 devepoll 。。。

                select  1. 有连接数限制FD_SIZE,且遍历所有连接,询问内核的方式存在用户态和内核态间的来回切换和内存copy,当连接数很大,活跃数比较小的时候会有性能问题。

                           2. linux 、windows都支持

                          3. 接口简单

                poll  类似select,但是没有像select一样有连接数限制FD_SIZE

                epoll  1. 遍历连接状态,用户空间到内核空间在开始的时候只copy一次,通过共享内存方式实现,内核存储用红黑数

                         2. 只返回可读的连接数,减少了遍历的数量

                        3. 解决了最大监听数的限制

                       4. 支持边缘触发edge-triggered和水平触发level-triggered(后面加的)

备注:应该说各个模型都有各自的特点和优势,关键在于根据实际场景选择,select跨平台性好,代码简单,连接数少的时候更好用。epoll适合高并发场景。

 

API

1. base 创建event上下文

event_base_new //替换了老版本里面的event_init 老版线程不安全,新函数线程安全

                    内部实现 event_config_new  和  event_base_new_with_config

2  环境配置和初始化

event_config_new 

2.1. event_config_set_flag 

EVENT_BASE_FLAG_NOLOCK(在创建base的时候不加锁)、_IGNORE_ENV (忽略环境变量,不受环境变量而影响)、

_START_IOCP(win设置cpu数量启用任何必需iocp分发逻辑,设置cpu数量event_config_set_num_..  evthread_use_windows_thread)

用iocp需要设置使用windows线程(会创建线程池)

      evthread_use_windows_thread();

设置cpu数量

     SYSTEM_INFO si;

     GetSystemInfo(&si);//windows获取cpu数量

     event_config_set_num_cpus_hint(config, si.dwNumberOfProcessors);

_NO_CACHE_TIME、_EPOLL_USE_CHANGELIST(epoll下多次触发调用一次,fd复制有bug不能用)

_PRECISE_TIMER(win \linux 都是非实时系统)

 

2.2. event_config_require_features(config, EV_....) 特征的设置, event_method_feature(ET(LT) 、FEATURE_01 是否要求添加删除事件或激活的时间复杂度位O1、

FDS支持任意套接字(网络和文件都ok,win不行,和epoll互斥)、EARLY_CLOSE(检测连接关闭支持,不一定能支持))

    获取已经设置的默认特征 event_base_get_features(base) 返回枚举值的位运算结果

 

2.3. event_config_avoid_methed(conf , "epoll")  //选择使用的网络模型,把前面的avoid掉直到自己要的

      2.3.1. 获取当前的网络模型:event_base_get_method        

      2.3.2  获取支持哪些模型,通过event_get_supported_methods方法获取模型

3. event_base_new_with_config(config)

4. 销毁环境配置 

event_config_free(config)

5.  base的销毁(上下文的销毁)

event_base_free(base)

6. event_assign //替换老版本的event_set 、event_base_set  //但是debug模式下存在内存泄漏问题,推荐使用event_new函数

7.event_new函数 (事件IO)

       1. event_new 创建event   --------  innitialized(nopending)

          (base 上下文,   fd 描述符,   which 事件标志 EV_TIMEOUT| EV_READ| EV_WRITE| EV_PERSIST,    ev_cb事件回调函数,   arg回调函数的参数)

           EV_PERSIST  持久化,不用触发后再调用add,大部分都设置持久

      2.  event_add(ev,   超时时间可以传0)  ---------------------pending, 设置超时可以当定时器用?

             even 的base初始化的时候有是否加锁,在event_add中就会判断,多线程调用event_add的时候base初始化要打开锁

      3. 触发 ----------------active

     4. event_del -------------disable事件,nopending状态

     5. event_free(ev) 才是真的销毁

 

信号

event *csig = evsignal_new(base,SIGINT, cb_func, base)   //event_new(base, SIGINT, EV_SIGNAL|EV_PERSIST, cb_func, arg)

//event_self_cbarg()  就是event自己,在event_new时可以当arg传给事件回调函数

//event_pengding( ev, NULL)  是否处于待决状态

if ( 0 != event_add(csig, 0) ){//error

}

event_base_dispatch(base); //into event loop

event_free(csig)

event_base_free(base);

 

定时器

//ev = event_new(base, -1,  EV_PERSIST, timer2, 0); //持久的

ev = evtimer_new(base, timer1, event_self_cbarg()); //默认非持久的,可以用pending判断后del再add变持久化效果

 

evtimer_del(ev);

evtimer_add(ev, &t1); //event_add    (超时统计,含了定时开始后业务处理消耗的时间)

    //add time_out for client can nomsg to here
    //event_add(ev, 0); 
    timeval t = {10, 0};
    event_add(ev, &t);

evtimer_pending(ev, &t1)

t1             struct timeval {  tv_sec; tv_usec}

定时器事件存储,默认是二叉堆(binary heap)的数据结构 O(log n)

大量相同超时值的定时任务时 用event_base_init_common_timeout返回值,即使用双向队列存储, 不同超时值的情况,用这个时间复杂度O(n)

static timeval tv_in = {3, 0};

const timeval* t3 = event_base_init_common_timeout(base, &tv_in);

event_add(ev3, t3);

否则用默认的二叉堆存储方式性能更高  O(log n)

 

文件监听事件

event_config* config = event_config_new();

event_config_require_features(config, EV_FEATURE_FDS);  //支持文件描述符可监听?

event_base * base = event_base_new_with_config(config);

event_config_free(config);;

int fd = open(filepath+filename, O_RDONLY|O_NONBLOCK, 0); //非阻塞只读打开文件

lseek(fd, 0, SEEK_END);//移动到文件尾

event * fev = event_new(base, fd, FV_PERSIST|FV_READ,   read_cb, event_self_cbarg());   //initial it ,   tip: read_cb可能一直进入,需要通过判断是否读到内容,再进行后续处理,网上看到有libevent+ inotify的结合使用。

event_add(fev, 0);//pending it

event_base_dispach(base);//loop dispatch it

event_free(fev);

event_base_free(base);

8. 缓冲IO befferEvent (对多线程的支持不是很好,如果不在线程中使用,需要添加声明开启多线程支持, event需要支持BEV_OPT_THREADSAFE

https://www.cnblogs.com/adinosaur/p/7113631.html

eg:

    int fd = socket(AF_INET, SOCK_STREAM, 0);

#ifdef WIN32

    evthread_use_windows_threads();//win上设置

#else

    evthread_use_pthreads();//unix上设置

#endif

    struct event_base *base = event_base_new();

//文件描叙符可以设置为-1,只要稍后用bufferevent_setfd或者bufferevent_socket_connect()来设置它

    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE);

    bufferevent_socket_connect(bev, (struct sockaddr*)&sin, sizeof(sin));

    bufferevent_setcb(bev, event_on_read, event_on_write, event_on_connect, base);//回调函数不允许阻塞影响loop,所以接收完数据后启动子线程去处理业务

    bufferevent_enable(bev, EV_READ | EV_WRITE);

    stClient.bev = bev;  /我这里设置多线程支持是因为我要在其他线程用这个bev去发送数据//bufferevent_write(stClient.bev, datapackage, packlen);

    event_base_dispatch(base);

 

eg:server

evconnlistener_new_bind

连接进入,调用socket_listenercb  //存储参数fd,后续进行数据回发

event_base *base = evconnlistener_get_base(listener);

int32_t enable = 1;

    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*) &enable, sizeof(enable));

    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const char*) &enable, sizeof(enable));

    evutil_make_socket_nonblocking(fd);//把连接描述符置成非阻塞模式,边沿触发while读取的时候(recv)才不会阻塞

bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

bufferevent_setcb(bev, socket_readcb, NULL, socket_eventcb, user_data); 

bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);

9. 循环(loop)事件的分发处理

event_base_dispatch //替换老版本的event_dispatch

10. event_reinit 上下文的reinit

fork进程后有些资源的copy是无效的,使用event_reinit可以让copy是生效的,保证fork之后工作正常且释放event_base内部分配的空间及本身对象的空间,而不释放事件、socket和回调函数中申请的空间。但是其实可以在fork之后再去创建event,就不会有这种情况,不会需要用到event_reinit

 

libevent的sock封装 (跨平台的socket,主要还是用事件)

1. close_socket 、closee -> evutil_closesocket()

2. evutil_make_socket_nonblocking  //置文件描述符为不阻塞,此时recv时读不到会返回0

3. evutil_make_listen_socket_reuseable //SO_REUSEADDR...

 

//event2/listen.h

event*  ev= evconnlistener_new_bind     //create sock 、bind  and listen绑定事件的封装

base  //libevent的上下文

listen_cb //listen接到连接的回调函数指针

arg //listen 回调函数获取的参数,可以传base

flag  //LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE  地址重用及关闭连接时做相应的关闭释放

num //缓冲队列最多连接个数(排队,不应太大,否则处理不过来容易造成阻塞)

struct socketaddr_in* //绑定ip地址和端口 ipv4 ipv6

len //sizeof(sockaddr)

event_base_dispatch(base)

evconnlistener_free(ev) //事件的销毁

event_base_free(上下文的销毁)

 

你可能感兴趣的:(C++,libevent)