libuv-tcp-echo

使用libuv建立tcp server


更新記錄

item note
20160517 第一版

目錄


libuv tcp server

範例說明

  • source code
  • uv_tcp_t is a subclass of uv_stream_t.
    • Represents a TCP stream or TCP server.
  • uv_ip4_addr(ip, port, addr) : 由ip/port取得ip4 addr
  • uv_tcp_bind
  • Call uv_listen on the handle to have a callback invoked whenever a new connection is established by a client.

tcp

範例內容

1
2
3
4
5
6
7
8
9
10
11
12
int main() {
..

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);
..
}

on_new_connection

  • Use uv_accept to accept the connection.
1
2
3
4
5
6
7
8
9
void on_new_connection(uv_stream_t *server, int status) {
...
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);
}
...
}

echo_read

  • 分析取得資料
  • 及回應client(response)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
int i;
LOG(" [echo_read]");

if (nread < 0) {
..
} else if (nread > 0) {

printf("nread size:%d\n",nread);
printf("buf:\n");
for(i=0; i<nread; i++)
printf("%c",buf->base[i]);

uv_write_t *req = (uv_write_t *) malloc(sizeof(uv_write_t));
uv_buf_t wrbuf = uv_buf_init(buf->base, nread);
uv_write(req, client, &wrbuf, 1, echo_write);
}

if (buf->base)
free(buf->base);
}

tcp client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void on_connect(uv_connect_t *req, int status)
{
...

char *req_message = "POST /signup HTTP/1.1\n"
"User-Agent: HTTPTool/1.0\n";
uv_buf_t bufs[] = {
{ .len = strlen(req_message), .base = req_message }
};
int bufs_len = sizeof(bufs)/sizeof(uv_buf_t);

uv_stream_t *tcp = req->handle;
uv_write_t write_req;
uv_write(&write_req, tcp, bufs, bufs_len, after_write);
}

int main(int argc, char const *argv[])
{
uv_tcp_t socket;
assert(!uv_tcp_init(uv_default_loop(), &socket));

struct sockaddr_in dest;
assert(!uv_ip4_addr("127.0.0.1", DEFAULT_PORT, &dest));

uv_connect_t connect;
assert(!uv_tcp_connect(&connect, &socket, (struct sockaddr *) &dest, on_connect));
uv_run(uv_default_loop(), UV_RUN_ONCE);
return 0;
}

範例測試

tcp server

1
2
3
4
5
6
7
8
9
10
[ubuntu](master-79103b8)15h41m root@e8b0cd737680:[t07_tcp-echo-server]$ ./t07-main-ubuntu 
listening on 0.0.0.0:7000
[on_new_connection]
[echo_read]
nread size:47
buf:
POST /signup HTTP/1.1
User-Agent: HTTPTool/1.0
[echo_write]
[echo_read]

tcp client

1
2
[ubuntu](master-79103b8)15h41m root@e8b0cd737680:[t07_tcp-client]$ ./t07-main-ubuntu 
[on_connect]

其它

getaddrinfo取得相關訊息

參考來源,ref1,ref2

1
2
3
4
5
6
7
8
9
10
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
  • ai_family
    • This field specifies the desired address family for the returned addresses.
    • ai_family参数指定调用者期待返回的套接口地址结构的类型。它的值包括三种:AF_INET,AF_INET6和AF_UNSPEC。
    • 如果指定AF_INET,那么函数九不能返回任何IPV6相关的地址信息
    • 指定了AF_INET6,则就不能返回任何IPV4地址信息
    • AF_UNSPEC
    • 某个主机既有AAPC记录(IPV6)地址,同时又有BBPC记录(IPV4)地址,那么AAPC记录将作为sockaddr_in6结构返回,而BBPC记录则作为sockaddr_in结构返回
    • AF_UNSPEC则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址
    • The value AF_UNSPEC indicates that getaddrinfo() should return socket addresses for any address family (either IPv4 or IPv6, for example)

參考來源