• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Networking
2==========
3
4Networking in libuv is not much different from directly using the BSD socket
5interface, some things are easier, all are non-blocking, but the concepts stay
6the same. In addition libuv offers utility functions to abstract the annoying,
7repetitive and low-level tasks like setting up sockets using the BSD socket
8structures, DNS lookup, and tweaking various socket parameters.
9
10The ``uv_tcp_t`` and ``uv_udp_t`` structures are used for network I/O.
11
12.. NOTE::
13
14  The code samples in this chapter exist to show certain libuv APIs. They are
15  not examples of good quality code. They leak memory and don't always close
16  connections properly.
17
18TCP
19---
20
21TCP is a connection oriented, stream protocol and is therefore based on the
22libuv streams infrastructure.
23
24Server
25++++++
26
27Server sockets proceed by:
28
291. ``uv_tcp_init`` the TCP handle.
302. ``uv_tcp_bind`` it.
313. Call ``uv_listen`` on the handle to have a callback invoked whenever a new
32   connection is established by a client.
334. Use ``uv_accept`` to accept the connection.
345. Use :ref:`stream operations <buffers-and-streams>` to communicate with the
35   client.
36
37Here is a simple echo server
38
39.. rubric:: tcp-echo-server/main.c - The listen socket
40.. literalinclude:: ../../code/tcp-echo-server/main.c
41    :language: c
42    :linenos:
43    :lines: 68-
44    :emphasize-lines: 4-5,7-10
45
46You can see the utility function ``uv_ip4_addr`` being used to convert from
47a human readable IP address, port pair to the sockaddr_in structure required by
48the BSD socket APIs. The reverse can be obtained using ``uv_ip4_name``.
49
50.. NOTE::
51
52    There are ``uv_ip6_*`` analogues for the ip4 functions.
53
54Most of the setup functions are synchronous since they are CPU-bound.
55``uv_listen`` is where we return to libuv's callback style. The second
56arguments is the backlog queue -- the maximum length of queued connections.
57
58When a connection is initiated by clients, the callback is required to set up
59a handle for the client socket and associate the handle using ``uv_accept``.
60In this case we also establish interest in reading from this stream.
61
62.. rubric:: tcp-echo-server/main.c - Accepting the client
63.. literalinclude:: ../../code/tcp-echo-server/main.c
64    :language: c
65    :linenos:
66    :lines: 51-66
67    :emphasize-lines: 9-10
68
69The remaining set of functions is very similar to the streams example and can
70be found in the code. Just remember to call ``uv_close`` when the socket isn't
71required. This can be done even in the ``uv_listen`` callback if you are not
72interested in accepting the connection.
73
74Client
75++++++
76
77Where you do bind/listen/accept on the server, on the client side it's simply
78a matter of calling ``uv_tcp_connect``. The same ``uv_connect_cb`` style
79callback of ``uv_listen`` is used by ``uv_tcp_connect``. Try::
80
81    uv_tcp_t* socket = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
82    uv_tcp_init(loop, socket);
83
84    uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t));
85
86    struct sockaddr_in dest;
87    uv_ip4_addr("127.0.0.1", 80, &dest);
88
89    uv_tcp_connect(connect, socket, (const struct sockaddr*)&dest, on_connect);
90
91where ``on_connect`` will be called after the connection is established. The
92callback receives the ``uv_connect_t`` struct, which has a member ``.handle``
93pointing to the socket.
94
95UDP
96---
97
98The `User Datagram Protocol`_ offers connectionless, unreliable network
99communication. Hence libuv doesn't offer a stream. Instead libuv provides
100non-blocking UDP support via the `uv_udp_t` handle (for receiving) and
101`uv_udp_send_t` request (for sending) and related functions. That said, the
102actual API for reading/writing is very similar to normal stream reads. To look
103at how UDP can be used, the example shows the first stage of obtaining an IP
104address from a `DHCP`_ server -- DHCP Discover.
105
106.. note::
107
108    You will have to run `udp-dhcp` as **root** since it uses well known port
109    numbers below 1024.
110
111.. rubric:: udp-dhcp/main.c - Setup and send UDP packets
112.. literalinclude:: ../../code/udp-dhcp/main.c
113    :language: c
114    :linenos:
115    :lines: 7-11,104-
116    :emphasize-lines: 8,10-11,17-18,21
117
118.. note::
119
120    The IP address ``0.0.0.0`` is used to bind to all interfaces. The IP
121    address ``255.255.255.255`` is a broadcast address meaning that packets
122    will be sent to all interfaces on the subnet.  port ``0`` means that the OS
123    randomly assigns a port.
124
125First we setup the receiving socket to bind on all interfaces on port 68 (DHCP
126client) and start a read on it. This will read back responses from any DHCP
127server that replies. We use the UV_UDP_REUSEADDR flag to play nice with any
128other system DHCP clients that are running on this computer on the same port.
129Then we setup a similar send socket and use ``uv_udp_send`` to send
130a *broadcast message* on port 67 (DHCP server).
131
132It is **necessary** to set the broadcast flag, otherwise you will get an
133``EACCES`` error [#]_. The exact message being sent is not relevant to this
134book and you can study the code if you are interested. As usual the read and
135write callbacks will receive a status code of < 0 if something went wrong.
136
137Since UDP sockets are not connected to a particular peer, the read callback
138receives an extra parameter about the sender of the packet.
139
140``nread`` may be zero if there is no more data to be read. If ``addr`` is NULL,
141it indicates there is nothing to read (the callback shouldn't do anything), if
142not NULL, it indicates that an empty datagram was received from the host at
143``addr``. The ``flags`` parameter may be ``UV_UDP_PARTIAL`` if the buffer
144provided by your allocator was not large enough to hold the data. *In this case
145the OS will discard the data that could not fit* (That's UDP for you!).
146
147.. rubric:: udp-dhcp/main.c - Reading packets
148.. literalinclude:: ../../code/udp-dhcp/main.c
149    :language: c
150    :linenos:
151    :lines: 17-40
152    :emphasize-lines: 1,23
153
154UDP Options
155+++++++++++
156
157Time-to-live
158~~~~~~~~~~~~
159
160The TTL of packets sent on the socket can be changed using ``uv_udp_set_ttl``.
161
162IPv6 stack only
163~~~~~~~~~~~~~~~
164
165IPv6 sockets can be used for both IPv4 and IPv6 communication. If you want to
166restrict the socket to IPv6 only, pass the ``UV_UDP_IPV6ONLY`` flag to
167``uv_udp_bind`` [#]_.
168
169Multicast
170~~~~~~~~~
171
172A socket can (un)subscribe to a multicast group using:
173
174.. code::block:: c
175
176    int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership);
177
178where ``membership`` is ``UV_JOIN_GROUP`` or ``UV_LEAVE_GROUP``.
179
180The concepts of multicasting are nicely explained in `this guide`_.
181
182.. _this guide: https://www.tldp.org/HOWTO/Multicast-HOWTO-2.html
183
184Local loopback of multicast packets is enabled by default [#]_, use
185``uv_udp_set_multicast_loop`` to switch it off.
186
187The packet time-to-live for multicast packets can be changed using
188``uv_udp_set_multicast_ttl``.
189
190Querying DNS
191------------
192
193libuv provides asynchronous DNS resolution. For this it provides its own
194``getaddrinfo`` replacement [#]_. In the callback you can
195perform normal socket operations on the retrieved addresses. Let's connect to
196Libera.chat to see an example of DNS resolution.
197
198.. rubric:: dns/main.c
199.. literalinclude:: ../../code/dns/main.c
200    :language: c
201    :linenos:
202    :lines: 61-
203    :emphasize-lines: 12
204
205If ``uv_getaddrinfo`` returns non-zero, something went wrong in the setup and
206your callback won't be invoked at all. All arguments can be freed immediately
207after ``uv_getaddrinfo`` returns. The `hostname`, `servname` and `hints`
208structures are documented in `the getaddrinfo man page <getaddrinfo_>`_. The
209callback can be ``NULL`` in which case the function will run synchronously.
210
211In the resolver callback, you can pick any IP from the linked list of ``struct
212addrinfo(s)``. This also demonstrates ``uv_tcp_connect``. It is necessary to
213call ``uv_freeaddrinfo`` in the callback.
214
215.. rubric:: dns/main.c
216.. literalinclude:: ../../code/dns/main.c
217    :language: c
218    :linenos:
219    :lines: 42-60
220    :emphasize-lines: 8,16
221
222libuv also provides the inverse `uv_getnameinfo`_.
223
224.. _uv_getnameinfo: http://docs.libuv.org/en/v1.x/dns.html#c.uv_getnameinfo
225
226Network interfaces
227------------------
228
229Information about the system's network interfaces can be obtained through libuv
230using ``uv_interface_addresses``. This simple program just prints out all the
231interface details so you get an idea of the fields that are available. This is
232useful to allow your service to bind to IP addresses when it starts.
233
234.. rubric:: interfaces/main.c
235.. literalinclude:: ../../code/interfaces/main.c
236    :language: c
237    :linenos:
238    :emphasize-lines: 9,17
239
240``is_internal`` is true for loopback interfaces. Note that if a physical
241interface has multiple IPv4/IPv6 addresses, the name will be reported multiple
242times, with each address being reported once.
243
244.. _c-ares: https://c-ares.haxx.se
245.. _getaddrinfo: https://man7.org/linux/man-pages/man3/getaddrinfo.3.html
246
247.. _User Datagram Protocol: https://en.wikipedia.org/wiki/User_Datagram_Protocol
248.. _DHCP: https://tools.ietf.org/html/rfc2131
249
250----
251
252.. [#] https://beej.us/guide/bgnet/html/#broadcast-packetshello-world
253.. [#] on Windows only supported on Windows Vista and later.
254.. [#] https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html#ss6.1
255.. [#] libuv use the system ``getaddrinfo`` in the libuv threadpool. libuv
256    v0.8.0 and earlier also included c-ares_ as an alternative, but this has been
257    removed in v0.9.0.
258