日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【slighttpd】基于lighttpd架构的Server项目实战(2)—预备知识之libevent

發(fā)布時間:2024/1/23 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【slighttpd】基于lighttpd架构的Server项目实战(2)—预备知识之libevent 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載地址:https://blog.csdn.net/jiange_zh/article/details/50631393

簡介


由于本項目是純異步的,而對于大量 socket 連接,使用 select 并不高效。(參見我的另一篇博文:epoll簡介)

事實上,大部分系統(tǒng)提供了處理大量 socket 連接的解決方案:

  • Linux 下的 epoll()
  • BSD 下的 kqueue()
  • Solaris 下的 evports
  • Windows 下的 IOCP

  • 由于各個平臺使用了不同的接口,所以想編寫跨平臺的高性能異步程序就需要做一層跨平臺封裝。此時 Libevent 是一個不錯的選擇(當(dāng)然你也可以自己來實現(xiàn)這個事-。-),其最底層 API(event 和 event_base API)為各個平臺實現(xiàn)高性能異步程序提供了一致的接口。

    一些概念


    event:會綁定文件描述符、回調(diào)函數(shù)并表示一個或者多個條件(例如,文件描述符可以讀、寫或者超時等)。event 表示的條件如果被觸發(fā)了,那么 event 會變?yōu)榛钴S的,它綁定的回調(diào)函數(shù)就會被執(zhí)行。

    event_base: 持有一組 event 并進(jìn)行事件循環(huán),event_base 會存在一個后端(也叫做方法),常見的后端包括 epoll、kqueue 等。

    結(jié)構(gòu)


    組件:

    evutil 用于抽象不同的平臺的網(wǎng)絡(luò)(基礎(chǔ)的)實現(xiàn);

    event、event_base 為 Libevent 的核心,為不同的平臺下基于事件的非阻塞 I/O 提供了一套抽象的接口;

    bufferevent 對 Libevent 的基于事件的核心的封裝。應(yīng)用程序的讀寫請求是基于緩沖區(qū)的;

    evbuffer 為 bufferevent 實現(xiàn)的緩沖區(qū);

    evhttp 一個簡單的 HTTP client/server 的實現(xiàn);

    evdns 一個簡單的 DNS client/server 的實現(xiàn);

    evrpc 一個簡單的 RPC 實現(xiàn);

    庫:

    libevent_core 包括 util、event_base、evbuffer、bufferevent;

    libevent_extra 包括 HTTP、DNS、RPC;

    libevent 此庫由于歷史原因而存在,不要使用它;

    libevent_pthreads 此庫為基于 pthread 的線程和鎖的實現(xiàn);

    libevent_openssl 此庫通過 openssl 和 bufferevent 提供了加密通訊;

    頭文件:?
    ? ? 所有的公用頭文件位于 event2 目錄中。

    創(chuàng)建和銷毀 event_base


    event_base 是最基本的,故需要首先被創(chuàng)建。

    event_base 結(jié)構(gòu)持有一個 event 集合。如果 event_base 被設(shè)置了使用鎖,那么它在多個線程中可以安全的訪問。但是對 event_base 的循環(huán)只能在某個線程中執(zhí)行。如果希望在多個線程中進(jìn)行循環(huán),那么應(yīng)該為每一個線程創(chuàng)建一個 event_base。

    event_base 存在多個后端可以選擇(我們也把 event_base 后端叫做 event_base 的方法):

    select
    poll
    epoll
    kqueue
    devpoll
    evport
    win32


    在創(chuàng)建 event_base 時,libevent 會為我們選擇最快的后端。

    創(chuàng)建 event_base 的函數(shù):

    // 創(chuàng)建成功返回一個擁有默認(rèn)設(shè)置的 event base
    // 創(chuàng)建失敗返回 NULL
    struct event_base *event_base_new(void);

    // 查看 event_base 實際上使用到的后端
    const char *event_base_get_method(const struct event_base *base);


    event_base 的釋放使用函數(shù):

    void event_base_free(struct event_base *base);


    事件循環(huán)(event loop)


    event_base 會持有一組 event。如果我們向 event_base 中注冊了一些 event,那么就可以讓 libevent 開始事件循環(huán)了:

    // 指定一個 event_base 并開始事件循環(huán)
    // 此函數(shù)內(nèi)部被實現(xiàn)為一個不斷進(jìn)行的循環(huán)
    // 此函數(shù)返回 0 表示成功退出
    // 此函數(shù)返回 -1 表示存在未處理的錯誤
    int event_base_dispatch(struct event_base *base);


    event_base_dispatch會在以下情況停止:

    如果 base 中沒有 event,那么事件循環(huán)將停止
    調(diào)用 event_base_loopbreak(),那么事件循環(huán)將停止
    調(diào)用 event_base_loopexit(),那么事件循環(huán)將停止
    如果出現(xiàn)錯誤,那么事件循環(huán)將停止


    在事件循環(huán)中,會監(jiān)聽是否存在活躍事件,若存在活躍事件,則調(diào)用事件對應(yīng)的回調(diào)函數(shù)。

    按照上面說的,如果想停止事件循環(huán):

    ? ? ?1.移除 event_base 中的 event。

    ? ? ?2.如果想在 event_base 中存在 event 的情況下停止事件循環(huán),可以調(diào)用以下函數(shù):

    ? ? // 這兩個函數(shù)成功返回 0 失敗返回 -1
    ? ? // 指定在 tv 時間后停止事件循環(huán)
    ? ? // 如果 tv == NULL 那么將無延時的停止事件循環(huán)
    ? ? int event_base_loopexit(struct event_base *base,
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? const struct timeval *tv);
    ? ? // 立即停止事件循環(huán)(而不是無延時的停止)
    ? ? int event_base_loopbreak(struct event_base *base);


    event_base_loopexit(base, NULL) :如果當(dāng)前正在為多個活躍事件調(diào)用回調(diào)函數(shù),那么不會立即退出,而是等到所有的活躍事件的回調(diào)函數(shù)都執(zhí)行完成后才退出事件循環(huán);

    event_base_loopbreak(base) :如果當(dāng)前正在為多個活躍事件調(diào)用回調(diào)函數(shù),那么當(dāng)前正在調(diào)用的回調(diào)函數(shù)會被執(zhí)行,然后馬上退出事件循環(huán),而并不處理其他的活躍事件了。

    在事件的回調(diào)函數(shù)中獲取當(dāng)前的時間可以使用event_base_gettimeofday_cached()(你的系統(tǒng)可能實現(xiàn) gettimeofday() 為一個系統(tǒng)調(diào)用,故用下面函數(shù)可避免系統(tǒng)調(diào)用的開銷):

    // 獲取到的時間為開始執(zhí)行此輪事件回調(diào)函數(shù)的時間
    // 成功返回 0 失敗返回負(fù)數(shù)
    int event_base_gettimeofday_cached(struct event_base *base,
    ? ? struct timeval *tv_out);


    event事件


    event是一組觸發(fā)條件,例如:

    ? ? ?1. 一個文件描述符可讀或者可寫;
    ? ? ?2. 超時;
    ? ? ?3. 產(chǎn)生信號;
    ? ? ?4. 用戶觸發(fā)了一個事件。
    相關(guān)術(shù)語:

    一個 event 通過 event_new() 創(chuàng)建出來,那么它是已初始化的,另外 event_assign() 也被用來初始化 event(但是有它特定的用法);

    一個 event 被注冊到(通過 add 函數(shù)添加到)一個 event_base 中,其為 pending 狀態(tài);

    如果 pending event 表示的條件被觸發(fā)了,那么此 event 會變?yōu)榛钴S的,其為 active 狀態(tài),則 event 相關(guān)的回調(diào)函數(shù)被調(diào)用;

    event 可以是 persistent(持久的)也可以是非 persistent 的。event 相關(guān)的回調(diào)函數(shù)被調(diào)用之后,只有 persistent event 會繼續(xù)轉(zhuǎn)為 pending 狀態(tài)。對于非 persistent 的 event 你可以在 event 相關(guān)的事件回調(diào)函數(shù)中調(diào)用 event_add() 使得此 event 轉(zhuǎn)為 pending 狀態(tài),否則該事件將會被刪除(即該事件是一次性的)。

    常用 API:

    ? ? // 定義了各種條件
    ? ? // 超時
    ? ? #define EV_TIMEOUT ? ? ?0x01
    ? ? // 文件描述符可讀
    ? ? #define EV_READ ? ? ? ? 0x02
    ? ? // 文件描述符可寫
    ? ? #define EV_WRITE ? ? ? ?0x04
    ? ? // 用于信號檢測
    ? ? #define EV_SIGNAL ? ? ? 0x08
    ? ? // 用于指定 event 為 persistent
    ? ? #define EV_PERSIST ? ? ?0x10
    ? ? // 用于指定 event 會被邊緣觸發(fā)
    ? ? #define EV_ET ? ? ? ? ? 0x20

    ? ? // event 的回調(diào)函數(shù)
    ? ? // 參數(shù) evutil_socket_t --- event 關(guān)聯(lián)的文件描述符
    ? ? // 參數(shù) short --- 當(dāng)前發(fā)生的條件類型
    ? ? // 參數(shù) void* --- 其為 event_new 函數(shù)中的 arg 參數(shù)
    ? ? typedef void (*event_callback_fn)(evutil_socket_t, short, void *);

    ? ? // 創(chuàng)建 event
    ? ? // base --- 使用此 event 的 event_base
    ? ? // what --- 指定 event 關(guān)心的條件
    ? ? // fd --- 文件描述符
    ? ? // cb --- event 相關(guān)的回調(diào)函數(shù)
    ? ? // arg --- 用戶自定義數(shù)據(jù)
    ? ? // 函數(shù)執(zhí)行失敗返回 NULL
    ? ? struct event *event_new
    ? ? (struct event_base *base,?
    ? ? ?evutil_socket_t fd,
    ? ? ?short what,?
    ? ? ?event_callback_fn cb,
    ? ? ?void *arg);

    ? ? // 釋放 event(真正釋放內(nèi)存,對應(yīng) event_new 使用)
    ? ? // 可以用來釋放由 event_new 分配的 event
    ? ? // 若 event 處于 pending 或者 active 狀態(tài)釋放也不會存在問題
    ? ? void event_free(struct event *event);

    ? ? // 清理 event(并不是真正釋放內(nèi)存)
    ? ? // 可用于已經(jīng)初始化的、pending、active 的 event
    ? ? // 此函數(shù)會將 event 轉(zhuǎn)換為非 pending、非 active 狀態(tài)的
    ? ? // 函數(shù)返回 0 表示成功 -1 表示失敗
    ? ? int event_del(struct event *event);

    ? ? // 用于向 event_base 中注冊 event
    ? ? // tv 用于指定超時時間,為 NULL 表示無超時時間
    ? ? // 函數(shù)返回 0 表示成功 -1 表示失敗
    ? ? int event_add(struct event *ev, const struct timeval *tv);


    信號 event 相關(guān)的函數(shù):

    ?

    // base --- event_base
    // signum --- 信號,例如 SIGHUP
    // callback --- 信號出現(xiàn)時調(diào)用的回調(diào)函數(shù)
    // arg --- 用戶自定義數(shù)據(jù)
    #define evsignal_new(base, signum, callback, arg) \
    ? ? event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg)

    // 將信號 event 注冊到 event_base
    #define evsignal_add(ev, tv) \
    ? ? event_add((ev),(tv))

    // 清理信號 event
    #define evsignal_del(ev) \
    ? ? event_del(ev)


    注意,一個進(jìn)程中 Libevent 只能允許一個 event_base 監(jiān)聽信號。

    重用 event ,避免 event 在堆上的頻繁分配和釋放:

    ? ? // 此函數(shù)用于初始化 event(包括可以初始化棧上和靜態(tài)存儲區(qū)中的 event)
    ? ? // event_assign() 和 event_new() 除了 event 參數(shù)之外,使用了一樣的參數(shù)
    ? ? // event 參數(shù)用于指定一個未初始化的且需要初始化的 event
    ? ? // 函數(shù)成功返回 0 失敗返回 -1
    ? ? int event_assign
    ? ? (struct event *event,?
    ? ? ?struct event_base *base,
    ? ? ?evutil_socket_t fd, short what,
    ? ? ?void (*callback)(evutil_socket_t, short, void *),?
    ? ? ?void *arg);

    ? ? // 類似上面的函數(shù),此函數(shù)被信號 event 使用
    ? ? #define evsignal_assign(event, base, signum, callback, arg) \
    ? ? ? ? event_assign(event, base, signum, EV_SIGNAL|EV_PERSIST, callback, arg)


    已經(jīng)初始化或者處于 pending 的 event,首先需要調(diào)用 event_del() 后才能調(diào)用 event_assign()。

    event_new() 實際上完成了兩件事情:

    通過內(nèi)存分配函數(shù)在堆上分配了 event
    使用 event_assign() 初始化了此 event


    event_free() 實際上完成了兩件事情:

    調(diào)用 event_del() 進(jìn)行 event 的清理工作
    通過內(nèi)存分配函數(shù)在堆上釋放此 event


    常用基本數(shù)據(jù)類型

    evutil_socket_t 用于保存 socket
    ev_uint64_t 取值范圍 [0, EV_UINT64_MAX]
    ev_int64_t 取值范圍 [EV_INT64_MIN, EV_INT64_MAX]
    ev_uint32_t 取值范圍 [0, EV_UINT32_MAX]
    ev_int32_t 取值范圍 [EV_INT32_MIN, EV_INT32_MAX]
    ev_uint16_t 取值范圍 [0, EV_UINT16_MAX]
    ev_int16_t 取值范圍 [EV_INT16_MIN, EV_INT16_MAX]
    ev_uint8_t 取值范圍 [0, EV_UINT8_MAX]
    ev_int8_t 取值范圍 [EV_INT8_MIN, EV_INT8_MAX]
    ev_ssize_type(signed size_t)取值范圍 [EV_SSIZE_MIN, EV_SSIZE_MAX]


    時間相關(guān)


    // 用于加或者減前兩個參數(shù),結(jié)果被保存在第三個參數(shù)中
    #define evutil_timeradd(tvp, uvp, vvp) /* ... */
    #define evutil_timersub(tvp, uvp, vvp) /* ... */

    // 清除 timeval 將其值設(shè)置為 0
    #define evutil_timerclear(tvp) /* ... */
    // 判斷 timeval 是否為 0,如果是 0 返回 false,否則返回 true
    #define evutil_timerisset(tvp) /* ... */

    // 比較兩個 timeval
    // 使用的時候這樣用:
    // evutil_timercmp(t1, t2, <=) 含義為判斷 t1 <= t2 是否成立
    // cmp 為所有的 C 關(guān)系操作符
    #define evutil_timercmp(tvp, uvp, cmp)

    // 獲取當(dāng)前時間并保存到 tv
    // tz 目前無用
    int evutil_gettimeofday(struct timeval *tv, struct timezone *tz);


    Socket相關(guān)

    // 用于關(guān)閉一個 socket
    int evutil_closesocket(evutil_socket_t s);
    #define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)

    // 返回當(dāng)前線程的最后一次 socket 操作的錯誤碼
    #define EVUTIL_SOCKET_ERROR()
    // 改變當(dāng)前 socket 的錯誤碼
    #define EVUTIL_SET_SOCKET_ERROR(errcode)
    // 返回特定的 sock 的錯誤碼
    #define evutil_socket_geterror(sock)
    // 通過 socket 錯誤碼獲取到一個字符串描述
    #define evutil_socket_error_to_string(errcode)

    // 設(shè)置 sock 為非阻塞的 socket
    int evutil_make_socket_nonblocking(evutil_socket_t sock);

    // 設(shè)置 sock 的地址可重用
    int evutil_make_listen_socket_reuseable(evutil_socket_t sock);


    字符串相關(guān)

    // 它們對應(yīng)于標(biāo)準(zhǔn)的 snprintf 和 vsnprintf
    int evutil_snprintf(char *buf, size_t buflen, const char *format, ...);
    int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap);


    結(jié)語


    以上是libevent的基本介紹,在本次的項目中,會使用到最基本的event以及信號event。

    對于數(shù)據(jù)、緩沖區(qū)的管理,bufferevent也許用得上,這是后話,需要用到的時候繼續(xù)查資料學(xué)習(xí)!
    ?

    總結(jié)

    以上是生活随笔為你收集整理的【slighttpd】基于lighttpd架构的Server项目实战(2)—预备知识之libevent的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。