0. 前言
我使用的版本是libevent-2.0.21-stable。高级的应用还是得看官网文档 http://www.wangafu.net/~nickm/libevent-2.0/doxygen/html/
1. 编译
1.1 Linux版编译
在目录下
./configure && make
即可在./.lib/下得到5个.a静态库。
不确定是否在此之前我已安装好各种依赖库所以没遇到任何障碍
。
liuhx@uc ~/Downloads/libevent-2.0.21-stable/.libs $ ll *.a -rw-r--r-- 1 liuhx liuhx 2309114 Feb 17 13:38 libevent.a -rw-r--r-- 1 liuhx liuhx 1431730 Feb 17 13:38 libevent_core.a -rw-r--r-- 1 liuhx liuhx 877456 Feb 17 13:38 libevent_extra.a -rw-r--r-- 1 liuhx liuhx 195868 Feb 17 13:38 libevent_openssl.a -rw-r--r-- 1 liuhx liuhx 21998 Feb 17 13:38 libevent_pthreads.a
查看Makefile文件的内容,可得知4个静态库对应的源文件:
CORE_SRC = event.c evthread.c buffer.c \ bufferevent.c bufferevent_sock.c bufferevent_filter.c \ bufferevent_pair.c listener.c bufferevent_ratelim.c \ evmap.c log.c evutil.c evutil_rand.c strlcpy.c $(SYS_SRC) EXTRA_SRC = event_tagging.c http.c evdns.c evrpc.c libevent_pthreads_la_SOURCES = evthread_pthread.c libevent_openssl_la_SOURCES = bufferevent_openssl.c
即libevent_core.a里是核心功能,其中$(SYS_SRC)在各个平台会不同,linux下是select.c、poll.c、 epoll.c、signal.c等,windows下是win32select.c evthread_win32.c等,不一一列举了。libevent_extra.a里包含http、dns等功能。另外两个libevent_*.a 就见名知意了。而libevent.a是其它4个的集合。
1.2 Windows版编译
可以用VS的Command Prompt(开始菜单->Visual Studio 2013->Visual Studio Tools->VS2013 x64 Native Tools Command Prompt,VS和CPU版本应对应到你所用的)在libevent目录下
nmake Makefile.nmake
就能得到
2015/02/26 10:40 336,040 libevent_extras.lib 2015/02/26 10:40 789,110 libevent.lib 2015/02/26 10:40 453,366 libevent_core.lib
3个静态库文件。
不过一般会习惯做成VS工程。所以新建一个VS静态库工程,对着Makefile.nmake的内容添加源文件、引用目录、预编译命令就行了。vcproj的部分内容如下:
<ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\buffer.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\bufferevent.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\bufferevent_async.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\bufferevent_filter.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\bufferevent_pair.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\bufferevent_ratelim.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\bufferevent_sock.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\buffer_iocp.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\evdns.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\event.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\event_iocp.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\event_tagging.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\evmap.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\evrpc.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\evthread.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\evthread_win32.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\evutil.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\evutil_rand.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\http.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\listener.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\log.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\signal.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\strlcpy.c" /> <ClCompile Include="..\..\..\third_party\libevent-2.0.21-stable\win32select.c" />
windows平台在链接时需要加入ws2_32.lib和wsock32.lib
1.3 Android版编译
若是能看懂linux和windows的Makefile,那是很容易写好Android.mk的。但在此之前需要生成好android版的config.h和event-config.h。
目录下运行(ndk版本随意,请替换成对应的路径):
SYSROOT=~/Applications/android-ndk-r8e/platforms/android-8/arch-arm ./configure --host=arm-linux-androideabi CC=~/Applications/android-ndk-r8e/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc CFLAGS=--sysroot=$SYSROOT
这样就会生成好config.h了。然后再
make
过程中会调用sed修改好event-config.h。顺利的话会生成四个.a
liuhx@uc ~/Downloads/libevent-2.0.21-stable/.libs $ ll *.a -rw-r--r-- 1 liuhx liuhx 455082 Feb 26 14:59 libevent.a -rw-r--r-- 1 liuhx liuhx 267398 Feb 26 14:59 libevent_core.a -rw-r--r-- 1 liuhx liuhx 187756 Feb 26 14:59 libevent_extra.a -rw-r--r-- 1 liuhx liuhx 4014 Feb 26 14:59 libevent_pthreads.a
因为没有编译OpenSSL,所以不会有libevent_openssl.a。
前面的过程会弄好config.h和event-config.h,其中event-config.h是对应android版本的配置,是必须 的,不然编不过,所以无法一上来就用Android.mk。过了make这一步才行,也就是要保留修改过的event-config.h来使用下面的 Android.mk
LOCAL_PATH := $(call my-dir)/.. include $(CLEAR_VARS) LOCAL_MODULE := libevent LOCAL_SRC_FILES := \ event.c \ evutil.c \ epoll.c \ log.c \ poll.c \ select.c \ signal.c \ http.c \ buffer.c \ evthread.c \ evmap.c \ bufferevent.c \ listener.c \ evutil_rand.c \ bufferevent_sock.c \ bufferevent_filter.c \ bufferevent_pair.c \ strlcpy.c \ event_tagging.c \ evrpc.c \ bufferevent_ratelim.c \ evdns.c \ evthread_pthread.c LOCAL_C_INCLUDES := \ $(LOCAL_PATH) \ $(LOCAL_PATH)/include \ $(LOCAL_PATH)/compat LOCAL_CFLAGS := -DHAVE_CONFIG_H include $(BUILD_SHARED_LIBRARY)
注意最后一行在集成到app时应该把BUILD_SHARED_LIBRARY应该替换成BUILD_STATIC_LIBRARY,这里仅为了编译。
2. 功能总结
libevent的核心作用是实现消息循环、消息队列管理与回调,可用来监听文件属性变化、超时、锁状态变化,其中超时可以用作Timer。
额外的功能是作为HTTP的server或client。
3. 使用
3.1 初始化
#include "event2/event.h" struct event_base* g_main_event_base = 0; struct event* g_time_limit_event = 0; void TimeLimitCallback(evutil_socket_t fd, short what, void* arg) { event_del(g_time_limit_event); } int main(int argc, char* argv[]) { #ifdef _WIN32 WSADATA WSAData; WSAStartup(0x201, &WSAData); #endif g_main_event_base = event_base_new(); g_time_limit_event = evtimer_new(g_main_event_base, &TimeLimitCallback, NULL); struct timeval tv = {5 * 60, 0 }; // 5 mins evtimer_add(g_time_limit_event, &tv); event_base_dispatch(g_main_event_base); event_base_free(g_main_event_base); #ifdef _WIN32 WSACleanup(); #endif return 0; }
Windows平台需要额外地初始化网络库。
以上是相当于创建一个5分钟后触发的timer。event_base_dispatch会在所有event结束时才return。
3.2 HTTP client的实现
C++的封装。只是示例,HttpRequest和HttpResponse就不贴代码了。
// HttpClientTransaction.h class HttpRequest; class HttpResponse; class HttpClientTransaction { public: HttpClientTransaction(struct event_base* base); ~HttpClientTransaction(); // Prioritization used in various parts of the networking code such // as connection prioritization and resource loading prioritization. enum Priority { kIdle = 0, kMinimumPriority = kIdle, kLowest, kDefaultPriority = kLowest, kLow, kMedium, kHighest, kMaximumPriority = kHighest }; class Delegate { public: virtual ~Delegate() {} virtual void OnResponseReceived(HttpClientTransaction* transaction, HttpResponse* response) = 0; virtual void OnBeforeRedirect(HttpClientTransaction* transaction, const char* new_location) {} virtual void OnDataReceived(HttpClientTransaction* transaction, const char* data, size_t length) = 0; virtual void OnFinished(HttpClientTransaction* transaction) = 0; virtual void OnError(HttpClientTransaction* transaction, int error_code) = 0; }; virtual void Start(int* out_error_code); virtual void Cancel(); virtual HttpRequest* request() { return http_request_; } virtual void set_request(HttpRequest* request) { http_request_ = request; } virtual Delegate* delegate() { return delegate_; } // implementation MUST NOT delete delegate virtual void set_delegate(Delegate* delegate) { delegate_ = delegate; } virtual Priority priority() { return priority_; } virtual void set_priority(Priority priority) { priority_ = priority; } void OnEvRequestCallback(evhttp_request* req); private: struct event_base* base_; HttpRequest* http_request_; Delegate* delegate_; Priority priority_; struct evhttp_connection* ev_connection_; struct evhttp_request* ev_request_; };
-------------------------------------------------------------------
// HttpClientTransaction.cpp #include "HttpClientTransaction.h" #include "event2/http.h" #include "event2/buffer.h" #include "event2/keyvalq_struct.h" #include <string> #include "http_request.h" #include "mutable_http_response.h" void RequestCallback(struct evhttp_request* req, void *arg) { HttpClientTransaction* transaction = static_cast<HttpClientTransaction*>(arg); transaction->OnEvRequestCallback(req); } HttpClientTransaction::HttpClientTransaction(struct event_base* base) : base_(base), http_request_(NULL), delegate_(NULL), priority_(kDefaultPriority) { } HttpClientTransaction::~HttpClientTransaction() { Cancel(); } void HttpClientTransaction::Start(int* out_error_code) { if (!delegate_) { *out_error_code = 1; return; } if (!http_request_) { *out_error_code = 4; return; } const char* url = http_request_->url(); if (!url) { *out_error_code = 11; return; } struct evhttp_uri *uri = evhttp_uri_parse(url); if (!uri) { *out_error_code = 12; return; } const char* host = evhttp_uri_get_host(uri); if (!host) { evhttp_uri_free(uri); *out_error_code = 13; return; } int port = evhttp_uri_get_port(uri); if (port == -1) port = 80; const char* method = http_request_->method(); if (!method) { evhttp_uri_free(uri); *out_error_code = 14; return; } evhttp_cmd_type cmd_type; if (strcmp(method, "GET") == 0) cmd_type = EVHTTP_REQ_GET; else if (strcmp(method, "POST") == 0) cmd_type = EVHTTP_REQ_POST; else { evhttp_uri_free(uri); *out_error_code = 15; return; } ev_connection_ = evhttp_connection_base_new(base_, NULL, host, port); if (!ev_connection_) { evhttp_uri_free(uri); *out_error_code = 2; return; } ev_request_ = evhttp_request_new(&RequestCallback, this); void* iter = NULL; const char* name = NULL; const char* value = NULL; struct evkeyvalq *headers = evhttp_request_get_output_headers(ev_request_); while (http_request_->EnumerateHeaderLines(&iter, &name, &value)) { evhttp_add_header(headers, name, value); } value = http_request_->GetHeader("host"); if (!value) { evhttp_add_header(headers, "host", host); } const char* body = NULL; size_t length = 0; if (http_request_->body(&body, &length)) { struct evbuffer *buffer = evhttp_request_get_output_buffer(ev_request_); evbuffer_add(buffer, body, length); } int rv = evhttp_make_request(ev_connection_, ev_request_, cmd_type, evhttp_uri_get_path(uri)); evhttp_uri_free(uri); if (rv == -1) { ev_request_ = NULL; evhttp_connection_free(ev_connection_); ev_connection_ = NULL; *out_error_code = 3; return; } *out_error_code = 0; evhttp_connection_set_timeout(m_conn, http_request_->timeoutInterval()); } void HttpClientTransaction::Cancel() { if (ev_request_) { evhttp_cancel_request(ev_request_); ev_request_ = NULL; } if (ev_connection_) { evhttp_connection_free(ev_connection_); ev_connection_ = NULL; } } void HttpClientTransaction::OnEvRequestCallback(evhttp_request* req) { ev_request_ = NULL; if (req == NULL) { delegate_->OnError(this, 0xdead); } else { MutableHttpResponse* response = MutableHttpResponse::Create(); const char* url = http_request_->url(); response->set_url(url); response->set_status_code(evhttp_request_get_response_code(req)); struct evkeyvalq* headers = evhttp_request_get_input_headers(req); for (struct evkeyval* header = headers->tqh_first; header; header = header->next.tqe_next) { response->AddHeader(header->key, header->value); } delegate_->OnResponseReceived(this, response); delete response; response = NULL; struct evbuffer *buf = evhttp_request_get_input_buffer(req); while (evbuffer_get_length(buf)) { int n; char cbuf[128]; n = evbuffer_remove(buf, cbuf, sizeof(buf)-1); if (n > 0) delegate_->OnDataReceived(this, cbuf, n); } delegate_->OnFinished(this); } evhttp_connection_free(ev_connection_); ev_connection_ = NULL; }
3.3 Timer的实现
比较简单,纯看代码能懂,就不解释了。
struct TimerCallbackArgument { struct event* ev_event; missile::Task* task; }; void TimerCallback(evutil_socket_t fd, short what, void* arg) { TimerCallbackArgument* ea = static_cast<TimerCallbackArgument*>(arg); ea->task->Run(); event_del(ea->ev_event); delete ea; } void PostDelayedTaskOnCurrentThread(Task* task, unsigned delay_ms) { TimerCallbackArgument* tca = new TimerCallbackArgument; tca->task = task; event* timer = evtimer_new(event_base_, &TimerCallback, tca); tca->ev_event = timer; struct timeval tv = {delay_ms / 1000000, delay_ms % 1000 * 1000}; evtimer_add(timer, &tv); }
转载请注明出处: