uvcc
libuv C++ bindings
lxjs2012-demo

The example used by Bert Belder at LXJS 2012 to introduce the libuv basics.

See also
Video at youtube.com: "LXJS 2012 - Bert Belder - libuv".

The original code is slightly modified to work with recent libuv versions > 0.10.

example/lxjs2012-demo-uv.c 
1 
2 /* node.js equivalent stuff:
3  * require('net').connect(80, 'www.nyan.cat').pipe(process.stdout);
4  */
5 
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <uv.h>
10 
11 
12 void after_getaddrinfo(uv_getaddrinfo_t*, int, struct addrinfo*);
13 void after_connect(uv_connect_t*, int);
14 void after_write(uv_write_t*, int);
15 void on_alloc(uv_handle_t*, size_t, uv_buf_t*);
16 void on_read(uv_stream_t*, ssize_t, const uv_buf_t*);
17 void on_close(uv_handle_t*);
18 
19 
20 int main(int _argc, char *_argv[])
21 {
22  uv_getaddrinfo_t* gai_req = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t));
23 
24  uv_getaddrinfo(
25  uv_default_loop(),
26  gai_req,
27  after_getaddrinfo,
28  "www.nyan.cat",
29  "80",
30  NULL
31  );
32 
33  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
34 
35  return 0;
36 }
37 
38 
39 
40 void after_getaddrinfo(uv_getaddrinfo_t *_gai_req, int _status, struct addrinfo *_ai)
41 {
42  uv_tcp_t *tcp_handle;
43  uv_connect_t *connect_req;
44 
45  if (_status < 0) abort(); /* handle the error */
46 
47  tcp_handle = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
48  uv_tcp_init(uv_default_loop(), tcp_handle);
49 
50  connect_req = (uv_connect_t*)malloc(sizeof(uv_connect_t));
51  uv_tcp_connect(
52  connect_req,
53  tcp_handle,
54  _ai->ai_addr,
55  after_connect
56  );
57 
58  free(_gai_req);
59  uv_freeaddrinfo(_ai);
60 }
61 
62 
63 void after_connect(uv_connect_t *_connect_req, int _status)
64 {
65  uv_write_t *write_req;
66  uv_buf_t buf;
67 
68  if (_status < 0) abort(); /* handle the error */
69 
70  write_req = (uv_write_t*)malloc(sizeof(uv_write_t));
71 
72  buf.base = (char*)"GET / HTTP/1.0\r\n"
73  "Host: www.nyan.cat\r\n"
74  "\r\n";
75  buf.len = strlen(buf.base);
76 
77  uv_write(
78  write_req,
79  _connect_req->handle,
80  &buf,
81  1,
82  after_write
83  );
84 
85  uv_read_start(
86  _connect_req->handle,
87  on_alloc,
88  on_read
89  );
90 
91  free(_connect_req);
92 }
93 
94 
95 void after_write(uv_write_t *_write_req, int _status)
96 {
97  if (_status < 0) abort(); /* handle the error */
98  free(_write_req);
99 }
100 
101 
102 void on_alloc(uv_handle_t *_handle, size_t _suggested_size, uv_buf_t* _buf)
103 {
104  _buf->base = (char*)malloc(_suggested_size);
105  _buf->len = _suggested_size;
106 }
107 
108 
109 void on_read(uv_stream_t *_tcp_handle, ssize_t _nread, const uv_buf_t *_buf)
110 {
111  if (_nread < 0)
112  { /* error or EOF */
113  if (_nread == UV_EOF)
114  { /* no more data; close the connection */
115  uv_close(
116  (uv_handle_t*) _tcp_handle,
117  on_close
118  );
119  }
120  else
121  { /* that's an error */
122  abort();
123  }
124  }
125 
126  if (_nread > 0)
127  { /* print it! FTW!!! */
128  fwrite(_buf->base, 1, _nread, stdout);
129  }
130 
131  free(_buf->base);
132 }
133 
134 
135 void on_close(uv_handle_t *_handle)
136 {
137  free(_handle);
138 }
139 
T free(T... args)
T strlen(T... args)
T fwrite(T... args)
T malloc(T... args)
T abort(T... args)

Here is the very same program being rewritten using uvcc. One can find that the code has become more compact and has far less "translation noise".

example/lxjs2012-demo-uvcc.cpp 
1 
2 /* node.js equivalent stuff:
3  * require('net').connect(80, 'www.nyan.cat').pipe(process.stdout);
4  */
5 
6 #include "uvcc.hpp"
7 #include <cstring> // strlen()
8 #include <cstdio>
9 
10 
11 #define PRINT_UV_ERR(code, printf_args...) do {\
12  fflush(stdout);\
13  fprintf(stderr, "" printf_args);\
14  fprintf(stderr, ": %s (%i): %s\n", ::uv_err_name(code), (int)(code), ::uv_strerror(code));\
15  fflush(stderr);\
16 } while (0)
17 
18 
19 void connect_cb(uv::connect);
20 
21 
22 int main(int _argc, char *_argv[])
23 {
24  // a getaddrinfo DNS request
25  uv::getaddrinfo gai_req;
26  // set a getaddrinfo request callback function that will be called after the DNS resolving request complete
27  gai_req.on_request() = [](uv::getaddrinfo _gai_req)
28  {
29  if (!_gai_req)
30  {
31  PRINT_UV_ERR(_gai_req.uv_status(), "getaddrinfo");
32  return;
33  }
34 
35  // a tcp handle
36  uv::tcp tcp_handle(uv::loop::Default());
37 
38  // a connect request
39  uv::connect connect_req;
40  // set a connect request callback function that will be called when the connection is established
41  connect_req.on_request() = connect_cb;
42  // run the connect request on the tcp handle to connect to the resolved peer
43  connect_req.run(tcp_handle, *_gai_req.addrinfo()->ai_addr);
44  };
45 
46  // run the getaddrinfo request
47  gai_req.run(uv::loop::Default(), "www.nyan.cat", "80");
48 
49  return uv::loop::Default().run(UV_RUN_DEFAULT);
50 }
51 
52 
53 
54 void connect_cb(uv::connect _connect_req)
55 {
56  if (!_connect_req)
57  {
58  PRINT_UV_ERR(_connect_req.uv_status(), "connect");
59  return;
60  }
61 
62  // get the tcp handle this connect request has performed on
63  uv::tcp tcp_handle = static_cast< uv::tcp&& >(_connect_req.handle());
64 
65  // fill in a buffer with HTTP request data
66  uv::buffer buf;
67  buf.base() = const_cast< char* >(
68  "GET / HTTP/1.0\r\n"
69  "Host: www.nyan.cat\r\n"
70  "\r\n"
71  );
72  buf.len() = std::strlen(buf.base());
73 
74  // write the HTTP request to the tcp stream
75  uv::write wr;
76  wr.on_request() = [](uv::write _wr, uv::buffer){ if (!_wr) PRINT_UV_ERR(_wr.uv_status(), "write"); };
77  wr.run(tcp_handle, buf);
78 
79  // start reading from the tcp stream
80  tcp_handle.read_start(
81  // the callback function that will be called when a new input buffer get needed to be allocated for data read
82  [](uv::handle, std::size_t _suggested_size){ return uv::buffer{ _suggested_size }; },
83  // the callback function that will be called when data has been read from the stream
84  [](uv::io _io, ssize_t _nread, uv::buffer _buf, int64_t, void*)
85  {
86  if (_nread < 0)
87  {
88  if (_nread != UV_EOF) PRINT_UV_ERR(_nread, "read");
89  _io.read_stop();
90  }
91  else if (_nread > 0)
92  {
93  // print the received data
94  fwrite(_buf.base(), 1, _nread, stdout);
95  fflush(stdout);
96  }
97  }
98  );
99 }
Stream connect request type.
Getaddrinfo request type.
Definition: request-dns.hpp:22
decltype(uv_t::len) & len(const std::size_t _i=0) const noexcept
The .len field of the _i-th buffer structure.
Definition: buffer.hpp:242
int read_stop() const
Stop reading data from the I/O endpoint.
Definition: handle-io.hpp:254
The base class for the libuv handles.
Definition: handle-base.hpp:33
T fflush(T... args)
int run(tcp &_tcp, const _T_ &_sockaddr)
Run the request for uv::tcp stream.
static loop & Default() noexcept
Returns the initialized loop that can be used as a global default loop throughout the program...
Definition: loop.hpp:220
int run(uv::loop &_loop, const char *_hostname, const char *_service, const ::addrinfo &_hints)
Run the request.
int run(::uv_run_mode _mode)
Go into a loop and process events and their callbacks with the current thread.
Definition: loop.hpp:255
int uv_status() const noexcept
The status value returned by the last executed libuv API function on this request.
T strlen(T... args)
Stream write request type.
T fwrite(T... args)
The base class for handles representing I/O endpoints: a file, TCP/UDP socket, pipe, TTY.
Definition: handle-io.hpp:25
TCP handle.
decltype(uv_t::base) & base(const std::size_t _i=0) const noexcept
The .base field of the _i-th buffer structure.
Definition: buffer.hpp:240
stream handle() const noexcept
The stream which this connect request has been running on.
Encapsulates uv_buf_t data type and provides uv_buf_t[] functionality.
Definition: buffer.hpp:28
int read_start(std::size_t _size=0, int64_t _offset=-1) const
Start reading incoming data from the I/O endpoint.
Definition: handle-io.hpp:191
See also
There are another helpful introductory talks on libuv that can be found at youtube.com:
"Introduction to libuv – Thorsten Lorenz",
"using libuv and http parser to build a webserver".

Here are the examples of the elementary TCP client and server programs written with uvcc. Each program sends a greeting to the remote peer, closes its write side of the TCP connection and keeps reading from the remote peer until it closes the connection.

example/hello-tcp-server.cpp 
1 
2 #include "uvcc.hpp"
3 #include <cstdio>
4 
5 
6 #define PRINT_UV_ERR(code, printf_args...) do {\
7  fflush(stdout);\
8  fprintf(stderr, "" printf_args);\
9  fprintf(stderr, ": %s (%i): %s\n", ::uv_err_name(code), (int)(code), ::uv_strerror(code));\
10  fflush(stderr);\
11 } while (0)
12 
13 
14 int main(int _argc, char *_argv[])
15 {
16  // obtaing a listening address:port and initialize a sockaddr structure
17  sockaddr_storage listen_addr;
18 
19  const char *ip = _argc > 1 ? _argv[1] : "127.0.0.1"; // or "::1" for IPv6 loopback address
20  const char *port = _argc > 2 ? _argv[2] : "54321";
21 
22  int status = uv::init(listen_addr, ip, port); // IP agnostic address initialization
23  if (status != 0)
24  {
25  PRINT_UV_ERR(status, "ip address");
26  return status;
27  }
28  /* here is the example for direct IPv4 address initialization:
29  uv::init(listen_addr, AF_INET);
30  reinterpret_cast< sockaddr_in& >(listen_addr).sin_port = uv::hton16(54321);
31  reinterpret_cast< sockaddr_in& >(listen_addr).sin_addr.s_addr = uv::hton32(0x7F000001); // 127.0.0.1
32  */
33 
34  // initialize a tcp socket and bind it to the desired address::port
35  uv::tcp server(uv::loop::Default(), listen_addr.ss_family);
36  server.bind(listen_addr);
37  if (!server)
38  {
39  PRINT_UV_ERR(server.uv_status(), "tcp socket bind");
40  return server.uv_status();
41  }
42 
43  // fill in the buffer with greeting message
44  uv::buffer greeting;
45  greeting.base() = const_cast< char* >("server: Hello from uvcc!\n");
46  greeting.len() = strlen(greeting.base());
47 
48  // start listening for and accepting incoming connections
49  server.listen(5,
50  [&greeting](uv::stream _server)
51  {
52  if (!_server)
53  {
54  PRINT_UV_ERR(_server.uv_status(), "incoming connection");
55  return;
56  }
57  else
58  _server.attached(false); // detach server handle from the loop after the first client connection - aka stop listening
59 
60  // accept a new connection
61  auto client = static_cast< uv::tcp&& >(_server.accept());
62  if (!client) PRINT_UV_ERR(client.uv_status(), "accept");
63 
64  // dispatch to send a greeting
65  uv::write wr;
66  wr.on_request() = [](uv::write _wr, uv::buffer){ if (!_wr) PRINT_UV_ERR(_wr.uv_status(), "write"); };
67  wr.run(client, greeting);
68  if (!wr) PRINT_UV_ERR(wr.uv_status(), "write initiation");
69 
70  // shutdown the write side of the connection
71  uv::shutdown shut_wr;
72  shut_wr.run(client);
73 
74  // start reading from the remote peer
75  client.read_start(
76  // a buffer allocation callback
77  [](uv::handle, std::size_t _suggested_size){ return uv::buffer{ _suggested_size }; },
78  // a read callback
79  [](uv::io _io, ssize_t _nread, uv::buffer _buf, int64_t, void*)
80  {
81  if (_nread < 0)
82  {
83  if (_nread != UV_EOF) PRINT_UV_ERR(_nread, "read");
84  _io.read_stop();
85  }
86  else if (_nread > 0)
87  {
88  // print received data
89  fwrite(_buf.base(), 1, _nread, stdout);
90  fflush(stdout);
91  }
92  }
93  );
94  if (!client) PRINT_UV_ERR(client.uv_status(), "read initiation");
95  }
96  );
97  if (!server)
98  {
99  PRINT_UV_ERR(server.uv_status(), "listen");
100  return server.uv_status();
101  }
102 
103  return uv::loop::Default().run(UV_RUN_DEFAULT);
104 }
Stream handle.
int run(stream &_stream)
Run the request.
decltype(uv_t::len) & len(const std::size_t _i=0) const noexcept
The .len field of the _i-th buffer structure.
Definition: buffer.hpp:242
Stream shutdown request type.
int uv_status() const noexcept
The status value returned by the last executed libuv API function on this handle. ...
int read_stop() const
Stop reading data from the I/O endpoint.
Definition: handle-io.hpp:254
The base class for the libuv handles.
Definition: handle-base.hpp:33
T fflush(T... args)
static loop & Default() noexcept
Returns the initialized loop that can be used as a global default loop throughout the program...
Definition: loop.hpp:220
int run(::uv_run_mode _mode)
Go into a loop and process events and their callbacks with the current thread.
Definition: loop.hpp:255
int uv_status() const noexcept
The status value returned by the last executed libuv API function on this request.
T strlen(T... args)
Stream write request type.
T fwrite(T... args)
The base class for handles representing I/O endpoints: a file, TCP/UDP socket, pipe, TTY.
Definition: handle-io.hpp:25
int init(::sockaddr_in &_sin)
Initialize a sockaddr_in structure.
Definition: netstruct.hpp:35
TCP handle.
decltype(uv_t::base) & base(const std::size_t _i=0) const noexcept
The .base field of the _i-th buffer structure.
Definition: buffer.hpp:240
Encapsulates uv_buf_t data type and provides uv_buf_t[] functionality.
Definition: buffer.hpp:28
stream accept() const
Accept incoming connections.
example/hello-tcp-client.cpp 
1 
2 #include "uvcc.hpp"
3 #include <cstdio>
4 
5 
6 #define PRINT_UV_ERR(code, printf_args...) do {\
7  fflush(stdout);\
8  fprintf(stderr, "" printf_args);\
9  fprintf(stderr, ": %s (%i): %s\n", ::uv_err_name(code), (int)(code), ::uv_strerror(code));\
10  fflush(stderr);\
11 } while (0)
12 
13 
14 int main(int _argc, char *_argv[])
15 {
16  // obtain server's address and initialize a sockaddr structure
17  sockaddr_storage server_addr;
18 
19  const char *ip = _argc > 1 ? _argv[1] : "127.0.0.1";
20  const char *port = _argc > 2 ? _argv[2] : "54321";
21 
22  int status = uv::init(server_addr, ip, port);
23  if (status != 0)
24  {
25  PRINT_UV_ERR(status, "ip address");
26  return status;
27  }
28 
29  // initialize a tcp socket
30  uv::tcp peer(uv::loop::Default(), server_addr.ss_family);
31  if (!peer)
32  {
33  PRINT_UV_ERR(peer.uv_status(), "tcp socket");
34  return peer.uv_status();
35  }
36 
37  // fill in the buffer with greeting message
38  uv::buffer greeting;
39  greeting.base() = const_cast< char* >("client: Hello from uvcc!\n");
40  greeting.len() = strlen(greeting.base());
41 
42  // a connect request
43  uv::connect conn;
44  conn.on_request() = [&greeting](uv::connect _conn)
45  {
46  if (!_conn)
47  {
48  PRINT_UV_ERR(_conn.uv_status(), "connect");
49  return;
50  }
51  // a tcp stream for the connection
52  auto peer = static_cast< uv::tcp&& >(_conn.handle());
53 
54  // dispatch to send a greeting
55  uv::write wr;
56  wr.on_request() = [](uv::write _wr, uv::buffer){ if (!_wr) PRINT_UV_ERR(_wr.uv_status(), "write"); };
57  wr.run(peer, greeting);
58  if (!wr) PRINT_UV_ERR(wr.uv_status(), "write initiation");
59 
60  // shutdown the write side of the connection
61  uv::shutdown shut_wr;
62  shut_wr.run(peer);
63 
64  // start reading from the remote peer
65  peer.read_start(
66  // a buffer allocation callback
67  [](uv::handle, std::size_t _suggested_size){ return uv::buffer{ _suggested_size }; },
68  // a read callback
69  [](uv::io _io, ssize_t _nread, uv::buffer _buf, int64_t, void*)
70  {
71  if (_nread < 0)
72  {
73  if (_nread != UV_EOF) PRINT_UV_ERR(_nread, "read");
74  _io.read_stop();
75  }
76  else if (_nread > 0)
77  {
78  // print received data
79  fwrite(_buf.base(), 1, _nread, stdout);
80  fflush(stdout);
81  }
82  }
83  );
84  if (!peer) PRINT_UV_ERR(peer.uv_status(), "read initiation");
85  };
86 
87  // attach the connect request to the loop
88  conn.run(peer, server_addr);
89  if (!conn)
90  {
91  PRINT_UV_ERR(conn.uv_status(), "connect initiation");
92  return conn.uv_status();
93  }
94 
95  return uv::loop::Default().run(UV_RUN_DEFAULT);
96 }
Stream connect request type.
int run(stream &_stream, const buffer &_buf)
Run the request.
int run(stream &_stream)
Run the request.
decltype(uv_t::len) & len(const std::size_t _i=0) const noexcept
The .len field of the _i-th buffer structure.
Definition: buffer.hpp:242
Stream shutdown request type.
int read_stop() const
Stop reading data from the I/O endpoint.
Definition: handle-io.hpp:254
The base class for the libuv handles.
Definition: handle-base.hpp:33
T fflush(T... args)
static loop & Default() noexcept
Returns the initialized loop that can be used as a global default loop throughout the program...
Definition: loop.hpp:220
int run(::uv_run_mode _mode)
Go into a loop and process events and their callbacks with the current thread.
Definition: loop.hpp:255
int uv_status() const noexcept
The status value returned by the last executed libuv API function on this request.
T strlen(T... args)
Stream write request type.
T fwrite(T... args)
The base class for handles representing I/O endpoints: a file, TCP/UDP socket, pipe, TTY.
Definition: handle-io.hpp:25
int init(::sockaddr_in &_sin)
Initialize a sockaddr_in structure.
Definition: netstruct.hpp:35
TCP handle.
decltype(uv_t::base) & base(const std::size_t _i=0) const noexcept
The .base field of the _i-th buffer structure.
Definition: buffer.hpp:240
Encapsulates uv_buf_t data type and provides uv_buf_t[] functionality.
Definition: buffer.hpp:28