NANOMSG Experiment--pubsub
The Publish subscription model is a common feature provided by many message middleware. The message mechanism enables the message publisher and the message to receive (consume) people
To decouple. PubSub mode is also one of the message models directly supported by NANOMSG, so through the PubSub model experiment,
At the same time, we also have a general understanding of the basic usage of nanomsg.
Service Side
The code is as follows |
Copy Code |
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <unistd.h> #include <nanomsg/nn.h> #include <nanomsg/pubsub.h> void usage (const char *name) { fprintf (stderr, "%s [Bind Url]n", name); } int main (int argc, char **argv) { if (argc!= 2) { Usage (argv[0]); Exit (-1); } const char *url = argv[1]; int sock = Nn_socket (af_sp, nn_pub); if (sock < 0) { fprintf (stderr, "Nn_socket failed:%sn", Nn_strerror (errno)); Exit (-1); } if (Nn_bind (sock, URL) < 0) { fprintf (stderr, "Nn_bind failed:%sn", Nn_strerror (errno)); Exit (-1); } while (1) { time_t Rawtime; struct TM * TIMEINFO; Time (&rawtime); Timeinfo = LocalTime (&rawtime); Char *text = asctime (Timeinfo); int Textlen = strlen (text); Text[textlen-1] = '; printf ("Server:publishing DATE%sn", text); Nn_send (sock, text, Textlen, 0); Sleep (1); } return 0; } |
Nanomsg is very simple to use, as long as the direct include nanomsg/nn.h, you can use the basic API. Using the built-in communication mode,
need to introduce corresponding header files, such as PubSub mode, the introduction of Nonomsg/pubsub.h can be.
PubSub server, you need to first create a socket through a nn_socket call, which mimics the POSIX interface,
function returns a file descriptor. Therefore, you can determine whether or not to create a success directly by judging whether the return value is greater than 0. Note that the second parameter is a protocol,
The corresponding macros are defined in the protocol-related header file. All operations are then based on this file descriptor.
Like Berkeley sockets, the server needs to bind a port, nanomsg need to bind a URL. The formats currently supported by NANOMSG are:
* In-process communication (INPROC): URL format is inproc://test
* Inter-process (IPC): URL format is IPC:///TMP/TEST.IPC
* TCP communication: URL format is tcp://*:5555
GitHub on the source seems to have supported WebSocket.
The nanomsg error is the same as UNIX, and the errno is set after the failure, and the corresponding error text can be obtained by Nn_strerror.
After the bind is over, you can send a message to the socket via the Nn_send function. This function parameter is similar to the Berkeley Sockets API interface.
The current time is directly obtained here and then sent to all subscribers.
Client
The code is as follows |
Copy Code |
#include <stdio.h> #include <stdlib.h> #include <nanomsg/nn.h> #include <nanomsg/pubsub.h> int main (int argc, char **argv) { if (argc!= 3) { fprintf (stderr, "Usage:%s NAME bind_urln", argv[0]); Exit (-1); } const char *name = argv[1]; const char *url = argv[2]; int sock = Nn_socket (af_sp, nn_sub); if (sock < 0) { fprintf (stderr, "fail to create socket:%SN", Nn_strerror (errno)); Exit (-1); } if (nn_setsockopt (sock, Nn_sub, Nn_sub_subscribe, "", 0) < 0) { fprintf (stderr, "fail to set Sorket opts:%sn", Nn_strerror (errno)); Exit (-1); } if (Nn_connect (sock, URL) < 0) { fprintf (stderr, "fail to connect to%s:%sn", URL, Nn_strerror (errno)); Exit (-1); } while (1) { char *buf = NULL; int bytes = NN_RECV (sock, &buf, nn_msg, 0); printf ("CLIENT (%s): RECEIVED%sn", name, BUF); Nn_freemsg (BUF); } Nn_shutdown (sock, 0); return 0; } |
Client initialization is similar to the service side, and the current socket needs to be set to a message subscriber through nn_setsockopt before the server is connected. The
then connects the publisher through the Nn_connect, with the same parameters as the service-side bind, and also a socket, a URL.
The URL here is the same as the URL of the service-side bind. After that, a dead loop is constantly receiving messages from the publisher. The
test
is compiled first, the same as normal C programs, and only adds link nanomsg.
Gcc-o pubserver pubserver.c-lnanomsg
gcc-o pubclient pubclient.c-lnanomsg
To facilitate testing, write a simple sh Ell script:
code is as follows |
copy code |
#!/bin/bash base= "$ (CD" $ (dirname "$") "&& pwd" PU b= $BASE/pubserver sub= $BASE/pubclient url= "tcp://127.0.0.1:1234" Echo "Start Pubserver to bind TCP : $URL $PUB tcp://127.0.0.1:1234 & Echo ' start to start pubclient ' for ((i = 0; I < i++) do echo "Start client$i" $SUB client$i $URL & sleep 1 Done Sleep Echo, kill all process and exit for-pid in ' jobs-p ' do echo Kill $pi D " kill $pid Done Wait |
The script is simple, start a message publisher first, and then start a message recipient per second. After waiting for 20s, kill all child processes.
The output of the script:
The code is as follows |
Copy Code |
Start Pubserver to bind tcp:tcp://127.0.0.1:1234 Start to start pubclient Start client0 Server:publishing DATE Tue Feb 17 15:12:11 2015 Start CLIENT1 Server:publishing DATE Tue Feb 17 15:12:12 2015 CLIENT (client0): RECEIVED Tue Feb 17 15:12:12 2015 CLIENT (CLIENT1): RECEIVED Tue Feb 17 15:12:12 2015 Start Client2 Server:publishing DATE Tue Feb 17 15:12:13 2015 CLIENT (client0): RECEIVED Tue Feb 17 15:12:13 2015 CLIENT (CLIENT1): RECEIVED Tue Feb 17 15:12:13 2015 CLIENT (CLIENT2): RECEIVED Tue Feb 17 15:12:13 2015 Start Client3 Server:publishing DATE Tue Feb 17 15:12:14 2015 CLIENT (client0): RECEIVED Tue Feb 17 15:12:14 2015 CLIENT (CLIENT1): RECEIVED Tue Feb 17 15:12:14 2015 CLIENT (CLIENT2): RECEIVED Tue Feb 17 15:12:14 2015 ... Server:publishing DATE Tue Feb 17 15:12:41 2015 CLIENT (client0): RECEIVED Tue Feb 17 15:12:41 2015 CLIENT (CLIENT1): RECEIVED Tue Feb 17 15:12:41 2015 CLIENT (CLIENT2): RECEIVED Tue Feb 17 15:12:41 2015 CLIENT (CLIENT3): RECEIVED Tue Feb 17 15:12:41 2015 CLIENT (CLIENT4): RECEIVED Tue Feb 17 15:12:41 2015 CLIENT (CLIENT5): RECEIVED Tue Feb 17 15:12:41 2015 CLIENT (CLIENT6): RECEIVED Tue Feb 17 15:12:41 2015 CLIENT (client7): RECEIVED Tue Feb 17 15:12:41 2015 CLIENT (client8): RECEIVED Tue Feb 17 15:12:41 2015 CLIENT (CLIENT9): RECEIVED Tue Feb 17 15:12:41 2015 Kill all process and exit |
You can see that each time a new subscriber is started, each Subscriber can receive the current time of the publisher's publication.
nanomsg Experiment--survey
The survey mode is a pattern that is queried by the server, and the client responds to requests for replies. This pattern is very useful in distributed systems,
Can be used to do service discovery, distributed things and other distributed queries.
Client
The client implementation is more convenient, in addition to the basic call (create socket, connection URL), is to receive the server query first
(in the case of simple, server-side inquiries are fixed, so there is no check on the content) to send a response to the query
(In the example, the current time of the Send server)
The code is as follows |
Copy Code |
#include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <nanomsg/nn.h> #include <nanomsg/survey.h> using namespace Std; int main (int argc, const char **argv) { if (argc!= 3) { fprintf (stderr, "Usage:%s NAME urln", argv[0]); Exit (-1); } const char *name = argv[1]; const char *url = argv[2]; int sock = Nn_socket (af_sp, nn_respondent); if (sock < 0) { fprintf (stderr, "Nn_socket fail:%sn", Nn_strerror (errno)); Exit (-1); } if (Nn_connect (sock, URL) < 0) { fprintf (stderr, "Nn_connect fail:%sn", Nn_strerror (errno)); Exit (-1); } while (1) { char *buf = NULL; int bytes = NN_RECV (sock, &buf, nn_msg, 0); if (bytes > 0) { printf ("CLIENT (%s): RECEIVED"%s "SURVEY requestn", name, BUF); Nn_freemsg (BUF); Char sendbuffer[128]; time_t Rawtime; struct TM * TIMEINFO; Time (&rawtime); Timeinfo = LocalTime (&rawtime); Char *timetext = asctime (Timeinfo); int textlen = strlen (timetext); Timetext[textlen-1] = '; sprintf (Sendbuffer, "[%s]%s", name, timetext); int sendsize = strlen (sendbuffer) + 1; int actualsendsize = Nn_send (sock, Sendbuffer, sendsize, 0); if (actualsendsize!= sendsize) { fprintf (stderr, "nn_send fail, expect length%d, actual length%dn", Sendsize, actualsendsize); Continue } } } Nn_shutdown (sock, 0); return 0; } |
Once the message is received, it is simply printed and the response data is written to the server.
Service Side
There is a problem with the server, it is not normal to search for several examples before. After trying and simply looking at the code, it turns out that by nanomsg the underlying API,
Unable to get how many clients are currently present. However, if the response of all currently connected clients has been received, after calling Nn_recv again,
Returns 1 directly, indicating that the read failed, and that errno (obtained by the errno function) is set to EFSM, indicating that the current state machine is not in the correct state.
The code is as follows |
Copy Code |
#include <cstdio> #include <cstdlib> #include <cstring> #include <unistd.h> #include <nanomsg/nn.h> #include <nanomsg/survey.h> using namespace Std; const char *survey_type = "DATE"; int main (int argc, char** argv) { if (argc!= 2) { fprintf (stderr, "Usage:%s urln", argv[0]); Exit (-1); } const char *url = argv[1]; int sock = Nn_socket (af_sp, Nn_surveyor); if (sock < 0) { fprintf (stderr, "Nn_socket failed:%sn", Nn_strerror (errno)); Exit (-1); } if (Nn_bind (sock, URL) < 0) { fprintf (stderr, "Nn_bind fail:%sn", Nn_strerror (errno)); Exit (-1); } while (1) { int sendsize = strlen (survey_type) + 1; int actualsendsize; printf ("Server:sending DATE SURVEY requestn"); if ((actualsendsize = Nn_send (sock, Survey_type, sendsize, 0))!= sendsize) { fprintf (stderr, "nn_send fail, expect length%d, actual length%dn", Sendsize, actualsendsize); Continue } int count = 0; while (1) { char *buf = NULL; int bytes = NN_RECV (sock, &buf, nn_msg, 0); if (bytes < 0 && nn_errno () = = etimedout) break; if (bytes >= 0) { printf ("server:received"%s "SURVEY Responsen", buf); ++count; Nn_freemsg (BUF); } else { fprintf (stderr, "Nn_recv fail:%sn", Nn_strerror (errno)); Break } } printf ("Server:current Receive%d survey RESPONSE.N", count); Sleep (1); } Nn_shutdown (sock, 0); return 0; } |
There are two dead loops here, and the outer loop is constantly trying to ask the client. After completing the query, read all the client responses through another dead loop,
Exits the loop when the read fails.
Before the source is found to directly determine whether the error is etimedout, after printing will find that each time there is no timeout, but the state machine error:
The code is as follows |
Copy Code |
/* IF No survey is going in return EFSM error. */ if (Nn_slow (!nn_surveyor_inprogress (surveyor)) RETURN-EFSM; |
Test
Testing is similar to the previous article, start a server, and then start the client again:
code is as follows |
copy code |
#!/bin/bash base= "$ (CD" $ (dirname "$") "&& pwd" SE rver= $BASE/surveyserver client= $BASE/surveyclient url= "tcp://127.0.0.1:1234" Echo "Start Surveyserver to bind TCP: $URL $SERVER tcp://127.0.0.1:1234 & Echo ' start to start surveyclient ' for ((i = 0; I < i++)) do echo ' Start CLI Ent$i $CLIENT client$i $URL & sleep 1 do Sleep 20 echo "Kill all process and exit" for PID in ' jobs-p ' do echo "Kill $pid" kill $pid Done Wait |
The output is:
The code is as follows |
Copy Code |
Start Surveyserver to bind tcp:tcp://127.0.0.1:1234 Start to start surveyclient Start client0 Server:sending DATE SURVEY REQUEST Start CLIENT1 Nn_recv Fail:operation cannot be performed Server:current Receive 0 survey response. Start Client2 Server:sending DATE SURVEY REQUEST CLIENT (client0): RECEIVED "DATE" SURVEY REQUEST Server:received "[client0] Tue Feb 23:32:43 2015" SURVEY RESPONSE CLIENT (CLIENT1): RECEIVED "DATE" SURVEY REQUEST Server:received "[client1] Tue Feb 23:32:43 2015" SURVEY RESPONSE Nn_recv Fail:operation cannot be performed Server:current receive 2 survey response. Start Client3 Server:sending DATE SURVEY REQUEST CLIENT (client0): RECEIVED "DATE" SURVEY REQUEST CLIENT (CLIENT1): RECEIVED "DATE" SURVEY REQUEST CLIENT (CLIENT2): RECEIVED "DATE" SURVEY REQUEST ... Server:sending DATE SURVEY REQUEST CLIENT (client0): RECEIVED "DATE" SURVEY REQUEST CLIENT (CLIENT1): RECEIVED "DATE" SURVEY REQUEST CLIENT (CLIENT2): RECEIVED "DATE" SURVEY REQUEST CLIENT (CLIENT3): RECEIVED "DATE" SURVEY REQUEST CLIENT (CLIENT4): RECEIVED "DATE" SURVEY REQUEST CLIENT (CLIENT5): RECEIVED "DATE" SURVEY REQUEST CLIENT (CLIENT6): RECEIVED "DATE" SURVEY REQUEST CLIENT (client7): RECEIVED "DATE" SURVEY REQUEST CLIENT (CLIENT9): RECEIVED "DATE" SURVEY REQUEST CLIENT (client8): RECEIVED "DATE" SURVEY REQUEST Server:received "[client0] Tue Feb 23:33:09 2015" SURVEY RESPONSE Server:received "[client1] Tue Feb 23:33:09 2015" SURVEY RESPONSE Server:received "[Client2] Tue Feb 23:33:09 2015" SURVEY RESPONSE Server:received "[Client3] Tue Feb 23:33:09 2015" SURVEY RESPONSE Server:received "[Client4] Tue Feb 23:33:09 2015" SURVEY RESPONSE Server:received "[client5] Tue Feb 23:33:09 2015" SURVEY RESPONSE Server:received "[Client6] Tue Feb 23:33:09 2015" SURVEY RESPONSE Server:received "[client7] Tue Feb 23:33:09 2015" SURVEY RESPONSE Server:received "[Client9] Tue Feb 23:33:09 2015" SURVEY RESPONSE Server:received "[client8] Tue Feb 23:33:09 2015" SURVEY RESPONSE Nn_recv Fail:operation cannot be performed Server:current Receive survey response. |
You can see from the output that each time the last receive is completed, there will be a "Operation cannot be performed"
Error, which is EFSM error.