fixes #759 Add some demo programs
diff --git a/demo/README b/demo/README
new file mode 100644
index 0000000..4ee4cf8
--- /dev/null
+++ b/demo/README
@@ -0,0 +1,5 @@
+The programs here are intended to demonstrate how to use the nanomsg API.
+The intention is that you can use these programs (or parts thereof) in
+your own code.
+
+We welcome further contributions here.
diff --git a/demo/async_demo.c b/demo/async_demo.c
new file mode 100644
index 0000000..b82a2aa
--- /dev/null
+++ b/demo/async_demo.c
@@ -0,0 +1,284 @@
+/*
+ Copyright 2016 Garrett D'Amore <garrett@damore.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+ "nanomsg" is a trademark of Martin Sustrik
+*/
+
+/* This program serves as an example for how to write an async RPC service,
+ using the RAW request/reply pattern and nn_poll. The server receives
+ messages and keeps them on a list, replying to them.
+
+ Our demonstration application layer protocol is simple. The client sends
+ a number of milliseconds to wait before responding. The server just gives
+ back an empty reply after waiting that long.
+
+ To run this program, start the server as async_demo <url> -s
+ Then connect to it with the client as async_client <url> <msec>.
+
+ For example:
+
+ % ./async_demo tcp://127.0.0.1:5555 -s &
+ % ./async_demo tcp://127.0.0.1:5555 323
+ Request took 324 milliseconds.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <nanomsg/nn.h>
+#include <nanomsg/reqrep.h>
+
+/* MAXJOBS is a limit on the on the number of outstanding requests we
+ can queue. We will not accept new inbound jobs if we have more than
+ this queued. The reason for this limit is to prevent a bad client
+ from consuming all server resources with new job requests. */
+
+#define MAXJOBS 100
+
+/* The server keeps a list of work items, sorted by expiration time,
+ so that we can use this to set the timeout to the correct value for
+ use in poll. */
+struct work {
+ struct work *next;
+ struct nn_msghdr request;
+ uint64_t expire;
+ void *control;
+};
+
+/* Return the UNIX time in milliseconds. You'll need a working
+ gettimeofday(), so this won't work on Windows. */
+uint64_t milliseconds (void)
+{
+ struct timeval tv;
+ gettimeofday (&tv, NULL);
+ return (((uint64_t)tv.tv_sec * 1000) + ((uint64_t)tv.tv_usec / 1000));
+}
+
+/* The server runs forever. */
+int server(const char *url)
+{
+ int fd;
+ struct work *worklist = NULL;
+ int npending = 0;
+
+ /* Create the socket. */
+ fd = nn_socket(AF_SP_RAW, NN_REP);
+ if (fd < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ return (-1);
+ }
+
+ /* Bind to the URL. This will bind to the address and listen
+ synchronously; new clients will be accepted asynchronously
+ without further action from the calling program. */
+
+ if (nn_bind (fd, url) < 0) {
+ fprintf (stderr, "nn_bind: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ /* Main processing loop. */
+
+ for (;;) {
+ uint32_t timer;
+ int rc;
+ int timeout;
+ uint64_t now;
+ struct work *work, **srch;
+ uint8_t *body;
+ void *control;
+ struct nn_iovec iov;
+ struct nn_msghdr hdr;
+ struct nn_pollfd pfd[1];
+
+ /* Figure out if any work requests are finished, and can be
+ responded to. */
+
+ timeout = -1;
+ while ((work = worklist) != NULL) {
+
+ now = milliseconds ();
+ if (work->expire > now) {
+ timeout = (work->expire - now);
+ break;
+ }
+ worklist = work->next;
+ npending--;
+ rc = nn_sendmsg (fd, &work->request, NN_DONTWAIT);
+ if (rc < 0) {
+ fprintf (stderr, "nn_sendmsg: %s\n",
+ nn_strerror (nn_errno ()));
+ nn_freemsg (work->request.msg_control);
+ }
+ free (work);
+ }
+
+ /* This check ensures that we don't allow more than a set limit
+ of concurrent jobs to be queued. This protects us from resource
+ exhaustion by malicious or defective clients. */
+
+ if (npending >= MAXJOBS) {
+ nn_poll (pfd, 0, timeout);
+ continue;
+ }
+
+ pfd[0].fd = fd;
+ pfd[0].events = NN_POLLIN;
+ pfd[0].revents = 0;
+ nn_poll (pfd, 1, timeout);
+
+ if ((pfd[0].revents & NN_POLLIN) == 0) {
+ continue;
+ }
+
+ /* So there should be a message waiting for us to receive.
+ We handle it by parsing it, creating a work request for it,
+ and adding the work request to the worklist. */
+
+ memset (&hdr, 0, sizeof (hdr));
+ control = NULL;
+ iov.iov_base = &body;
+ iov.iov_len = NN_MSG;
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ hdr.msg_control = &control;
+ hdr.msg_controllen = NN_MSG;
+
+ rc = nn_recvmsg (fd, &hdr, 0);
+ if (rc < 0) {
+ /* Any error here is unexpected. */
+ fprintf (stderr, "nn_recv: %s\n", nn_strerror (nn_errno ()));
+ break;
+ }
+ if (rc != sizeof (uint32_t)) {
+ fprintf (stderr, "nn_recv: wanted %d, but got %d\n",
+ (int) sizeof (uint32_t), rc);
+ nn_freemsg (body);
+ nn_freemsg (control);
+ continue;
+ }
+
+ memcpy (&timer, body, sizeof (timer));
+ nn_freemsg (body);
+
+ work = malloc (sizeof (*work));
+ if (work == NULL) {
+ fprintf (stderr, "malloc: %s\n", strerror (errno));
+ /* Fatal error -- other programs can try to handle it better. */
+ break;
+ }
+ memset (work, 0, sizeof (*work));
+ work->expire = milliseconds () + ntohl (timer);
+ work->control = control;
+ work->request.msg_iovlen = 0; /* No payload data to send. */
+ work->request.msg_iov = NULL;
+ work->request.msg_control = &work->control;
+ work->request.msg_controllen = NN_MSG;
+
+ /* Insert the work request into the list in order. */
+ srch = &worklist;
+ for (;;) {
+ if ((*srch == NULL) || ((*srch)->expire > work->expire)) {
+ work->next = *srch;
+ *srch = work;
+ npending++;
+ break;
+ }
+ srch = &((*srch)->next);
+ }
+ }
+
+ /* This may wind up orphaning requests in the queue. We are going
+ to exit with an error anyway, so don't worry about it. */
+
+ nn_close (fd);
+ return (-1);
+}
+
+/* The client runs just once, and then returns. */
+int client (const char *url, const char *msecstr)
+{
+ int fd;
+ int rc;
+ char *greeting;
+ uint8_t msg[sizeof (uint32_t)];
+ uint64_t start;
+ uint64_t end;
+ unsigned msec;
+
+ msec = atoi(msecstr);
+
+ fd = nn_socket (AF_SP, NN_REQ);
+ if (fd < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ return (-1);
+ }
+
+ if (nn_connect (fd, url) < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ msec = htonl(msec);
+ memcpy (msg, &msec, sizeof (msec));
+
+ start = milliseconds ();
+
+ if (nn_send (fd, msg, sizeof (msg), 0) < 0) {
+ fprintf (stderr, "nn_send: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ rc = nn_recv (fd, &msg, sizeof (msg), 0);
+ if (rc < 0) {
+ fprintf (stderr, "nn_recv: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ nn_close (fd);
+
+ end = milliseconds ();
+
+ printf ("Request took %u milliseconds.\n", (uint32_t)(end - start));
+ return (0);
+}
+
+int main (int argc, char **argv)
+{
+ int rc;
+ if (argc < 3) {
+ fprintf (stderr, "Usage: %s <url> [-s|name]\n", argv[0]);
+ exit (EXIT_FAILURE);
+ }
+ if (strcmp (argv[2], "-s") == 0) {
+ rc = server (argv[1]);
+ } else {
+ rc = client (argv[1], argv[2]);
+ }
+ exit (rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/demo/pthread_demo.c b/demo/pthread_demo.c
new file mode 100644
index 0000000..b377682
--- /dev/null
+++ b/demo/pthread_demo.c
@@ -0,0 +1,244 @@
+/*
+ Copyright 2016 Garrett D'Amore <garrett@damore.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+ "nanomsg" is a trademark of Martin Sustrik
+*/
+
+/* This program serves as an example for how to write a threaded RPC service,
+ using the RAW request/reply pattern and pthreads. Multiple worker threads
+ are spawned on a single socket, and each worker processes jobs in order.
+
+ Our demonstration application layer protocol is simple. The client sends
+ a number of milliseconds to wait before responding. The server just gives
+ back an empty reply after waiting that long.
+
+ To run this program, start the server as pthread_demo <url> -s
+ Then connect to it with the client as pthread_client <url> <msec>.
+
+ For example:
+
+ % ./pthread_demo tcp://127.0.0.1:5555 -s &
+ % ./pthread_demo tcp://127.0.0.1:5555 323
+ Request took 324 milliseconds.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <poll.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+#include <nanomsg/nn.h>
+#include <nanomsg/reqrep.h>
+
+/* MAXWORKERS is a limit on the on the number of workers we will fire
+ off. Since each worker processes jobs sequentially, this is a limit
+ on the concurrency of the server. New inbound messages will queue up
+ waiting for a worker to receive them. */
+
+#define MAXWORKERS 100
+
+/* Return the UNIX time in milliseconds. You'll need a working
+ gettimeofday(), so this won't work on Windows. */
+uint64_t milliseconds (void)
+{
+ struct timeval tv;
+ gettimeofday (&tv, NULL);
+ return (((uint64_t)tv.tv_sec * 1000) + ((uint64_t)tv.tv_usec / 1000));
+}
+
+void *worker (void *arg)
+{
+ int fd = (intptr_t)arg;
+
+ /* Main processing loop. */
+
+ for (;;) {
+ uint32_t timer;
+ int rc;
+ int timeout;
+ uint8_t *body;
+ void *control;
+ struct nn_iovec iov;
+ struct nn_msghdr hdr;
+
+ memset (&hdr, 0, sizeof (hdr));
+ control = NULL;
+ iov.iov_base = &body;
+ iov.iov_len = NN_MSG;
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ hdr.msg_control = &control;
+ hdr.msg_controllen = NN_MSG;
+
+ rc = nn_recvmsg (fd, &hdr, 0);
+ if (rc < 0) {
+ if (nn_errno () == EBADF) {
+ return (NULL); /* Socket closed by another thread. */
+ }
+ /* Any error here is unexpected. */
+ fprintf (stderr, "nn_recv: %s\n", nn_strerror (nn_errno ()));
+ break;
+ }
+
+ if (rc != sizeof (uint32_t)) {
+ fprintf (stderr, "nn_recv: wanted %d, but got %d\n",
+ (int) sizeof (uint32_t), rc);
+ nn_freemsg (body);
+ nn_freemsg (control);
+ continue;
+ }
+
+ memcpy (&timer, body, sizeof (timer));
+ nn_freemsg (body);
+
+ /* Poor mans usleep but in msec. */
+ poll (NULL, 0, ntohl (timer));
+
+ hdr.msg_iov = NULL;
+ hdr.msg_iovlen = 0;
+
+ rc = nn_sendmsg (fd, &hdr, 0);
+ if (rc < 0) {
+ fprintf (stderr, "nn_send: %s\n", nn_strerror (nn_errno ()));
+ nn_freemsg (control);
+ }
+ }
+
+ /* We got here, so close the file. That will cause the other threads
+ to shut down too. */
+
+ nn_close (fd);
+ return (NULL);
+}
+/* The server runs forever. */
+int server(const char *url)
+{
+ int fd;
+ int i;
+ pthread_t tids [MAXWORKERS];
+ int rc;
+
+ /* Create the socket. */
+ fd = nn_socket(AF_SP_RAW, NN_REP);
+ if (fd < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ return (-1);
+ }
+
+ /* Bind to the URL. This will bind to the address and listen
+ synchronously; new clients will be accepted asynchronously
+ without further action from the calling program. */
+
+ if (nn_bind (fd, url) < 0) {
+ fprintf (stderr, "nn_bind: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ memset (tids, 0, sizeof (tids));
+
+ /* Start up the threads. */
+ for (i = 0; i < MAXWORKERS; i++) {
+ rc = pthread_create (&tids[i], NULL, worker, (void *)(intptr_t)fd);
+ if (rc < 0) {
+ fprintf (stderr, "pthread_create: %s\n", strerror (rc));
+ nn_close (fd);
+ break;
+ }
+ }
+
+ /* Now wait on them to finish. */
+ for (i = 0; i < MAXWORKERS; i++) {
+ if (tids[i] != NULL) {
+ pthread_join (tids[i], NULL);
+ }
+ }
+ return (-1);
+}
+
+/* The client runs just once, and then returns. */
+int client (const char *url, const char *msecstr)
+{
+ int fd;
+ int rc;
+ char *greeting;
+ uint8_t msg[sizeof (uint32_t)];
+ uint64_t start;
+ uint64_t end;
+ unsigned msec;
+
+ msec = atoi(msecstr);
+
+ fd = nn_socket (AF_SP, NN_REQ);
+ if (fd < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ return (-1);
+ }
+
+ if (nn_connect (fd, url) < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ msec = htonl(msec);
+ memcpy (msg, &msec, sizeof (msec));
+
+ start = milliseconds ();
+
+ if (nn_send (fd, msg, sizeof (msg), 0) < 0) {
+ fprintf (stderr, "nn_send: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ rc = nn_recv (fd, &msg, sizeof (msg), 0);
+ if (rc < 0) {
+ fprintf (stderr, "nn_recv: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ nn_close (fd);
+
+ end = milliseconds ();
+
+ printf ("Request took %u milliseconds.\n", (uint32_t)(end - start));
+ return (0);
+}
+
+int main (int argc, char **argv)
+{
+ int rc;
+ if (argc < 3) {
+ fprintf (stderr, "Usage: %s <url> [-s|name]\n", argv[0]);
+ exit (EXIT_FAILURE);
+ }
+ if (strcmp (argv[2], "-s") == 0) {
+ rc = server (argv[1]);
+ } else {
+ rc = client (argv[1], argv[2]);
+ }
+ exit (rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/demo/pubsub_demo.c b/demo/pubsub_demo.c
new file mode 100644
index 0000000..f9a8fc7
--- /dev/null
+++ b/demo/pubsub_demo.c
@@ -0,0 +1,178 @@
+/*
+ Copyright 2016 Garrett D'Amore <garrett@damore.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+ "nanomsg" is a trademark of Martin Sustrik
+*/
+
+/* This program serves as an example for how to write a simple PUB SUB
+ service, The server is just a single threaded for loop which broadcasts
+ messages to clients, every so often. The message is a binary format
+ message, containing two 32-bit unsigned integers. The first is UNIX time,
+ and the second is the number of directly connected subscribers.
+
+ The clients stay connected and print a message with this information
+ along with their process ID to standard output.
+
+ To run this program, start the server as pubsub_demo <url> -s
+ Then connect to it with the client as pubsub_demo <url>
+ For example:
+
+ % ./pubsub_demo tcp://127.0.0.1:5555 -s &
+ % ./pubsub_demo tcp://127.0.0.1:5555 &
+ % ./pubsub_demo tcp://127.0.0.1:5555 &
+ 11:23:54 <pid 1254> There are 2 clients connected.
+ 11:24:04 <pid 1255> There are 2 clients connected.
+ ..
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <netinet/in.h> /* For htonl and ntohl */
+#include <unistd.h>
+
+#include <nanomsg/nn.h>
+#include <nanomsg/pubsub.h>
+
+/* The server runs forever. */
+int server(const char *url)
+{
+ int fd;
+
+ /* Create the socket. */
+ fd = nn_socket (AF_SP, NN_PUB);
+ if (fd < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ return (-1);
+ }
+
+ /* Bind to the URL. This will bind to the address and listen
+ synchronously; new clients will be accepted asynchronously
+ without further action from the calling program. */
+
+ if (nn_bind (fd, url) < 0) {
+ fprintf (stderr, "nn_bind: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ /* Now we can just publish results. Note that there is no explicit
+ accept required. We just start writing the information. */
+
+ for (;;) {
+ uint8_t msg[2 * sizeof (uint32_t)];
+ uint32_t secs, subs;
+ int rc;
+
+ secs = (uint32_t) time (NULL);
+ subs = (uint32_t) nn_get_statistic (fd, NN_STAT_CURRENT_CONNECTIONS);
+
+ secs = htonl (secs);
+ subs = htonl (subs);
+
+ memcpy (msg, &secs, sizeof (secs));
+ memcpy (msg + sizeof (secs), &subs, sizeof (subs));
+
+ rc = nn_send (fd, msg, sizeof (msg), 0);
+ if (rc < 0) {
+ /* There are several legitimate reasons this can fail.
+ We note them for debugging purposes, but then ignore
+ otherwise. */
+ fprintf (stderr, "nn_send: %s (ignoring)\n",
+ nn_strerror (nn_errno ()));
+ }
+ sleep(10);
+ }
+
+ /* NOTREACHED */
+ nn_close (fd);
+ return (-1);
+}
+
+/* The client runs in a loop, displaying the content. */
+int client (const char *url)
+{
+ int fd;
+ int rc;
+
+ fd = nn_socket (AF_SP, NN_SUB);
+ if (fd < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ return (-1);
+ }
+
+ if (nn_connect (fd, url) < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ /* We want all messages, so just subscribe to the empty value. */
+ if (nn_setsockopt (fd, NN_SUB, NN_SUB_SUBSCRIBE, "", 0) < 0) {
+ fprintf (stderr, "nn_setsockopt: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ for (;;) {
+ uint8_t msg[2 * sizeof (uint32_t)];
+ char hhmmss[9]; /* HH:MM:SS\0 */
+ uint32_t subs, secs;
+ time_t t;
+
+ rc = nn_recv (fd, msg, sizeof (msg), 0);
+ if (rc < 0) {
+ fprintf (stderr, "nn_recv: %s\n", nn_strerror (nn_errno ()));
+ break;
+ }
+ if (rc != sizeof (msg)) {
+ fprintf (stderr, "nn_recv: got %d bytes, wanted %d\n",
+ rc, (int)sizeof (msg));
+ break;
+ }
+ memcpy (&secs, msg, sizeof (secs));
+ memcpy (&subs, msg + sizeof (secs), sizeof (subs));
+
+ t = (time_t) ntohl(secs);
+ strftime (hhmmss, sizeof (hhmmss), "%T", localtime (&t));
+
+ printf ("%s <pid %u> There are %u clients connected.\n", hhmmss,
+ (unsigned) getpid(), (unsigned) ntohl(subs));
+ }
+
+ nn_close (fd);
+ return (-1);
+}
+
+int main (int argc, char **argv)
+{
+ int rc;
+ if ((argc == 3) && (strcmp (argv[2], "-s") == 0)) {
+ rc = server (argv[1]);
+ } else if (argc == 2) {
+ rc = client (argv[1]);
+ } else {
+ fprintf (stderr, "Usage: %s <url> [-s]\n", argv[0]);
+ exit (EXIT_FAILURE);
+ }
+ exit (rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/demo/rpc_demo.c b/demo/rpc_demo.c
new file mode 100644
index 0000000..c8d87d6
--- /dev/null
+++ b/demo/rpc_demo.c
@@ -0,0 +1,207 @@
+/*
+ Copyright 2016 Garrett D'Amore <garrett@damore.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+ "nanomsg" is a trademark of Martin Sustrik
+*/
+
+/* This program serves as an example for how to write a simple RPC service,
+ using the request/reply pattern. The server is just a single threaded
+ for loop, which processes each request. The requests run quickly enough
+ that there is no need for parallelism.
+
+ Our demonstration application layer protocol is simple. The client sends
+ a name, and the server replies with a greeting based on the time of day.
+ The messages are sent in ASCII, and are not zero terminated.
+
+ To run this program, start the server as rpc_demo <url> -s
+ Then connect to it with the client as rpc_client <url> <name>.
+ The client will print a timezone appropriate greeting, based on
+ the time at the server. For example:
+
+ % ./rpc_demo tcp://127.0.0.1:5555 -s &
+ % ./rpc_demo tcp://127.0.0.1:5555 Garrett
+ Good morning, Garrett.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <nanomsg/nn.h>
+#include <nanomsg/reqrep.h>
+
+/* The server runs forever. */
+int server(const char *url)
+{
+ int fd;
+
+ /* Create the socket. */
+ fd = nn_socket (AF_SP, NN_REP);
+ if (fd < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ return (-1);
+ }
+
+ /* Bind to the URL. This will bind to the address and listen
+ synchronously; new clients will be accepted asynchronously
+ without further action from the calling program. */
+
+ if (nn_bind (fd, url) < 0) {
+ fprintf (stderr, "nn_bind: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ /* Now we can just process results. Note that there is no explicit
+ accept required. We just receive a request, and reply to it.
+ Its important to note that we must not issue two receives in a
+ row without replying first, or the following receive(s) will
+ cancel any unreplied requests. */
+
+ for (;;) {
+ char username[128];
+ char greeting[128];
+ time_t secs;
+ struct tm *now;
+ char *daytime;
+ int rc;
+ char *fmt;
+
+ rc = nn_recv (fd, username, sizeof (username), 0);
+ if (rc < 0) {
+ /* Any error here is unexpected. */
+ fprintf (stderr, "nn_recv: %s\n", nn_strerror (nn_errno ()));
+ break;
+ }
+
+ secs = time (NULL);
+ now = localtime (&secs);
+ if (now->tm_hour < 12) {
+ daytime = "morning";
+
+ } else if (now->tm_hour < 17) {
+ daytime = "afternoon";
+
+ } else if (now->tm_hour < 20) {
+ daytime = "evening";
+
+ } else {
+ daytime = "night";
+ }
+
+ /* Ensure ASCIIZ terminated string. */
+ if (rc < sizeof (username)) {
+ username[rc] = '\0';
+ } else {
+ username[sizeof (username) - 1] = '\0';
+ }
+
+ fmt = "Good %s, %s.";
+
+ /* Technically this might be overly pessimistic about size. */
+ if ((strlen (username) + strlen (daytime) + strlen (fmt)) >=
+ sizeof (greeting)) {
+
+ fmt = "I'm sorry, your name is too long. But good %s anyway.";
+ }
+
+ /* snprintf would be safer, but the above check protects us. */
+ sprintf (greeting, fmt, daytime, username);
+
+ rc = nn_send (fd, greeting, strlen (greeting), 0);
+ if (rc < 0) {
+ /* There are several legitimate reasons this can fail.
+ We note them for debugging purposes, but then ignore
+ otherwise. If the socket is closed or failing, we will
+ notice in recv above, and exit then. */
+ fprintf (stderr, "nn_send: %s (ignoring)\n",
+ nn_strerror (nn_errno ()));
+ }
+ }
+
+ nn_close (fd);
+ return (-1);
+}
+
+/* The client runs just once, and then returns. */
+int client (const char *url, const char *username)
+{
+ int fd;
+ int rc;
+ char *greeting;
+ char *msg;
+
+ fd = nn_socket (AF_SP, NN_REQ);
+ if (fd < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ return (-1);
+ }
+
+ if (nn_connect (fd, url) < 0) {
+ fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ if (nn_send (fd, username, strlen (username), 0) < 0) {
+ fprintf (stderr, "nn_send: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ /* Here we ask the library to allocate response buffer for us (NN_MSG). */
+ rc = nn_recv (fd, &msg, NN_MSG, 0);
+ if (rc < 0) {
+ fprintf (stderr, "nn_recv: %s\n", nn_strerror (nn_errno ()));
+ nn_close (fd);
+ return (-1);
+ }
+
+ nn_close (fd);
+
+ /* Response is not ASCIIZ terminated. */
+ greeting = malloc (rc + 1);
+ if (greeting == NULL) {
+ fprintf (stderr, "malloc: %s\n", strerror (errno));
+ return (-1);
+ }
+ memcpy(greeting, msg, rc);
+
+ nn_freemsg (msg);
+ printf ("%s\n", greeting);
+ return (0);
+}
+
+int main (int argc, char **argv)
+{
+ int rc;
+ if (argc < 3) {
+ fprintf (stderr, "Usage: %s <url> [-s|name]\n", argv[0]);
+ exit (EXIT_FAILURE);
+ }
+ if (strcmp (argv[2], "-s") == 0) {
+ rc = server (argv[1]);
+ } else {
+ rc = client (argv[1], argv[2]);
+ }
+ exit (rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/tests/README b/tests/README
index 04c2198..e7088b1 100644
--- a/tests/README
+++ b/tests/README
@@ -1,5 +1,9 @@
This directory contains automatic tests for nanomsg library. To run the tests
-do "make test" in the build directory.
+do "make test" in the build directory, or alternatively run ctest.
-The WebSocket stress test depends upon a separate installation of Autobahn
-Testsuite, available at: http://autobahn.ws/testsuite/installation.html
+Note that the code here is probably ill-suited for demonstration purposes, as
+it is primarily oriented towards testing the library, rather than serving as
+any sort of example.
+
+Instead, we recommend looking in ../demo for some example programs that
+demonstrate the API as we feel it is meant to be used.