文章系列(部分):
Networking
libuv 中的网络与直接使用 BSD 套接字接口没有太大区别,都是非阻塞的,但概念保持不变。 此外,libuv 提供实用功能来抽象烦人的、重复的和低级任务,例如使用 BSD 套接字结构体来设置sockets、DNS 查找和调整各种套接字参数等。
uv_tcp_t 和 uv_udp_t 结构用于网络 I/O。
注意:本章中的代码示例用于展示某些 libuv API。 它们不是优质代码的示例。 它们会泄漏内存且并不总是正确关闭连接。
TCP
TCP 是一种面向连接的流协议,因此基于 libuv streams基础设施。
server
服务器套接字通过以下方式进行:
- uv_tcp_init 初始化TCP 句柄
- uv_tcp_bind 绑定
- 在句柄上调用uv_listen, 并在客户端请求建立连接时调用回调函数.
- 使用uv_accept接收连接(在回调函数中执行)
- 使用stream operations与客户端进行交互
tcp-echo-server/main.c – The listen socket
uv_close((uv_handle_t*) client, on_close);
}
}
int main() {
loop = uv_default_loop();
uv_tcp_t server;
uv_tcp_init(loop, &server);
uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);
uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection);
if (r) {
fprintf(stderr, "Listen error %s\n", uv_strerror(r));
return 1;
}
return uv_run(loop, UV_RUN_DEFAULT);
}
uv_ip4_addr将我们熟悉的Ipv4的地址和端口号,转化为BSD套接字复杂的sockaddr_in结构. 当然, 也可以使用uv_ip4_name得到进行相反的操作.
注意:uv_ip6_* 的操作与ipv4对应的API是类似的.
上述代码大都是同步的, 因为都是CPU任务, 只有uv_listen采用了回调的方式, 其第二个参数是其实就是listen的backlog参数, 详情可以看listen(2).
其实, listen本身并不是一个阻塞的函数, listen是可以直接返回的, 真正的阻塞函数其实是accept, 也就是说, 如果当前没有连接要建立, 那么listen是不会阻塞, 而accept会阻塞!
listen的回调函数中, 我们需要调用accept, 创建连接(即为client创建socket), 并监听这个连接(监听这个socket), 当连接有数据到达时, 触发回调函数, 来读取数据, 这个过程其实就是标准的使用epoll进行网络IO的处理, 和我在Pistache源码中HTTP请求的处理过程是完全一致的.
tcp-echo-server/main.c – Accepting the client
free(buf->base);
}
void on_new_connection(uv_stream_t *server, int status) {
if (status < 0) {
fprintf(stderr, "New connection error %s\n", uv_strerror(status));
// error!
return;
}
uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
uv_tcp_init(loop, client);
if (uv_accept(server, (uv_stream_t*) client) == 0) {
uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
}
其余的函数集与流示例非常相似,可以在代码中找到。 只要记住在不需要套接字时调用 uv_close 即可。 如果您对接受连接不感兴趣,这甚至可以在 uv_listen 回调中完成。
…..(未完)
Pingback:libuv : User guide » Processes – Kingdo Station
Pingback:libuv : User guide » Utilities – Kingdo Station