uv_pipe_t — Pipe handle
Pipe handles provide an abstraction over local domain sockets on Unix and named pipes on Windows.
libuv中的uv_pipe起到的是unix like系統中unix域socket以及windows中命名管道的抽象封裝,也就意味着我們可以使用這一工具簡單的實現進程間通訊(IPC)。
主要有以下幾個API:
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc)
初始化一個pipe handle,ipc指示了該管道是否會用來進行進程間handle傳輸(這里應該指的是進程間文件描述符傳遞吧?Unix域socket之間的)。
int uv_pipe_open(uv_pipe_t* handle, uv_file file)
打開現存的文件描述符,會被設置成非阻塞模式
int uv_pipe_bind(uv_pipe_t* handle, const char* name)
綁定到路徑(名字)
void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb)
連接(connect)到指定pipe,並調用回調cb
int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size)
獲得指定pipe綁定的sockname
int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size)
獲得pipe連接的sockname(服務器端)
uv_tty_t — TTY handle
TTY handles represent a stream for the console.
TTY抽象綁定了指定的終端流,uv_tty_t同樣是uv_stream_t的子類
主要API有:
int uv_tty_init(uv_loop_t* loop, uv_tty_t* handle, uv_file fd, int readable)
tty handle初始化,fd的常用值為
• 0 = stdin
• 1 = stdout
• 2 = stderr
int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode)
改變tty模式
其他操作和普通stream差不多。
簡單的unix域回射服務器/客戶斷例子
libuv的官方源碼給出了一個基於uv_pipe的echo服務器端程序,我自己寫了一個基於tty和pipe的回射客戶端,下面來看源碼:
pipe-echo-server.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <uv.h> 5 6 uv_loop_t *loop; 7 8 typedef struct { 9 uv_write_t req; 10 uv_buf_t buf; 11 } write_req_t; 12 13 void free_write_req(uv_write_t *req) { 14 write_req_t *wr = (write_req_t*) req; 15 free(wr->buf.base); 16 free(wr); 17 } 18 19 void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {//用於讀數據時的緩沖區分配 20 buf->base = (char*)malloc(suggested_size); 21 buf->len = suggested_size; 22 } 23 24 void echo_write(uv_write_t *req, int status) { 25 if (status < 0) { 26 fprintf(stderr, "Write error %s\n", uv_err_name(status)); 27 } 28 free_write_req(req); 29 } 30 31 void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { 32 if (nread > 0) { 33 write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t)); 34 req->buf = uv_buf_init(buf->base, nread);//這是個構造函數,這里僅僅是淺拷貝!指針只是進行了賦值而已!!! 35 uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write); 36 return; 37 } 38 39 if (nread < 0) { 40 if (nread != UV_EOF) 41 fprintf(stderr, "Read error %s\n", uv_err_name(nread)); 42 uv_close((uv_handle_t*) client, NULL); 43 } 44 45 free(buf->base); 46 } 47 48 void on_new_connection(uv_stream_t *server, int status) { 49 if (status == -1) { 50 // error! 51 return; 52 } 53 54 uv_pipe_t *client = (uv_pipe_t*) malloc(sizeof(uv_pipe_t)); 55 uv_pipe_init(loop, client, 0); 56 if (uv_accept(server, (uv_stream_t*) client) == 0) {//accept成功之后開始讀 57 uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);//開始從pipe讀(在loop中注冊讀事件),讀完回調 58 } 59 else { 60 uv_close((uv_handle_t*) client, NULL);//出錯關閉 61 } 62 } 63 64 void remove_sock(int sig) { 65 uv_fs_t req; 66 uv_fs_unlink(loop, &req, "echo.sock", NULL);//刪除操作 67 exit(0); 68 } 69 70 int main() { 71 loop = uv_default_loop(); 72 73 uv_pipe_t server; 74 uv_pipe_init(loop, &server, 0); 75 76 signal(SIGINT, remove_sock);//收到結束信號順便刪除相關pipe文件 77 78 int r; 79 if ((r = uv_pipe_bind(&server, "echo.sock"))) {//服務器端綁定unix域地址 80 fprintf(stderr, "Bind error %s\n", uv_err_name(r)); 81 return 1; 82 } 83 if ((r = uv_listen((uv_stream_t*) &server, 128, on_new_connection))) {//開始連接,出現回調,enent loop由此開始 84 fprintf(stderr, "Listen error %s\n", uv_err_name(r)); 85 return 2; 86 } 87 return uv_run(loop, UV_RUN_DEFAULT); 88 }
接下來是客戶端代碼:
pipe-echo-client.c(自己寫的,僅供參考,很多異常處理代碼都省略了)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <uv.h> 5 6 uv_loop_t * loop; 7 uv_tty_t tty_stdin,tty_stdout; 8 uv_pipe_t server; 9 10 typedef struct {//該結構存在的意義就是向回調函數傳遞緩沖區地址,並及時釋放 11 uv_write_t req; 12 uv_buf_t buf; 13 } write_req_t; 14 15 void free_write_req(uv_write_t *req) { 16 write_req_t *wr = (write_req_t*) req; 17 free(wr->buf.base); 18 free(wr); 19 } 20 21 void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 22 buf->base = (char*)malloc(suggested_size); 23 buf->len = suggested_size; 24 } 25 26 void write_to_stdout_cb(uv_write_t* req, int status){ 27 if(status){ 28 fprintf(stderr, "Write error %s\n", uv_strerror(status)); 29 exit(0); 30 } 31 free_write_req(req); 32 } 33 34 void read_from_pipe_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf){ 35 write_req_t *wri = (write_req_t *)malloc(sizeof(write_req_t)); 36 wri->buf = uv_buf_init(buf->base,nread); 37 uv_write((uv_write_t*)wri,(uv_stream_t*)&tty_stdout,&wri->buf,1,write_to_stdout_cb); 38 } 39 40 void write_to_pipe_cb(uv_write_t* req, int status){ 41 if(status){ 42 fprintf(stderr, "Write error %s\n", uv_strerror(status)); 43 exit(0); 44 } 45 uv_read_start((uv_stream_t*)&server,alloc_buffer,read_from_pipe_cb);//再一次構造緩沖區 46 free_write_req(req);//釋放動態分配的所有數據 47 } 48 49 void read_from_input_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf){ 50 write_req_t *wri = (write_req_t *)malloc(sizeof(write_req_t));//實例化新結構,主要2個作用:產生write所需的uv_write_t;臨時存儲buf(同時提供給回調函數析構方法)便於該數據的及時free 51 wri->buf=uv_buf_init(buf->base, nread);//buf復制 52 uv_write((uv_write_t*)wri,(uv_stream_t*)&server,&wri->buf,1,write_to_pipe_cb);//需要注意的是write調用的時候&wri->buf必須依然有效!所以這里直接用buf會出現問題! 53 //write完成之后當前緩沖區也就失去了意義,於是向回調函數傳遞buf指針,並由回調函數負責析構該緩沖區 54 } 55 56 int main() { 57 loop = uv_default_loop(); 58 59 uv_pipe_init(loop, &server, 1); 60 uv_tty_init(loop,&tty_stdin,0,1); 61 uv_tty_init(loop,&tty_stdout,1,0); 62 63 uv_connect_t conn; 64 uv_pipe_connect((uv_connect_t*)&conn,&server,"echo.sock",NULL);//連接pipe 65 uv_read_start((uv_stream_t*)&tty_stdin,alloc_buffer,read_from_input_cb);//從stdin讀數據,並由此觸發回調,每次alloc_buffer回調產生一個緩沖數據結構uv_buf_t並在堆上分配數據! 66 67 return uv_run(loop, UV_RUN_DEFAULT); 68 }
一些注釋已經寫在代碼旁邊了,特別需要注意的就是回調函數的調用時機與非阻塞調用的流程,以及buffer相關的操作。。。坑挺多的,也只有一步一步踩過才能真正算懂。