Libevent详解与实践

libevent文档

官方网站

官方文档

官方GitHub

libevent-2.1.11-stable.tar.gz

前言

Libevent是用于开发可伸缩网络服务器的事件通知库。Libevent API提供了一种机制,在文件描述符上发生特定事件或达到超时后执行回调函数。此外,Libevent还支持由于信号或定期超时而进行的回调。

Libevent用于取代在事件驱动的网络服务器中的事件的循环。应用程序只需调用event_base_dispatch(),然后动态添加或删除事件,而无需更改事件循环。

目前,Libevent支持/dev/poll、kqueue(2)、select(2)、poll(2)、epoll(4)和evports。内部事件机制完全独立于公开的事件API,简单的Libevent更新可以提供新的功能,而无需重新设计应用程序。因此,Libevent允许进行可移植的应用程序开发,并提供操作系统上可用的最可伸缩的事件通知机制。Libevent也可以用于多线程程序。Libevent应该在Linux、*BSD、Mac OS X、Solaris和Windows上编译。

设计目标是:

  • 可移植性:使用 libevent 编写的程序应该可以在 libevent 支持的所有平台上工作。即使 没有好的方式进行非阻塞 IO,libevent 也应该支持一般的方式,让程序可以在受限的环境中运行。

  • 速度:libevent 尝试使用每个平台上最高速的非阻塞 IO 实现,并且不引入太多的额外开销。

  • 可扩展性:libevent 被设计为程序即使需要上万个活动套接字的时候也可以良好工作。

  • 方便:无论何时,最自然的使用 libevent 编写程序的方式应该是稳定的、可移植的。

libevent 由下列组件构成

  • evutil:用于抽象不同平台网络实现差异的通用功能。
  • event 和 event_base:libevent 的核心,为各种平台特定的、基于事件的非阻塞 IO 后 端提供抽象 API,让程序可以知道套接字何时已经准备好,可以读或者写,并且处理基 本的超时功能,检测 OS 信号。
  • bufferevent:为 libevent 基于事件的核心提供使用更方便的封装。除了通知程序套接字 已经准备好读写之外,还让程序可以请求缓冲的读写操作,可以知道何时 IO 已经真正 发生。
  • evbuffer:在 bufferevent 层之下实现了缓冲功能,并且提供了方便有效的访问函数。
  • evhttp:一个简单的 HTTP 客户端/服务器实现。
  • evdns:一个简单的 DNS 客户端/服务器实现。
  • evrpc:一个简单的 RPC 实现。

编译

linux

$ ./configure
$ make
$ make verify   # (optional)
$ sudo make install

ARM

$ ./configure --host=arm-linux --prefix=/usr/local/libevent_arm CC=arm-none-linux-gnueabi-gcc CXX=arm-none-linux-gnueabi-g++
$ make
$ make verify   # (optional)
$ sudo make install

Building and installing Libevent

生成库

创建 libevent 时,默认安装下列库:

  • ibevent_core:所有核心的事件和缓冲功能,包含了所有的 event_base、evbuffer、 bufferevent 和工具函数。

  • ibevent_extra:定义了程序可能需要,也可能不需要的协议特定功能,包括 HTTP、 DNS 和 RPC。

  • libevent:这个库因为历史原因而存在,它包含 libevent_core 和 libevent_extra 的内容。 不应该使用这个库,未来版本的 libevent 可能去掉这个库。

某些平台上可能安装下列库:

  • libevent_pthreads:添加基于 pthread 可移植线程库的线程和锁定实现。它独立于 libevent_core,这样程序使用 libevent 时就不需要链接到 pthread,除非实际上是以多线程的方式使用libevent。

  • libevent_openssl:这个库为使用 bufferevent 和 OpenSSL 进行加密的通信提供支持。 它独立于 libevent_core,这样程序使用 libevent 时就不需要链接到 OpenSSL,除非实际使用的是加密连接。

概述

标准用法

使用Libevent的每个程序都必须包含头文件,并将-levent传递给链接器。(如果只需要主事件和缓冲的基于IO的代码,而不想链接任何协议代码,则可以改为链接-levent_core。)

库设置

在调用任何其他Libevent函数之前,需要设置库。如果要在多线程应用程序的多个线程中使用Libevent,则需要初始化线程支持:通常使用evthread_use_pthreads()或evthread_use_windows_threads()。有关更多信息,请参见

这也是可以用event_set_mem_functions替换Libevent的内存管理函数,并用event_enable_debug_mode()启用调试模式的地方。

创建event base

接下来,需要使用event_base_new()或event_base_new_with_config()创建一个event_base结构体。event_base负责跟踪哪些事件是“待处理的”(也就是说,被监视以查看它们是否变为活动的)以及哪些事件是“活动的”。每个事件都与单个event_base相关联。

事件通知

对于要监视的每个文件描述符,必须使用event_new()创建事件结构。(您也可以声明一个事件结构并调用event_assign()来初始化该结构的成员。)要启用通知,您可以通过调用event_add()将该结构添加到监视的事件列表中。只要事件结构处于活动状态,它就必须保持分配状态,因此通常应该在堆上分配它。

调度事件。

最后,调用event_base_dispatch()循环和分派事件。您还可以使用event_base_loop()进行更细粒度的控制。

目前,一次只能有一个线程调度给定的event_base 。如果希望一次在多个线程中运行事件,可以有一个将工作添加到工作队列中的event_base ,也可以创建多个event_base对象。

I/O缓冲区

Libevent在常规事件回调的基础上提供了缓冲I/O抽象。这个抽象称为bufferevent。bufferevent提供自动填充和移除的输入和输出缓冲区。缓冲事件的用户不再直接处理I/O,而是读取输入和写入输出缓冲区。

一旦通过bufferevent_socket_new()初始化,bufferevent结构就可以与bufferevent_enable()和bufferevent_disable()一起重复使用。您将调用bufferevent_read()和bufferevent_write(),而不是直接读取和写入套接字。

启用读取后,bufferevent将尝试从文件描述符读取并调用读取回调。每当输出缓冲区被排放到写低水位线(默认为0)以下时,就会执行写回调。

有关更多信息,请参阅

计时器

Libevent还可以用来创建计时器,在一定时间过期后调用回调。evtimer_new()宏返回要用作计时器的事件结构。要激活计时器,请调用evtimer_add()。可以通过调用evtimer_del()来停用计时器。(这些宏是围绕event_new()、event_add()和event_del()的精简封装;您也可以使用它们。)

异步DNS解析

Libevent提供了一个异步DNS解析器,应该使用它来代替标准DNS解析器函数。有关更多详细信息,请参阅函数。

事件驱动的HTTP服务器

Libevent提供了一个非常简单的事件驱动HTTP服务器,可以嵌入到程序中并用于服务HTTP请求。

要使用此功能,您需要在程序中包含头。有关详细信息,请参阅该标题。

RPC服务器和客户机的框架

Libevent提供了一个创建RPC服务器和客户端的框架。它负责对所有数据结构进行编组和解组。

API参考

要浏览libevent API的完整文档,请单击以下任何链接。

event2/event.h :主libevent头

event2/thread.h :多线程程序

event2/buffer.h 和event2/bufferevent.h :网络读写的缓冲区管理

event2/util.h : 可移植非阻塞网络代码的实用函数

event2/dns.h :异步DNS解析

event2/http.h :基于libevent的嵌入式HTTP服务器

event2/rpc.h :创建RPC服务器和客户端的框架

event2/watch.h :“准备”和“检查”观察者

详细说明

基于Libevent 2.0+,在C语言中编写快速可移植的异步网络IO程序。

Libevent详解与实践(一)

Libevent详解与实践(二)

Libevent详解与实践(三)

Libevent详解与实践(四)

Libevent详解与实践(五)

Libevent详解与实践(六)

Libevent详解与实践(七)

Libevent详解与实践(八)

Libevent详解与实践(九)

Libevent详解与实践(十)

示例

Timer

示例

#include 
#include 

static int numCalls = 0;
static int numCalls_now = 0;
struct timeval lasttime;
struct timeval lasttime_now;

static void timeout_cb(evutil_socket_t fd, short event, void *arg)
{
    struct event *ev = (struct event *)arg;
    struct timeval newtime,tv_diff;
    double elapsed;

    evutil_gettimeofday(&newtime, NULL);
    evutil_timersub(&newtime, &lasttime, &tv_diff);

    elapsed = tv_diff.tv_sec +  (tv_diff.tv_usec / 1.0e6);

    lasttime = newtime;
    printf("[%.3f]timeout_cb %d \n",elapsed,++numCalls);
    
}

static void now_timeout_cb(evutil_socket_t fd, short event, void *arg)
{
    struct event *ev = (struct event *)arg;
    struct timeval tv = {3,0};
    struct timeval newtime,tv_diff;
    double elapsed;

    evutil_gettimeofday(&newtime, NULL);
    evutil_timersub(&newtime, &lasttime_now, &tv_diff);
    elapsed = tv_diff.tv_sec +  (tv_diff.tv_usec / 1.0e6);

    lasttime_now = newtime;
    printf("[%.3f]now_timeout_cb %d \n",elapsed,++numCalls_now);

    //每次回调都将当前先del,再次添加
    //if (!event_pending(ev,EV_PERSIST|EV_TIMEOUT,NULL))
    //{
    //  printf("if\n");
    //  event_del(ev);
    //  event_add(ev, &tv);
    //}
    //添加新的event
    if (!event_pending(ev,EV_PERSIST|EV_TIMEOUT,NULL))
    {
        struct event_base *evBase = event_get_base(ev);
        event_del(ev);
        event_free(ev);
    
        ev = event_new(evBase, -1, EV_PERSIST|EV_TIMEOUT, now_timeout_cb, event_self_cbarg());
        event_add(ev, &tv);
    }
}

int main(int argc, char **argv)
{
    struct event_base *evBase = NULL;
    struct event_config *evConf = NULL;
    struct event *ev_now_timeout = NULL;
    struct event *ev_timeout = NULL;
    struct timeval tv = {0,0};
    struct timeval tv_now = {0,0};
    //创建简单的event_base
    evBase = event_base_new();

    //创建带配置的event_base
    evConf = event_config_new();//创建event_config
    evBase = event_base_new_with_config(evConf);

    //创建event
    //传递自己event_self_cbarg()
    ev_now_timeout = evtimer_new(evBase, now_timeout_cb, event_self_cbarg());

    //设置时间
    tv.tv_sec = 1;
    tv.tv_usec = 500 * 1000;
    ev_timeout = event_new(evBase, -1, EV_PERSIST|EV_TIMEOUT, timeout_cb, event_self_cbarg());

    //添加event
    event_add(ev_now_timeout, &tv_now);//立即执行一次,然后定时
    event_add(ev_timeout, &tv);

    //获取时间
    evutil_gettimeofday(&lasttime, NULL);
    evutil_gettimeofday(&lasttime_now, NULL);
    //循环
    //event_base_loop(evBase, 0);
    event_base_dispatch(evBase);
    
    //释放
    event_free(ev_timeout);
    event_free(ev_now_timeout);
    event_config_free(evConf);
    event_base_free(evBase);
}

实现了一个Timer定时器,添加了2个event,ev_now_timeout立即执行一次,然后按3s周期执行;ev_timeout以1.5s周期执行。

TCP

Server

#include 
#include 
#include 
#include 
#include 
#include  
// 读缓冲区回调
static void read_cb(struct bufferevent *bev, void *arg)
{
    //读到buff
    char buf[1024] = {0};   
    bufferevent_read(bev, buf, sizeof(buf));

    //读取evbuffer
    //struct evbuffer *input = bufferevent_get_input(bev);
    //size_t size = evbuffer_get_length(input);
    //
    //char *buf = (char *)malloc(size);   
    //evbuffer_remove(input, buf, size);

    printf("[server]rece client data\n");
    printf("[server]client say: %s\n", buf);
 
}
 
// 写缓冲区回调
static void write_cb(struct bufferevent *bev, void *arg)
{
    printf("[server]write_cb\n"); 
}
 
// 事件
static void event_cb(struct bufferevent *bev, short events, void *arg)
{
    if (events & BEV_EVENT_EOF)
    {
        printf("[server]connection close\n");  
    }
    else if(events & BEV_EVENT_ERROR)   
    {
        printf("[server]connection error\n");
    }else if(events & BEV_EVENT_CONNECTED)
    {
        printf("[server]connection success\n");
        return;
    }
    
    bufferevent_free(bev);    
    printf("[server]bufferevent free\n"); 
}
 
static void send_cb(evutil_socket_t fd, short what, void *arg)
{
    char buf[1024] = {0}; 
    struct bufferevent* bev = (struct bufferevent*)arg;
    
    read(fd, buf, sizeof(buf));

    //struct evbuffer *output = bufferevent_get_output(bev);
    //evbuffer_add(output, buf, strlen(buf)+1);

    bufferevent_write(bev, buf, strlen(buf)+1);
}

static void cb_listener(
        struct evconnlistener *listener, 
        evutil_socket_t fd, 
        struct sockaddr *addr, 
        int len, void *ptr)
{
   printf("[server]connect new client\n");
 
   struct event_base* base = (struct event_base*)ptr;
   // 通信操作
   // 添加新事件
   struct bufferevent *bev;
   bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
 
   if (!bev) {
        fprintf(stderr, "[server]error constructing bufferevent!");
        event_base_loopbreak(base);
        return;
    }

   // 给bufferevent缓冲区设置回调
   bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);

   bufferevent_enable(bev, EV_READ);
   
   // 创建一个事件
   struct event* ev = event_new(base, STDIN_FILENO, 
                                 EV_READ | EV_PERSIST, 
                                 send_cb, bev);
   event_add(ev, NULL);
   
}

static void accept_error_cb(struct evconnlistener *listener, void *ctx)
{
    struct event_base *base = evconnlistener_get_base(listener);
    int err = EVUTIL_SOCKET_ERROR();
    printf("[Server]Got an error %d (%s) on the listener.Shutting down.\n", 
        err, evutil_socket_error_to_string(err));
    event_base_loopexit(base, NULL);
}

 
int main(int argc, const char* argv[])
{
    struct sockaddr_in serv;
    struct event_base* base;
    struct evconnlistener* listener;

    // init server 
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(6666);
    serv.sin_addr.s_addr = htonl(INADDR_ANY);
    
    base = event_base_new();

    if (!base) {
        printf("[server]Couldn't open event base\n");
        return 1;
    }

    // 创建套接字
    // 绑定
    // 接收连接请求  
    //LEV_OPT_CLOSE_ON_FREE 如果设置了这个选项,释放连接监听器会关闭底层套接字。 
    //LEV_OPT_REUSEABLE 某些平台在默认情况下,关闭某监听套接字后,要过一会儿其他套接字才可以绑定到同一个端口。
    //设置这个标志会让 libevent 标记套接字是可重用的,这样一旦关闭,可以立即打开其他套接字,在相同端口进行监听。
    listener = evconnlistener_new_bind(base, cb_listener, base, 
                                  LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 
                                  36, (struct sockaddr*)&serv, sizeof(serv));
 
    if (!listener) {
        perror("[server]Couldn't create listener");
        return 1;
    }

    //error处理
    evconnlistener_set_error_cb(listener, accept_error_cb);

    event_base_dispatch(base);
 
    //释放
    evconnlistener_free(listener);
    event_base_free(base);
 
    return 0;
}

实现了一个TCP server,监听6666端口,与客户端建立连接,以后可以互相发送消息。

Client

实现了一个TCP client,连接本地6666端口,与客户端建立连接,以后可以互相发送消息。

#include 
#include 
#include 
#include 
#include 
#include  

void read_cb(struct bufferevent *bev, void *arg)
{
    char buf[1024] = {0}; 
    bufferevent_read(bev, buf, sizeof(buf));
    printf("[client]rece server data\n");
    printf("[client]server say: %s\n", buf);
}
 
void write_cb(struct bufferevent *bev, void *arg)
{
   printf("[client]write_cb\n"); 
}
 
void event_cb(struct bufferevent *bev, short events, void *arg)
{
    if (events & BEV_EVENT_EOF)
    {
        printf("[client]connection close\n");  
    }
    else if(events & BEV_EVENT_ERROR)   
    {
        printf("[client]connection error\n");
    }
    else if(events & BEV_EVENT_CONNECTED)
    {
        printf("[client]connection success\n");
        return;
    }
    
    bufferevent_free(bev);
    printf("[client]bufferevent free\n");
}
 
void send_cb(evutil_socket_t fd, short what, void *arg)
{
    char buf[1024] = {0}; 
    struct bufferevent* bev = (struct bufferevent*)arg;
    read(fd, buf, sizeof(buf));
    bufferevent_write(bev, buf, strlen(buf)+1);
}

int main(int argc, const char* argv[])
{
    struct event_base *base;
    struct bufferevent* bev;
    struct sockaddr_in serv;
    struct event* ev;

    base = event_base_new();

    if (!base) {
        printf("[client]Couldn't open event base\n");
        return 1;
    }

    bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);

    //连接服务器
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(6666);
    //解析 IP 地址
    evutil_inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
    //连接
    bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));
    //设置回调
    bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
    bufferevent_enable(bev, EV_READ);

    // 创建一个事件
    //STDIN_FILENO:接收键盘的输入
    ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, 
                                 send_cb, bev);
    event_add(ev, NULL);
    
    event_base_dispatch(base);

    //释放
    bufferevent_free(bev);
    event_free(ev);
    event_base_free(base);

}

HTTP

Server

实现了一个http server。

#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 

char uri_root[512];

static const struct table_entry {
    const char *extension;
    const char *content_type;
} content_type_table[] = {
    { "txt", "text/plain" },
    { "c", "text/plain" },
    { "h", "text/plain" },
    { "html", "text/html" },
    { "htm", "text/htm" },
    { "css", "text/css" },
    { "gif", "image/gif" },
    { "jpg", "image/jpeg" },
    { "jpeg", "image/jpeg" },
    { "png", "image/png" },
    { "pdf", "application/pdf" },
    { "ps", "application/postscript" },
    { NULL, NULL },
};

/* 尝试猜测“path”的内容类型 */
static const char *
guess_content_type(const char *path)
{
    const char *last_period, *extension;
    const struct table_entry *ent;
    last_period = strrchr(path, '.');
    if (!last_period || strchr(last_period, '/'))
        goto not_found; /* no exension */
    extension = last_period + 1;
    for (ent = &content_type_table[0]; ent->extension; ++ent) {
        if (!evutil_ascii_strcasecmp(ent->extension, extension))
            return ent->content_type;
    }

not_found:
    return "application/misc";
}

/* 用于/test URI请求的回调 */
static void
dump_request_cb(struct evhttp_request *req, void *arg)
{
    const char *cmdtype;
    struct evkeyvalq *headers;
    struct evkeyval *header;
    struct evbuffer *buf_in;
    struct evbuffer *buf_out;
    struct evhttp_uri *decoded = NULL;
    struct evkeyvalq params;
    char *decoded_path;
    const char *path;
    char cbuf[1024] = {0};
    const char *uri = evhttp_request_get_uri(req);

    switch (evhttp_request_get_command(req)) {
    case EVHTTP_REQ_GET: cmdtype = "GET"; break;
    case EVHTTP_REQ_POST: cmdtype = "POST"; break;
    case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break;
    case EVHTTP_REQ_PUT: cmdtype = "PUT"; break;
    case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break;
    case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break;
    case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break;
    case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break;
    case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break;
    default: cmdtype = "unknown"; break;
    }

    printf("Received a %s request for %s\nHeaders:\n",
        cmdtype, uri);

    headers = evhttp_request_get_input_headers(req);

    for (header = headers->tqh_first; header;
        header = header->next.tqe_next) {
        printf("  %s: %s\n", header->key, header->value);
    }

    /*********************************/
    /* 解析 URI */
    decoded = evhttp_uri_parse(uri);
    if (!decoded) {
        printf("It's not a good URI. Sending BADREQUEST\n");
        evhttp_send_error(req, HTTP_BADREQUEST, 0);
        return;
    }

    /* 获取path */
    path = evhttp_uri_get_path(decoded);
    if (!path){
        evhttp_send_error(req, HTTP_BADREQUEST, 0);
        return;
    }
    printf("path: %s\n", path);

    
    //解析URI的参数
    //将URL数据封装成key-value格式,q=value1, s=value2
    evhttp_parse_query(uri, ¶ms);
    //得到a所对应的value
    const char *a_data = evhttp_find_header(¶ms, "a");

    printf("a=%s\n",a_data);
    /*********************************/
    if (strcmp(cmdtype,"POST") == 0)
    {
        //获取POST方法的数据
        buf_in = evhttp_request_get_input_buffer(req);

        if (buf_in==NULL)
        {
            printf("evBuf null, err\n");
            goto err;
        }
        //获取长度
        int buf_in_len = evbuffer_get_length(buf_in);
    
        printf("evBuf len:%d\n",buf_in_len);
    
        if(buf_in_len <= 0)
        {
            goto err;
        }
        //将数据从evbuff中移动到char *
        int str_len = evbuffer_remove(buf_in,cbuf,sizeof(cbuf));

        if (str_len <= 0)
        {
            printf("post parameter null err\n");
            goto err;
        }
        printf("str_len:%d cbuf:%s\n",str_len,cbuf);
    }
    /*********************************/
    buf_out = evbuffer_new();
    if(!buf_out)
    {
        puts("failed to create response buffer \n");
        return;
    }
    evbuffer_add_printf(buf_out,"%s","success");
    evhttp_send_reply(req, 200, "OK", buf_out);
    return;
err:
    evhttp_send_error(req, HTTP_INTERNAL, 0);
}

static void
send_document_cb(struct evhttp_request *req, void *arg)
{
    evhttp_send_error(req, 404, "url was not found");
}

static void
do_term(int sig, short events, void *arg)
{
    struct event_base *base = (struct event_base *)arg;
    event_base_loopbreak(base);
    fprintf(stderr, "Got %i, Terminating\n", sig);
}

static int
display_listen_sock(struct evhttp_bound_socket *handle)
{
    struct sockaddr_storage ss;
    evutil_socket_t fd;
    ev_socklen_t socklen = sizeof(ss);
    char addrbuf[128];
    void *inaddr;
    const char *addr;
    int got_port = -1;

    fd = evhttp_bound_socket_get_fd(handle);
    memset(&ss, 0, sizeof(ss));
    if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) {
        perror("getsockname() failed");
        return 1;
    }

    if (ss.ss_family == AF_INET) {
        got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
        inaddr = &((struct sockaddr_in*)&ss)->sin_addr;
    } else if (ss.ss_family == AF_INET6) {
        got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
        inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr;
    }
    else {
        fprintf(stderr, "Weird address family %d\n",
            ss.ss_family);
        return 1;
    }

    addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf,
        sizeof(addrbuf));
    if (addr) {
        printf("Listening on %s:%d\n", addr, got_port);
        evutil_snprintf(uri_root, sizeof(uri_root),
            "http://%s:%d",addr,got_port);
    } else {
        fprintf(stderr, "evutil_inet_ntop failed\n");
        return 1;
    }

    return 0;
}

int
main(int argc, char **argv)
{

    struct event_base *base = NULL;
    struct evhttp *http = NULL;
    struct evhttp_bound_socket *handle = NULL;
    struct evconnlistener *lev = NULL;
    struct event *term = NULL;
    int ret = 0;

    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
        ret = 1;
        goto err;
    }

    //event_base
    base = event_base_new();

    if (!base) {
        fprintf(stderr, "Couldn't create an event_base: exiting\n");
        ret = 1;
    }

    /* 创建一个新的evhttp对象来处理请求。 */
    http = evhttp_new(base);
    if (!http) {
        fprintf(stderr, "couldn't create evhttp. Exiting.\n");
        ret = 1;
    }

    /* / test URI将所有请求转储到stdout并说200 OK。 */
    evhttp_set_cb(http, "/test", dump_request_cb, NULL);

    /*要接受任意请求,需要设置一个“通用”cb。 还可以为特定路径添加回调。 */
    evhttp_set_gencb(http, send_document_cb, NULL);

    //绑定socket
    handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", 8888);

    if (!handle) {
        fprintf(stderr, "couldn't bind to port %d. Exiting.\n", 8888);
        ret = 1;
        goto err;
    }
    
    //监听socket
    if (display_listen_sock(handle)) {
        ret = 1;
        goto err;
    }

    //终止信号
    term = evsignal_new(base, SIGINT, do_term, base);
    if (!term)
        goto err;
    if (event_add(term, NULL))
        goto err;

    //事件分发
    event_base_dispatch(base);

err:

    if (http)
        evhttp_free(http);
    if (term)
        event_free(term);
    if (base)
        event_base_free(base);

    return ret;
}

Client

实现了一个http client。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  

static int ignore_cert = 0;

static void
http_request_done(struct evhttp_request *req, void *ctx)
{
    char buffer[256];
    int nread;
    struct evbuffer *buf_in;
    char cbuf[1024] = {0};

    //错误处理,打印
    if (!req || !evhttp_request_get_response_code(req)) {
        
        struct bufferevent *bev = (struct bufferevent *) ctx;
        unsigned long oslerr;
        int printed_err = 0;
        int errcode = EVUTIL_SOCKET_ERROR();
        fprintf(stderr, "some request failed - no idea which one though!\n");

        /* 尝试打印 */
        if (! printed_err)
            fprintf(stderr, "socket error = %s (%d)\n",
                evutil_socket_error_to_string(errcode),
                errcode);
        return;
    }

    fprintf(stderr, "Response line: %d %s\n",
        evhttp_request_get_response_code(req),
        evhttp_request_get_response_code_line(req));

    //获取数据
    buf_in = evhttp_request_get_input_buffer(req);

    if (buf_in==NULL)
    {
        printf("evBuf null, err\n");
    }
    //获取长度
    int buf_in_len = evbuffer_get_length(buf_in);
    
    printf("evBuf len:%d\n",buf_in_len);
    
    if(buf_in_len <= 0)
    {
    }
    //将数据从evbuff中移动到char *
    int str_len = evbuffer_remove(buf_in,cbuf,sizeof(cbuf));

    if (str_len <= 0)
    {
        printf("post parameter null err\n");
    }
    printf("str_len:%d cbuf:%s\n",str_len,cbuf);
}

static void
err(const char *msg)
{
    fputs(msg, stderr);
}

int
main(int argc, char **argv)
{
    int r;
    struct event_base *base = NULL;
    struct evhttp_uri *http_uri = NULL;
    const char *url = NULL, *data_file = NULL;
    const char *scheme, *host, *path, *query;
    char uri[256];
    int port;
    int retries = 0;
    int timeout = -1;

    struct bufferevent *bev;
    struct evhttp_connection *evcon = NULL;
    struct evhttp_request *req;
    struct evkeyvalq *output_headers;
    struct evbuffer *output_buffer;

    int i;
    int ret = 0;
    
    //初始化url
    url = "http://127.0.0.1:8888/test?a=123";
    if (!url) {
        goto error;
    }

    http_uri = evhttp_uri_parse(url);
    if (http_uri == NULL) {
        err("malformed url");
        goto error;
    }

    scheme = evhttp_uri_get_scheme(http_uri);
    //忽略大小写比较字符串
    if (scheme == NULL || strcasecmp(scheme, "http") != 0) {
        err("url must be http");
        goto error;
    }

    host = evhttp_uri_get_host(http_uri);
    if (host == NULL) {
        err("url must have a host");
        goto error;
    }

    port = evhttp_uri_get_port(http_uri);

    if (port == -1) {
        port = 80;
    }

    path = evhttp_uri_get_path(http_uri);
    if (strlen(path) == 0) {
        path = "/";
    }

    query = evhttp_uri_get_query(http_uri);

    if (query == NULL) {
        //将可变参数 “…” 按照format的格式格式化为字符串,然后再将其拷贝至str中。
        snprintf(uri, sizeof(uri) - 1, "%s", path);
    } else {
        snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
    }

    uri[sizeof(uri) - 1] = '\0';

    // 创建 event base
    base = event_base_new();
    if (!base) {
        perror("event_base_new()");
        goto error;
    }

    if (strcasecmp(scheme, "http") == 0) {
        bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
    } 

    if (bev == NULL) {
        fprintf(stderr, "bufferevent_socket_new() failed\n");
        goto error;
    }

    evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev, host, port);
    if (evcon == NULL) {
        fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
        goto error;
    }

    //重试
    if (retries > 0) {
        evhttp_connection_set_retries(evcon, retries);
    }

    //超时
    if (timeout >= 0) {
        evhttp_connection_set_timeout(evcon, timeout);
    }

    //回调
    req = evhttp_request_new(http_request_done, bev);

    if (req == NULL) {
        fprintf(stderr, "evhttp_request_new() failed\n");
        goto error;
    }

    output_headers = evhttp_request_get_output_headers(req);
    evhttp_add_header(output_headers, "Host", host);
    evhttp_add_header(output_headers, "Connection", "close");

    //文件路径
    data_file = "/home/zza/libevent/demo/test.txt";

    if (data_file) {

        char buf[1024];
        output_buffer = evhttp_request_get_output_buffer(req);

        //post file传统复制
        //FILE* f = fopen(data_file, "rb");     
        //size_t s;
        //size_t bytes = 0;
        //if (!f) {
        //  goto error;
        //}     
        //
        //while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
        //  evbuffer_add(output_buffer, buf, s);
        //  bytes += s;
        //}
        //evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
        //evhttp_add_header(output_headers, "Content-Length", buf);
        //fclose(f);

        //************************************************************
        //post file使用evbuffer_add_file()或 
        //evbuffer_add_file_segment(),以避免不必要的复制
        //int fd = open(data_file, O_RDONLY);

        //if (fd == -1)
        //{
        //  fprintf(stderr, "open %s failed\n", data_file);
        //  goto error;
        //}

        //evbuffer_add_file(output_buffer,fd,0,-1);
        //ev_ssize_t size =  evbuffer_copyout(output_buffer, buf, sizeof(buf));     
        //evhttp_add_header(output_headers, "Content-Length", buf);

        //************************************************************
        //http post json
        //sprintf(buf,"%s","{\"a\":\"b\"}");
        //evbuffer_add(output_buffer, buf, strlen(buf));
        //
        //evhttp_add_header(output_headers, "Content-Type", "application/json;charset=UTF-8");

        //************************************************************
        //http post format
        sprintf(buf,"%s\r\n%s\r\n\r\n%s\r\n%s\r\n",
            "--------------------------123",
            "Content-Disposition: form-data; name=\"hello\"",
            "world!!!!",
            "--------------------------123");
        evbuffer_add(output_buffer, buf, strlen(buf));
        
        evhttp_add_header(output_headers, "Content-Type", 
            "multipart/form-data; boundary=--------------------------123");
        //************************************************************
    }
    //文件路径为NULL时为get请求,非空post
    //请求
    r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);

    if (r != 0) {
        fprintf(stderr, "evhttp_make_request() failed\n");
        goto error;
    }

    event_base_dispatch(base);

    goto cleanup;

error:
    printf("error stop");
    ret = 1;
cleanup:
    if (evcon)
        evhttp_connection_free(evcon);
    if (http_uri)
        evhttp_uri_free(http_uri);
    if (base)
        event_base_free(base);

    return ret;
}

HTTPS

Client

/*
  This is an example of how to hook up evhttp with bufferevent_ssl

  It just GETs an https URL given on the command-line and prints the response
  body to stdout.

  Actually, it also accepts plain http URLs to make it easy to compare http vs
  https code paths.

  Loosely based on le-proxy.c.
 */

#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

static int ignore_cert = 0;

static void
http_request_done(struct evhttp_request *req, void *ctx)
{
    char buffer[256];
    int nread;

    if (!req || !evhttp_request_get_response_code(req)) {
        /* If req is NULL, it means an error occurred, but
         * sadly we are mostly left guessing what the error
         * might have been.  We'll do our best... */
        struct bufferevent *bev = (struct bufferevent *) ctx;
        unsigned long oslerr;
        int printed_err = 0;
        int errcode = EVUTIL_SOCKET_ERROR();
        fprintf(stderr, "some request failed - no idea which one though!\n");
        /* Print out the OpenSSL error queue that libevent
         * squirreled away for us, if any. */
        while ((oslerr = bufferevent_get_openssl_error(bev))) {
            ERR_error_string_n(oslerr, buffer, sizeof(buffer));
            fprintf(stderr, "%s\n", buffer);
            printed_err = 1;
        }
        /* If the OpenSSL error queue was empty, maybe it was a
         * socket error; let's try printing that. */
        if (! printed_err)
            fprintf(stderr, "socket error = %s (%d)\n",
                evutil_socket_error_to_string(errcode),
                errcode);
        return;
    }

    fprintf(stderr, "Response line: %d %s\n",
        evhttp_request_get_response_code(req),
        evhttp_request_get_response_code_line(req));

    while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
            buffer, sizeof(buffer)))
           > 0) {
        /* These are just arbitrary chunks of 256 bytes.
         * They are not lines, so we can't treat them as such. */
        fwrite(buffer, nread, 1, stdout);
    }
}

static void
err(const char *msg)
{
    fputs(msg, stderr);
}

static void
err_openssl(const char *func)
{
    fprintf (stderr, "%s failed:\n", func);

    /* This is the OpenSSL function that prints the contents of the
     * error stack to the specified file handle. */
    ERR_print_errors_fp (stderr);

    exit(1);
}

int
main(int argc, char **argv)
{
    int r;
    struct event_base *base = NULL;
    struct evhttp_uri *http_uri = NULL;
    const char *url = NULL, *data_file = NULL;
    const char *crt = NULL;
    const char *scheme, *host, *path, *query;
    char uri[256];
    int port;
    int retries = 0;
    int timeout = -1;

    SSL_CTX *ssl_ctx = NULL;
    SSL *ssl = NULL;
    struct bufferevent *bev;
    struct evhttp_connection *evcon = NULL;
    struct evhttp_request *req;
    struct evkeyvalq *output_headers;
    struct evbuffer *output_buffer;

    int i;
    int ret = 0;
    enum { HTTP, HTTPS } type = HTTP;
        
    //初始化url
    url = "https://127.0.0.1:8888/login";

    if (!url) {
        goto error;
    }

    http_uri = evhttp_uri_parse(url);
    if (http_uri == NULL) {
        err("malformed url");
        goto error;
    }

    scheme = evhttp_uri_get_scheme(http_uri);
    if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
                           strcasecmp(scheme, "http") != 0)) {
        err("url must be http or https");
        goto error;
    }

    host = evhttp_uri_get_host(http_uri);
    if (host == NULL) {
        err("url must have a host");
        goto error;
    }

    port = evhttp_uri_get_port(http_uri);
    if (port == -1) {
        port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
    }

    path = evhttp_uri_get_path(http_uri);
    if (strlen(path) == 0) {
        path = "/";
    }

    query = evhttp_uri_get_query(http_uri);
    if (query == NULL) {
        snprintf(uri, sizeof(uri) - 1, "%s", path);
    } else {
        snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
    }
    uri[sizeof(uri) - 1] = '\0';

#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
    (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
    // Initialize OpenSSL
    SSL_library_init();
    ERR_load_crypto_strings();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();
#endif

    /* This isn't strictly necessary... OpenSSL performs RAND_poll
     * automatically on first use of random number generator. */
    r = RAND_poll();
    if (r == 0) {
        err_openssl("RAND_poll");
        goto error;
    }

    /* Create a new OpenSSL context */
    ssl_ctx = SSL_CTX_new(SSLv23_method());
    if (!ssl_ctx) {
        err_openssl("SSL_CTX_new");
        goto error;
    }
    // Create event base
    base = event_base_new();
    if (!base) {
        perror("event_base_new()");
        goto error;
    }

    // Create OpenSSL bufferevent and stack evhttp on top of it
    ssl = SSL_new(ssl_ctx);
    if (ssl == NULL) {
        err_openssl("SSL_new()");
        goto error;
    }

    #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
    // Set hostname for SNI extension
    SSL_set_tlsext_host_name(ssl, host);
    #endif

    if (strcasecmp(scheme, "http") == 0) {
        bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
    } else {
        type = HTTPS;
        bev = bufferevent_openssl_socket_new(base, -1, ssl,
            BUFFEREVENT_SSL_CONNECTING,
            BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
    }

    if (bev == NULL) {
        fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
        goto error;
    }

    bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);

    // For simplicity, we let DNS resolution block. Everything else should be
    // asynchronous though.
    evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
        host, port);
    if (evcon == NULL) {
        fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
        goto error;
    }

    if (retries > 0) {
        evhttp_connection_set_retries(evcon, retries);
    }
    if (timeout >= 0) {
        evhttp_connection_set_timeout(evcon, timeout);
    }

    // Fire off the request
    req = evhttp_request_new(http_request_done, bev);
    if (req == NULL) {
        fprintf(stderr, "evhttp_request_new() failed\n");
        goto error;
    }

    output_headers = evhttp_request_get_output_headers(req);
    evhttp_add_header(output_headers, "Host", host);
    evhttp_add_header(output_headers, "Connection", "close");

    if (data_file) {
        /* NOTE: In production code, you'd probably want to use
         * evbuffer_add_file() or evbuffer_add_file_segment(), to
         * avoid needless copying. */
        FILE * f = fopen(data_file, "rb");
        char buf[1024];
        size_t s;
        size_t bytes = 0;

        if (!f) {
            goto error;
        }

        output_buffer = evhttp_request_get_output_buffer(req);
        while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
            evbuffer_add(output_buffer, buf, s);
            bytes += s;
        }
        evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
        evhttp_add_header(output_headers, "Content-Length", buf);
        fclose(f);
    }

    r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
    if (r != 0) {
        fprintf(stderr, "evhttp_make_request() failed\n");
        goto error;
    }

    event_base_dispatch(base);
    goto cleanup;

error:
    ret = 1;
cleanup:
    if (evcon)
        evhttp_connection_free(evcon);
    if (http_uri)
        evhttp_uri_free(http_uri);
    if (base)
        event_base_free(base);

    if (ssl_ctx)
        SSL_CTX_free(ssl_ctx);
    if (type == HTTP && ssl)
        SSL_free(ssl);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
    (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
    EVP_cleanup();
    ERR_free_strings();

#if OPENSSL_VERSION_NUMBER < 0x10000000L
    ERR_remove_state(0);
#else
    ERR_remove_thread_state(NULL);
#endif

    CRYPTO_cleanup_all_ex_data();

    sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
    (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) */

#ifdef _WIN32
    WSACleanup();
#endif

    return ret;
}

Server

#include 
#include 
#include 
#include 
#include 

#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
unsigned short serverPort = 8888;

void die_most_horribly_from_openssl_error (const char *func)
{ 
    fprintf (stderr, "%s failed:\n", func);

    /* This is the OpenSSL function that prints the contents of the
    * error stack to the specified file handle. */
    ERR_print_errors_fp (stderr);

    exit (EXIT_FAILURE);
}

/* This callback gets invoked when we get any http request that doesn't match
 * any other callback.  Like any evhttp server callback, it has a simple job:
 * it must eventually call evhttp_send_error() or evhttp_send_reply().
 */
static void
login_cb (struct evhttp_request *req, void *arg)
{ 
    struct evbuffer *evb = NULL;
    const char *uri = evhttp_request_get_uri (req);
    struct evhttp_uri *decoded = NULL;
 
    /* 判断 req 是否是GET 请求 */
    if (evhttp_request_get_command (req) == EVHTTP_REQ_GET)
    {
        struct evbuffer *buf = evbuffer_new();
        if (buf == NULL) return;
        evbuffer_add_printf(buf, "Requested: %s\n", uri);
        evhttp_send_reply(req, HTTP_OK, "OK", buf);
        return;
    }
 
    /* Get请求,直接return 200 OK  */
    if (evhttp_request_get_command (req) != EVHTTP_REQ_POST)
    { 
        evhttp_send_reply (req, 200, "OK", NULL);
        return;
    }

}
 
/**
 *  该回调负责创建新的SSL连接并将其包装在OpenSSL bufferevent中。
 *  这是我们实现https服务器而不是普通的http服务器的方式。
 */
static struct bufferevent* bevcb(struct event_base *base, void *arg)
{ 
    struct bufferevent* r;
    SSL_CTX *ctx = (SSL_CTX *) arg;
 
    r = bufferevent_openssl_socket_new (base,
            -1,
            SSL_new (ctx),
            BUFFEREVENT_SSL_ACCEPTING,
            BEV_OPT_CLOSE_ON_FREE);
    return r;
}
 
static void server_setup_certs (SSL_CTX *ctx,
        const char *certificate_chain,
        const char *private_key)
{ 
    printf ("Loading certificate chain from '%s'\n"
            "and private key from '%s'\n",
            certificate_chain, private_key);
 
    if (1 != SSL_CTX_use_certificate_chain_file (ctx, certificate_chain))
        die_most_horribly_from_openssl_error ("SSL_CTX_use_certificate_chain_file");
 
    if (1 != SSL_CTX_use_PrivateKey_file (ctx, private_key, SSL_FILETYPE_PEM))
        die_most_horribly_from_openssl_error ("SSL_CTX_use_PrivateKey_file");
 
    if (1 != SSL_CTX_check_private_key (ctx))
        die_most_horribly_from_openssl_error ("SSL_CTX_check_private_key");
}
 

static int
display_listen_sock(struct evhttp_bound_socket *handle)
{
    struct sockaddr_storage ss;
    evutil_socket_t fd;
    ev_socklen_t socklen = sizeof(ss);
    char addrbuf[128];
    void *inaddr;
    const char *addr;
    int got_port = -1;

    fd = evhttp_bound_socket_get_fd(handle);
    memset(&ss, 0, sizeof(ss));
    if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) {
        perror("getsockname() failed");
        return 1;
    }

    if (ss.ss_family == AF_INET) {
        got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
        inaddr = &((struct sockaddr_in*)&ss)->sin_addr;
    } else if (ss.ss_family == AF_INET6) {
        got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
        inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr;
    }
    else {
        fprintf(stderr, "Weird address family %d\n",
            ss.ss_family);
        return 1;
    }

    addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf,
        sizeof(addrbuf));
    if (addr) {
        printf("Listening on %s:%d\n", addr, got_port);
    } else {
        fprintf(stderr, "evutil_inet_ntop failed\n");
        return 1;
    }

    return 0;
}


static int serve_some_http (void)
{ 
    struct event_base *base;
    struct evhttp *http;
    struct evhttp_bound_socket *handle;
 
    //创建event_base
    base = event_base_new ();
    if (! base)
    { 
        fprintf (stderr, "Couldn't create an event_base: exiting\n");
        return 1;
    }
 
    /* 创建一个 evhttp 句柄,去处理用户端的requests请求 */
    http = evhttp_new (base);
    if (! http)
    {
        fprintf (stderr, "couldn't create evhttp. Exiting.\n");
        return 1;
    }
    /******************************************/
    /* 创建SSL上下文环境 ,可以理解为 SSL句柄 */
    SSL_CTX *ctx = SSL_CTX_new (SSLv23_server_method ());
    SSL_CTX_set_options (ctx,
            SSL_OP_SINGLE_DH_USE |
            SSL_OP_SINGLE_ECDH_USE |
            SSL_OP_NO_SSLv2);
 
    /* Cheesily pick an elliptic curve to use with elliptic curve ciphersuites.
     * We just hardcode a single curve which is reasonably decent.
     * See http://www.mail-archive.com/[email protected]/msg30957.html */
    EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
    if (! ecdh)
        die_most_horribly_from_openssl_error ("EC_KEY_new_by_curve_name");
    if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh))
        die_most_horribly_from_openssl_error ("SSL_CTX_set_tmp_ecdh");
 
    /* 选择服务器证书 和 服务器私钥. */
    const char *certificate_chain = "server-certificate-chain.pem";
    const char *private_key = "server-private-key.pem";
    /* 设置服务器证书 和 服务器私钥 到 
     OPENSSL ctx上下文句柄中 */
    server_setup_certs (ctx, certificate_chain, private_key);
    
    /* 
        使我们创建好的evhttp句柄 支持 SSL加密
        实际上,加密的动作和解密的动作都已经帮
        我们自动完成,我们拿到的数据就已经解密之后的

        设置用于为与给定evhttp对象的连接创建新的bufferevent的回调。
        您可以使用它来覆盖默认的bufferevent类型,
        例如,使此evhttp对象使用SSL缓冲区事件而不是未加密的事件。
        新的缓冲区事件必须在未设置fd的情况下进行分配。
    */
    evhttp_set_bevcb (http, bevcb, ctx);
    /******************************************/
    /* 设置http回调函数 */
    //默认回调
    //evhttp_set_gencb (http, send_document_cb, NULL);
    //专属uri路径回调
    evhttp_set_cb(http, "/login", login_cb, NULL);
 
    /* 设置监听IP和端口 */
    handle = evhttp_bind_socket_with_handle (http, "0.0.0.0", serverPort);
    if (! handle)
    { 
        fprintf (stderr, "couldn't bind to port %d. Exiting.\n",(int) serverPort);
        return 1;
    }

    //监听socket
    if (display_listen_sock(handle)) {
        return 1;
    }
 
    /* 开始阻塞监听 (永久执行) */
    event_base_dispatch (base);
 
 
    return 0;
}
 
int main (int argc, char **argv)
{ 
    /*OpenSSL 初始化 */
    signal (SIGPIPE, SIG_IGN);

    SSL_library_init ();
    SSL_load_error_strings ();
    OpenSSL_add_all_algorithms ();

    printf ("Using OpenSSL version \"%s\"\nand libevent version \"%s\"\n",
            SSLeay_version (SSLEAY_VERSION),
            event_get_version ());
    /* now run http server (never returns) */
    return serve_some_http ();
}

Libevent详解与实践_第1张图片
欢迎关注我的公众号.png

你可能感兴趣的:(Libevent详解与实践)