libuv 异步网络编程之 TCP helloworld
发布于: 2020 年 08 月 10 日
指引
学习 libuv 官网文档 http://docs.libuv.org/en/v1.x/guide/networking.html 网络编程部分。
Libuv 的网络编程跟 BSD socket 接口没什么区别,保持了同样概念的情况下,所有的操作都是异步的。相比 BSD 比较原始的操作,libuv 提供了 DNS 查询、封装 socket 参数等实用函数。
libuv 版本:1.38.1
TCP 服务端
服务端程序的常规操作:
初始化地址
bind 该地址
开始 listen
accept 新连接
不过此处 accept 新连接是在回调函数 on_new_connection
里。
之后才能对新连接 client
进行读取数据/写入数据等操作。
TCP 客户端
客户端程序连接服务端的常规操作:
初始化地址
连接服务端
连接服务端成功后,需要在回调函数 on_connected
里去发送数据。
结果示例:
完整代码
https://github.com/Asphaltt/libuv-examples/tree/master/tcp-helloworld
server.c
例子里没有接收客户端的数据,接收数据的操作参考客户端
#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <uv.h>static uv_loop_t *loop;static void on_close(uv_handle_t* handle) { free(handle);}static void on_response_callback(uv_write_t* req, int status) { if (status < 0) { fprintf(stderr, "failed to response world to client, err: %s\n", uv_strerror(status)); } else { printf("succeeded in responsing world to client\n"); return; } fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); if (status == UV_ECANCELED) return; assert(status == UV_EPIPE); uv_close((uv_handle_t*)req->handle, on_close); free(req);}void on_new_connection(uv_stream_t *conn, int status) { int r; uv_write_t wr; if (status < 0) { fprintf(stderr, "failed to create new connection, err: %s\n", uv_strerror(status)); return; } uv_tcp_t *client = malloc(sizeof(uv_tcp_t)); uv_tcp_init(loop, client); if ((r = uv_accept(conn, (uv_stream_t *)client)) != 0) { printf("failed to accept a new connection, err: %s\n", uv_strerror(r)); return; } uv_buf_t b[] = { {.base = "world", .len = 5} }; if ((r = uv_write(&wr, (uv_stream_t *)client, b, 1, on_response_callback)) != 0) { printf("failed to response world to client, err: %s\n", uv_strerror(r)); }}int main() { struct sockaddr_in addr; uv_tcp_t server; int res = 0; loop = uv_default_loop(); uv_tcp_init(loop, &server); if ((res = uv_ip4_addr("127.0.0.1", 10086, &addr)) != 0) { printf("failed to init listening address, err: %s\n", uv_strerror(res)); return 1; } if ((res = uv_tcp_bind(&server, (const struct sockaddr *)&addr, 0)) != 0) { printf("failed to bind the listening address, err: %s\n", uv_strerror(res)); return 2; } if ((res = uv_listen((uv_stream_t *)&server, 5, on_new_connection)) != 0) { printf("failed to listen on the address, err: %s\n", uv_strerror(res)); return 3; } return uv_run(loop, UV_RUN_DEFAULT);}
client.c
#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <uv.h>static uv_loop_t *loop;static void on_close(uv_handle_t* handle) { free(handle);}static void on_shutdown(uv_shutdown_t *sd, int status) { if (status < 0) { fprintf(stderr, "failed to shutdown connection, err: %s\n", uv_strerror(status)); return; } else { uv_close((uv_handle_t *)sd->handle, on_close); } free(sd);}void read_alloc_callback(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { buf->base = malloc(suggested_size); buf->len = suggested_size;}void read_callback(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { if (nread < 0) { fprintf(stderr, "failed to read from server, err: %s\n", uv_strerror(nread)); free(buf->base); exit(1); return; } else if (nread > 0) { buf->base[nread] = '\0'; printf("recv %s\n", buf->base); uv_shutdown_t *sd = malloc(sizeof(uv_shutdown_t)); uv_shutdown(sd, stream, on_shutdown); } free(buf->base);}static void on_response_callback(uv_write_t* req, int status) { if (status < 0) { fprintf(stderr, "failed to write hello to server, err: %s\n", uv_strerror(status)); } else { return; } fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); if (status == UV_ECANCELED) return; assert(status == UV_EPIPE); uv_close((uv_handle_t *)req->handle, on_close); free(req);}void on_connected(uv_connect_t *conn, int status) { int r; uv_write_t wr; if (status < 0) { fprintf(stderr, "failed to dial to server, err: %s\n", uv_strerror(status)); return; } uv_buf_t b[] = { {.base = "hello", .len = 5} }; if ((r = uv_write(&wr, conn->handle, b, 1, on_response_callback)) != 0) { printf("failed to send hello to server, err: %s\n", uv_strerror(r)); return; } printf("sent hello\n"); if ((r = uv_read_start(conn->handle, read_alloc_callback, read_callback)) != 0) { printf("failed to start reading data from server, err: %s\n", uv_strerror(r)); return ; }}int main() { int r; struct sockaddr_in addr; uv_tcp_t *client = malloc(sizeof(uv_tcp_t)); uv_connect_t *connect = malloc(sizeof(uv_connect_t)); loop = uv_default_loop(); uv_tcp_init(loop, client); if ((r = uv_ip4_addr("127.0.0.1", 10086, &addr)) != 0) { printf("failed to init server address, err: %s\n", uv_strerror(r)); return 1; } if ((r = uv_tcp_connect(connect, client, (const struct sockaddr *)&addr, on_connected)) != 0) { printf("failed to connect to server, err: %s\n", uv_strerror(r)); return 2; } return uv_run(loop, UV_RUN_DEFAULT);}
划线
评论
复制
发布于: 2020 年 08 月 10 日 阅读数: 118
版权声明: 本文为 InfoQ 作者【Huayra】的原创文章。
原文链接:【http://xie.infoq.cn/article/8eae86e25998538a12f11b7b3】。未经作者许可,禁止转载。
Huayra
关注
Gopher & Pythonista 2017.12.12 加入
还未添加个人简介
评论