Libuv TCP工作原理

libuv tcp结构体之间关系

在使用TCP时,常常使用结构体uv_tcp_t和uv_stream_t。
它们之间的关系是uv_tcp_t包含uv_stream_t。

typedef struct uv_stream_s uv_stream_t;
typedef struct uv_tcp_s uv_tcp_t;

再看:

/*
 * uv_tcp_t is a subclass of uv_stream_t.
 *
 * Represents a TCP stream or TCP server.
 */
struct uv_tcp_s {
  UV_HANDLE_FIELDS
  UV_STREAM_FIELDS
  UV_TCP_PRIVATE_FIELDS
};

/*
 * uv_stream_t is a subclass of uv_handle_t.
 *
 * uv_stream is an abstract class.
 *
 * uv_stream_t is the parent class of uv_tcp_t, uv_pipe_t and uv_tty_t.
 */
struct uv_stream_s {
  UV_HANDLE_FIELDS
  UV_STREAM_FIELDS
};

从这我们可以看出,uv_stream_s仅是uv_tcp_s的前2大部分(包括UV_HANDLE_FIELDS和UV_STREAM_FIELDS)。

libuv tcp实始化事件注册

在初始化TCP时,我们经常调用uv_tcp_init函数来进行TCP的初始化.

UV_EXTERN int uv_tcp_init(uv_loop_t*, uv_tcp_t* handle);

uv_tcp_ini的内部实现正如libuv工作原理节介绍的,其主要是向注册tcp的回调事件。
这个函数的第一个参数uv_loop_t*指向需要注册的loop,一般为默认的loop,我们可以使用函数uv_default_loop()来获取。

uv_tcp_init内部实现原理

  1. uv_tcp_init内部调用uv_tcp_init_ex来实现,后者只是比前者多了一个参数AF_UNSPEC用于指定协议类型。
    为什么是AF_UNSPEC呢?注释中说的很清楚,AF_UNSPEC是用于向后兼容的,但现们使用的网络其实内部使用 address family来指定。
  2. uv_tcp_init_ex内部就比较简单了,就是初始化传递进来的uv_tcp_t结构体,并调用uv_stream_init函数将其插入loop中,即完成tcp事件的注册。事件类型为:UV_TCP
  3. uv__handle_init其实是个宏,我们可以看到是进行链表成员的插入,并插入到链表的尾部。其并没有进行在插入时进行同步的动作,所以说这个函数并不是线程安全的。

tcp的内部事件

当uv_tcp_init执行成功后(返回值为0),当系统中有tcp事件时,会调用相关的tcp回调函数。但这个tcp回调函数是那个呢?
我们知道,tcp一般分为客户端和服务端。对于服务端会有新的socket连接上,而客户端会有连接远程服务端,并且都有收发数据的功能。所以说uv_tcp_init仅仅是注册了大的tcp事件,但tcp具体的事件类型就由具体的函数来注册了。
这些函数包括:

  • uv_tcp_connect 用于客户端连接服务器结果的回调。
  • uv_listen 用于服务器端被客户端连接上的回调。
  • uv_read_start 用于接收数据
  • uv_write 用于发送数据
  • uv_close 关闭套接字

这些函数的类型分别如下:

UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb);
UV_EXTERN int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb,uv_read_cb read_cb);
UV_EXTERN int uv_write(uv_write_t* req,uv_stream_t* handle, const uv_buf_t bufs[],unsigned int nbufs,uv_write_cb cb);
UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb);
UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb);

当然我们看到这些函数都有一个共同的特点,都会传递一个参数uv_handle_tuv_stream_t类型的指针。而这个指针代表的就是一个套接字的对象,并且是同一个片内容。