Skip to content
This repository has been archived by the owner on Nov 20, 2022. It is now read-only.

Commit

Permalink
Update networking chapter
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilm committed Feb 5, 2015
1 parent a260e6d commit 9098cf9
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 43 deletions.
3 changes: 1 addition & 2 deletions code/dns/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
Expand Down
16 changes: 9 additions & 7 deletions code/tcp-echo-server/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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;
}
Expand Down
14 changes: 7 additions & 7 deletions code/udp-dhcp/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand All @@ -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);
Expand Down
69 changes: 42 additions & 27 deletions source/networking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <buffers-and-streams>` to communicate with the
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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::

Expand All @@ -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::
Expand All @@ -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
Expand Down Expand Up @@ -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.

Expand All @@ -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 <getaddrinfo>`_.
structures are documented in `the getaddrinfo man page <getaddrinfo>`_. 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
Expand All @@ -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
------------------

Expand Down

0 comments on commit 9098cf9

Please sign in to comment.