Just a second...

Start publishing with C

Create a C client that publishes data through topics on Diffusion™ Cloud .

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.6.1 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 and a development system with the .NET Framework installed on it. For more information about getting a Diffusion Cloud service, see Getting started with Diffusion Cloud.

You also require a named user that has a role with the modify_topic and update_topic permissions. For example, the "ADMINISTRATOR" role. For more information about roles and permissions, see Role-based authorization.

This example illustrates how to create a client that publishes a list of running processes to a JSON topic. The full code example is provided after the steps.
  1. Get the Diffusion Cloud C client library for your platform and extract the ZIP file.
    The C client library is available from http://download.diffusiondata.com/cloud/latest/sdks.html.
  2. Create a C file called cjson-publishing-example.c.

    1. Include the following libraries:
      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      
      #ifndef WIN32
              #include <unistd.h>
      #else
              #define sleep(x) Sleep(1000 * x)
      #endif
      
      #include "diffusion.h"
      #include "args.h"
      #include "conversation.h"
      

    2. Create some required values:
      #define SYNC_DEFAULT_TIMEOUT 5000 * 1000
      
      
      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
      };
      
    3. Create a main method.
      This does standard parsing of the command-line parameters.
    4. Set up a mutex and condition variable, then create a session with the server.
      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;
      }
      
    5. Create a JSON value and publish it to a topic provided in the command-line parameters (or the default one), wait 10 seconds and then shut down.
      BUF_T *buf = buf_create();
      write_diffusion_json_value("{\"hello\" : \"world\"}", buf);
      
      TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_JSON);
      DIFFUSION_TOPIC_UPDATE_ADD_AND_SET_PARAMS_T topic_add_and_set_params = {
              .topic_path = topic_name,
              .specification = spec,
              .datatype = DATATYPE_JSON,
              .update = buf,
              .on_topic_update_add_and_set = on_topic_creation_result
      };
      
      // Add a topic and set its value
      diffusion_topic_update_add_and_set(session, topic_add_and_set_params);
      
      sleep(10);
      
      // close session
      session_close(session, NULL);
      
      // cleanup
      session_free(session);
      credentials_free(credentials);
      hash_free(options, NULL, free);
      topic_specification_free(spec);
      buf_free(buf);
      
      return EXIT_SUCCESS;
      
  3. Add handlers.
    /**
     * Copyright © 2022 - 2023 DiffusionData Ltd.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     * This example is written in C99. Please use an appropriate C99 capable compiler
     *
     * @author DiffusionData Limited
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #ifndef WIN32
            #include <unistd.h>
    #else
            #define sleep(x) Sleep(1000 * x)
    #endif
    
    #include "diffusion.h"
    #include "args.h"
    #include "conversation.h"
    
    #define SYNC_DEFAULT_TIMEOUT 5000 * 1000
    
    
    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
    };
    
    // start::handlers[]
    // Define the callback functions
    static int on_topic_creation_result(
            DIFFUSION_TOPIC_CREATION_RESULT_T result,
            void *context)
    {
            // topic has been added
            return HANDLER_SUCCESS;
    }
    
    // Program entry point.
    int main(int argc, char** argv)
    {
            // Standard command-line parsing.
            HASH_T *options = parse_cmdline(argc, argv, arg_opts);
            if(options == NULL || hash_get(options, "help") != NULL) {
                    show_usage(argc, argv, arg_opts);
                    return EXIT_FAILURE;
            }
    
            const char *url = hash_get(options, "url");
            const char *principal = hash_get(options, "principal");
            const char *password = hash_get(options, "credentials");
            const char *topic_name = hash_get(options, "topic");
    
            CREDENTIALS_T *credentials = NULL;
            if(password != NULL) {
                    credentials = credentials_create_password(password);
            }
    
            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;
            }
    
            BUF_T *buf = buf_create();
            write_diffusion_json_value("{\"hello\" : \"world\"}", buf);
    
            TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_JSON);
            DIFFUSION_TOPIC_UPDATE_ADD_AND_SET_PARAMS_T topic_add_and_set_params = {
                    .topic_path = topic_name,
                    .specification = spec,
                    .datatype = DATATYPE_JSON,
                    .update = buf,
                    .on_topic_update_add_and_set = on_topic_creation_result
            };
    
            // Add a topic and set its value
            diffusion_topic_update_add_and_set(session, topic_add_and_set_params);
    
            sleep(10);
    
            // close session
            session_close(session, NULL);
    
            // cleanup
            session_free(session);
            credentials_free(credentials);
            hash_free(options, NULL, free);
            topic_specification_free(spec);
            buf_free(buf);
    
            return EXIT_SUCCESS;
    }
    
  4. Build your C client.
    1. Create a Makefile in the same directory as your C file.
      An example Makefile is provided after the steps.
    2. Run the make command to build the example.
      The cjson-publishing-example binary is created in the target/bin directory.
  5. Run your C client from the command line.

The client updates the value of the processes topic. You can see the value of the processes topic by using the Diffusion Cloud Dashboard's test client or by creating a subscribing client to subscribe to the topic. For more information, see Start subscribing with C.

The completed cjson-publishing-example file contains the following code:
/**
 * Copyright © 2022 - 2023 DiffusionData Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * This example is written in C99. Please use an appropriate C99 capable compiler
 *
 * @author DiffusionData Limited
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#ifndef WIN32
        #include <unistd.h>
#else
        #define sleep(x) Sleep(1000 * x)
#endif

#include "diffusion.h"
#include "args.h"
#include "conversation.h"

#define SYNC_DEFAULT_TIMEOUT 5000 * 1000


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
};

// start::handlers[]
// Define the callback functions
static int on_topic_creation_result(
        DIFFUSION_TOPIC_CREATION_RESULT_T result,
        void *context)
{
        // topic has been added
        return HANDLER_SUCCESS;
}

// Program entry point.
int main(int argc, char** argv)
{
        // Standard command-line parsing.
        HASH_T *options = parse_cmdline(argc, argv, arg_opts);
        if(options == NULL || hash_get(options, "help") != NULL) {
                show_usage(argc, argv, arg_opts);
                return EXIT_FAILURE;
        }

        const char *url = hash_get(options, "url");
        const char *principal = hash_get(options, "principal");
        const char *password = hash_get(options, "credentials");
        const char *topic_name = hash_get(options, "topic");

        CREDENTIALS_T *credentials = NULL;
        if(password != NULL) {
                credentials = credentials_create_password(password);
        }

        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;
        }

        BUF_T *buf = buf_create();
        write_diffusion_json_value("{\"hello\" : \"world\"}", buf);

        TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_JSON);
        DIFFUSION_TOPIC_UPDATE_ADD_AND_SET_PARAMS_T topic_add_and_set_params = {
                .topic_path = topic_name,
                .specification = spec,
                .datatype = DATATYPE_JSON,
                .update = buf,
                .on_topic_update_add_and_set = on_topic_creation_result
        };

        // Add a topic and set its value
        diffusion_topic_update_add_and_set(session, topic_add_and_set_params);

        sleep(10);

        // close session
        session_close(session, NULL);

        // cleanup
        session_free(session);
        credentials_free(credentials);
        hash_free(options, NULL, free);
        topic_specification_free(spec);
        buf_free(buf);

        return EXIT_SUCCESS;
}
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

# Extra definitions from parent directory, if they exist.
-include ../makefile.defs

CC      = gcc
CFLAGS  += $(INCLUDES) \
           -g -std=c99 -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=700 \
           -Wall -Werror -Wno-error=deprecated-declarations \
           -I$(DIFFUSION_C_CLIENT_INCDIR)

LDFLAGS += -lpthread -lpcre -lcurl -lz \
           $(DIFFUSION_C_CLIENT_LIBDIR)/libdiffusion.a \
           $(LIBS)

# Detect the platform the Diffusion Client will be running on
PLATFORM =  $(shell uname -s | tr '[A-Z]' '[a-z]' | sed -e 's/darwin/osx/')

# If not MacOS, add `-lrt -ldl` to the linker flags
ifneq ($(PLATFORM),osx)
        LDFLAGS += -lrt -ldl
endif

ARFLAGS     +=
SOURCES     = json/cjson-publishing-example.c
TARGETDIR   = target
OBJDIR      = $(TARGETDIR)/objs
BINDIR      = $(TARGETDIR)/bin
OBJECTS     = $(SOURCES:.c=.o)
TARGETS     = cjson-publishing-example

all:        prepare $(TARGETS)
.PHONY:     all

prepare:
        mkdir -p $(OBJDIR) $(BINDIR)

$(OBJDIR)/%.o:  %.c
        $(CC) $(CFLAGS) -c -o $@ $<

cjson-publishing-example:   json/cjson-publishing-example.c json/cJSON.c
        $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BINDIR)/$@

clean:
        rm -rf $(TARGETS) $(OBJECTS) $(TARGETDIR) core a.out