一、引言
最近在工作中接觸到了 libevent 庫,老大想讓我用 libevent 做一些網關相關的開發工作。因此,在這個不一樣的春節裏(肆虐的新冠病毒)我一直在閱讀有關 libevent 相關的資料。
學習 libevent 的資料,我選擇了最穩妥也是最慢的方法,libevent 的官方教程文檔。這個文檔大概在 2012 年之後就沒有再更新了,國內也有朋友製作成了 pdf 方便大家閱讀,我大概看了下,有 126 頁之多。這裏值得一說的就是,libevent 的官方教程文檔還是寫的很不錯的,內容循序漸進,即使是英文,高中水平應該也是可以很順暢的閱讀的。
libevent 的詳細介紹在 libevent 官網(http://libevent.org/) 上都有詳細的介紹,實際上也有很多中國的程序員寫的博客可以閱讀。不過最詳細最權威的,還是 libevent 的官方文檔。
在 libevent 官網上,你可以看到 libevent 目前維護了 4 個版本:
1.4.x.stable
2.0.x.stable
2.1.x.stable
master
其中最新的穩定版本是 2.1.x.stable,master 是開發分支的最新代碼,不具有生產使用意義。不過這裏使用哪個版本不僅需要跟你的系統有關,還需要考慮其配套使用的 openssl 庫的版本限制。
不過廢話少說,這篇博客的目標讀者是剛接觸 libevent 的新手,對於新手來說,剛讀完了(或者正在讀)libevent 官方文檔,最想做的事情是什麼呢:
當然是把 libevent 安裝包裏面的 sample 程序跑起來了:)
這裏,我基於 libevent 2.1.x.stable 版本的最新安裝包 libevent-2.1.11-stable.tar.gz 進行講解。
二、sample 示例程序編譯
sample 下的程序實際上在 make 的時候就會自動生成,在 sample 下有以下程序:
sample/dns-example
sample/event-read-fifo
sample/hello-world
sample/http-server
sample/http-connect
sample/signal-test
sample/time-test
sample/le-proxy
sample/https-client
這些程序在你 make 完了之後就會自動生成。
編譯方式在哪裏看呢
我們可以打開 sample 文件夾下的 include.am 文件,這裏記錄了 sample 下面的程序的編譯方式:
SAMPLES = \
sample/dns-example \
sample/event-read-fifo \
sample/hello-world \
sample/http-server \
sample/http-connect \
sample/signal-test \
sample/time-test
if OPENSSL
SAMPLES += sample/le-proxy
sample_le_proxy_SOURCES = sample/le-proxy.c
sample_le_proxy_LDADD = libevent.la libevent_openssl.la $(OPENSSL_LIBS) $(OPENSSL_LIBADD)
sample_le_proxy_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_INCS)
SAMPLES += sample/https-client
sample_https_client_SOURCES = \
sample/https-client.c \
sample/hostcheck.c \
sample/openssl_hostname_validation.c
sample_https_client_LDADD = libevent.la libevent_openssl.la $(OPENSSL_LIBS) $(OPENSSL_LIBADD)
sample_https_client_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_INCS)
noinst_HEADERS += \
sample/hostcheck.h \
sample/openssl_hostname_validation.h
endif
代碼我們不需要完全看懂,我們大概就能看得出來有 3 種編譯的方式。
只需要鏈接 libevent 庫
只需要鏈接 libevent 庫的編譯方式:
$ gcc -o hello-world hello-world.c -levent
如果你是按照默認的方式安裝的 libevent,也就是:
$ ./configure
$ make
$ sudo make install
那麼,你的 libevent 多半就安裝在 /usr/local 下,其中 /usr/local/lib 下放置了 libevent 的相關庫文件,而 /usr/local/include 下則放置了 libevent的相關頭文件。
這裏,可能會有些朋友在執行 hello-world 的時候出現錯誤:
$ ./hello-world
執行後報錯:
error while loading shared libraries: libevent-2.1.so.7: cannot open shared object file: No such file or directory
這是爲什麼呢?這是因爲在運行的時候,ldd 程序找不到你所說的 libevent 庫文件。那麼怎麼才能讓它找得到呢,這裏我通過查找資料,找到了一個解決辦法:
1. 查看 hello-world 程序有哪一個庫沒有找到:
$ ldd ./hello-world
輸出:
linux-vdso.so.1 (0x00007fffd55f7000)
libevent-2.1.so.7 => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4be8160000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f4be7f40000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4be8a00000)
2. 將沒找到的 libevent-2.1.so.7 庫軟連接到 ldd 默認掃描的目錄 /usr/lib 下:
$ sudo ln -s /usr/local/lib/libevent-2.1.so.7 /usr/lib/libevent-2.1.so.7
因爲是操作 /usr/lib 文件夾內容,因此需要 sudo 提高權限。
3. 重新運行 hello-world 即可
$ ./hello-world
這裏的解決思路很簡單,你的 ldd 不是找不到 libevent 庫文件嗎,那麼我自己軟連接一個 ldd 默認搜索的地方,然後連接到我庫的地方即可。
ps:
a. 這個方法參考博客 error while loading shared libraries: libevent-2.0.so.5解決辦法。
b. 實際上還有其他方法,可以參考博客 “error while loading shared libraries: xxx.so.x” 錯誤的原因和解決辦法。
除了 libevent 庫,還需要再鏈接一個 libevent_openssl 庫
通過 sample 文件夾下的 include.am 文件,我們得知 le-proxy.c 文件的編譯還需要添加一個 libevent_openssl 庫。編譯的語句也很簡單:
$ gcc -o le-proxy le-proxy.c -levent -levent_openssl -lcrypto -lssl -I ../
注意:這裏鏈接 libevent_openssl 庫,還需要鏈接 openssl 的庫文件,也就是 libcrypto 和 libssl,這兩個庫不能少。另外,le-proxy.c 還有一個頭文件位於 sample 上一層文件夾,也需要指定頭文件包含。
有些朋友在編譯 le-proxy.c 的時候可能會發現:
在自己的 /usr/local/include/event2 下(假定你也是按照默認安裝路徑 /usr/local 進行安裝)根本就沒有 bufferevent_ssl.h 頭文件存在!再仔細一看,發現根本就沒有生成 libevent_openssl 庫文件!
這當然會導致 le-proxy.c 編譯失敗。
那麼這是爲什麼呢:
因爲在 libevent 的安裝過程中,如果它能夠找到 openssl 的庫文件,則會自動生成 libevent_openssl 庫;但是如果沒有找到 openssl 庫文件,則不會生成 libevent_openssl 庫文件。
解決這個問題的辦法也很簡單,重裝 openssl:
1. openssl 官網下載 openssl 安裝包,解壓到本地
$ tar -xvf openssl-1.1.1d
2. 配置 openssl
$ cd openssl-1.1.1d
$ ./config --prefix=/usr/local --openssldir=/usr/local/openssl
3. 安裝 openssl
$ make
$ sudo make install
4. 重新安裝 libevent
$ cd libevent-2.1.11-stable
$ ./configure
$ make
$ sudo make install
再重新編譯 le-proxy.c 即可。
ps:
參考博客 libevent實現https服務器。
比較複雜的 https_client 程序如何編譯
剩下一個比較複雜的 https_client 程序的編譯:
$ gcc -o https-client https-client.c hostcheck.c openssl_hostname_validation.c -L /usr/local/lib -levent -levent_openssl -lcrypto -lssl
只需要按照 sample 下的 install.am 裏面的描述進行填寫即可。
https-client 是一個非常好的示例程序,可以仔細閱讀,增加自己對於 libevent 的理解。
三、總結
在閱讀了 libevent 的官方文檔之後,我折騰了整整一個下午,才把 sample 下的示例程序全部編譯運行出來。在這過程中遇到了不少問題,也一一在博客中記錄了下來。
希望本篇博客能夠給你帶來一些幫助:)
最後,面對新型肺炎:
中國加油!武漢加油!