uv_pipe_t
— 管道句柄uv_pipe_t
管道句柄类型(结构体具体内容看这里)
int uv_pipe_init
(uv_loop_t* loop, uv_pipe_t* handle, int ipc)
初始化一个管道句柄。 ipc 参数是一个布尔值指明是否管道将用于在不同进程间传递句柄
int uv_pipe_open
(uv_pipe_t* handle, uv_file file)
打开一个已存在的文件描述符或者句柄作为一个管道
int uv_pipe_bind
(uv_pipe_t* handle, const char* name)
绑定管道到一个文件路径(Unix)或者名字(Windows)
void uv_pipe_connect
(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb)
连接到Unix域套接字或者有名管道
int uv_pipe_getsockname
(const uv_pipe_t* handle, char* buffer, size_t* size)
获取Unix域套接字或者有名管道的名字
int uv_pipe_getpeername
(const uv_pipe_t* handle, char* buffer, size_t* size)
获取被句柄连接的Unix域套接字或者有名管道的名字
int main() {
loop = uv_default_loop();
uv_pipe_t server;
uv_pipe_init(loop, &server, 0);
}
int main() {
loop = uv_default_loop();
uv_pipe_t server;
uv_pipe_init(loop, &server, 0);
int r;
// 我们把socket命名为echo.sock,意味着它将会在本地文件夹中被创造。对于stream API来说,本地socekt表现得和tcp的socket差不多
if ((r = uv_pipe_bind(&server, "echo.sock"))) {
fprintf(stderr, "Bind error %s\n", uv_err_name(r));
return 1;
}
}
int main() {
loop = uv_default_loop();
uv_pipe_t server;
uv_pipe_init(loop, &server, 0);
int r;
// 我们把socket命名为echo.sock,意味着它将会在本地文件夹中被创造。对于stream API来说,本地socekt表现得和tcp的socket差不多
if ((r = uv_pipe_bind(&server, "echo.sock"))) {
fprintf(stderr, "Bind error %s\n", 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 %s\n", uv_err_name(r));
return 2;
}
// 启动事件循环
return uv_run(loop, UV_RUN_DEFAULT);
}
#include
#include
#include
#include
uv_loop_t *loop;
typedef struct {
uv_write_t req;
uv_buf_t buf;
} write_req_t;
void free_write_req(uv_write_t *req) {
write_req_t *wr = (write_req_t*) req;
free(wr->buf.base);
free(wr);
}
void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = malloc(suggested_size);
buf->len = suggested_size;
}
void echo_write(uv_write_t *req, int status) {
if (status < 0) {
fprintf(stderr, "Write error %s\n", uv_err_name(status));
}
free_write_req(req);
}
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);
uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write);
return;
}
if (nread < 0) {
if (nread != UV_EOF)
fprintf(stderr, "Read error %s\n", uv_err_name(nread));
uv_close((uv_handle_t*) client, NULL);
}
free(buf->base);
}
// 有连接到来时的回调
void on_new_connection(uv_stream_t *server, int status) {
if (status == -1) {
// error!
return;
}
// 有连接到来,申请一个结构体表示他
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);
}
}
void remove_sock(int sig) {
uv_fs_t req;
// 删除 unix 域对应的路径
uv_fs_unlink(loop, &req, "echo.sock", NULL);
// 退出进程
exit(0);
}
/*
本地socket具有确定的名称,而且是以文件系统上的位置来标示的(例如,unix中socket是文件的一种存在形式),
那么它就可以用来在不相关的进程间完成通信任务
*/
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;
// 我们把socket命名为echo.sock,意味着它将会在本地文件夹中被创造。对于stream API来说,本地socekt表现得和tcp的socket差不多
if ((r = uv_pipe_bind(&server, "echo.sock"))) {
fprintf(stderr, "Bind error %s\n", 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 %s\n", uv_err_name(r));
return 2;
}
// 启动事件循环
return uv_run(loop, UV_RUN_DEFAULT);
}
struct uv_pipe_t {
// 句柄[handle]相关参数
void* data;
uv_loop_t* loop; /* 所属事件循环 */
uv_handle_type type; /* handle 类型 */
uv_close_cb close_cb; /* 关闭 handle 时的回调 */
void* handle_queue[2]; /* 用于插入事件循环的 handle 队列 */
union {
int fd;
void* reserved[4];
} u;
uv_handle_t* next_closing; /* 用于插入事件循环的 closing 阶段对应的队列 */
unsigned int flags; /* 各种标记 */
// 流[stream]相关参数
size_t write_queue_size; /* 用户写入流的字节大小,流缓存用户的输入,然后等到可写的时候才做真正的写 */
uv_alloc_cb alloc_cb; /* 分配内存的函数,内存由用户定义,主要用来保存读取的数据 */
uv_read_cb read_cb; /* 读取完成时候执行的回调函数 */
uv_connect_t *connect_req; /* 连接成功后,执行 connect_req 的回调(connect_req 在 uv__xxx_connect 中赋值) */
uv_shutdown_t *shutdown_req; /* 关闭写端的时候,发送完缓存的数据,执行 shutdown_req 的回调(shutdown_req 在 uv_shutdown 的时候赋值) */
uv__io_t io_watcher; /* 流对应的 io 观察者,即文件描述符+一个文件描述符事件触发时执行的回调 */
void* write_queue[2]; /* 流缓存下来的,待写的数据 */
void* write_completed_queue[2]; /* 已经完成了数据写入的队列 */
uv_connection_cb connection_cb; /* 完成三次握手后,执行的回调 */
int delayed_error; /* 操作流时出错码 */
int accepted_fd; /* accept 返回的通信 socket 对应的文件描述符 */
void* queued_fds; /* 同上,用于缓存更多的通信 socket 对应的文件描述符 */
UV_STREAM_PRIVATE_PLATFORM_FIELDS /* 目前为空 */
int ipc; /* 标记管道是否能在进程间传递 */
// pipe相关参数
const char* pipe_fname; /* 用于 unix 域通信的文件路径 */
}
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
handle->shutdown_req = NULL;
handle->connect_req = NULL;
handle->pipe_fname = NULL;
handle->ipc = ipc;
return 0;
}
// name 是 unix 路径名称
int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
struct sockaddr_un saddr;
const char* pipe_fname;
int sockfd;
int err;
pipe_fname = NULL;
if (uv__stream_fd(handle) >= 0)
return UV_EINVAL;
pipe_fname = uv__strdup(name);
if (pipe_fname == NULL)
return UV_ENOMEM;
name = NULL;
// unix 域套接字
err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
if (err < 0)
goto err_socket;
sockfd = err;
memset(&saddr, 0, sizeof saddr);
uv__strscpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path));
saddr.sun_family = AF_UNIX;
// 绑定到路径,tcp 是绑定到 ip 和端口
if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {
err = UV__ERR(errno);
/* Convert ENOENT to EACCES for compatibility with Windows. */
if (err == UV_ENOENT)
err = UV_EACCES;
uv__close(sockfd);
goto err_socket;
}
// 已经绑定
handle->flags |= UV_HANDLE_BOUND;
handle->pipe_fname = pipe_fname;
// 保存 socket fd,用于后面监听
handle->io_watcher.fd = sockfd;
return 0;
err_socket:
uv__free((void*)pipe_fname);
return err;
}
int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
if (uv__stream_fd(handle) == -1)
return UV_EINVAL;
if (handle->ipc)
return UV_EINVAL;
#if defined(__MVS__) || defined(__PASE__)
if (backlog == 0)
backlog = 1;
else if (backlog < 0)
backlog = SOMAXCONN;
#endif
// uv__stream_fd(handle)得到 bind 函数中获取的 socket
if (listen(uv__stream_fd(handle), backlog))
return UV__ERR(errno);
// 保存回调,有进程调用 connect 的时候时触发,由 uv__server_io 函数触发
handle->connection_cb = cb;
// io 观察者的回调,有进程调用 connect 的时候时触发(io 观察者的 fd 在 init 函数里设置了)
// uv__server_io 中会调用用户提供的 cb
handle->io_watcher.cb = uv__server_io;
// 注册 io 观察者到 libuv,等待连接,即读事件到来
uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
return 0;
}
void uv_pipe_connect(uv_connect_t* req,
uv_pipe_t* handle,
const char* name,
uv_connect_cb cb) {
struct sockaddr_un saddr;
int new_sock;
int err;
int r;
// 判断是否已经有 socket 了,没有的话需要申请一个,见下面
// #define uv__stream_fd(handle) ((handle)->io_watcher.fd)
new_sock = (uv__stream_fd(handle) == -1);
// 客户端还没有对应的 socket fd
if (new_sock) {
err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
if (err < 0)
goto out;
handle->io_watcher.fd = err;
}
// 需要连接的服务器信息。主要是 unix 域路径信息
memset(&saddr, 0, sizeof saddr);
uv__strscpy(saddr.sun_path, name, sizeof(saddr.sun_path));
saddr.sun_family = AF_UNIX;
// 连接服务器,unix 域路径是 name
do {
r = connect(uv__stream_fd(handle),
(struct sockaddr*)&saddr, sizeof saddr);
}
while (r == -1 && errno == EINTR);
if (r == -1 && errno != EINPROGRESS) {
err = UV__ERR(errno);
#if defined(__CYGWIN__) || defined(__MSYS__)
if (err == UV_EBADF)
err = UV_ENOTSOCK;
#endif
goto out;
}
// 忽略错误处理逻辑
err = 0;
// 设置 socket 的可读写属
if (new_sock) {
err = uv__stream_open((uv_stream_t*)handle,
uv__stream_fd(handle), // #define uv__stream_fd(handle) ((handle)->io_watcher.fd)
UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
}
// 把 io 观察者注册到 libuv,等到连接成功或者可以发送请求
if (err == 0)
uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
out:
// 记录错误码,如果有的话
handle->delayed_error = err;
// 连接成功时的回调
handle->connect_req = req;
uv__req_init(handle->loop, req, UV_CONNECT);
req->handle = (uv_stream_t*)handle;
req->cb = cb;
QUEUE_INIT(&req->queue);
// 如果连接出错,在 pending 节点会执行 req 对应的回调。错误码是 delayed_error
if (err)
uv__io_feed(handle->loop, &handle->io_watcher);
}