123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /*
- 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 device service, using
- a rendezvous. This works by having the program support three modes. The protocol is
- is REQ/REP, where the REQ is a name, and the REP is a greeting based on the name, and
- an instance number (we use the process ID) and time of day.
- We provide a rendezvous server running the device code, where servers and clients can
- connect. Both sides of the device are in bind mode, and both servers and clients run
- in connect mode. This lets us support many servers and clients simultaneously.
- For example, if I want to have servers rendezvous at port 5554 and clients at port 5555:
- % ./device_demo -d tcp://127.0.0.1:5554 tcp://127.0.0.1:5555 &
- % ./device_demo -s tcp://127.0.0.1:5554 &
- % ./device_demo -c tcp://127.0.0.1:5555 Garrett
- Good morning, Garrett.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <unistd.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);
- }
- /* Connect to the URL. This will connect to the address and listen
- synchronously; new clients will be accepted asynchronously
- without further action from the calling program. */
- if (nn_connect (fd, url) < 0) {
- fprintf (stderr, "nn_connect: %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 (from %d).";
- /* 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, (int)getpid());
- 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);
- }
- usleep(1000);
- 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 = calloc (rc + 1, 1);
- if (greeting == NULL) {
- fprintf (stderr, "calloc: %s\n", strerror (errno));
- return (-1);
- }
- memcpy(greeting, msg, rc);
- nn_freemsg (msg);
- printf ("%s\n", greeting);
- free (greeting);
- return (0);
- }
- int device (const char *url1, const char *url2)
- {
- int s1, s2, rv;
- s1 = nn_socket (AF_SP_RAW, NN_REQ);
- if (s1 < 0) {
- fprintf (stderr, "nn_socket: %s\n", nn_strerror (nn_errno ()));
- return (-1);
- }
- if (nn_bind (s1, url1) < 0) {
- fprintf (stderr, "nn_bind1(%s): %s\n", url1, nn_strerror (nn_errno ()));
- return (-1);
- }
- s2 = nn_socket (AF_SP_RAW, NN_REP);
- if (s2 < 0) {
- fprintf (stderr, "nn_socket: %s\n", nn_strerror(nn_errno ()));
- return (-1);
- }
- if (nn_bind (s2, url2) < 0) {
- fprintf (stderr, "nn_bind2(%s): %s\n", url2, nn_strerror (nn_errno ()));
- return (-1);
- }
- if (nn_device (s1, s2) != 0) {
- fprintf (stderr, "nn_device: %s\n", nn_strerror (nn_errno ()));
- return (-1);
- }
- return (0);
- }
- int main (int argc, char **argv)
- {
- int rc;
- if ((argc == 3) && (strcmp (argv[1], "-s") == 0)) {
- rc = server (argv[2]);
- } else if ((argc == 4) && (strcmp (argv[1], "-d") == 0)) {
- rc = device (argv[2], argv[3]);
- } else if ((argc == 4) && (strcmp (argv[1], "-c") == 0)) {
- rc = client (argv[2], argv[3]);
- } else {
- fprintf (stderr, "Usage: %s -s <serverurl>\n", argv[0]);
- fprintf (stderr, "Usage: %s -d <serverurl> <clienturl>\n", argv[0]);
- fprintf (stderr, "Usage: %s -c <clienturl> <name>\n", argv[0]);
- exit (EXIT_FAILURE);
- }
-
- exit (rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
- }
|