libevent学习笔记
libevent是一個事件觸發的網絡庫,適用于windows、linux、bsd等多種平臺,內部使用select、epoll、kqueue等系統調用管理事件機制。著名分布式緩存軟件memcached也是libevent based,而且libevent在使用上可以做到跨平臺。
libevent在linux上實現是使用epoll機制的。
平時在代碼里使用的是epoll,覺得沒有windows完成端口方便,需要自己去實現一些功能。
公司常用的linux自帶libevent版本1.1,現在最新的版本是2.0,但是剛剛推出,頭文件很多都改變了,最成熟的庫是1.4。
http://monkey.org/~provos/libevent/
使用回調函數來實現自定義功能。
一個簡單的網絡事件
void fifo_read(int fd, short event, void *arg)
{
?struct event *ev = (struct event *)arg;
?/* 重新安排事件 */
?event_add(ev, NULL);
?/* 讀socket */
?len = read(fd, buf, sizeof(buf) - 1);
}
main()
{
?……
?/* 初始化事件 */
?event_init();
?/* 設置事件為可讀,回調函數fifo_read */
?event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo);
?/* 添加事件,沒有超時時間 */
?event_add(&evfifo, NULL);
?/* 進入libevent主循環 */
?event_dispatch();
?……
}
一個簡單的信號處理
int called = 0;
static void signal_cb(int fd, short event, void *arg)
{
?struct event *signal = (struct event *)arg;
?printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal));
?/* 調用2次就銷毀事件 */
?if (called >= 2)
??event_del(signal);
?called++;
}
int main (int argc, char **argv)
{
?……
?struct event signal_int;
?/* 初始化,比較早期的例子代碼,這里變成了 event_base_new,而不是event_init()*/
?struct event_base* base = event_base_new();
?/* 初始化事件 */
?event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,&signal_int);
?event_base_set(base, &signal_int);
?event_add(&signal_int, NULL);
?event_base_dispatch(base);
?event_base_free(base);
?……
}
一個簡單的超時例子
int lasttime;
static void timeout_cb(int fd, short event, void *arg)
{
?struct timeval tv;
?struct event *timeout = (struct event *)arg;
?int newtime = time(NULL);
?printf("%s: called at %d: %d\n", __func__, newtime,
???? newtime - lasttime);
?lasttime = newtime;
?evutil_timerclear(&tv);
?tv.tv_sec = 2;
?event_add(timeout, &tv);
}
int
main (int argc, char **argv)
{
?……
?struct event timeout;
?struct timeval tv;?
?/* 初始化*/
?event_init();
?/* 一個封裝,等同于 event_set(timeout,-1,0,timeout_cb,&timeout) */
?evtimer_set(&timeout, timeout_cb, &timeout);
?evutil_timerclear(&tv);
?tv.tv_sec = 2;
?event_add(&timeout, &tv);
?lasttime = time(NULL);?
?event_dispatch();
?……
}
在定時器上使用了最小二叉堆的方式,提高了效率。
http服務支持
void generic_handler(struct evhttp_request *req, void *arg)
{
?struct evbuffer *buf;
?buf = evbuffer_new();
?if (buf == NULL)
??err(1, "failed to create response buffer");
?evbuffer_add_printf(buf, "Requested: %s", evhttp_request_uri(req));
?evhttp_send_reply(req, HTTP_OK, "OK", buf);
?evbuffer_free(buf);
}
int main(int argc, char **argv)
{
?……
?struct evhttp *httpd;
?event_init();
?httpd = evhttp_start("0.0.0.0", 8080);
?/* 用一個回調函數來支持get中的 "/specific". */
?/* evhttp_set_cb(httpd, "/specific", another_handler, NULL); */
?/* 用一個回調函數來支持其他的請求 */
?evhttp_set_gencb(httpd, generic_handler, NULL);
?event_dispatch();
?evhttp_free(httpd);
?……
}
libevent對于連接中有可能長時間阻塞的任務處理,那么還需要自己來加點處理,并沒有很現成的多線程處理模式,而且看http處理的源碼,并不支持多線程安全。
閱讀中對epoll重新溫習了一下。
早期用epoll編碼的時候發現過,當有壓力的時候,明明抓包收到了2個udp包,但是epoll只出發了一次事件,只有再接收一次報文的時候才會接收處理上次的udp包。然后發現是增加參數變成高速模式的原因,修改為事件中循環讀取到無數據為止。
“EPOLLLT和EPOLLET兩種觸發模式,LT是默認的模式,ET是“高速”模式。LT模式下,只要這個fd還有數據可讀,每次epoll_wait都會返回它的事件,提醒用戶程序去操作,而在ET(邊緣觸發)模式中,它只會提示一次,直到下次再有數據流入之前都不會再提示了,無論fd中是否還有數據可讀。所以在ET模式下,read一個fd的時候一定要把它的buffer讀光,也就是說一直讀到read的返回值小于請求值,或者遇到EAGAIN錯誤。”
libevent的epoll使用的是EPOLLLT,效率并不算太高。
感覺沒有太多自動的東西,比如多線程處理連接,或者自動淘汰syn但是沒有send的鏈接等等,畢竟只是個輕量級的lib庫。
可以看這個網頁,先是討論了,判斷syn超時的問題,然后討論了模式的區別。
http://www.chinaunix.net/jh/23/813588.html
轉載于:https://blog.51cto.com/xzq2000/1767388
總結
以上是生活随笔為你收集整理的libevent学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 百度公布2022年财报:净利润206.8
- 下一篇: 曝OPPO将裁撤电视业务 官方回应:不实