libuv 异步网络编程之 TCP helloworld

用户头像
Huayra
关注
发布于: 2020 年 08 月 10 日
libuv 异步网络编程之 TCP helloworld

指引

学习 libuv 官网文档 http://docs.libuv.org/en/v1.x/guide/networking.html 网络编程部分。

Libuv 的网络编程跟 BSD socket 接口没什么区别,保持了同样概念的情况下,所有的操作都是异步的。相比 BSD 比较原始的操作,libuv 提供了 DNS 查询、封装 socket 参数等实用函数。

libuv 版本:1.38.1

TCP 服务端

服务端程序的常规操作:

  1. 初始化地址

  2. bind 该地址

  3. 开始 listen

  4. accept 新连接

不过此处 accept 新连接是在回调函数 on_new_connection里。

之后才能对新连接 client进行读取数据/写入数据等操作。

TCP 客户端

客户端程序连接服务端的常规操作:

  1. 初始化地址

  2. 连接服务端

连接服务端成功后,需要在回调函数 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
用户头像

Huayra

关注

Gopher & Pythonista 2017.12.12 加入

还未添加个人简介

评论

发布
暂无评论
libuv 异步网络编程之 TCP helloworld