diff --git a/code/dns/main.c b/code/dns/main.c index 79e01ad..6496988 100644 --- a/code/dns/main.c +++ b/code/dns/main.c @@ -35,7 +35,7 @@ void on_connect(uv_connect_t *req, int status) { return; } - uv_read_start((uv_stream_t*) req->data, alloc_buffer, on_read); + uv_read_start((uv_stream_t*) req->handle, alloc_buffer, on_read); free(req); } @@ -53,7 +53,6 @@ void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) { uv_tcp_t *socket = (uv_tcp_t*) malloc(sizeof(uv_tcp_t)); uv_tcp_init(loop, socket); - connect_req->data = (void*) socket; uv_tcp_connect(connect_req, socket, (const struct sockaddr*) res->ai_addr, on_connect); uv_freeaddrinfo(res); diff --git a/code/tcp-echo-server/main.c b/code/tcp-echo-server/main.c index a226b9f..f0c43eb 100644 --- a/code/tcp-echo-server/main.c +++ b/code/tcp-echo-server/main.c @@ -16,7 +16,7 @@ void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { void echo_write(uv_write_t *req, int status) { if (status) { - fprintf(stderr, "Write error %s\n", uv_err_name(status)); + fprintf(stderr, "Write error %s\n", uv_strerror(status)); } free(req); } @@ -26,17 +26,19 @@ void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { if (nread != UV_EOF) fprintf(stderr, "Read error %s\n", uv_err_name(nread)); uv_close((uv_handle_t*) client, NULL); - return; + } else if (nread > 0) { + 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); } - 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); - free(buf->base); + if (buf->base) + free(buf->base); } void on_new_connection(uv_stream_t *server, int status) { - if (status == -1) { + if (status < 0) { + fprintf(stderr, "New connection error %s\n", uv_strerror(status)); // error! return; } diff --git a/code/udp-dhcp/main.c b/code/udp-dhcp/main.c index 090263b..fc2ca0c 100644 --- a/code/udp-dhcp/main.c +++ b/code/udp-dhcp/main.c @@ -96,7 +96,7 @@ uv_buf_t make_discover_msg() { void on_send(uv_udp_send_t *req, int status) { if (status) { - fprintf(stderr, "Send error %s\n", uv_err_name(status)); + fprintf(stderr, "Send error %s\n", uv_strerror(status)); return; } } @@ -106,21 +106,21 @@ int main() { uv_udp_init(loop, &recv_socket); struct sockaddr_in recv_addr; - assert(uv_ip4_addr("0.0.0.0", 68, &recv_addr) == 0); - uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, 0); + uv_ip4_addr("0.0.0.0", 68, &recv_addr); + uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR); uv_udp_recv_start(&recv_socket, alloc_buffer, on_read); uv_udp_init(loop, &send_socket); - struct sockaddr_in bind_addr; - assert(uv_ip4_addr("0.0.0.0", 0, &bind_addr) == 0); - uv_udp_bind(&send_socket, (const struct sockaddr *)&bind_addr, 0); + struct sockaddr_in broadcast_addr; + uv_ip4_addr("0.0.0.0", 0, &broadcast_addr); + uv_udp_bind(&send_socket, (const struct sockaddr *)&broadcast_addr, 0); uv_udp_set_broadcast(&send_socket, 1); uv_udp_send_t send_req; uv_buf_t discover_msg = make_discover_msg(); struct sockaddr_in send_addr; - assert(uv_ip4_addr("255.255.255.255", 67, &send_addr) == 0); + uv_ip4_addr("255.255.255.255", 67, &send_addr); uv_udp_send(&send_req, &send_socket, &discover_msg, 1, (const struct sockaddr *)&send_addr, on_send); return uv_run(loop, UV_RUN_DEFAULT); diff --git a/source/networking.rst b/source/networking.rst index a3d7cfc..697a0a7 100644 --- a/source/networking.rst +++ b/source/networking.rst @@ -20,9 +20,9 @@ Server Server sockets proceed by: -1. ``uv_tcp_init`` the TCP watcher. +1. ``uv_tcp_init`` the TCP handle. 2. ``uv_tcp_bind`` it. -3. Call ``uv_listen`` on the watcher to have a callback invoked whenever a new +3. Call ``uv_listen`` on the handle to have a callback invoked whenever a new connection is established by a client. 4. Use ``uv_accept`` to accept the connection. 5. Use :ref:`stream operations ` to communicate with the @@ -33,8 +33,8 @@ Here is a simple echo server .. rubric:: tcp-echo-server/main.c - The listen socket .. literalinclude:: ../code/tcp-echo-server/main.c :linenos: - :lines: 54- - :emphasize-lines: 4-5,7-9 + :lines: 56- + :emphasize-lines: 4-5,7-10 You can see the utility function ``uv_ip4_addr`` being used to convert from a human readable IP address, port pair to the sockaddr_in structure required by @@ -49,13 +49,13 @@ Most of the setup functions are synchronous since they are CPU-bound. arguments is the backlog queue -- the maximum length of queued connections. When a connection is initiated by clients, the callback is required to set up -a watcher for the client socket and associate the watcher using ``uv_accept``. +a handle for the client socket and associate the handle using ``uv_accept``. In this case we also establish interest in reading from this stream. .. rubric:: tcp-echo-server/main.c - Accepting the client .. literalinclude:: ../code/tcp-echo-server/main.c :linenos: - :lines: 38-52 + :lines: 38-54 :emphasize-lines: 9-10 The remaining set of functions is very similar to the streams example and can @@ -75,22 +75,25 @@ callback of ``uv_listen`` is used by ``uv_tcp_connect``. Try:: uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t)); - struct sockaddr_in dest = uv_ip4_addr("127.0.0.1", 80); + struct sockaddr_in dest; + uv_ip4_addr("127.0.0.1", 80, &dest); uv_tcp_connect(connect, socket, dest, on_connect); -where ``on_connect`` will be called after the connection is established. +where ``on_connect`` will be called after the connection is established. The +callback receives the ``uv_connect_t`` struct, which has a member ``.handle`` +pointing to the socket. UDP --- The `User Datagram Protocol`_ offers connectionless, unreliable network communication. Hence libuv doesn't offer a stream. Instead libuv provides -non-blocking UDP support via the `uv_udp_t` (for receiving) and `uv_udp_send_t` -(for sending) structures and related functions. That said, the actual API for -reading/writing is very similar to normal stream reads. To look at how UDP can -be used, the example shows the first stage of obtaining an IP address from -a `DHCP`_ server -- DHCP Discover. +non-blocking UDP support via the `uv_udp_t` handle (for receiving) and +`uv_udp_send_t` request (for sending) and related functions. That said, the +actual API for reading/writing is very similar to normal stream reads. To look +at how UDP can be used, the example shows the first stage of obtaining an IP +address from a `DHCP`_ server -- DHCP Discover. .. note:: @@ -100,7 +103,7 @@ a `DHCP`_ server -- DHCP Discover. .. rubric:: udp-dhcp/main.c - Setup and send UDP packets .. literalinclude:: ../code/udp-dhcp/main.c :linenos: - :lines: 7-11,107- + :lines: 7-11,104- :emphasize-lines: 8,10-11,17-18,21 .. note:: @@ -111,23 +114,26 @@ a `DHCP`_ server -- DHCP Discover. randomly assigns a port. First we setup the receiving socket to bind on all interfaces on port 68 (DHCP -client) and start a read watcher on it. Then we setup a similar send socket and -use ``uv_udp_send`` to send a *broadcast message* on port 67 (DHCP server). +client) and start a read on it. This will read back responses from any DHCP +server that replies. We use the UV_UDP_REUSEADDR flag to play nice with any +other system DHCP clients that are running on this computer on the same port. +Then we setup a similar send socket and use ``uv_udp_send`` to send +a *broadcast message* on port 67 (DHCP server). It is **necessary** to set the broadcast flag, otherwise you will get an -``EACCES`` error [#]_. The exact message being sent is irrelevant to this book -and you can study the code if you are interested. As usual the read and write -callbacks will receive a status code of -1 if something went wrong. +``EACCES`` error [#]_. The exact message being sent is not relevant to this +book and you can study the code if you are interested. As usual the read and +write callbacks will receive a status code of < 0 if something went wrong. Since UDP sockets are not connected to a particular peer, the read callback receives an extra parameter about the sender of the packet. -``nread`` may be zero if there is no more data to be read. If ``addr`` is not -NULL, it indicates that an empty datagram was received from the host at -``addr``. The ``flags`` -parameter may be ``UV_UDP_PARTIAL`` if the buffer provided by your allocator -was not large enough to hold the data. *In this case the OS will discard the -data that could not fit* (That's UDP for you!). +``nread`` may be zero if there is no more data to be read. If ``addr`` is NULL, +it indicates there is nothing to read (the callback shouldn't do anything), if +not NULL, it indicates that an empty datagram was received from the host at +``addr``. The ``flags`` parameter may be ``UV_UDP_PARTIAL`` if the buffer +provided by your allocator was not large enough to hold the data. *In this case +the OS will discard the data that could not fit* (That's UDP for you!). .. rubric:: udp-dhcp/main.c - Reading packets .. literalinclude:: ../code/udp-dhcp/main.c @@ -156,10 +162,14 @@ Multicast A socket can (un)subscribe to a multicast group using: .. literalinclude:: ../libuv/include/uv.h - :lines: 1021-1024 + :lines: 594-597 where ``membership`` is ``UV_JOIN_GROUP`` or ``UV_LEAVE_GROUP``. +The concepts of multicasting are nicely explained in `this guide`_. + +.. _this guide: http://www.tldp.org/HOWTO/Multicast-HOWTO-2.html + Local loopback of multicast packets is enabled by default [#]_, use ``uv_udp_set_multicast_loop`` to switch it off. @@ -183,7 +193,8 @@ Freenode to see an example of DNS resolution. If ``uv_getaddrinfo`` returns non-zero, something went wrong in the setup and your callback won't be invoked at all. All arguments can be freed immediately after ``uv_getaddrinfo`` returns. The `hostname`, `servname` and `hints` -structures are documented in `the getaddrinfo man page `_. +structures are documented in `the getaddrinfo man page `_. The +callback can be ``NULL`` in which case the function will run synchronously. In the resolver callback, you can pick any IP from the linked list of ``struct addrinfo(s)``. This also demonstrates ``uv_tcp_connect``. It is necessary to @@ -195,6 +206,10 @@ call ``uv_freeaddrinfo`` in the callback. :lines: 42-60 :emphasize-lines: 8,16 +libuv also provides the inverse `uv_getnameinfo`_. + +.. _uv_getnameinfo: http://docs.libuv.org/en/v1.x/dns.html#c.uv_getnameinfo + Network interfaces ------------------