Create a C client within minutes that connects to Diffusion™ Cloud. This example creates a client that subscribes to a JSON topic called "processes"
and prints its value to the console
when the topic is updated.
The C client libraries rely on a number of dependencies. Depending on the
platform you are using, these dependencies might
be included in the client library. If they are not included in the client
library, ensure that the dependencies are available on your development
system.
For more information about dependencies on each supported
platform, see C.
The C client library statically links to
Apache Portable Runtime (APR) version 1.5 with APR-util.
Ensure that you set
APR_DECLARE_STATIC and
APU_DECLARE_STATIC before you use any
APR includes. You can set these
values in the following ways:
- By including diffusion.h before any
APR includes. The
diffusion.h file sets these
values.
- As command-line flags
For more information, see
http://apr.apache.org
To complete this example, you need a Diffusion Cloud service. For more
information about getting a Diffusion Cloud
service, see Getting started with Diffusion Cloud.
You also must either:
- create a named user that has a role with the select_topic and read_topic permissions
- assign a role with those permissions to anonymous client sessions.
An example of a suitable role is
"CLIENT".
For more information about roles and permissions, see
Role-based authorization.
This example steps through the lines of code required to subscribe to a
topic. The full code example is
provided after the steps.
-
Get the Diffusion Cloud C client library
for your platform and extract the ZIP file.
-
Create a C file called
cjson-subscribing-example.c.
-
Include the following libraries:
#include <stdio.h>
#include <apr.h>
#include "diffusion.h"
#include "args.h"
-
Define some values to be used later.
#define SYNC_DEFAULT_TIMEOUT 5000 * 1000
apr_pool_t *pool = NULL;
apr_thread_mutex_t *mutex = NULL;
apr_thread_cond_t *cond = NULL;
ARG_OPTS_T arg_opts[] = {
ARG_OPTS_HELP,
{'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"},
{'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "user"},
{'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"},
{'t', "topic", "Topic name to subscribe", ARG_OPTIONAL, ARG_HAS_VALUE, "time"},
END_OF_ARG_OPTS
};
Where hostname is the name of the system
hosting your Diffusion Cloud server (for example,
yourserver.eu.diffusion.cloud)
,
port is the port Diffusion Cloud accepts client
connections on (80 or 443 for a secure connection),
user is the name of a
user with the permissions required to subscribe to a topic,
and password is the user's
password.
-
Create a main method.
This does standard parsing of the command-line parameters.
-
Set up a mutex and condition variable, then create a session with the server.
apr_initialize();
apr_pool_create(&pool, NULL);
apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool);
apr_thread_cond_create(&cond, pool);
DIFFUSION_ERROR_T error = { 0 };
SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error);
if(session == NULL) {
fprintf(stderr, "Failed to create session: %s\n", error.message);
return EXIT_FAILURE;
}
-
Handle any incoming subscription notifications, subscribe to a topic provided in the command line parameters (or the default one), process topic updates for 60 seconds
then shut down.
notify_subscription_register(session,(NOTIFY_SUBSCRIPTION_PARAMS_T) { .on_notify_subscription = on_notify_subscription });
subscribe(session, (SUBSCRIPTION_PARAMS_T) { .topic_selector = topic_name, .on_topic_message = on_topic_message, .on_subscribe = on_subscribe });
// Receive updates for 2 minutes
sleep(120);
session_close(session, NULL);
session_free(session);
credentials_free(credentials);
hash_free(options, NULL, free);
apr_thread_cond_destroy(cond);
apr_thread_mutex_destroy(mutex);
apr_pool_destroy(pool);
apr_terminate();
return EXIT_SUCCESS;
Note that this code relies on callbacks which we will add in the following steps.
-
Above the main method, create the on_notify_subscription callback to handle when this client receives a notification
that it has been subscribed to a topic. Note that this could be called for topics that we have not
explicitly subscribed to. Other control clients (or publishers) may request to subscribe this client to a topic.
static int
on_notify_subscription(SESSION_T *session, const SVC_NOTIFY_SUBSCRIPTION_REQUEST_T *request, void *context)
{
printf("on_notify_subscription: %d: \"%s\"\n",
request->topic_info.topic_id,
request->topic_info.topic_path);
return HANDLER_SUCCESS;
}
-
Create the on_subscribe callback to handle when the server responds to say that a topic
* subscription request has been received and processed.
static int
on_subscribe(SESSION_T *session, void *context_data)
{
printf("on_subscribe\n");
return HANDLER_SUCCESS;
}
-
Create the on_topic_message callback. When a subscribed message is received, this callback is invoked to print the JSON value.
static int
on_topic_message(SESSION_T *session, const TOPIC_MESSAGE_T *msg)
{
printf("Received message for topic %s (%ld bytes)\n", msg->name, msg->payload->len);
if(msg->details != NULL) {
if(msg->details->topic_type == TOPIC_TYPE_JSON) {
// Convert payload to a JSON string
BUF_T *json = cbor_to_json(msg->payload->data, msg->payload->len);
printf("As JSON: %.*s\n", (int)json->len, json->data);
buf_free(json);
}
else {
printf("Hexdump of binary data:\n");
hexdump_buf(msg->payload);
}
}
else {
printf("Payload: %.*s\n",
(int)msg->payload->len,
msg->payload->data);
}
return HANDLER_SUCCESS;
}
-
Build your C client.
-
Create a Makefile in the
same directory as your C file.
-
Run the make command to build
the example.
The cjson-subscribing-example
binary is created in the
target/bin directory.
-
Run your C client from the command line.
The client prints to the console every time the value of the subscribed topic
is updated.
You can update the value of the topic by using the Diffusion Cloud Dashboard's test
client or by creating a publishing client to update the
topic. By default, the client subscribes to the processes topic used
in the Start publishing with C example.
The completed
cjson-subscribing-example.c file contains the
following code:
The
Makefile contains the following code:
# The following two variables must be set.
#
# Directory containing the C client include files.
# DIFFUSION_C_CLIENT_INCDIR =
#
# Directory containing libdiffusion.a
# DIFFUSION_C_CLIENT_LIBDIR =
ifndef DIFFUSION_C_CLIENT_INCDIR
$(error DIFFUSION_C_CLIENT_INCDIR is not set)
endif
ifndef DIFFUSION_C_CLIENT_LIBDIR
$(error DIFFUSION_C_CLIENT_LIBDIR is not set)
endif
CC = gcc
# Extra definitions from parent directory, if they exist.
-include ../makefile.defs
CFLAGS += -g -Wall -Werror -std=c99 -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=700 -I$(DIFFUSION_C_CLIENT_INCDIR)
LDFLAGS += $(DIFFUSION_C_CLIENT_LIBDIR)/libdiffusion.a -lpthread -lpcre -lz $(LIBS)
# If you have openssl installed then you can uncomment these.
ifdef HAVE_OPEN_SSL
LDFLAGS += -lssl -lcrypto
endif
ARFLAGS +=
SOURCES = json/cjson-subscribing-example.c
TARGETDIR = target
OBJDIR = $(TARGETDIR)/objs
BINDIR = $(TARGETDIR)/bin
OBJECTS = $(SOURCES:.c=.o)
TARGETS = cjson-subscribing-example
all: prepare $(TARGETS)
.PHONY: all
prepare:
mkdir -p $(OBJDIR) $(BINDIR)
$(OBJDIR)/%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
cjson-subscribing-example: json/cjson-subscribing-example.c
$(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BINDIR)/$@
clean:
rm -rf $(TARGETS) $(OBJECTS) $(TARGETDIR) core a.out