您的位置 首页 > 腾讯云社区

libuv之unix域的使用---theanarkh

之前分析了unix域在libuv的基本原理。今天以一个简单的例子看一下如何使用它。本文涉及到一些网络编程的知识,不过文章不打算讲解这些,如果不了解可以先了解一下,或者留言。

void remove_sock(int sig) { uv_fs_t req; // 删除unix域对应的路径 uv_fs_unlink(loop, &req, PIPENAME, NULL); // 退出进程 exit(0); } int main() { loop = uv_default_loop(); uv_pipe_t server; uv_pipe_init(loop, &server, 0); // 注册SIGINT信号的信号处理函数是remove_sock signal(SIGINT, remove_sock); int r; // 绑定unix路径到socket if ((r = uv_pipe_bind(&server, PIPENAME))) { fprintf(stderr, "Bind error %sn", uv_err_name(r)); return 1; } /* 把unix域对应的文件文件描述符设置为listen状态。 开启监听请求的到来,连接的最大个数是128。有连接时的回调是on_new_connection */ if ((r = uv_listen((uv_stream_t*) &server, 128, on_new_connection))) { fprintf(stderr, "Listen error %sn", uv_err_name(r)); return 2; } // 启动事件循环 return uv_run(loop, UV_RUN_DEFAULT); }

对于看过之前的文章或者了解网络编程的同学来说。上面的代码看起来会比较简单。所以就不具体分析。他执行完后就是启动了一个服务。同主机的进程可以访问(连接)他。之前说过unix域的实现和tcp的实现类型。都是基于连接的模式。服务器启动等待连接,客户端去连接。然后服务器逐个摘下连接的节点进行处理。我们从处理连接的函数on_new_connection开始分析整个流程。

// 有连接到来时的回调 void on_new_connection(uv_stream_t *server, int status) { // 有连接到来,申请一个结构体表示他 uv_pipe_t *client = (uv_pipe_t*) malloc(sizeof(uv_pipe_t)); uv_pipe_init(loop, client, 0); // 把accept返回的fd记录到client,client是用于和客户端通信的结构体 if (uv_accept(server, (uv_stream_t*) client) == 0) { /* 注册读事件,等待客户端发送信息过来, alloc_buffer分配内存保存客户端的发送过来的信息, echo_read是回调 */ uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read); } else { uv_close((uv_handle_t*) client, NULL); } }

分析on_new_connection之前,我们先看一下该函数的执行时机。该函数是在uv__server_io函数中被执行,而uv__server_io是在监听的socket(即listen的那个)有可读事件时触发的回调。我们看看uv__server_io的部分逻辑。

// 有连接到来,进行accept err = uv__accept(uv__stream_fd(stream)); // 保存通信socket对应的文件描述符 stream->accepted_fd = err; /* 有连接,执行上层回调,connection_cb一般会调用uv_accept消费accepted_fd。 然后重新注册等待可读事件 */ stream->connection_cb(stream, 0);

当有连接到来时,服务器调用uv__accept摘取一个连接节点(实现上,操作系统会返回一个文件描述符,作用类似一个id)。然后把文件描述符保存到accepted_fd字段,接着执行connection_cb回调。就是我们设置的on_new_connection。

uv__stream_fd(stream)是我们启动的服务器对应的文件描述符。stream就是表示服务器的结构体。在unix域里,他实际上是一个uv_pipe_s结构体。uv_stream_s是uv_pipe_s的父类。类似c++的继承。

我们回头看一下on_new_connection的代码。主要逻辑如下。 1 申请一个uv_pipe_t结构体用于保存和客户端通信的信息。 2 执行uv_accept 3 执行uv_read_start开始等待数据的到来,然后读取数据。 我们分析一下2和3。我们看一下uv_accept的主要逻辑。

switch (client->type) { case UV_NAMED_PIPE: // 设置流的标记,保存文件描述符到流上 uv__stream_open( client,server->accepted_fd, UV_HANDLE_READABLE | UV_HANDLE_WRITABLE ); }

uv_accept中把刚才accept到的文件描述符保存到client中。这样我们后续就可以通过client和客户端通信。至于uv_read_start,之前在stream的文章中已经分析过。就不再深入分析。我们主要分析echo_read。echo_read在客户端给服务器发送信息时被触发。

void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { // 有数据,则回写 if (nread > 0) { write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t)); // 指向客户端发送过来的数据 req->buf = uv_buf_init(buf->base, nread); // 回写给客户端,echo_write是写成功后的回调 uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write); return; } // 没有数据了,关闭 if (nread < 0) { if (nread != UV_EOF) fprintf(stderr, "Read error %sn", uv_err_name(nread)); // 销毁和客户端通信的结构体,即关闭通信 uv_close((uv_handle_t*) client, NULL); } free(buf->base); }

没有数据的时候,直接销毁和客户端通信的结构体和撤销结构体对应的读写事件。我们主要分析有数据时的处理逻辑。当有数据到来时,服务器调用uv_write对数据进行回写。我们看到uv_write的第二个参数是client。即往client对应的文件描述符中写数据。也就是往客户端写。uv_write的逻辑在stream中已经分析过,所以也不打算深入分析。主要逻辑就是在client对应的stream上写入数据,缓存起来,然后等待可写时,再写到对端。写完成后执行echo_write释放数据占据的内存。这就是使用unix域通信的整个过程。unix域还有一个复杂的应用是涉及到传递文件描述符。即uv_pipe_s的ipc字段。这个后续再开一篇文章分析。

---来自腾讯云社区的---theanarkh

关于作者: 瞎采新闻

这里可以显示个人介绍!这里可以显示个人介绍!

热门文章

留言与评论(共有 0 条评论)
   
验证码: