libuv - 监视器watchers

来源:互联网 发布:数据控制语言 编辑:程序博客网 时间:2024/06/06 03:00

libuv是一个高性能事件驱动库,是node.js 的底层实现。libuv严格使用异步事件驱动的编程风格。其核心工作是提供事件循环及基于 I/O或其他活动事件的回调机制。libuv库包含了诸如计时器、非阻塞网络支持、异步文件系统访问、子进程等核心工具。在事件驱动程序设计中,应用程序关注特定事件并在它们发生时作出响应。libuv负责从操作系统那里收集事件或监视其他资源的事件,而用户可以注册在某个事件发生时要调用的回调函数。事件循环通常会不间断运行。

监视器是libuv用户用于监视特定事件的工具。即,使用libuv的应用通过特定的监视器watcher让libuv通知应用那些应用想要知道发生的事件。大致的过程是,应用创建监视器,在监视器上注册事件及其回调函数,当已注册的事件发生时libuv触发注册的回调函数。


监视器类型

我们从uv.h头文件中任意选取一个监视器定义一探究竟。就挑常在例程中出现的uv_tcp_t开始。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px">typedef struct uv_tcp_s uv_tcp_t;</span>  

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px">/* 
  2.  * uv_tcp_t is a subclass of uv_stream_t 
  3.  * 
  4.  * Represents a TCP stream or TCP server. 
  5.  */  
  6. struct uv_tcp_s {  
  7.   UV_HANDLE_FIELDS  
  8.   UV_STREAM_FIELDS  
  9.   UV_TCP_PRIVATE_FIELDS  
  10. };</span>  

uv_tcp_t只是一个别名,指向的是uv_tcp_s结构体。注释中指出uv_tcp_s是uv_stream_t的子类。我们明显看到这就是一个简单的结构体定义,未继承自任何其他的类或结构体。不知注释中的“子类”一说如何解释。既然提到了uv_stream_t,我们也看看它的定义。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px">typedef struct uv_stream_s uv_stream_t;</span>  
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px">/* 
  2.  * uv_stream_t is a subclass of uv_handle_t 
  3.  * 
  4.  * uv_stream is an abstract class. 
  5.  * 
  6.  * uv_stream_t is the parent class of uv_tcp_t, uv_pipe_t, uv_tty_t, and 
  7.  * soon uv_file_t. 
  8.  */  
  9. struct uv_stream_s {  
  10.   UV_HANDLE_FIELDS  
  11.   UV_STREAM_FIELDS  
  12. };</span>  
果不其然,uv_stream_t是uv_stream_s结构体的别名。注释中指出,uv_stream_s是uv_handle_t的子类,是个抽象类。同时还指出,uv_stream_s是uv_tcp_t、uv_pipe_t、uv_tty_t和uv_file_t的父类。这里同样使用了“子类”,但依然看不到任何继承声明。

接着看uv_handle_t。是uv_handle_s结构体的别名。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px">typedef struct uv_handle_s uv_handle_t;</span>  
uv_handle_s结构体定义如下。注释中说明这是所有句柄的抽象基类。到现在为止,我们还不确定这里的“所有句柄”是不是就是指所有的监视器。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px">/* The abstract base class of all handles.  */  
  2. struct uv_handle_s {  
  3.   UV_HANDLE_FIELDS  
  4. };</span>  

通过查看uv_tcp_t结构及分析相关联结构,从注释中发现libuv中的子类父类概念与C++面向对象思想中的继承关系不一致。通过分析uv_handle_s和uv_stream_s两个类可以了解到libuv中使用的继承概念。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px">/* The abstract base class of all handles.  */  
  2. struct uv_handle_s {  
  3.   UV_HANDLE_FIELDS  
  4. };</span>  
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px">/* 
  2.  * uv_stream_t is a subclass of uv_handle_t 
  3.  * 
  4.  * uv_stream is an abstract class. 
  5.  * 
  6.  * uv_stream_t is the parent class of uv_tcp_t, uv_pipe_t, uv_tty_t, and 
  7.  * soon uv_file_t. 
  8.  */  
  9. struct uv_stream_s {  
  10.   UV_HANDLE_FIELDS  
  11.   UV_STREAM_FIELDS  
  12. };</span>  

注释中已经说明了,uv_stream_s是uv_handle_s的子类。二者声明的重要不同在于内部成员的不同。uv_stream_s结构体只比uv_handle_s结构体多了UV_STREAM_FIELDS。他们都有共同的部分:UV_HANDLE_FIELDS。从C结构体的内存布局来看,uv_handle_s和uv_streams_s在结构体的前部是完全相同的。现在可以猜测libuv中的继承概念不同于面向对象中的继承概念。libuv并不通过编程语言自身提供的继承机制来达到继承目的。而是通过C语言结构体内存布局的特性来达到类似的目的。通过以上分析,我们猜测uv_tcp_s既然是uv_stream_s的子类。按照libuv的继承概念,uv_tcp_s的内部布局应该比uv_stream_s在尾部多了点内容。查看uv_tcp_s的声明确实如此。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px">struct uv_tcp_s {  
  2.   UV_HANDLE_FIELDS  
  3.   UV_STREAM_FIELDS  
  4.   UV_TCP_PRIVATE_FIELDS  
  5. };</span>  

在浏览uv.h头文件时发现了这样一块内容:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* Handle types. */  
  2. typedef struct uv_loop_s uv_loop_t;  
  3. typedef struct uv_handle_s uv_handle_t;  
  4. typedef struct uv_stream_s uv_stream_t;  
  5. typedef struct uv_tcp_s uv_tcp_t;  
  6. typedef struct uv_udp_s uv_udp_t;  
  7. typedef struct uv_pipe_s uv_pipe_t;  
  8. typedef struct uv_tty_s uv_tty_t;  
  9. typedef struct uv_poll_s uv_poll_t;  
  10. typedef struct uv_timer_s uv_timer_t;  
  11. typedef struct uv_prepare_s uv_prepare_t;  
  12. typedef struct uv_check_s uv_check_t;  
  13. typedef struct uv_idle_s uv_idle_t;  
  14. typedef struct uv_async_s uv_async_t;  
  15. typedef struct uv_process_s uv_process_t;  
  16. typedef struct uv_fs_event_s uv_fs_event_t;  
  17. typedef struct uv_fs_poll_s uv_fs_poll_t;  
  18. typedef struct uv_signal_s uv_signal_t;  
如果用libuv写过一些例子的话,应该知道这里的大部分uv_TYPE_t都是监视器类型。我挑选了uv_loop_t类型看看它是否也是uv_handle_t的“子类”。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. struct uv_loop_s {  
  2.   /* User data - use this for whatever. */  
  3.   void* data;  
  4.   /* Loop reference counting */  
  5.   unsigned int active_handles;  
  6.   void* handle_queue[2];  
  7.   void* active_reqs[2];  
  8.   /* Internal flag to signal loop stop */  
  9.   unsigned int stop_flag;  
  10.   UV_LOOP_PRIVATE_FIELDS  
  11. };  
从uv_loop_s的声明可以看出,它应该不是uv_handle_t的子类。只能说是看上去,因为uv_handle_s的声明中是一个宏:UV_HANDLE_FIELDS。宏UV_HANDLE_FIELDS的定义如下:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #define UV_HANDLE_FIELDS                                                      \  
  2.   /* public */                                                                \  
  3.   uv_close_cb close_cb;                                                       \  
  4.   void* data;                                                                 \  
  5.   /* read-only */                                                             \  
  6.   uv_loop_t* loop;                                                            \  
  7.   uv_handle_type type;                                                        \  
  8.   /* private */                                                               \  
  9.   void* handle_queue[2];                                                      \  
  10.   UV_HANDLE_PRIVATE_FIELDS                                                    \  
显然,二者是不同的。但还不能说,uv_loop_s就不是一个监视器。除了这个uv_loop_s外,其他所有的uv_TYPE_s的声明我都检查了一遍,发现其他所有uv_TYPE_s都“继承”自uv_handle_s,也就是说其他所有的uv_TYPE_s结构体与uv_handle_s结构体在内存区域的前部有着相同的结构。如果是这样,那么除了uv_loop_s外,所有其它的uv_TYPE_s都是监视器类型。我想这里应该就是所有监视器类型了,否则不用放在一块。

通过查看代码,我们发现所有的监视器类型都“继承”(libuv的说法)自uv_handle_s结构体。“继承”的意思是指它们有着相同的头部内存区块。我的疑问是,为何libuv不使用C++语言的继承机制来达成这个目的。是因为libuv希望也能在C语言环境下使用?还是C++语言的对象机制造成的对象内存布局无法满足libuv对于内存布局的要求?我想前者的可能性更大些,为了在更多的环境下被使用。


使用监视器

监视器使用前必须被初始化。一般情况下,libuv库为这些初始化函数取了相似的名称,均为uv_TYPE_init。监视器被初始化后必须调用uv_TYPE_start函数用来启动监视器。当不再需要监视器继续运作时需调用uv_TYPE_stop函数。符合这种使用模式的监视器只占半数。有这么一些:uv_timer_t、uv_prepare_t、uv_check_t、uv_idle_t、uv_fs_event_t、uv_fs_poll_t和uv_signal_t。几乎所有的非基类或非父类监视器都有一个形如uv_TYPE_init的初始化函数。只有一个监视器没有那就是uv_process_t。

与uv_process_t相关的函数有:uv_spawnuv_process_kill。uv_spawn的作用相当于uv_TYPE_init和uv_TYPE_start。uv_process_kill的作用类似于uv_TYPE_stop。有一点必须提示的是,除了调用uv_process_kill函数外关闭uv_process_t还必须再调用uv_close函数。这是libuv库针对uv_process_t的特殊处理。如果不调用将会造成何种后果不得而知,因为uv.h头文件的注释中并未明确给出相关信息。


uv_tcp_t

uv_tcp_t相关的函数有:uv_tcp_init、uv_listen、uv_accept、uv_close、uv_tcp_open、uv_tcp_nodelay、uv_tcp_keepalive、uv_tcp_simultaneous_acceptsuv_tcp_bind、uv_write、uv_read_start、uv_tcp_getsockname、uv_tcp_getpeernameuv_tcp_connect

服务端。首先得使用uv_tcp_init初始化监视器。接着使用uv_tcp_bind将监视器绑定到特定的地址和端口。使用uv_tcp_bind前可以使用uv_ip4_addr函数生成uv_tcp_bind可以使用的参数变量。然后是使用uv_listen启动监视器,并提供回调函数。uv_listen函数内需要的回调函数类型是uv_connection_cb。uv_connection_cb回调函数将在有客户端连接服务端时被调用。在uv_connection_cb回调函数内我们将再次依次使用uv_tcp_init和uv_accept,用来为此客户端连接新建一个监视器,以及接受这个连接请求。接着可以在任何时候使用uv_write向对端发送异步写请求,同时提供回调函数。回调函数类型为uv_write_cb。使用uv_write函数时,还将牵扯到这两个结构体:uv_buf_t和uv_write_t。关闭客户端连接可以使用uv_close函数。这个函数也需提供一个回调函数,类型为uv_close_cb。uv_close函数还需一个连接句柄,此句柄就是为客户端连接创建的监视器,此时可使用uv_write_t结构体的handle属性。此handle值无需在创建uv_write_t变量时显示赋值。在使用uv_write后,libuv会在内部为uv_write_t变量的handle属性赋上正确的值。这个值即为连接上的客户端的uv_tcp_t值。我们会在接受客户端连接回调函数uv_connection_cb内创建的这个表示客户端的uv_tcp_t变量。由于这个表示客户端的uv_tcp_t变量是由客户代码显示创建的,所以也必须由客户代码显示删除它。删除这个变量的时机就在关闭客户端连接回调函数内。除了向对端发送数据外,还需收取从对端发送来的数据。此时用到的函数不再是uv_tcp开头的函数,而是使用uv_read_start。需要的输入参数为,连接上的客户端uv_tcp_t变量值,读取数据前分配缓冲区的回调函数,以及实际读取操作的回调函数。

下面看一个示例:

init_net()是初始化函数。只有read_data函数内有特定应用相关的内容。Gb1212ToUTF_8函数未列出。大致的过程是:监听TCP端口35755,接受其他客户端连接请求并等待接收客户端发来的数据,处理客户端的请求并向其发送相关数据,数据发送完毕后关闭与此客户端的连接。这份代码里唯一不确定的是after_uv_shutdown函数内是否还有必要调用uv_close。我尝试过即便after_uv_shutdown函数内不调用uv_close代码也能正常执行,加上这句代码也能正常执行。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void after_uv_close(uv_handle_t * handle)  
  2. {  
  3. <span style="white-space:pre">  </span>free(handle);  
  4. }  
  5.   
  6. void after_uv_shutdown(uv_shutdown_t* req, int status)  
  7. {  
  8. <span style="white-space:pre">  </span>uv_close((uv_handle_t *)req->handle, after_uv_close);  
  9. <span style="white-space:pre">  </span>free(req);  
  10. }  
  11.   
  12. void write_data(uv_write_t * req, int status)  
  13. {  
  14.     if ( status == -1 )  
  15.     {  
  16.         return;  
  17.     }  
  18.     char * base = (char *)req->data;  
  19.     free(base);  
  20.     uv_shutdown_t * shutdownReq = new uv_shutdown_t;  
  21.     uv_shutdown(shutdownReq, req->handle, after_uv_shutdown);  
  22.     free(req);  
  23. }  
  24.   
  25. uv_buf_t alloc_before_read(uv_handle_t* handle, size_t suggested_size)  
  26. {  
  27.     return uv_buf_init((char*)malloc(suggested_size), suggested_size);  
  28. }  
  29.   
  30. void read_data(uv_stream_t* stream, ssize_t nread, uv_buf_t buf)  
  31. {  
  32.     if ( nread < 0 )  
  33.     {  
  34.         delete buf.base;  
  35.         uv_close((uv_handle_t *)stream, after_uv_close);  
  36.         return;  
  37.     }  
  38.     buf.base[nread] = 0;  
  39.   
  40.     std::string::size_type pos = g_recvdata.find("/realtimeinfo/ext.html");  
  41.     if ( pos != std::string::npos )  
  42.     {  
  43.         std::string sendText1 = "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><RealtimeInfo><Extensions>" + pangu.m_TSAPIRealTimeStatusString.AssembleAllExtensionStatusString() + "</Extensions></RealtimeInfo>";  
  44.         uv_write_t * req = (uv_write_t *)malloc(sizeof(uv_write_t));  
  45.         strcpy((char *)buf.base, "HTTP/1.0 200 Okay\r\nContent-Type:text/xml\r\n\r\n");  
  46.         req->data = (void*)buf.base;  
  47.         buf.len = strlen((char *)buf.base);  
  48.         uv_write(req, stream, &buf, 1, write_data);  
  49.   
  50.         uv_buf_t buf1 = alloc_before_read((uv_handle_t *)stream, 65536);  
  51.         char * utf8Buf = Gb2312ToUTF_8(sendText1.c_str());  
  52.         strcpy((char *)buf1.base, utf8Buf);  
  53.         delete []utf8Buf;  
  54.         uv_write_t * req1 = (uv_write_t *)malloc(sizeof(uv_write_t));  
  55.         req1->data = (void*)buf1.base;  
  56.         buf1.len = sendText1.length();  
  57.         uv_write(req1, stream, &buf1, 1, write_data);  
  58.         LOG4CXX_INFO(g_zhougongLog, sendText1.c_str());  
  59.     }  
  60.     else if ( ( pos = g_recvdata.find("/realtimeinfo/grp.html") ) != std::string::npos )  
  61.     {  
  62.         std::string sendText1 = "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><RealtimeInfo><Groups>" + pangu.m_TSAPIRealTimeStatusString.AssembleAllGroupStatusString() + "</Groups></RealtimeInfo>";  
  63.         uv_write_t * req = (uv_write_t *)malloc(sizeof(uv_write_t));  
  64.         strcpy((char *)buf.base, "HTTP/1.0 200 Okay\r\nContent-Type:text/xml\r\n\r\n");  
  65.         req->data = (void*)buf.base;  
  66.         buf.len = strlen((char *)buf.base);  
  67.         uv_write(req, stream, &buf, 1, write_data);  
  68.   
  69.         uv_buf_t buf1 = alloc_before_read((uv_handle_t *)stream, 65536);  
  70.         char * utf8Buf = Gb2312ToUTF_8(sendText1.c_str());  
  71.         strcpy((char *)buf1.base, utf8Buf);  
  72.         delete []utf8Buf;  
  73.         uv_write_t * req1 = (uv_write_t *)malloc(sizeof(uv_write_t));  
  74.         req1->data = (void*)buf1.base;  
  75.         buf1.len = sendText1.length();  
  76.         uv_write(req1, stream, &buf1, 1, write_data);  
  77.         LOG4CXX_INFO(g_zhougongLog, sendText1.c_str());  
  78.     }  
  79.     else if ( ( pos = g_recvdata.find("/realtimeinfo/vdnqueue.html") ) != std::string::npos )  
  80.     {  
  81.         std::string sendText1 = "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><RealtimeInfo><VDNQueues>" + pangu.m_TSAPIRealTimeStatusString.AssembleAllVDNStatusString() + "</VDNQueues></RealtimeInfo>";  
  82.         uv_write_t * req = (uv_write_t *)malloc(sizeof(uv_write_t));  
  83.         strcpy((char *)buf.base, "HTTP/1.0 200 Okay\r\nContent-Type:text/xml\r\n\r\n");  
  84.         req->data = (void*)buf.base;  
  85.         buf.len = strlen((char *)buf.base);  
  86.         uv_write(req, stream, &buf, 1, write_data);  
  87.   
  88.         uv_buf_t buf1 = alloc_before_read((uv_handle_t *)stream, 65536);  
  89.         char * utf8Buf = Gb2312ToUTF_8(sendText1.c_str());  
  90.         strcpy((char *)buf1.base, utf8Buf);  
  91.         delete []utf8Buf;  
  92.         uv_write_t * req1 = (uv_write_t *)malloc(sizeof(uv_write_t));  
  93.         req1->data = (void*)buf1.base;  
  94.         buf1.len = sendText1.length();  
  95.         uv_write(req1, stream, &buf1, 1, write_data);  
  96.         LOG4CXX_INFO(g_zhougongLog, sendText1.c_str());  
  97.     }  
  98.     else if ( ( pos = g_recvdata.find("/realtimeinfo/livecallsdetail.html") ) != std::string::npos )  
  99.     {  
  100.         std::string sendText1 = "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><RealtimeInfo><LiveCallsDetail>" + pangu.GetAllLiveCalls() + "</LiveCallsDetail></RealtimeInfo>";  
  101.         uv_write_t * req = (uv_write_t *)malloc(sizeof(uv_write_t));  
  102.         strcpy((char *)buf.base, "HTTP/1.0 200 Okay\r\nContent-Type:text/xml\r\n\r\n");  
  103.         req->data = (void*)buf.base;  
  104.         buf.len = strlen((char *)buf.base);  
  105.         uv_write(req, stream, &buf, 1, write_data);  
  106.   
  107.         uv_buf_t buf1 = alloc_before_read((uv_handle_t *)stream, 65536);  
  108.         char * utf8Buf = Gb2312ToUTF_8(sendText1.c_str());  
  109.         strcpy((char *)buf1.base, utf8Buf);  
  110.         uv_write_t * req1 = (uv_write_t *)malloc(sizeof(uv_write_t));  
  111.         req1->data = (void*)buf1.base;  
  112.         buf1.len = sendText1.length();  
  113.         uv_write(req1, stream, &buf1, 1, write_data);  
  114.         delete []utf8Buf;  
  115.         LOG4CXX_INFO(g_zhougongLog, sendText1.c_str());  
  116.     }  
  117.     else if ( ( pos = g_recvdata.find("/realtimeinfo/synctime.html") ) != std::string::npos )  
  118.     {  
  119.         std::string sendText1 = "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><RealtimeInfo><SyncTime>" + ConvertCurrentDateTime() + "</SyncTime></RealtimeInfo>";  
  120.         uv_write_t * req = (uv_write_t *)malloc(sizeof(uv_write_t));  
  121.         strcpy((char *)buf.base, "HTTP/1.0 200 Okay\r\nContent-Type:text/xml\r\n\r\n");  
  122.         req->data = (void*)buf.base;  
  123.         buf.len = strlen((char *)buf.base);  
  124.         uv_write(req, stream, &buf, 1, write_data);  
  125.   
  126.         uv_buf_t buf1 = alloc_before_read((uv_handle_t *)stream, 65536);  
  127.         char * utf8Buf = Gb2312ToUTF_8(sendText1.c_str());  
  128.         strcpy((char *)buf1.base, utf8Buf);  
  129.         uv_write_t * req1 = (uv_write_t *)malloc(sizeof(uv_write_t));  
  130.         req1->data = (void*)buf1.base;  
  131.         buf1.len = sendText1.length();  
  132.         uv_write(req1, stream, &buf1, 1, write_data);  
  133.         delete []utf8Buf;  
  134.         LOG4CXX_INFO(g_zhougongLog, sendText1.c_str());  
  135.     }  
  136.     else  
  137.         free(buf.base);  
  138. }  
  139.   
  140. void on_new_connection(uv_stream_t * server, int status)  
  141. {  
  142.     if ( status == 0 )  
  143.     {  
  144.         uv_tcp_t * client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));  
  145.         uv_tcp_init(loop, client);  
  146.         if ( uv_accept(server, (uv_stream_t *)client) == 0 )  
  147.             uv_read_start((uv_stream_t*)client, alloc_before_read, read_data);  
  148.         else  
  149.             uv_close((uv_handle_t *)client, NULL);  
  150.     }  
  151. }  
  152.   
  153. void init_net()  
  154. {  
  155.     struct sockaddr_in bind_addr = uv_ip4_addr("0.0.0.0", 35755);  
  156.     uv_tcp_init(loop, &server);  
  157.     uv_tcp_bind(&server, bind_addr);  
  158.     int ret = uv_listen((uv_stream_t *)&server, 128, on_new_connection);  
  159.     if ( ret )  
  160.     {  
  161.         g_bInitNet = false;  
  162.         return;  
  163.     }  
  164.     g_bInitNet = true;  
  165. }  


客户端。首先也得使用uv_tcp_init初始化监视器。接着使用uv_tcp_connect函数连接服务端。uv_tcp_connect函数需要一个uv_connect_t类型的请求对象、一个sockaddr_in地址结构变量和uv_connect_cb类型的回调函数。地址结构变量可用uv_ip4_addr函数生成。在uv_connect_cb类型的回调函数内可启动读取或写入操作。关闭与服务端的连接,也就是调用uv_close关闭监视器。无论是服务端还是客户端都有很多需要注意的地方,稍后在其它文章中详细说明。

uv_udp_t相关的函数有:uv_udp_inituv_udp_bind、uv_udp_getsockname、uv_udp_set_membership、uv_udp_set_multicast_loop、uv_udp_set_multicast_ttl、uv_udp_set_broadcast、uv_udp_set_ttl、uv_udp_send、uv_udp_recv_startuv_udp_recv_stop

uv_pipe_t相关的函数有:uv_pipe_inituv_pipe_open、uv_pipe_bind、uv_pipe_connectuv_pipe_pending_instances

与uv_pipe_t相关的函数有:uv_pipe_init、uv_pipe_open、uv_pipe_bind、uv_pipe_connect和uv_pipe_pending_instances。

与uv_tty_t相关的函数有:uv_tty_init、uv_tty_set_mode、uv_tty_reset_mode和uv_tty_get_winsize。

uv_pool_t相关的函数有:uv_pool_init、uv_pool_init_socket、uv_pool_start和uv_pool_stop。

uv_async_t相关的函数有:uv_async_init和uv_async_send。

0 0