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);
- }
- }
-