Libevent是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级。
Libevent包括事件管理、缓存管理、DNS、HTTP、缓存事件几大部分。事件管理包括各种IO(socket)、定时器、信号等事件;缓存管理是指evbuffer功能;DNS是libevent提供的一个异步DNS查询功能;HTTP是libevent的一个轻量级http实现,包括服务器和客户端。libevent也支持ssl,这对于有安全需求的网络程序非常的重要,但是其支持不是很完善,比如http server的实现就不支持ssl。
struct evhttp
{
/* Next vhost, if this is a vhost. */
TAILQ_ENTRY(evhttp) next_vhost;
/* All listeners for this host */
TAILQ_HEAD(boundq, evhttp_bound_socket) sockets;
TAILQ_HEAD(httpcbq, evhttp_cb) callbacks;
/* All live connections on this host. */
struct evconq connections;
TAILQ_HEAD(vhostsq, evhttp) virtualhosts;
TAILQ_HEAD(aliasq, evhttp_server_alias) aliases;
/* NULL if this server is not a vhost */
char *vhost_pattern;
int timeout;
size_t default_max_headers_size;
ev_uint64_t default_max_body_size;
/* Bitmask of all HTTP methods that we accept and pass to user * callbacks. */
ev_uint16_t allowed_methods;
/* Fallback callback if all the other callbacks for this connection don't match. */
void (*gencb)(struct evhttp_request *req, void *);
void *gencbarg;
struct event_base *base;
};
值得关注的有两个成员:
callbacks,一个链表,存放用户定义的回调函数
connections,一个链表,存放所有连接,每个连接对应一个evhttp_connection
/* A client or server connection. */
struct evhttp_connection
{
/* we use this tailq only if this connection was created for an http server */
TAILQ_ENTRY(evhttp_connection) next;
evutil_socket_t fd;
struct bufferevent *bufev;
struct event retry_ev; /* for retrying connects */
char *bind_address; /* address to use for binding the src */
u_short bind_port; /* local port for binding the src */
char *address; /* address to connect to */
u_short port;
size_t max_headers_size;
ev_uint64_t max_body_size;
int flags;
#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */
#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
#define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */
int timeout; /* timeout in seconds for events */
int retry_cnt; /* retry count */
int retry_max; /* maximum number of retries */
enum evhttp_connection_state state;
/* for server connections, the http server they are connected with */
struct evhttp *http_server;
TAILQ_HEAD(evcon_requestq, evhttp_request) requests;
void (*cb)(struct evhttp_connection *, void *);
void *cb_arg;
void (*closecb)(struct evhttp_connection *, void *);
void *closecb_arg;
struct deferred_cb read_more_deferred_cb;
struct event_base *base;
struct evdns_base *dns_base;
};
值得关注的有两个成员:
bufev,对应一个bufferevent
requests,一个链表,存放该连接上的所有请求,每个请求对应evhttp_request
struct evhttp_request
{
#if defined(TAILQ_ENTRY)
TAILQ_ENTRY(evhttp_request) next;
#else
struct
{
struct evhttp_request *tqe_next;
struct evhttp_request **tqe_prev;
} next;
#endif
/* the connection object that this request belongs to */
struct evhttp_connection *evcon;
int flags;
/** The request obj owns the evhttp connection and needs to free it */
#define EVHTTP_REQ_OWN_CONNECTION 0x0001
/** Request was made via a proxy */
#define EVHTTP_PROXY_REQUEST 0x0002
/** The request object is owned by the user; the user must free it */
#define EVHTTP_USER_OWNED 0x0004
/** The request will be used again upstack; freeing must be deferred */
#define EVHTTP_REQ_DEFER_FREE 0x0008
/** The request should be freed upstack */
#define EVHTTP_REQ_NEEDS_FREE 0x0010
struct evkeyvalq *input_headers; // 保存客户端请求的HTTP headers(key-value pairs)
struct evkeyvalq *output_headers; // 保存将要发送到客户端的HTTP headers(key-value pairs)
/* address of the remote host and the port connection came from */
char *remote_host;
ev_uint16_t remote_port;
/* cache of the hostname for evhttp_request_get_host */
char *host_cache;
enum evhttp_request_kind kind; // 可以是EVHTTP_REQUEST或EVHTTP_RESPONSE
enum evhttp_cmd_type type; // 可以是EVHTTP_REQ_GET, EVHTTP_REQ_POST或EVHTTP_REQ_HEAD
size_t headers_size;
size_t body_size;
char *uri; /* uri after HTTP request was parsed */
struct evhttp_uri *uri_elems; // 客户端请求的uri
char major; /* HTTP Major number */
char minor; /* HTTP Minor number */
int response_code; /* HTTP Response code */
char *response_code_line; /* Readable response */
struct evbuffer *input_buffer; // 客户端POST的数据
ev_int64_t ntoread;
unsigned chunked:1, /* a chunked request */
userdone:1; /* the user has sent all data */
struct evbuffer *output_buffer; // 输出到客户端的数据
/* Callback */
void (*cb)(struct evhttp_request *, void *);
void *cb_arg;
/*
* Chunked data callback - call for each completed chunk if
* specified. If not specified, all the data is delivered via
* the regular callback.
*/
void (*chunk_cb)(struct evhttp_request *, void *);
};
值得注意的是:
每个请求有自己的输入缓冲input_buffer、输出缓冲output_buffer。
总结一下evhttp:
1. 一个evhttp使用一个链表存放多个evhttp_connection,每个evhttp_connection使用链表存放多个evhttp_request。
2. 每个evhttp_connection包含一个bufferevent,每个evhttp_request包含两个evbuffer,用于输入输出缓冲。
定义参看:http://monkey.org/~provos/libevent/doxygen-1.4.10/event_8h-source.html。
struct evkeyvalq被定义为TAILQ_HEAD (evkeyvalq, evkeyval);
即struct evkeyval类型的tail queue。需要在代码之前包含
#include <sys/queue.h>
#include <event.h>
struct evkeyval为key-value queue(队列结构),主要用来保存HTTP headers,也可以被用来保存parse uri参数的结果。
/* 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
}
定义参看:http://monkey.org/~provos/libevent/doxygen-1.4.10/event_8h-source.html。
该结构体用于input和output的buffer。
/* These functions deal with buffering input and output */
struct evbuffer
{
u_char *buffer;
u_char *orig_buffer;
size_t misalign;
size_t totallen;
size_t off;
void (*cb)(struct evbuffer *, size_t, size_t, void *);
void *cbarg;
};
另外定义宏方便获取evbuffer中保存的内容和大小:
#define EVBUFFER_LENGTH(x) (x)->off
#define EVBUFFER_DATA(x) (x)->buffer
例如,获取客户端POST数据的内容和大小:
EVBUFFER_DATA(res->input_buffer);
EVBUFFER_LENGTH(res->input_buffer);
另外struct evbuffer用如下函数创建添加和释放:
struct evbuffer *buf;
buf = evbuffer_new();
//往buffer中添加内容
evbuffer_add_printf(buf, "It works! 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);
File Reference参看:https://monkey.org/~provos/libevent/doxygen-1.4.10/files.html。
struct evbuffer *evbuffer_new(void);
evbuffer_new()分配和返回一个新的空evbuffer。
void evbuffer_free(struct evbuffer *buf);
evbuffer_free()释放evbuffer和其内容。
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);
int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ···);
size_t evbuffer_get_length(const struct evbuffer *buf);
函数返回evbuffer存储的字节数。
unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size);
“线性化”buf前面的size字节,必要时将进行复制或者移动,以保证这些字节是连续的,占据相同的内存块。如果size是负的,函数会线性化整个缓冲区。如果size大于缓冲区中的字节数,函数返回NULL。否则,evbuffer_pullup()返回指向buf中首字节的指针。
调用evbuffer_pullup()时使用较大的size参数可能会非常慢,因为这可能需要复制整个缓冲区的内容。
struct event_base *event_base_new(void);
Initialize the event API.
Use event_base_new() to initialize a new event base, but does not set the current_base global. If using only event_base_new(), each event added must have an event base set with event_base_set()
int event_base_dispatch(struct event_base *eb);
struct evhttp *evhttp_new(struct event_base *base);
int evhttp_add_header(struct evkeyvalq *headers, const char *key, const char *);
void evhttp_parse_query(const char *uri, struct evkeyvalq *args);
可对uri的参数进行解析,结果保存在struct evkeyvalq的key-value pairs中。
void evhttp_set_gencb(struct evhttp *, void(*)(struct evhttp_request *, void *), void *);
Set a callback for all requests that are not caught by specific callbacks.
int evhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port);
const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len);
HTTP_Server例子参看:https://www.cdsy.xyz/computer/programme/vc/230302/cd41085.html
HTTP请求处理函数
static void http_request_cb(struct evhttp_request *req, void *arg)
{
const char *cmdtype;
struct evkeyvalq *headers;
struct evkeyval *header; // 用来保存HTTP headers的队列
struct evbuffer *buf;
struct evbuffer *evb = NULL;
unsigned int request_command = evhttp_request_get_command(req);
switch (request_command)
{
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;
}
// Debug信息输出start-------------------------
printf("Received a %s request for %s\nHeaders:\n", cmdtype, evhttp_request_get_uri(req));
headers = evhttp_request_get_input_headers(req);
for (header = headers->tqh_first; header; header = header->next.tqe_next)
{
cout << " " << header->key << ": " << header->value << endl;
}
// Debug信息输出end-------------------------
evb = evbuffer_new();
// Http Get请求
if(request_command == EVHTTP_REQ_GET)
{
evhttp_parse_query(evhttp_request_get_uri(req), headers); // 解析URI的参数
for (header = headers->tqh_first; header; header = header->next.tqe_next)
{
cout << " " << header->key << ": " << header->value << endl;
}
evbuffer_add_printf(evb, "<html>\n <head>\n"
" <title>almWeb</title>\n"
" </head>\n"
" <body>\n"
" %s\n",
"hello world");
evbuffer_add_printf(evb, "</body></html>\n");
evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/html");
}
// Http POST请求
else if(request_command == EVHTTP_REQ_POST)
{
buf = evhttp_request_get_input_buffer(req);
int postDataLen = evbuffer_get_length(buf);
evbuffer_add (buf, "", 1);
char *payload = (char *) evbuffer_pullup(buf, -1);
int post_data_len = evbuffer_get_length(buf);
char request_data_buf[post_data_len];
memcpy(request_data_buf, payload, post_data_len);
cout << "[post_data][" << post_data_len << "]="<< request_data_buf << endl;
Json::Reader reader;
Json::Value JsonRoot;
if(!reader.parse(request_data_buf, JsonRoot))
{
cout << "Json parse err" << endl;
}
else
{
if(JsonRoot.isMember("Module"))
{
string strModule = JsonRoot["Module"].asString();
CHCIModule *pHCIModule = new CHCIModule(strModule);
pHCIModule->doMatch(&JsonRoot);
delete pHCIModule;
}
cout << "" << endl;
}
evbuffer_add_printf(evb, "%s\n", JsonRoot.toStyledString().c_str());
evbuffer_add_printf(evb, "</ul></body></html>\n");
evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "application/json");
evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close");
}
evhttp_send_reply(req, 200, "OK", evb);
if (evb)
{
evbuffer_free(evb);
}
}