evhttp

libevent库使得高并发响应HTTP Server的编写变得很容易。
因此实际中,使用libevent可以为任何应用(如数据库)提供一个HTTP based的网络接口,方便多个clients采用任何支持HTTP protocol的语言与server进行交互

最近将之前用gsoap写的webserver改为使用libevent库。gsoap是为了实现soap协议,如果客户端只调用restful风格的接口的话,用gsoap实现http server有很多多余的东西了,而且处理起来也不太方便。

虽然之前使用过libevent,不过evhttp还是第一次用,还是一如既往的好用。再此简单记录下,方便以后取用。
代码(https://github.com/xuleicode/lb,单线程,多线程。供自己参考)

整个过程包括如下几步:

  1. 初始化
  2. 创建HTTP Server
  3. 指定回调函数(根据情况可指定多个),
  4. 进入事件循环
  5. 在回调函数中,可以获取客户端请求(request的HTTP
    Header和参数等),进行相应处理后,再将结果发送给客户端(response的HTTP Header和内容)。
#include <evhttp.h>

void httpd_handler(struct evhttp_request *req, void *arg);
void specific_handler(struct evhttp_request *req, void *arg);


int starthttp(){
        /* 使用libevent创建HTTP Server */

        //初始化event API
        event_init();

        //创建一个http server
        struct evhttp *httpd;
         //默认参数
        char *httpd_option_listen = "0.0.0.0";
        int httpd_option_port = 8080;
        int httpd_option_daemon = 0;
        int httpd_option_timeout = 120; //in seconds
        httpd = evhttp_start(httpd_option_listen, httpd_option_port);
        evhttp_set_timeout(httpd, httpd_option_timeout);
        //
        //evhttp_set_allowed_methods( httpd , EVHTTP_REQ_GET);
        //指定generic callback
        evhttp_set_gencb(httpd, httpd_handler, NULL);
        //Set a callback for a specified URI
        evhttp_set_cb(httpd, "/spec", specific_handler, NULL);

        //循环处理events
        event_dispatch();
        evhttp_free(httpd);
        return 0;
}
//linux编译:需要levent库
g++ -o myhttpd -Wall -levent myhttpd.cpp
//windwos 下需要添加依赖库
ws2_32.lib
wsock32.lib
libevent.lib
libevent_core.lib
libevent_extras.lib
//并且windows下使用libevnet网络时需要
//初始化winsocket
#ifdef WIN32
   WSADATA wsaData;
    if(WSAStartup(MAKEWORD(2,2) , &wsaData) != 0) 
    {
        return -1;
    }
#endif
//和释放
#ifdef WIN32
    WSACleanup();  
#endif   

主要结构体
evhttp_request *req 中包含了http请求的所有内容

struct evkeyvalq *input_headers;    //保存客户端请求的HTTP headers(key-value pairs)
struct evkeyvalq *output_headers;   //保存将要发送到客户端的HTTP headers(key-value pairs)

//客户端的ip和port
char *remote_host;
u_short remote_port;

enum evhttp_request_kind kind;  //可以是EVHTTP_REQUEST或EVHTTP_RESPONSE//enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };                  
enum evhttp_cmd_type type;  //可以是EVHTTP_REQ_GET, EVHTTP_REQ_POST或EVHTTP_REQ_HEAD等,详见最后

char *uri;          //客户端请求的uri
char major;         //HTTP major number
char minor;         //HTTP major number

int response_code;      //HTTP response code
char *response_code_line;   //readable response

struct evbuffer *input_buffer;  //客户端POST的数据
struct evbuffer *output_buffer; //输出到客户端的数据
...
//还有一些回调函数指针

保存参数的结构体

 /*
 * Key-Value pairs.  Can be used for HTTP headers but also for
 * query argument parsing.
 */
struct evkeyval {
    TAILQ_ENTRY(evkeyval) next;

    char *key;
    char *value;
};
宏TAILQ_ENTRY(evkeyval)被定义为:

#define TAILQ_ENTRY(type)
    struct {
        struct type *tqe_next;      //next element
        struct type **tqe_prev;     //address of previous next element
    }

buffer结构体

//buffer.h
struct evbuffer
#ifdef EVENT_IN_DOXYGEN_
{}
#endif
;
//evbuffer-internal.h文件  
struct evbuffer_chain;  
struct evbuffer {  
    struct evbuffer_chain *first;  
    struct evbuffer_chain *last;  
    //二级指针。使用*last_with_datap时,指向的是链表中最后一个有数据的evbuffer_chain。  
    //所以last_with_datap存储的是倒数第二个evbuffer_chain的next成员地址。  
    //一开始buffer->last_with_datap = &buffer->first;此时first为NULL。所以当链表没有节点时  
    //*last_with_datap为NULL。当只有一个节点时*last_with_datap就是first。      
    struct evbuffer_chain **last_with_datap;  
    size_t total_len;//链表中所有chain的总字节数  
    ...  
};  
struct evbuffer_chain {  
    struct evbuffer_chain *next;  
    size_t buffer_len;//buffer的大小  
    //错开不使用的空间。该成员的值一般等于0  
    ev_off_t misalign;  
    //evbuffer_chain已存数据的字节数  
    //所以要从buffer + misalign + off的位置开始写入数据  
    size_t off;  
    ...     
    unsigned char *buffer;  
};  

另外定义宏方便获取evbuffer中保存的内容和大小:

  #define EVBUFFER_LENGTH(x)      (x)->off
  #define EVBUFFER_DATA(x)        (x)->buffer

例如,获取客户端POST数据的内容和大小:

    EVBUFFER_DATA(res->input_buffer);
    EVBUFFER_LENGTH(res->input_buffer);//这个和res->body_size相同

另外struct evbuffer用如下函数创建添加和释放:

    struct evbuffer *buf;
    buf = evbuffer_new();
    //往buffer中添加内容
    evbuffer_add_printf(buf, "I got it! you just requested: %s\n", req->uri);   //Append a formatted string to the end of an evbuffer.
    //将内容输出到客户端
    evhttp_send_reply(req, HTTP_OK, "OK", buf);
    //释放掉buf
    evbuffer_free(buf);

关键函数

获取客户端请求的URI

//使用
req->uri
//或使用函数
const char *evhttp_request_uri(struct evhttp_request *req);//即(evhttp_request_uri(req);)。

对获取的URI进行解析和其他操作

使用函数

void evhttp_parse_query(const char *uri, struct evkeyvalq *args);

可对uri的参数进行解析,结果保存在struct evkeyvalq的key-value pairs中,例如:

    char *uri = "http://foo.com/?id=1&infor=hello";
    struct evkeyvalq args;
    evhttp_parse_query(uri, &args);
    //然后通过evhttp_find_header等函数获取各个参数及对应的值
    evhttp_find_header(&args, "id"); //得到test
    evhttp_find_header(&args, "infor"); //得到some thing  

如下两个函数对URI进行encode和decode:

    char *evhttp_encode_uri(const char *uri);
    char *evhttp_decode_uri(const char *uri);

URI encode的结果是所有非alphanumeric及-_的字符都被类似于%和一个2位16进制字符替换(其中空格被+号替换)。如上两个函数返回的字符串需要free掉。
Escape特殊的HTML字符

char *evhttp_htmlescape(const char *html);

特殊字符:&被替换为&;”被替换为";’被替换为'; <被替换为<;>被替换为>。该函数返回的字符串需要free掉。

处理HTTP headers相关的函数
HTTP headers保存在req的input_headers中,这个是struct evkeyvalq 的结构体(key-value pairs),使用如下函数可对其进行修改:

const char *evhttp_find_header(const struct evkeyvalq *, const char *);
int evhttp_remove_header(struct evkeyvalq *, const char *);
int evhttp_add_header(struct evkeyvalq *, const char *, const char *);
void evhttp_clear_headers(struct evkeyvalq *);

设定只识别get请求,

evhttp_set_allowed_methods( httpd , EVHTTP_REQ_GET);

设置后,只处理get请求,其他请求返回405 Method not allowed,我自己使用的时候发现返回的是501 Not Implemented
请求类型如下:

enum evhttp_cmd_type {
    EVHTTP_REQ_GET     = 1 << 0,
    EVHTTP_REQ_POST    = 1 << 1,
    EVHTTP_REQ_HEAD    = 1 << 2,
    EVHTTP_REQ_PUT     = 1 << 3,
    EVHTTP_REQ_DELETE  = 1 << 4,
    EVHTTP_REQ_OPTIONS = 1 << 5,
    EVHTTP_REQ_TRACE   = 1 << 6,
    EVHTTP_REQ_CONNECT = 1 << 7,
    EVHTTP_REQ_PATCH   = 1 << 8
};
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值