Mercurial
comparison third_party/libuv/docs/src/guide/networking.rst @ 160:948de3f54cea
[ThirdParty] Added libuv
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 14 Jan 2026 19:39:52 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 159:05cf9467a1c3 | 160:948de3f54cea |
|---|---|
| 1 Networking | |
| 2 ========== | |
| 3 | |
| 4 Networking in libuv is not much different from directly using the BSD socket | |
| 5 interface, some things are easier, all are non-blocking, but the concepts stay | |
| 6 the same. In addition libuv offers utility functions to abstract the annoying, | |
| 7 repetitive and low-level tasks like setting up sockets using the BSD socket | |
| 8 structures, DNS lookup, and tweaking various socket parameters. | |
| 9 | |
| 10 The ``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 | |
| 18 TCP | |
| 19 --- | |
| 20 | |
| 21 TCP is a connection oriented, stream protocol and is therefore based on the | |
| 22 libuv streams infrastructure. | |
| 23 | |
| 24 Server | |
| 25 ++++++ | |
| 26 | |
| 27 Server sockets proceed by: | |
| 28 | |
| 29 1. ``uv_tcp_init`` the TCP handle. | |
| 30 2. ``uv_tcp_bind`` it. | |
| 31 3. Call ``uv_listen`` on the handle to have a callback invoked whenever a new | |
| 32 connection is established by a client. | |
| 33 4. Use ``uv_accept`` to accept the connection. | |
| 34 5. Use :ref:`stream operations <buffers-and-streams>` to communicate with the | |
| 35 client. | |
| 36 | |
| 37 Here 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 | |
| 46 You can see the utility function ``uv_ip4_addr`` being used to convert from | |
| 47 a human readable IP address, port pair to the sockaddr_in structure required by | |
| 48 the 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 | |
| 54 Most 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 | |
| 56 arguments is the backlog queue -- the maximum length of queued connections. | |
| 57 | |
| 58 When a connection is initiated by clients, the callback is required to set up | |
| 59 a handle for the client socket and associate the handle using ``uv_accept``. | |
| 60 In 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 | |
| 69 The remaining set of functions is very similar to the streams example and can | |
| 70 be found in the code. Just remember to call ``uv_close`` when the socket isn't | |
| 71 required. This can be done even in the ``uv_listen`` callback if you are not | |
| 72 interested in accepting the connection. | |
| 73 | |
| 74 Client | |
| 75 ++++++ | |
| 76 | |
| 77 Where you do bind/listen/accept on the server, on the client side it's simply | |
| 78 a matter of calling ``uv_tcp_connect``. The same ``uv_connect_cb`` style | |
| 79 callback 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 | |
| 91 where ``on_connect`` will be called after the connection is established. The | |
| 92 callback receives the ``uv_connect_t`` struct, which has a member ``.handle`` | |
| 93 pointing to the socket. | |
| 94 | |
| 95 UDP | |
| 96 --- | |
| 97 | |
| 98 The `User Datagram Protocol`_ offers connectionless, unreliable network | |
| 99 communication. Hence libuv doesn't offer a stream. Instead libuv provides | |
| 100 non-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 | |
| 102 actual API for reading/writing is very similar to normal stream reads. To look | |
| 103 at how UDP can be used, the example shows the first stage of obtaining an IP | |
| 104 address 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 | |
| 125 First we setup the receiving socket to bind on all interfaces on port 68 (DHCP | |
| 126 client) and start a read on it. This will read back responses from any DHCP | |
| 127 server that replies. We use the UV_UDP_REUSEADDR flag to play nice with any | |
| 128 other system DHCP clients that are running on this computer on the same port. | |
| 129 Then we setup a similar send socket and use ``uv_udp_send`` to send | |
| 130 a *broadcast message* on port 67 (DHCP server). | |
| 131 | |
| 132 It 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 | |
| 134 book and you can study the code if you are interested. As usual the read and | |
| 135 write callbacks will receive a status code of < 0 if something went wrong. | |
| 136 | |
| 137 Since UDP sockets are not connected to a particular peer, the read callback | |
| 138 receives 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, | |
| 141 it indicates there is nothing to read (the callback shouldn't do anything), if | |
| 142 not 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 | |
| 144 provided by your allocator was not large enough to hold the data. *In this case | |
| 145 the 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 | |
| 154 UDP Options | |
| 155 +++++++++++ | |
| 156 | |
| 157 Time-to-live | |
| 158 ~~~~~~~~~~~~ | |
| 159 | |
| 160 The TTL of packets sent on the socket can be changed using ``uv_udp_set_ttl``. | |
| 161 | |
| 162 IPv6 stack only | |
| 163 ~~~~~~~~~~~~~~~ | |
| 164 | |
| 165 IPv6 sockets can be used for both IPv4 and IPv6 communication. If you want to | |
| 166 restrict the socket to IPv6 only, pass the ``UV_UDP_IPV6ONLY`` flag to | |
| 167 ``uv_udp_bind``. | |
| 168 | |
| 169 Multicast | |
| 170 ~~~~~~~~~ | |
| 171 | |
| 172 A 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 | |
| 178 where ``membership`` is ``UV_JOIN_GROUP`` or ``UV_LEAVE_GROUP``. | |
| 179 | |
| 180 The concepts of multicasting are nicely explained in `this guide`_. | |
| 181 | |
| 182 .. _this guide: https://www.tldp.org/HOWTO/Multicast-HOWTO-2.html | |
| 183 | |
| 184 Local loopback of multicast packets is enabled by default [#]_, use | |
| 185 ``uv_udp_set_multicast_loop`` to switch it off. | |
| 186 | |
| 187 The packet time-to-live for multicast packets can be changed using | |
| 188 ``uv_udp_set_multicast_ttl``. | |
| 189 | |
| 190 Querying DNS | |
| 191 ------------ | |
| 192 | |
| 193 libuv provides asynchronous DNS resolution. For this it provides its own | |
| 194 ``getaddrinfo`` replacement [#]_. In the callback you can | |
| 195 perform normal socket operations on the retrieved addresses. Let's connect to | |
| 196 Libera.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 | |
| 205 If ``uv_getaddrinfo`` returns non-zero, something went wrong in the setup and | |
| 206 your callback won't be invoked at all. All arguments can be freed immediately | |
| 207 after ``uv_getaddrinfo`` returns. The `hostname`, `servname` and `hints` | |
| 208 structures are documented in `the getaddrinfo man page <getaddrinfo_>`_. The | |
| 209 callback can be ``NULL`` in which case the function will run synchronously. | |
| 210 | |
| 211 In the resolver callback, you can pick any IP from the linked list of ``struct | |
| 212 addrinfo(s)``. This also demonstrates ``uv_tcp_connect``. It is necessary to | |
| 213 call ``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 | |
| 222 libuv also provides the inverse `uv_getnameinfo`_. | |
| 223 | |
| 224 .. _uv_getnameinfo: http://docs.libuv.org/en/v1.x/dns.html#c.uv_getnameinfo | |
| 225 | |
| 226 Network interfaces | |
| 227 ------------------ | |
| 228 | |
| 229 Information about the system's network interfaces can be obtained through libuv | |
| 230 using ``uv_interface_addresses``. This simple program just prints out all the | |
| 231 interface details so you get an idea of the fields that are available. This is | |
| 232 useful 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 | |
| 241 interface has multiple IPv4/IPv6 addresses, the name will be reported multiple | |
| 242 times, 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 .. [#] https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html#ss6.1 | |
| 254 .. [#] libuv use the system ``getaddrinfo`` in the libuv threadpool. libuv | |
| 255 v0.8.0 and earlier also included c-ares_ as an alternative, but this has been | |
| 256 removed in v0.9.0. |