|
| 1 | +.. _NATS_Client_Sample: |
| 2 | + |
| 3 | + |
| 4 | +NATS Client Implementation Sample |
| 5 | +################################# |
| 6 | + |
| 7 | + |
| 8 | +Overview |
| 9 | +******** |
| 10 | + |
| 11 | +`NATS <http://nats.io/documentation/internals/nats-protocol/>`__ is a |
| 12 | +publisher/subscriber protocol implemented on top of TCP. It is specified in |
| 13 | +`NATS Protocol documentation <http://nats.io/documentation/internals/nats-protocol/>`__, |
| 14 | +and this is a sample implementation for Zephyr using the new IP stack. |
| 15 | +The API is loosely based off of the `Golang API |
| 16 | +<https://github.com/nats-io/go-nats>`__. |
| 17 | + |
| 18 | +With this sample, it's possible to subscribe/unsubscribe to a given subject, |
| 19 | +and be notified of changes asynchronously. In order to conserve resources, |
| 20 | +the implementation does not keep track of subscribed subjects; that |
| 21 | +must be performed by the application itself, so it can ignore unknown/undesired |
| 22 | +subjects. |
| 23 | + |
| 24 | +TLS is not supported yet, although basic authentication is. The client will indicate |
| 25 | +if it supports username/password if a certain callback is set in the ``struct |
| 26 | +nats``. This callback will then be called, and the user must copy the |
| 27 | +username/password to the supplied user/pass buffers. |
| 28 | + |
| 29 | +Content might be also published for a given subject. |
| 30 | + |
| 31 | +The sample application lets one observe the subject "led0", and turn it |
| 32 | +"on", "off", or "toggle" its value. Changing the value will, if supported, |
| 33 | +act on a status LED on the development board. The new status will be |
| 34 | +published. |
| 35 | + |
| 36 | +Also worth noting is that most of the networking and GPIO boilerplate has |
| 37 | +been shamelessly copied from the IRC bot example. (Curiously, both |
| 38 | +protocols are similar.) |
| 39 | + |
| 40 | +Requirements |
| 41 | +************ |
| 42 | + |
| 43 | +To test the sample, build the Zephyr application for your platform. This |
| 44 | +has only been tested with the QEMU emulator as provided by the Zephyr SDK, |
| 45 | +but it should work with other supported hardware as long as they have enough |
| 46 | +memory, the network stack has TCP enabled, and the connectivity hardware is |
| 47 | +supported. |
| 48 | + |
| 49 | +As far as the software goes, this has been tested with the official `gnatsd |
| 50 | +<https://github.com/nats-io/gnatsd>`__ for the server, and the official |
| 51 | +`go-nats <https://github.com/nats-io/go-nats>`__ client library. Both the |
| 52 | +server and clients were set up as per instructions found in their respective |
| 53 | +``README.md`` files. |
| 54 | + |
| 55 | +The client was a one-off test that is basically the same code provided in |
| 56 | +the `Basic Usage |
| 57 | +<https://github.com/nats-io/go-nats/blob/e6bb81b5a5f37ef7bf364bb6276e13813086c6ee/README.md#basic-usage>`__ |
| 58 | +section as found in the ``go-nats`` README file, however, subscribing to the |
| 59 | +topic used in this sample: ``led0``, and publishing values as described |
| 60 | +above (``on``, ``off``, and ``toggle``). |
| 61 | + |
| 62 | +Library Usage |
| 63 | +************* |
| 64 | + |
| 65 | +Allocate enough space for a ``struct nats``, setting a few callbacks so |
| 66 | +that you're notified as events happen: |
| 67 | + |
| 68 | +:: |
| 69 | + |
| 70 | + struct nats nats_ctx = { |
| 71 | + .on_auth_required = on_auth_required, |
| 72 | + .on_message = on_message |
| 73 | + }; |
| 74 | + |
| 75 | +The ``on_auth_required()`` and ``on_message()`` functions are part of |
| 76 | +your application, and each must have these signatures: |
| 77 | + |
| 78 | +:: |
| 79 | + |
| 80 | + int on_auth_required(struct nats *nats, char **user, char **pass); |
| 81 | + int on_message(struct nats *nats, struct nats_msg *msg); |
| 82 | + |
| 83 | +Both functions should return 0 to signal that they could successfully |
| 84 | +handle their role, and a negative value, if they couldn't for any |
| 85 | +reason. It's recommended to use a negative integer as provided by |
| 86 | +errno.h in order to ease debugging. |
| 87 | + |
| 88 | +The first function, ``on_auth_required()``, is called if the server |
| 89 | +notifies that it requires authentication. It's not going to be called if |
| 90 | +that's not the case, so it is optional. However, if the server asks for |
| 91 | +credentials and this function is not provided, the connection will be |
| 92 | +closed and an error will be returned by ``nats_connect()``. |
| 93 | + |
| 94 | +The second function, ``on_message()``, will be called whenever the |
| 95 | +server has been notified of a value change. The ``struct nats_msg`` has the |
| 96 | +following fields: |
| 97 | + |
| 98 | +:: |
| 99 | + |
| 100 | + struct nats_msg { |
| 101 | + const char *subject; |
| 102 | + const char *sid; |
| 103 | + const char *reply_to; |
| 104 | + }; |
| 105 | + |
| 106 | +The field ``reply_to`` may be passed directly to ``nats_publish()``, |
| 107 | +in order to publish a reply to this message. If it's ``NULL`` (no |
| 108 | +reply-to field in the message from the server), the |
| 109 | +``nats_publish()`` function will not reply to a specific mailbox and |
| 110 | +will just update the topic value. |
| 111 | + |
| 112 | +In order to manage topic subscription, these functions can be used: |
| 113 | + |
| 114 | +:: |
| 115 | + |
| 116 | + int nats_subscribe(struct nats *nats, const char *subject, |
| 117 | + const char *queue_group, const char *sid); |
| 118 | + |
| 119 | +``subject`` and ``sid`` are validated so that they're actually valid |
| 120 | +per the protocol rules. ``-EINVAL`` is returned if they're not. |
| 121 | + |
| 122 | +If ``queue_group`` is NULL, it's not sent to the server. |
| 123 | + |
| 124 | +:: |
| 125 | + |
| 126 | + int nats_unsubscribe(struct nats *nats, const char *sid, |
| 127 | + size_t max_msgs); |
| 128 | + |
| 129 | +``sid`` is validated so it's actually valid per the protocol rules. |
| 130 | +-EINVAL is returned if it's not. |
| 131 | + |
| 132 | +``max_msgs`` specifies the number of messages that the server will |
| 133 | +send before actually unsubscribing the message. Can be 0 to |
| 134 | +immediately unsubscribe. |
| 135 | + |
| 136 | +Both of these functions will return ``-ENOMEM`` if they couldn't build |
| 137 | +the message to transmit to the server. They can also return any error |
| 138 | +that ``net_context_send()`` can return. |
| 139 | + |
| 140 | +In order to conserve resources, the Zephyr implementation will not make |
| 141 | +not of subscribed topics. This is left as a task for the user of the API |
| 142 | +to handle, for instance, when the ``on_message()`` callback is called. |
| 143 | + |
| 144 | +Topics can be published by using the following function: |
| 145 | + |
| 146 | +:: |
| 147 | + |
| 148 | + int nats_publish(struct nats *nats, const char *subject, |
| 149 | + const char *reply_to, const char *payload, |
| 150 | + size_t payload_len); |
| 151 | + |
| 152 | +As usual, ``subject`` is validated and ``-EINVAL`` will be returned if |
| 153 | +it's in an invalid format. The ``reply_to`` field can be ``NULL``, in |
| 154 | +which case, subscribers to this topic won't receive this information as |
| 155 | +well. |
| 156 | + |
| 157 | +As ``net_subscribe()`` and ``net_unsubscribe()``, this function can |
| 158 | +return ``ENOMEM`` -or any other errors that ``net_context_send()`` |
| 159 | +returns. |
0 commit comments