1. 程式人生 > >libevent2筆記(Timer和Http Client的應用和注意點)

libevent2筆記(Timer和Http Client的應用和注意點)

1.初始化

Windows上需要自己初始化系統網路庫,即呼叫

WSADATA WSAData;
WSAStartup(0x201, &WSAData);

結束使用時再

WSACleanup();

另外還需要初始化執行緒設定
windows上
evthread_use_windows_threads
linux上
evthread_use_pthreads

綜上,使用其它libevent的函式前需要這樣:

#if defined(WIN32)
  WSADATA WSAData;
  WSAStartup(0x201, &WSAData);
  evthread_use_windows_threads();
#else
evthread_use_pthreads(); #endif

2.Loop

因為event_base_loop在沒有event的時候就會結束,所以作為事件迴圈,需要加一個超時時間很長的timer event作為預設的負載,在真正結束事件迴圈的時候再把這個event delete掉。

3.Timer

event_new出來的物件是需要自己釋放的,而且無法從event_base裡再獲取所有的還沒執行的events,所以需要把event*儲存起來,執行後再event_del。也就是需要自己做一個封裝,callback函式的arg肯定不會是目標的物件。

4.多執行緒

libevent即使提供了多執行緒初始化函式,它也不能簡單地跨執行緒發訊息。一般來說會想當然地在當前執行緒中對另一個執行緒中的event_base加入一個超時為0的timer event,這樣就會在另一個執行緒執行那個事件,達到發執行緒訊息的效果。然而如果短時間內連續這樣做,這些timer events並不能保證按序執行。
安全的做法是:

  1. 每個執行緒建立自己的訊息佇列,發執行緒訊息就是往佇列增加一個訊息。當然,這個過程得加鎖。
  2. 建立一對互相連線好的socket,執行緒的event_base加入event監聽EV_READ事件,觸發就按序執行訊息佇列。
  3. 每發一個執行緒訊息就等於往佇列增加一個訊息,然後往socket寫一個位元組(任意內容)。即目標執行緒因為socket可讀而結束等待繼而執行訊息。
    綜上,用libevent來作為多執行緒間的事件迴圈和互發訊息是還有工作要做的。

5.HTTP client

evhttp有挺多的坑,如回撥的時機不明確、回撥的引數有可能為空,connection和request的生命週期奇異等。這些在文件中沒有過多的說明,只能吃一塹長一智。用C語言來寫的庫雖然效率高,但確實不怎麼好維護。
下面是自己的實驗程式碼,不想過多解釋,請自行參考。

// HttpTransaction.h

struct event_base;
struct evhttp_connection;
struct evhttp_request;
struct event;

class HttpTransaction {
 public:
  explicit HttpTransaction(event_base* base);
  ~HttpTransaction();

  class Delegate {
   public:
    virtual ~Delegate() {}

    virtual void OnResponseReceived(HttpTransaction* transaction,
        const HttpResponse& response) = 0;

    virtual void OnDataReceived(HttpTransaction* transaction,
        const char* data, size_t length) = 0;

    virtual void OnFinished(HttpTransaction* transaction) = 0;

    virtual void OnError(HttpTransaction* transaction, int error_code) = 0;
  };

  virtual void Start(int* out_error_code);

  virtual void Cancel();

  virtual HttpRequest* request() const { return http_request_; }
  virtual void set_request(HttpRequest* request) {
    http_request_ = request;
  }

  virtual Delegate* delegate() const { return delegate_; }
  virtual void set_delegate(Delegate* delegate) { delegate_ = delegate; }

  void OnEvRequestCallback(evhttp_request* req);
  void OnTimerCallback();

 private:
  enum State {
    STATE_NONE,
    STATE_WAITING_REQUEST_CALLBACK,
    STATE_REPORT_RESPONSE,
    STATE_REPORT_DATA,
    STATE_REPORT_FINISH,
    STATE_REPORT_ERROR
  };

  void SetupTimer();
  void DoReportResponse();
  void DoReportData();
  void DoReportFinish();
  void DoReportError();

  event_base* base_;
  HttpRequest* http_request_;
  Delegate* delegate_;
  evhttp_connection* ev_connection_;
  evhttp_request* ev_request_;
  event* call_delegate_timer_;
  State next_state_;
};
// HttpTransaction.cpp
#include <string.h>
#include <assert.h>
#include "event2/event.h"
#include "event2/http.h"
#include "event2/buffer.h"
#include "event2/keyvalq_struct.h"
#include "http_transaction_impl.h"
#include "http_request.h"
#include "mutable_http_response.h"

void RequestCallback(struct evhttp_request* req, void *arg) {
  HttpTransaction* transaction = static_cast<HttpTransaction*>(arg);
  transaction->OnEvRequestCallback(req);
}

void TimerCallback(evutil_socket_t fd, short what, void* arg) {
  HttpTransaction* transaction = static_cast<HttpTransaction*>(arg);
  transaction->OnTimerCallback();
}

HttpTransaction::HttpTransaction(struct event_base* base)
    : base_(base),
      http_request_(NULL),
      delegate_(NULL),
      ev_connection_(NULL),
      ev_request_(NULL),
      call_delegate_timer_(NULL),
      next_state_(STATE_NONE) {
}

HttpTransaction::~HttpTransaction() {
  Cancel();
}

void HttpTransaction::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);
  }

  // evhttp_make_request() may trigger RequestCallback() synchronously and the
  // return code is 0. An example is DNS error.
  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;
  }

  if (next_state_ == STATE_NONE)
    next_state_ = STATE_WAITING_REQUEST_CALLBACK;
  *out_error_code = 0;
  // evhttp_connection_set_timeout(m_conn, (int)request.timeoutInterval());
}

void HttpTransaction::Cancel() {
  next_state_ = STATE_NONE;
  if (call_delegate_timer_) {
    event_del(call_delegate_timer_);
    event_free(call_delegate_timer_);
    call_delegate_timer_ = NULL;
  }
  if (ev_request_) {
    if (evhttp_request_is_owned(ev_request_))
      evhttp_request_free(ev_request_);
    ev_request_ = NULL;
  }
  if (ev_connection_) {
    evhttp_connection_free(ev_connection_);
    ev_connection_ = NULL;
  }
}

void HttpTransaction::OnEvRequestCallback(evhttp_request* req) {
  SetupTimer();
  if (req == NULL ||
      evhttp_request_get_response_code(ev_request_) == 0) {
    ev_request_ = NULL;
    next_state_ = STATE_REPORT_ERROR;
  } else {
    assert(ev_request_ == req);
    evhttp_request_own(req);
    next_state_ = STATE_REPORT_RESPONSE;
  }
}

void HttpTransaction::OnTimerCallback() {
  State state = next_state_;
  next_state_ = STATE_NONE;
  switch (state) {
  case STATE_REPORT_RESPONSE:
    DoReportResponse();
    break;
  case STATE_REPORT_DATA:
    DoReportData();
    break;
  case STATE_REPORT_FINISH:
    DoReportFinish();
    break;
  case STATE_REPORT_ERROR:
    DoReportError();
    break;
  default:
    assert(0);
    break;
  }
}

void HttpTransaction::SetupTimer() {
  if (!call_delegate_timer_) {
    call_delegate_timer_ = evtimer_new(base_, &TimerCallback, this);
  }
  struct timeval tv = { 0, 0 };
  event_add(call_delegate_timer_, &tv);
}

void HttpTransaction::DoReportResponse() {
  SetupTimer();
  next_state_ = STATE_REPORT_DATA;
  MutableHttpResponse* response =
      MutableHttpResponse::Create();
  const char* url = http_request_->url();
  response->set_url(url);
  response->set_status_code(evhttp_request_get_response_code(ev_request_));
  struct evkeyvalq* headers = evhttp_request_get_input_headers(ev_request_);
  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;
}

void HttpTransaction::DoReportData() {
  struct evbuffer *buf = evhttp_request_get_input_buffer(ev_request_);
  if (evbuffer_get_length(buf)) {
    const int kBufferSize = 1024 * 12;
    char* buffer = new char[kBufferSize];
    int n = evbuffer_remove(buf, buffer, kBufferSize);

    SetupTimer();
    if (evbuffer_get_length(buf))
      next_state_ = STATE_REPORT_DATA;
    else
      next_state_ = STATE_REPORT_FINISH;

    if (n > 0) {
      struct timeval tv = { 0, 0 };
      event_add(call_delegate_timer_, &tv);
      delegate_->OnDataReceived(this, buffer, n);
    }
    delete buffer;
  } else {
    DoReportFinish();
  }
}

void HttpTransaction::DoReportFinish() {
  Cancel();
  delegate_->OnFinished(this);
}

void HttpTransaction::DoReportError() {
  Cancel();
  delegate_->OnError(this, 0xdead);
}

相關推薦

libevent2筆記TimerHttp Client應用注意

1.初始化 Windows上需要自己初始化系統網路庫,即呼叫 WSADATA WSAData; WSAStartup(0x201, &WSAData); 結束使用時再 WSACleanup(); 另外還需要初始化執行緒設定 windows

Friends and Berries URAL - 2067 計算三共線計算的時候的注意

題目連結:https://cn.vjudge.net/problem/URAL-2067 具體思路:判斷三點共線就可以了,只有一對點能滿足,如果一對就沒有那就沒有滿足的. 在計算的時候,要注意,如果是按照斜率算的話,可以把除法轉換為乘法,防止精度的損失. 如果是按照距離算的話,一定要

JAVA隨筆篇一Timer源代碼分析scheduleAtFixedRate的使用

exce 啟動 get stat dsm ldr 基礎篇 ask pty 寫完了基礎篇,想了非常久要不要去寫進階篇。去寫JSP等等的用法。最後決定先不去寫。由於自己並非JAVA方面的大牛。眼下也在邊做邊學,所以決定先將自己不懂的拿出來學並記下來。 Timer是Java自

python學習筆記模塊初識、pycPyCodeObject是什麽

hello 計算 pat 學python 語言 log pre clas 運行 一、模塊初識(一) 模塊,也叫庫。庫有標準庫第三方庫。 註意事項:文件名不能和導入的模塊名相同 1. sys模塊 import sys print(sys.path) #打印環境變量 prin

安卓權威編程指南-筆記第24章 Looper Handler HandlerThread

結果 pan color font 特定 消息循環 現在 返回 消息 AsyncTask是執行後臺線程的最簡單方式,但它不適用於那些重復且長時間運行的任務。 1. Looper Android中,線程擁有一個消息隊列(message queue),使用消息隊列的線程叫做

HAproxy的負載均衡,以及基於TCPHTTP應用程式代理

HAProxy是一個使用C語言編寫的自由及開放原始碼軟體[1],其提供高可用性、負載均衡,以及基於TCP和HTTP的應用程式代理。 HAProxy特別適用於那些負載特大的web站點,這些站點通常又需要會話保持或七層處理。HAProxy執行在當前的硬體上,完全可以支援數以萬計的

Python語言程式設計MOOC崇天第七章檔案資料格式化學習筆記自動軌跡繪製+政府工作報告詞雲

複習: 數字型別及操作: 字串型別和操作: 程式分支結構 程式的迴圈結構 函式的定義與使用 程式碼複用與函式遞迴 集合型別及操作 序列型別及操作 字典型別及操作 本週內容: 檔案和資料格式化   檔案的使用 統

題目筆記閉包,深複製淺複製,原生js實現Promise

就面試筆試題的一些筆記: 閉包( 實現add(2)(5) ) 深複製和淺複製 原生js實現Promise △ –>閉包知識: 實現add(2)(5) function add (x) { return functio

CSS筆記字型樣式,文字屬性顏色樣式

1 字型樣式 1.1 字體系列(font family) 在CSS中,字型劃分為 五組“字體系列”,分別為: sans-serif字體系列:沒有襯線的字型,最常用不為過,如 Arial、Verdana,Arial Black,Geneva等 seri

libevent2筆記linux、windows、android的編譯

0. 前言1. 功能總結libevent的核心作用是實現訊息迴圈、訊息佇列管理與回撥,可用來監聽檔案(socket也算檔案)屬性變化、超時、鎖狀態變化,其中超時可以用作Timer。額外的功能是作為HTTP的server或client。2. 編譯2.1 Linux版編譯在目錄下

oracle學習筆記聚合函式以及group by having 的用法

                                                                                                           今天學習了聚合函式以及group by 的用法。      

org.apache.http.client.HttpClientorg.apache.commons.httpclient.HttpClient的區別

最近看專案的程式碼,看到工程中有兩個jar包張的很像,一個是commons.httpclient-3.1.jar,一個是httpclient4.2.1.jar,很納悶,而且這兩個包裡都有HttpClient這個類,但是包名卻不一樣,然後就查找了一番資料,看下這兩個包到底是

《圖解HTTP》讀書筆記六:HTTP狀態碼

新網 分享圖片 ces 成功 png per 部分 客戶端 span 狀態碼的職責是當客戶端向服務器端發送請求時, 描述返回的請求結果。 借助狀態碼, 用戶可以知道服務器端是正常處理了請求, 還是出現了錯誤。 狀態碼類別 HTTP狀態碼由三個十進制數字組成,第一

03 . Prometheus監控容器HTTP探針應用

#### Eeporter是什麼及來源? ##### 是什麼? > 廣義上講所有可以向Prometheus提供監控樣本資料的程式都可以被稱為一個Exporter。而Exporter的一個例項稱為target,如下所示,Prometheus通過輪詢的方式定期從這些target中獲取樣本資料: ![](ht

2017-9-17C#筆記方法,方法參數 ,foreach語句

inter clas 移動 接口 類型轉換 如果 array 處理機 臨時 方法: 方法作為類中最常見的最有用的一個成員,算是完成特定任務,實現特定任務的重要的編程模式. “更少的代碼,更多的復用” (有些教程中,將方法稱為函數,函數和方法沒有本質的區別,但是通常自己寫

基於Spring框架的Shiro配置轉發:http://kdboy.iteye.com/blog/1103794

alt work actor proxy post end url return images 一、在web.xml中添加shiro過濾器 Xml代碼 <!-- Shiro filter--> <filter> <

makedown學習筆記以後可能會用makedown寫博客

chang 推薦 學習 com tag markdown ant 列表 ~~ 學習手冊 https://www.zybuluo.com/mdeditor?url=https%3A%2F%2Fwww.zybuluo.com%2Fstatic%2Feditor%2Fmd-hel

《SQL入門經典》筆記第二章:建立資料庫之資料型別

“建立資料庫”包括五個內容:定義資料結構、管理資料庫物件、規格化過程、操作資料以及管理資料庫事務   1. 什麼是資料型別? 資料型別用於指定特定列所包含資料的規則,它決定了資料儲存在列裡的方式。SQL最基本的資料型別有字串、數值、日期和時間(其實每個實現都有自己的資料型別

大三筆記ssm專案中使用layui進行分頁

  本篇文章較為簡單,實現的功能是分頁。下面貼程式碼。 <script src="layui.js" charset="utf-8"></script> <!-- 注意:如果你直接複製所有程式碼到本地,上述js路徑需要改成你本地的 -->

大資料分析學習筆記Z檢驗,分類器以及Association Rule

大資料分析學習筆記(Z檢驗,分類器以及Association Rule) Task 1 – Hypothesis Testing To improve student learning performance, a teacher developed two new learning app