llibevent2笔记(linux、windows、android的编译和HTTP client应用)

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

转载请注明出处:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值