<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                #Utilities 本章介紹的工具和技術對于常見的任務非常的實用。libuv吸收了[libev用戶手冊頁](http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#COMMON_OR_USEFUL_IDIOMS_OR_BOTH)中所涵蓋的一些模式,并在此基礎上對API做了少許的改動。本章還包含了一些無需用完整的一章來介紹的libuv API。 ##Timers 在定時器啟動后的特定時間后,定時器會調用回調函數。libuv的定時器還可以設定為,按時間間隔定時啟動,而不是只啟動一次。 可以簡單地使用超時時間`timeout`作為參數初始化一個定時器,還有一個可選參數`repeat`。定時器能在任何時間被終止。 ```c uv_timer_t timer_req; uv_timer_init(loop, &timer_req); uv_timer_start(&timer_req, callback, 5000, 2000); ``` 上述操作會啟動一個循環定時器(repeating timer),它會在調用`uv_timer_start`后,5秒(timeout)啟動回調函數,然后每隔2秒(repeat)循環啟動回調函數。你可以使用: ```c uv_timer_stop(&timer_req); ``` 來停止定時器。這個函數也可以在回調函數中安全地使用。 循環的間隔也可以隨時定義,使用: ```c uv_timer_set_repeat(uv_timer_t *timer, int64_t repeat); ``` 它會在**可能的時候**發揮作用。如果上述函數是在定時器回調函數中調用的,這意味著: >* 如果定時器未設置為循環,這意味著定時器已經停止。需要先用`uv_timer_start`重新啟動。 * 如果定時器被設置為循環,那么下一次超時的時間已經被規劃好了,所以在切換到新的間隔之前,舊的間隔還會發揮一次作用。 函數: ```c int uv_timer_again(uv_timer_t *) ``` **只適用于循環定時器**,相當于停止定時器,然后把原先的`timeout`和`repeat`值都設置為之前的`repeat`值,啟動定時器。如果當該函數調用時,定時器未啟動,則調用失敗(錯誤碼為`UV_EINVAL`)并且返回-1。 下面的一節會出現使用定時器的例子。 ##Event loop reference count event-loop在沒有了活躍的handle之后,便會終止。整套系統的工作方式是:在handle增加時,event-loop的引用計數加1,在handle停止時,引用計數減少1。當然,libuv也允許手動地更改引用計數,通過使用: ```c void uv_ref(uv_handle_t*); void uv_unref(uv_handle_t*); ``` 這樣,就可以達到允許loop即使在有正在活動的定時器時,仍然能夠推出。或者是使用自定義的uv_handle_t對象來使得loop保持工作。 第二個函數可以和間隔循環定時器結合使用。你會有一個每隔x秒執行一次的垃圾回收器,或者是你的網絡服務器會每隔一段時間向其他人發送一次心跳信號,但是你不想只有在所有垃圾回收完或者出現錯誤時才能停止他們。如果你想要在你其他的監視器都退出后,終止程序。這時你就可以立即unref定時器,即便定時器這時是loop上唯一還在運行的監視器,你依舊可以停止`uv_run()`。 它們同樣會出現在node.js中,如js的API中封裝的libuv方法。每一個js的對象產生一個`uv_handle_t`(所有監視器的超類),同樣可以被uv_ref和uv_unref。 ####ref-timer/main.c ```c uv_loop_t *loop; uv_timer_t gc_req; uv_timer_t fake_job_req; int main() { loop = uv_default_loop(); uv_timer_init(loop, &gc_req); uv_unref((uv_handle_t*) &gc_req); uv_timer_start(&gc_req, gc, 0, 2000); // could actually be a TCP download or something uv_timer_init(loop, &fake_job_req); uv_timer_start(&fake_job_req, fake_job, 9000, 0); return uv_run(loop, UV_RUN_DEFAULT); } ``` 首先初始化垃圾回收器的定時器,然后在立刻`unref`它。注意觀察9秒之后,此時fake_job完成,程序會自動退出,即使垃圾回收器還在運行。 ##Idler pattern 空轉的回調函數會在每一次的event-loop循環激發一次。空轉的回調函數可以用來執行一些優先級較低的活動。比如,你可以向開發者發送應用程序的每日性能表現情況,以便于分析,或者是使用用戶應用cpu時間來做[SETI](http://www.seti.org)運算:)。空轉程序還可以用于GUI應用。比如你在使用event-loop來下載文件,如果tcp連接未中斷而且當前并沒有其他的事件,則你的event-loop會阻塞,這也就意味著你的下載進度條會停滯,用戶會面對一個無響應的程序。面對這種情況,空轉監視器可以保持UI可操作。 ####idle-compute/main.c ```c uv_loop_t *loop; uv_fs_t stdin_watcher; uv_idle_t idler; char buffer[1024]; int main() { loop = uv_default_loop(); uv_idle_init(loop, &idler); uv_buf_t buf = uv_buf_init(buffer, 1024); uv_fs_read(loop, &stdin_watcher, 0, &buf, 1, -1, on_type); uv_idle_start(&idler, crunch_away); return uv_run(loop, UV_RUN_DEFAULT); } ``` 上述程序中,我們將空轉監視器和我們真正關心的事件排在一起。`crunch_away`會被循環地調用,直到輸入字符并回車。然后程序會被中斷很短的時間,用來處理數據讀取,然后在接著調用空轉的回調函數。 ####idle-compute/main.c ```c void crunch_away(uv_idle_t* handle) { // Compute extra-terrestrial life // fold proteins // computer another digit of PI // or similar fprintf(stderr, "Computing PI...\n"); // just to avoid overwhelming your terminal emulator uv_idle_stop(handle); } ``` ##Passing data to worker thread 在使用`uv_queue_work`的時候,你通常需要給工作線程傳遞復雜的數據。解決方案是自定義struct,然后使用`uv_work_t.data`指向它。一個稍微的不同是必須讓`uv_work_t`作為這個自定義struct的成員之一(把這叫做接力棒)。這么做就可以使得,同時回收數據和`uv_wortk_t`。 ```c struct ftp_baton { uv_work_t req; char *host; int port; char *username; char *password; } ``` ```c ftp_baton *baton = (ftp_baton*) malloc(sizeof(ftp_baton)); baton->req.data = (void*) baton; baton->host = strdup("my.webhost.com"); baton->port = 21; // ... uv_queue_work(loop, &baton->req, ftp_session, ftp_cleanup); ``` 現在我們創建完了接力棒,并把它排入了隊列中。 現在就可以隨性所欲地獲取自己想要的數據啦。 ```c void ftp_session(uv_work_t *req) { ftp_baton *baton = (ftp_baton*) req->data; fprintf(stderr, "Connecting to %s\n", baton->host); } void ftp_cleanup(uv_work_t *req) { ftp_baton *baton = (ftp_baton*) req->data; free(baton->host); // ... free(baton); } ``` 我們既回收了接力棒,同時也回收了監視器。 ##External I/O with polling 通常在使用第三方庫的時候,需要應對他們自己的IO,還有保持監視他們的socket和內部文件。在此情形下,不可能使用標準的IO流操作,但第三方庫仍然能整合進event-loop中。所有這些需要的就是,第三方庫就必須允許你訪問它的底層文件描述符,并且提供可以處理有用戶定義的細微任務的函數。但是一些第三庫并不允許你這么做,他們只提供了一個標準的阻塞IO函數,此函數會完成所有的工作并返回。在event-loop的線程直接使用它們是不明智的,而是應該使用libuv的工作線程。當然,這也意味著失去了對第三方庫的顆粒化控制。 libuv的`uv_poll`簡單地監視了使用了操作系統的監控機制的文件描述符。從某方面說,libuv實現的所有的IO操作,的背后均有`uv_poll`的支持。無論操作系統何時監視到文件描述符的改變,libuv都會調用響應的回調函數。 現在我們簡單地實現一個下載管理程序,它會通過[libcurl](http://curl.haxx.se/libcurl/)來下載文件。我們不會直接控制libcurl,而是使用libuv的event-loop,通過非阻塞的異步的[多重接口](http://curl.haxx.se/libcurl/c/libcurl-multi.html)來處理下載,與此同時,libuv會監控IO的就緒狀態。 ####uvwget/main.c - The setup ```c #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <uv.h> #include <curl/curl.h> uv_loop_t *loop; CURLM *curl_handle; uv_timer_t timeout; } int main(int argc, char **argv) { loop = uv_default_loop(); if (argc <= 1) return 0; if (curl_global_init(CURL_GLOBAL_ALL)) { fprintf(stderr, "Could not init cURL\n"); return 1; } uv_timer_init(loop, &timeout); curl_handle = curl_multi_init(); curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket); curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout); while (argc-- > 1) { add_download(argv[argc], argc); } uv_run(loop, UV_RUN_DEFAULT); curl_multi_cleanup(curl_handle); return 0; } ``` 每種庫整合進libuv的方式都是不同的。以libcurl的例子來說,我們注冊了兩個回調函數。socket回調函數`handle_socket`會在socket狀態改變的時候被觸發,因此我們不得不開始輪詢它。`start_timeout`是libcurl用來告知我們下一次的超時間隔的,之后我們就應該不管當前IO狀態,驅動libcurl向前。這些也就是libcurl能處理錯誤或驅動下載進度向前的原因。 可以這么調用下載器: ``` $ ./uvwget [url1] [url2] ... ``` 我們可以把url當成參數傳入程序。 ####uvwget/main.c - Adding urls ```c void add_download(const char *url, int num) { char filename[50]; sprintf(filename, "%d.download", num); FILE *file; file = fopen(filename, "w"); if (file == NULL) { fprintf(stderr, "Error opening %s\n", filename); return; } CURL *handle = curl_easy_init(); curl_easy_setopt(handle, CURLOPT_WRITEDATA, file); curl_easy_setopt(handle, CURLOPT_URL, url); curl_multi_add_handle(curl_handle, handle); fprintf(stderr, "Added download %s -> %s\n", url, filename); } ``` 我們允許libcurl直接向文件寫入數據。 `start_timeout`會被libcurl立即調用。它會啟動一個libuv的定時器,使用`CURL_SOCKET_TIMEOUT`驅動`curl_multi_socket_action`,當其超時時,調用它。`curl_multi_socket_action`會驅動libcurl,也會在socket狀態改變的時候被調用。但在我們深入講解它之前,我們需要輪詢監聽socket,等待`handle_socket`被調用。 ####uvwget/main.c - Setting up polling ```c void start_timeout(CURLM *multi, long timeout_ms, void *userp) { if (timeout_ms <= 0) timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */ uv_timer_start(&timeout, on_timeout, timeout_ms, 0); } int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp) { curl_context_t *curl_context; if (action == CURL_POLL_IN || action == CURL_POLL_OUT) { if (socketp) { curl_context = (curl_context_t*) socketp; } else { curl_context = create_curl_context(s); curl_multi_assign(curl_handle, s, (void *) curl_context); } } switch (action) { case CURL_POLL_IN: uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform); break; case CURL_POLL_OUT: uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform); break; case CURL_POLL_REMOVE: if (socketp) { uv_poll_stop(&((curl_context_t*)socketp)->poll_handle); destroy_curl_context((curl_context_t*) socketp); curl_multi_assign(curl_handle, s, NULL); } break; default: abort(); } return 0; } ``` 我們關心的是socket的文件描述符s,還有action。對應每一個socket,我們都創造了`uv_poll_t`,并用`curl_multi_assign`把它們關聯起來。每當回調函數被調用時,`socketp`都會指向它。 在下載完成或失敗后,libcurl需要移除poll。所以我們停止并回收了poll的handle。 我們使用`UV_READABLE`或`UV_WRITABLE`開始輪詢,基于libcurl想要監視的事件。當socket已經準備好讀或寫后,libuv會調用輪詢的回調函數。在相同的handle上調用多次`uv_poll_start`是被允許的,這么做可以更新事件的參數。`curl_perform`是整個程序的關鍵。 ####uvwget/main.c - Driving libcurl. ```c void curl_perform(uv_poll_t *req, int status, int events) { uv_timer_stop(&timeout); int running_handles; int flags = 0; if (status < 0) flags = CURL_CSELECT_ERR; if (!status && events & UV_READABLE) flags |= CURL_CSELECT_IN; if (!status && events & UV_WRITABLE) flags |= CURL_CSELECT_OUT; curl_context_t *context; context = (curl_context_t*)req; curl_multi_socket_action(curl_handle, context->sockfd, flags, &running_handles); check_multi_info(); } ``` 首先我們要做的是停止定時器,因為內部還有其他要做的事。接下來我們我們依據觸發回調函數的事件,來設置flag。然后,我們使用上述socket和flag作為參數,來調用`curl_multi_socket_action`。在此刻libcurl會在內部完成所有的工作,然后盡快地返回事件驅動程序在主線程中急需的數據。libcurl會在自己的隊列中將傳輸進度的消息排隊。對于我們來說,我們只關心是否傳輸完成,這類消息。所以我們將這類消息提取出來,并將傳輸完成的handle回收。 ####uvwget/main.c - Reading transfer status. ```c void check_multi_info(void) { char *done_url; CURLMsg *message; int pending; while ((message = curl_multi_info_read(curl_handle, &pending))) { switch (message->msg) { case CURLMSG_DONE: curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL, &done_url); printf("%s DONE\n", done_url); curl_multi_remove_handle(curl_handle, message->easy_handle); curl_easy_cleanup(message->easy_handle); break; default: fprintf(stderr, "CURLMSG default\n"); abort(); } } } ``` ##Loading libraries libuv提供了一個跨平臺的API來加載[共享庫shared libraries](http://liaoph.com/linux-shared-libary/)。這就可以用來實現你自己的插件/擴展/模塊系統,它們可以被nodejs通過`require()`調用。只要你的庫輸出的是正確的符號,用起來還是很簡單的。在載入第三方庫的時候,要注意錯誤和安全檢查,否則你的程序就會表現出不可預測的行為。下面這個例子實現了一個簡單的插件,它只是打印出了自己的名字。 首先看下提供給插件作者的接口。 ####plugin/plugin.h ```c #ifndef UVBOOK_PLUGIN_SYSTEM #define UVBOOK_PLUGIN_SYSTEM // Plugin authors should use this to register their plugins with mfp. void mfp_register(const char *name); #endif ``` 你可以在你的程序中給插件添加更多有用的功能(mfp is My Fancy Plugin)。使用了這個api的插件的例子: ####plugin/hello.c ```c #include "plugin.h" void initialize() { mfp_register("Hello World!"); } ``` 我們的接口定義了,所有的插件都應該有一個能被程序調用的`initialize`函數。這個插件被編譯成了共享庫,因此可以被我們的程序在運行的時候載入。 ``` $ ./plugin libhello.dylib Loading libhello.dylib Registered plugin "Hello World!" ``` #####Note >共享庫的后綴名在不同平臺上是不一樣的。在Linux上是libhello.so。 使用`uv_dlopen`首先載入了共享庫`libhello.dylib`。再使用`uv_dlsym`獲取了該插件的`initialize`函數,最后在調用它。 ####plugin/main.c ```c #include "plugin.h" typedef void (*init_plugin_function)(); void mfp_register(const char *name) { fprintf(stderr, "Registered plugin \"%s\"\n", name); } int main(int argc, char **argv) { if (argc == 1) { fprintf(stderr, "Usage: %s [plugin1] [plugin2] ...\n", argv[0]); return 0; } uv_lib_t *lib = (uv_lib_t*) malloc(sizeof(uv_lib_t)); while (--argc) { fprintf(stderr, "Loading %s\n", argv[argc]); if (uv_dlopen(argv[argc], lib)) { fprintf(stderr, "Error: %s\n", uv_dlerror(lib)); continue; } init_plugin_function init_plugin; if (uv_dlsym(lib, "initialize", (void **) &init_plugin)) { fprintf(stderr, "dlsym error: %s\n", uv_dlerror(lib)); continue; } init_plugin(); } return 0; } ``` 函數`uv_dlopen`需要傳入一個共享庫的路徑作為參數。當它成功時返回0,出錯時返回-1。使用`uv_dlerror`可以獲取出錯的消息。 `uv_dlsym`的第三個參數保存了一個指向第二個參數所保存的函數的指針。`init_plugin_function`是一個函數的指針,它指向了我們所需要的程序插件的函數。 ##TTY 文字終端長期支持非常標準化的[控制序列](https://en.wikipedia.org/wiki/ANSI_escape_code)。它經常被用來增強終端輸出的可讀性。例如`grep --colour`。libuv提供了跨平臺的,`uv_tty_t`抽象(stream)和相關的處理ANSI escape codes 的函數。這也就是說,libuv同樣在Windows上實現了對等的ANSI codes,并且提供了獲取終端信息的函數。 首先要做的是,使用讀/寫文件描述符來初始化`uv_tty_t`。如下: ```c int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int readable) ``` 設置`readable`為true,意味著你打算使用`uv_read_start`從stream從中讀取數據。 最好還要使用`uv_tty_set_mode`來設置其為正常模式。也就是運行大多數的TTY格式,流控制和其他的設置。其他的模式還有[這些](http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_mode_t)。 記得當你的程序退出后,要使用`uv_tty_reset_mode`恢復終端的狀態。這才是禮貌的做法。另外要注意禮貌的地方是關心重定向。如果使用者將你的命令的輸出重定向到文件,控制序列不應該被重寫,因為這會阻礙可讀性和grep。為了保證文件描述符確實是TTY,可以使用`uv_guess_handle`函數,比較返回值是否為`UV_TTY`。 下面是一個把白字打印到紅色背景上的例子。 ####tty/main.c ```c #include <stdio.h> #include <string.h> #include <unistd.h> #include <uv.h> uv_loop_t *loop; uv_tty_t tty; int main() { loop = uv_default_loop(); uv_tty_init(loop, &tty, 1, 0); uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL); if (uv_guess_handle(1) == UV_TTY) { uv_write_t req; uv_buf_t buf; buf.base = "\033[41;37m"; buf.len = strlen(buf.base); uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL); } uv_write_t req; uv_buf_t buf; buf.base = "Hello TTY\n"; buf.len = strlen(buf.base); uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL); uv_tty_reset_mode(); return uv_run(loop, UV_RUN_DEFAULT); } ``` 最后要說的是`uv_tty_get_winsize()`,它能獲取到終端的寬和長,當成功獲取后返回0。下面這個小程序實現了一個動畫的效果。 ####tty-gravity/main.c ```c #include <stdio.h> #include <string.h> #include <unistd.h> #include <uv.h> uv_loop_t *loop; uv_tty_t tty; uv_timer_t tick; uv_write_t write_req; int width, height; int pos = 0; char *message = " Hello TTY "; void update(uv_timer_t *req) { char data[500]; uv_buf_t buf; buf.base = data; buf.len = sprintf(data, "\033[2J\033[H\033[%dB\033[%luC\033[42;37m%s", pos, (unsigned long) (width-strlen(message))/2, message); uv_write(&write_req, (uv_stream_t*) &tty, &buf, 1, NULL); pos++; if (pos > height) { uv_tty_reset_mode(); uv_timer_stop(&tick); } } int main() { loop = uv_default_loop(); uv_tty_init(loop, &tty, 1, 0); uv_tty_set_mode(&tty, 0); if (uv_tty_get_winsize(&tty, &width, &height)) { fprintf(stderr, "Could not get TTY information\n"); uv_tty_reset_mode(); return 1; } fprintf(stderr, "Width %d, height %d\n", width, height); uv_timer_init(loop, &tick); uv_timer_start(&tick, update, 200, 200); return uv_run(loop, UV_RUN_DEFAULT); } ``` escape codes的對應表如下: 代碼 | 意義 ------------ | ------------- 2 J | Clear part of the screen, 2 is entire screen H | Moves cursor to certain position, default top-left n B | Moves cursor down by n lines n C | Moves cursor right by n columns m | Obeys string of display settings, in this case green background (40+2), white text (30+7) 正如你所見,它能輸出酷炫的效果,你甚至可以發揮想象,用它來制作電子游戲。更有趣的輸出,可以使用`http://www.gnu.org/software/ncurses/ncurses.html`。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看