2013-10-18 11:12:48 +02:00
|
|
|
/*
|
2015-01-22 10:32:06 +01:00
|
|
|
Copyright (c) 2007-2015 Contributors as noted in the AUTHORS file
|
2013-10-18 11:12:48 +02:00
|
|
|
|
2015-06-02 22:33:55 +02:00
|
|
|
This file is part of libzmq, the ZeroMQ core engine in C++.
|
2013-10-18 11:12:48 +02:00
|
|
|
|
2015-06-02 22:33:55 +02:00
|
|
|
libzmq is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU Lesser General Public License (LGPL) as published
|
|
|
|
by the Free Software Foundation; either version 3 of the License, or
|
2013-10-18 11:12:48 +02:00
|
|
|
(at your option) any later version.
|
|
|
|
|
2015-06-02 22:33:55 +02:00
|
|
|
As a special exception, the Contributors give you permission to link
|
|
|
|
this library with independent modules to produce an executable,
|
|
|
|
regardless of the license terms of these independent modules, and to
|
|
|
|
copy and distribute the resulting executable under terms of your choice,
|
|
|
|
provided that you also meet, for each linked independent module, the
|
|
|
|
terms and conditions of the license of that module. An independent
|
|
|
|
module is a module which is not derived from or based on this library.
|
|
|
|
If you modify this library, you must extend this exception to your
|
|
|
|
version of the library.
|
|
|
|
|
|
|
|
libzmq is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
License for more details.
|
2013-10-18 11:12:48 +02:00
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "testutil.hpp"
|
|
|
|
#include "../include/zmq_utils.h"
|
|
|
|
|
|
|
|
// Asynchronous client-to-server (DEALER to ROUTER) - pure libzmq
|
|
|
|
//
|
|
|
|
// While this example runs in a single process, that is to make
|
|
|
|
// it easier to start and stop the example. Each task may have its own
|
|
|
|
// context and conceptually acts as a separate process. To have this
|
|
|
|
// behaviour, it is necessary to replace the inproc transport of the
|
|
|
|
// control socket by a tcp transport.
|
|
|
|
|
|
|
|
// This is our client task
|
|
|
|
// It connects to the server, and then sends a request once per second
|
|
|
|
// It collects responses as they arrive, and it prints them out. We will
|
|
|
|
// run several client tasks in parallel, each with a different random ID.
|
|
|
|
|
|
|
|
#define CONTENT_SIZE 13
|
|
|
|
#define CONTENT_SIZE_MAX 32
|
|
|
|
#define ID_SIZE 10
|
|
|
|
#define ID_SIZE_MAX 32
|
2013-11-06 13:30:41 +01:00
|
|
|
#define QT_WORKERS 5
|
|
|
|
#define QT_CLIENTS 3
|
2013-10-18 11:12:48 +02:00
|
|
|
#define is_verbose 0
|
|
|
|
|
|
|
|
static void
|
|
|
|
client_task (void *ctx)
|
|
|
|
{
|
|
|
|
void *client = zmq_socket (ctx, ZMQ_DEALER);
|
|
|
|
assert (client);
|
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
// Control socket receives terminate command from main over inproc
|
2013-10-18 11:12:48 +02:00
|
|
|
void *control = zmq_socket (ctx, ZMQ_SUB);
|
|
|
|
assert (control);
|
|
|
|
int rc = zmq_setsockopt (control, ZMQ_SUBSCRIBE, "", 0);
|
|
|
|
assert (rc == 0);
|
2015-07-20 19:41:03 -07:00
|
|
|
int linger = 0;
|
|
|
|
rc = zmq_setsockopt (control, ZMQ_LINGER, &linger, sizeof (linger));
|
|
|
|
assert (rc == 0);
|
2013-10-18 11:12:48 +02:00
|
|
|
rc = zmq_connect (control, "inproc://control");
|
|
|
|
assert (rc == 0);
|
|
|
|
|
|
|
|
char content [CONTENT_SIZE_MAX];
|
2013-11-06 13:30:41 +01:00
|
|
|
// Set random identity to make tracing easier
|
|
|
|
char identity [ID_SIZE];
|
|
|
|
sprintf (identity, "%04X-%04X", rand() % 0xFFFF, rand() % 0xFFFF);
|
2013-10-18 11:12:48 +02:00
|
|
|
rc = zmq_setsockopt (client, ZMQ_IDENTITY, identity, ID_SIZE); // includes '\0' as an helper for printf
|
|
|
|
assert (rc == 0);
|
2015-07-20 19:41:03 -07:00
|
|
|
linger = 0;
|
|
|
|
rc = zmq_setsockopt (client, ZMQ_LINGER, &linger, sizeof (linger));
|
|
|
|
assert (rc == 0);
|
2014-04-30 14:51:57 +02:00
|
|
|
rc = zmq_connect (client, "tcp://127.0.0.1:5563");
|
2013-10-18 11:12:48 +02:00
|
|
|
assert (rc == 0);
|
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
zmq_pollitem_t items [] = { { client, 0, ZMQ_POLLIN, 0 }, { control, 0, ZMQ_POLLIN, 0 } };
|
|
|
|
int request_nbr = 0;
|
|
|
|
bool run = true;
|
|
|
|
while (run) {
|
|
|
|
// Tick once per 200 ms, pulling in arriving messages
|
|
|
|
int centitick;
|
|
|
|
for (centitick = 0; centitick < 20; centitick++) {
|
|
|
|
zmq_poll (items, 2, 10);
|
|
|
|
if (items [0].revents & ZMQ_POLLIN) {
|
|
|
|
int rcvmore;
|
|
|
|
size_t sz = sizeof (rcvmore);
|
|
|
|
rc = zmq_recv (client, content, CONTENT_SIZE_MAX, 0);
|
|
|
|
assert (rc == CONTENT_SIZE);
|
|
|
|
if (is_verbose) printf("client receive - identity = %s content = %s\n", identity, content);
|
|
|
|
// Check that message is still the same
|
|
|
|
assert (memcmp (content, "request #", 9) == 0);
|
|
|
|
rc = zmq_getsockopt (client, ZMQ_RCVMORE, &rcvmore, &sz);
|
|
|
|
assert (rc == 0);
|
|
|
|
assert (!rcvmore);
|
|
|
|
}
|
|
|
|
if (items [1].revents & ZMQ_POLLIN) {
|
|
|
|
rc = zmq_recv (control, content, CONTENT_SIZE_MAX, 0);
|
2014-02-13 18:33:52 +01:00
|
|
|
if (is_verbose) printf("client receive - identity = %s command = %s\n", identity, content);
|
|
|
|
if (memcmp (content, "TERMINATE", 9) == 0) {
|
|
|
|
run = false;
|
|
|
|
break;
|
2013-11-06 13:30:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sprintf(content, "request #%03d", ++request_nbr); // CONTENT_SIZE
|
|
|
|
rc = zmq_send (client, content, CONTENT_SIZE, 0);
|
|
|
|
assert (rc == CONTENT_SIZE);
|
|
|
|
}
|
2013-10-18 11:12:48 +02:00
|
|
|
|
|
|
|
rc = zmq_close (client);
|
|
|
|
assert (rc == 0);
|
|
|
|
rc = zmq_close (control);
|
|
|
|
assert (rc == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is our server task.
|
|
|
|
// It uses the multithreaded server model to deal requests out to a pool
|
|
|
|
// of workers and route replies back to clients. One worker can handle
|
|
|
|
// one request at a time but one client can talk to multiple workers at
|
|
|
|
// once.
|
|
|
|
|
|
|
|
static void server_worker (void *ctx);
|
|
|
|
|
|
|
|
void
|
2014-02-13 18:33:52 +01:00
|
|
|
server_task (void *ctx)
|
2013-10-18 11:12:48 +02:00
|
|
|
{
|
|
|
|
// Frontend socket talks to clients over TCP
|
|
|
|
void *frontend = zmq_socket (ctx, ZMQ_ROUTER);
|
|
|
|
assert (frontend);
|
2015-07-20 19:41:03 -07:00
|
|
|
int linger = 0;
|
|
|
|
int rc = zmq_setsockopt (frontend, ZMQ_LINGER, &linger, sizeof (linger));
|
|
|
|
assert (rc == 0);
|
|
|
|
rc = zmq_bind (frontend, "tcp://127.0.0.1:5563");
|
2013-10-18 11:12:48 +02:00
|
|
|
assert (rc == 0);
|
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
// Backend socket talks to workers over inproc
|
2013-10-18 11:12:48 +02:00
|
|
|
void *backend = zmq_socket (ctx, ZMQ_DEALER);
|
|
|
|
assert (backend);
|
2015-07-20 19:41:03 -07:00
|
|
|
rc = zmq_setsockopt (backend, ZMQ_LINGER, &linger, sizeof (linger));
|
|
|
|
assert (rc == 0);
|
2013-10-18 11:12:48 +02:00
|
|
|
rc = zmq_bind (backend, "inproc://backend");
|
|
|
|
assert (rc == 0);
|
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
// Control socket receives terminate command from main over inproc
|
2013-10-18 11:12:48 +02:00
|
|
|
void *control = zmq_socket (ctx, ZMQ_SUB);
|
|
|
|
assert (control);
|
|
|
|
rc = zmq_setsockopt (control, ZMQ_SUBSCRIBE, "", 0);
|
|
|
|
assert (rc == 0);
|
2015-07-20 19:41:03 -07:00
|
|
|
rc = zmq_setsockopt (control, ZMQ_LINGER, &linger, sizeof (linger));
|
|
|
|
assert (rc == 0);
|
2013-10-18 11:12:48 +02:00
|
|
|
rc = zmq_connect (control, "inproc://control");
|
|
|
|
assert (rc == 0);
|
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
// Launch pool of worker threads, precise number is not critical
|
|
|
|
int thread_nbr;
|
|
|
|
void* threads [5];
|
|
|
|
for (thread_nbr = 0; thread_nbr < QT_WORKERS; thread_nbr++)
|
|
|
|
threads[thread_nbr] = zmq_threadstart (&server_worker, ctx);
|
2013-10-18 11:12:48 +02:00
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
// Connect backend to frontend via a proxy
|
2014-02-13 18:33:52 +01:00
|
|
|
zmq_proxy_steerable (frontend, backend, NULL, control);
|
2013-10-18 11:12:48 +02:00
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
for (thread_nbr = 0; thread_nbr < QT_WORKERS; thread_nbr++)
|
|
|
|
zmq_threadclose (threads[thread_nbr]);
|
2013-10-18 11:12:48 +02:00
|
|
|
|
|
|
|
rc = zmq_close (frontend);
|
|
|
|
assert (rc == 0);
|
|
|
|
rc = zmq_close (backend);
|
|
|
|
assert (rc == 0);
|
|
|
|
rc = zmq_close (control);
|
|
|
|
assert (rc == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Each worker task works on one request at a time and sends a random number
|
|
|
|
// of replies back, with random delays between replies:
|
|
|
|
// The comments in the first column, if suppressed, makes it a poller version
|
|
|
|
|
|
|
|
static void
|
|
|
|
server_worker (void *ctx)
|
|
|
|
{
|
|
|
|
void *worker = zmq_socket (ctx, ZMQ_DEALER);
|
|
|
|
assert (worker);
|
2015-07-20 19:41:03 -07:00
|
|
|
int linger = 0;
|
|
|
|
int rc = zmq_setsockopt (worker, ZMQ_LINGER, &linger, sizeof (linger));
|
|
|
|
assert (rc == 0);
|
|
|
|
rc = zmq_connect (worker, "inproc://backend");
|
2013-10-18 11:12:48 +02:00
|
|
|
assert (rc == 0);
|
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
// Control socket receives terminate command from main over inproc
|
2013-10-18 11:12:48 +02:00
|
|
|
void *control = zmq_socket (ctx, ZMQ_SUB);
|
|
|
|
assert (control);
|
|
|
|
rc = zmq_setsockopt (control, ZMQ_SUBSCRIBE, "", 0);
|
|
|
|
assert (rc == 0);
|
2015-07-20 19:41:03 -07:00
|
|
|
rc = zmq_setsockopt (control, ZMQ_LINGER, &linger, sizeof (linger));
|
|
|
|
assert (rc == 0);
|
2013-10-18 11:12:48 +02:00
|
|
|
rc = zmq_connect (control, "inproc://control");
|
|
|
|
assert (rc == 0);
|
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
char content [CONTENT_SIZE_MAX]; // bigger than what we need to check that
|
|
|
|
char identity [ID_SIZE_MAX]; // the size received is the size sent
|
|
|
|
|
|
|
|
bool run = true;
|
|
|
|
while (run) {
|
|
|
|
rc = zmq_recv (control, content, CONTENT_SIZE_MAX, ZMQ_DONTWAIT); // usually, rc == -1 (no message)
|
|
|
|
if (rc > 0) {
|
2014-02-13 18:33:52 +01:00
|
|
|
if (is_verbose)
|
2013-11-06 13:30:41 +01:00
|
|
|
printf("server_worker receives command = %s\n", content);
|
2014-02-13 18:33:52 +01:00
|
|
|
if (memcmp (content, "TERMINATE", 9) == 0)
|
2013-11-06 13:30:41 +01:00
|
|
|
run = false;
|
|
|
|
}
|
|
|
|
// The DEALER socket gives us the reply envelope and message
|
|
|
|
// if we don't poll, we have to use ZMQ_DONTWAIT, if we poll, we can block-receive with 0
|
|
|
|
rc = zmq_recv (worker, identity, ID_SIZE_MAX, ZMQ_DONTWAIT);
|
|
|
|
if (rc == ID_SIZE) {
|
|
|
|
rc = zmq_recv (worker, content, CONTENT_SIZE_MAX, 0);
|
|
|
|
assert (rc == CONTENT_SIZE);
|
|
|
|
if (is_verbose)
|
|
|
|
printf ("server receive - identity = %s content = %s\n", identity, content);
|
|
|
|
|
|
|
|
// Send 0..4 replies back
|
|
|
|
int reply, replies = rand() % 5;
|
|
|
|
for (reply = 0; reply < replies; reply++) {
|
|
|
|
// Sleep for some fraction of a second
|
|
|
|
msleep (rand () % 10 + 1);
|
|
|
|
// Send message from server to client
|
|
|
|
rc = zmq_send (worker, identity, ID_SIZE, ZMQ_SNDMORE);
|
|
|
|
assert (rc == ID_SIZE);
|
|
|
|
rc = zmq_send (worker, content, CONTENT_SIZE, 0);
|
|
|
|
assert (rc == CONTENT_SIZE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-18 11:12:48 +02:00
|
|
|
rc = zmq_close (worker);
|
|
|
|
assert (rc == 0);
|
|
|
|
rc = zmq_close (control);
|
|
|
|
assert (rc == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The main thread simply starts several clients and a server, and then
|
|
|
|
// waits for the server to finish.
|
|
|
|
|
2014-02-13 18:33:52 +01:00
|
|
|
int main (void)
|
2013-10-18 11:12:48 +02:00
|
|
|
{
|
|
|
|
setup_test_environment ();
|
|
|
|
|
|
|
|
void *ctx = zmq_ctx_new ();
|
|
|
|
assert (ctx);
|
2013-11-06 13:30:41 +01:00
|
|
|
// Control socket receives terminate command from main over inproc
|
2013-10-18 11:12:48 +02:00
|
|
|
void *control = zmq_socket (ctx, ZMQ_PUB);
|
|
|
|
assert (control);
|
2015-07-20 19:41:03 -07:00
|
|
|
int linger = 0;
|
|
|
|
int rc = zmq_setsockopt (control, ZMQ_LINGER, &linger, sizeof (linger));
|
|
|
|
assert (rc == 0);
|
|
|
|
rc = zmq_bind (control, "inproc://control");
|
2013-10-18 11:12:48 +02:00
|
|
|
assert (rc == 0);
|
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
void *threads [QT_CLIENTS + 1];
|
|
|
|
for (int i = 0; i < QT_CLIENTS; i++)
|
|
|
|
threads[i] = zmq_threadstart (&client_task, ctx);
|
2014-02-13 18:33:52 +01:00
|
|
|
threads[QT_CLIENTS] = zmq_threadstart (&server_task, ctx);
|
|
|
|
msleep (500); // Run for 500 ms then quit
|
2013-10-18 11:12:48 +02:00
|
|
|
|
2014-02-13 18:33:52 +01:00
|
|
|
rc = zmq_send (control, "TERMINATE", 9, 0);
|
2013-12-01 20:29:03 +00:00
|
|
|
assert (rc == 9);
|
2013-10-18 11:12:48 +02:00
|
|
|
|
|
|
|
rc = zmq_close (control);
|
|
|
|
assert (rc == 0);
|
|
|
|
|
2013-11-06 13:30:41 +01:00
|
|
|
for (int i = 0; i < QT_CLIENTS + 1; i++)
|
|
|
|
zmq_threadclose (threads[i]);
|
|
|
|
|
2013-10-18 11:12:48 +02:00
|
|
|
rc = zmq_ctx_term (ctx);
|
|
|
|
assert (rc == 0);
|
2013-11-06 13:30:41 +01:00
|
|
|
return 0;
|
2013-10-18 11:12:48 +02:00
|
|
|
}
|