CFreeRTOSFreeRTOSWindows CmbedPosix CFreeRTOS Publish & Subscribe API Reference for Realtime Apps

The pubnub_publish() function is used to send a message to all subscribers of a channel. To publish a message you must first specify a valid publish_key at initialization. A successfully published message is replicated across the PubNub Real-Time Network and sent simultaneously to all subscribed clients on a channel.
Messages in transit can be secured from potential eavesdroppers with SSL/TLS by setting ssl to true during initialization.
 
Publish Anytime
It is not required to be subscribed to a channel in order to publish to that channel.
Message Data:
The message argument can contain any JSON serializable data, including: Objects, Arrays, Ints and Strings. Message data should not contain special FreeRTOS classes or functions as these will not serialize. String content can include any single-byte or multi-byte UTF-8 character.
Message Size:
The maximum number of characters per message is 32K by default. The maximum message size is based on the final escaped character count, including the channel name. An ideal message size is under 1800 bytes which allows a message to be compressed and sent using single IP datagram (1.5KB) providing optimal network performance.
If the message you publish exceeds the configured size, you will receive the following message:
Message Too Large Error
["PUBLISHED",[0,"Message Too Large","13524237335750949"]]

For further details please check: https://support.pubnub.com/support/discussions/topics/14000006322

Message Publish Rate:
Messages can be published as fast as bandwidth conditions will allow. There is a soft limit based on max throughput since messages will be discarded if the subscriber cannot keep pace with the publisher.
For example, if 200 messages are published simultaneously before a subscriber has had a chance to receive any messages, the subscriber may not receive the first 100 messages because the message queue has a limit of only 100 messages stored in memory.
Publishing to Multiple Channels.
It is not possible to publish a message to multiple channels simultaneously. The message must be published to one channel at a time.
Publishing Messages Reliably.
There are some best practices to ensure messages are delivered when publishing to a channel:
  • Publish to any given channel in a serial manner (i.e. not concurrently).
  • Check that the return code is success (e.g. [1,"Sent","136074940..."])
  • Publish the next message only after receiving a success return code.
  • If a failure code is returned ([0,"blah","<timetoken>"]), retry the publish.
  • Avoid exceeding the in-memory queue's capacity of 100 messages. An overflow situation (aka missed messages) can occur if slow subscribers fail to keep up with the publish pace in a given period of time.
  • Throttle publish bursts in accordance with your app's latency needs e.g. Publish no faster than 5 msgs per second to any one channel.
 
JSON serialize!
It is important to note that you should JSON serialize when sending signals/messages via PUBNUB.
 

Single string messages are automatically sent to APNS if mobile push is enabled and devices are registered for push on that channel. So if you use encryption (cipher key in the init) and publish the message, then it will be sent to APNS if there is a device(s) registered to that channel.

This is a legacy feature and will be deprecated soon.

To Publish a message you can use the following method(s) in the FreeRTOS SDK:
  1. ParameterTypeRequiredDescription
    ppubnub_t*YesPointer to pubnub context. Can't be NULL
    channelconst char*YesPointer to string with the channel name (or comma-delimited list of channel names) to publish to.
    messageconst char*YesPointer to string containing message to publish in JSON format.
  2. ParameterTypeRequiredDescription
    ppubnub_t*YesPointer to Pubnub Client Context
    channelconst char*YesPointer to string with the channel name (or comma-delimited list of channel names) to publish to.
    messageconst char*YesPointer to string containing message to publish in JSON format.
    store_in_historyboolYesIf false, message will not be stored in history of the channel
    eat_after_readingconst char*YesIf true, message will not be stored for delayed or repeated retrieval or display

Publish a message to a channel:

pubnub_t *ctx = pubnub_alloc();
if (NULL == ctx) {
    return -1;
}
pubnub_init(ctx, "demo", "demo");
pubnub_publish(ctx, "hello_world", "\"Hello from Pubnub C-core docs!\"");
pbresult = pubnub_await(ctx);
if (pbresult != PNR_OK) {
    pubnub_free(ctx);
    return -1;
}
 
Before running the above publish example, either using the Debug Console, or in a separate script running in a separate terminal window, subscribe to the same channel that is being published to.
The function returns the following formatted response:
[1, "Sent", "13769558699541401"]
  1. pubnub_t *ctx = pubnub_alloc();
    if (NULL == ctx) {
        return -1;
    }
    pubnub_init(ctx, "demo", "demo");
    pubnub_publish(ctx, "hello_world", "{\"msg\": \"Hello from Pubnub C-core docs!\"}");
    pbresult = pubnub_await(ctx);
    if (pbresult != PNR_OK) {
        pubnub_free(ctx);
        return -1;
    }
  2. res = pubnub_publish_encrypted(pbp, chan, "\"Hello world from crypto sync!\"", cipher_key);
    if (res != PNR_STARTED) {
        printf("pubnub_publish() returned unexpected: %d\n", res);
        pubnub_free(pbp);
        return -1;
    }
  3. Using this method you can reuse the cipherKey from the options.
    struct pubnub_publish_options opt = pubnub_publish_defopts();
    opt.cipher_key = my_cipher_key;
    pbresult = pubnub_publish_ex(pn, "my_channel", "42", opt);

Holds all the options for extended publish.

  1. struct pubnub_publish_options { bool store; char const* cipher_key; bool replicate; char const* meta; enum pubnub_publish_method method; };

    MemberTypeDescription
    storeboolIf true, the message is stored in history. If false, the message is not stored in history.
    cipher_keychar const*If not NULL, the key used to encrypt the message before sending it to PubNub. Keep in mind that encryption is a CPU intensive task. Also, it uses more bandwidth, most of which comes from the fact that decrypted data is sent as Base64 encoded JSON string. This means that the actual amount of data that can be sent as encrypted in a message is at least 25% smaller (than un-encrypted). Another point to be made is that it also does some memory management (allocating and deallocating).
    replicateboolIf true, the message is replicated, thus will be received by all subscribers. If false, the message is not replicated and will be delivered only to Function event handlers. Setting false here and false on store is referred to as a Fire (instead of a publish).
    metachar const*An optional JSON object, used to send additional (meta) data about the message, which can be used for stream filtering.
    methodenum pubnub_publish_methodDefines the method by which publish transaction will be performed. Can be HTTP GET or POST. If using POST, content can be GZIP compressed.

This returns the default options for publish V1 transactions. Will set:

ParameterDefault
storetrue
cipher_keyNULL
replicatetrue
metaNULL
methodpubnubPublishViaGET
  1. struct pubnub_publish_options pubnub_publish_defopts(void);

    This method doesn't take any argument.

struct pubnub_publish_options opts = pubnub_publish_defopts();
TypeValueDescription
struct pubnub_publish_optionsThe default options for publish.

The extended publish V1. Basically the same as pubnub_publish(), but with added optional parameters in @p opts.

  1. enum pubnub_res pubnub_publish_ex(pubnub_t *p, const char *channel, const char *message, struct pubnub_publish_options opts);

    ParameterTypeRequiredDescription
    ppubnub_t*YesThe Pubnub context.
    channelchar const*YesThe string with the channel name to publish to.
    messagechar const*YesThe message to publish. It needs to be JSON encoded.
    optsstruct pubnub_publish_optionsYesThe Publish options.
struct pubnub_publish_options opt = pubnub_publish_defopts();
	opt.store = false;
	pbresult = pubnub_publish_ex(pn, "my_channel", "42", opt);
TypeValueDescription
enum pubnub_resPNR_STARTEDSuccess.
otherIndicates the type of error.

Sends a signal @p message (in JSON format) on @p channel, using the @p pb context. This actually means "initiate a signal transaction".

It has similar behaviour as publish, but unlike publish transaction, signal erases previous signal message on server (on a given channel,) and you can not send any metadata.

There can be only up to one signal message at the time. If it's not renewed by another signal, signal message disappears from channel history after a certain amount of time.

You can't (send a) signal if a transaction is in progress on @p pb context.

If transaction is not successful (@c PNR_PUBLISH_FAILED), you can get the string describing the reason for failure by calling pubnub_last_publish_result().

Keep in mind that the time token from the signal operation response is not parsed by the library, just relayed to the user. Only time-tokens from the subscribe operation are parsed by the library.

Also, for all error codes known at the time of this writing, the HTTP error will also be set, so the result of the Pubnub operation will not be @c PNR_OK (but you will still be able to get the result code and the description).

By default, signals are limited to a message payload size of 30 bytes. This limit applies only to the payload, and not to the URI or headers. If you require a larger payload size, please contact support.

  1. enum pubnub_res pubnub_signal(pubnub_t* pb, char const* channel, char const* message);

    ParameterTypeRequiredDescription
    pbpubnub_t*YesThe Pubnub context in which to parse the response.
    channelchar const*YesThe string with the channel to signal to.
    messagechar const*YesThe string with the signal, expected to be in JSON format.

Signal a message to a channel:

enum pubnub_res = pubnub_signal(pb, "my_channel", "\"signalling\"");
TypeValueDescription
enum pubnub_resPNR_OKTransaction finished (signal sent).
PNR_STARTEDTransaction started (started to send signal), will finish later.
otherwiseError, value indicates the reason for failure.

Defines the type of a message that is retrieved with subscribe V2:

SymbolValueDescription
pbsbPublished0Published message (sent via Publish transaction)
pbsbSignal1A signal message (sent via Signal transaction)
This function causes the client to create an open TCP socket to the PubNub Real-Time Network and begin listening for messages on a specified channel. To subscribe to a channel the client must send the appropriate subscribe_key at initialization.
By default a newly subscribed client will only receive messages published to the channel after the pubnub_subscribe() call completes.
 
Typically, you will want two separate contexts for publish and subscribe. When changing the active set of subscribed channels, first call pubnub_leave() on the old set.
The pubnub_subscribe() interface is essentially a transaction to start listening on the channel for arrival of next message. This has to be followed by pubnub_get()call to retrieve the actual message, once the subscribe transaction completes successfully. This needs to be performed every time it is desired to retrieve a message from the channel.
 Unsubscribing from all the channel(s) and then subscribing to a new channel Y is not the same as Subscribing to channel Y and then unsubscribing from the previously subscribed channel(s). Unsubscribing from all the channels resets the timetoken and thus, there could be some gaps in the subscription that may lead to a message loss.
To Subscribe to a channel you can use the following method(s) in the FreeRTOS SDK:
  1. ParameterTypeRequiredDescription
    ppubnub_t*YesPointer to Pubnub client context. Can't be NULL.
    channelconst char*OptionalThe string with the channel name (or comma-delimited list of channel names) to subscribe to.
    channel_groupconst char*OptionalThe string with the channel group name (or comma-delimited list of channel group names) to subscribe to.
Subscribe to a channel:
pubnub_t *ctx = pubnub_alloc();
 
if (NULL == ctx) {
    return -1;
}
pubnub_init(ctx, "demo", "demo");
pubnub_subscribe(ctx, "hello_world", NULL);
pbresult = pubnub_await(ctx);
if (pbresult != PNR_OK) {
    printf("Failed to subscribe, error %d\n", pbresult);
    pubnub_free(ctx);
    return -1;
}
else {
    char const *msg = pubnub_get(ctx);
    char const *channel = pubnub_get_channel(ctx);
    while (msg != NULL) {
        msg = pubnub_get(ctx);
        channel = pubnub_get_channel(ctx);
    }
}
pubnub_free(ctx);
The output below demonstrates the response format to a successful call:
[[], "Time Token"]
  1. Requires Stream Controller add-on XRequires that the Stream Controller add-on is enabled for your key. See this page on enabling add-on features on your keys:

    http://www.pubnub.com/knowledge-base/discussion/644/how-do-i-enable-add-on-features-for-my-keys.
    It is possible to subscribe to more than one channel over a single TCP socket by taking advantage of Multiplexing feature. See the Multiplexing section for more info on this feature as well as the examples below using a list or an array to specify channel name.
    pubnub_t *ctx = pubnub_alloc();
    if (NULL == ctx) {
        return -1;
    }
    pubnub_init(ctx, "demo", "demo");
    pubnub_subscribe(ctx, "hello_world1,hello_world2,hello_world3", NULL);
    pbresult = pubnub_await(ctx);
    if (pbresult != PNR_OK) {
        pubnub_free(ctx);
        return -1;
    }
    else {
        char const *msg = pubnub_get(ctx);
        char const *channel = pubnub_get_channel(ctx);
        while (msg != NULL) {
            msg = pubnub_get(ctx);
            channel = pubnub_get_channel(ctx);
        }
    }
    pubnub_free(ctx);
  2. Requires Presence add-on XRequires that the Presence add-on is enabled for your key. See this page on enabling add-on features on your keys:

    http://www.pubnub.com/knowledge-base/discussion/644/how-do-i-enable-add-on-features-for-my-keys.
    For any given channel there is an associated Presence channel. You can subscribe directly to the channel by appending -pnpres to the channel name. For example the channel named my_channel would have the presence channel named my_channel-pnpres.
    // Sync
    char *presence_channel = malloc(strlen(channel) + strlen(PUBNUB_PRESENCE_SUFFIX) + 1);
    strcpy(presence_channel, channel);
    strcat(presence_channel, PUBNUB_PRESENCE_SUFFIX);
    pubnub_subscribe(ctx, presence_channel, NULL);
    pbresult = pubnub_await(ctx);
    if (PNR_OK == pbresult) {
        char const *presence_event = pubnub_get(ctx);
        while (presnce_event != NULL) {
            presence_event = pubnub_get(ctx);
        }
    }
     
    // Callback
    void some_function(pubnub_t *ctx)
    {
        char *presence_channel = malloc(strlen(channel) + strlen(PUBNUB_PRESENCE_SUFFIX) + 1);
        strcpy(presence_channel, channel);
        strcat(presence_channel, PUBNUB_PRESENCE_SUFFIX);
        pubnub_subscribe(ctx, presence_channel, NULL);
    }
    
    void example_callback(pubnub_t *pb, enum pubnub_trans trans, enum pubnub_res result, void *user_data)
    {
        if (PNR_OK == result) {
            char const *presence_event = pubnub_get(pb);
            while (presence_event != NULL) {
                presence_event = pubnub_get(pb);
            }
        }
    }
    
    {
    	"action": "join",
    	"timestamp": 1345546797,
    	"uuid": "175c2c67-b2a9-470d-8f4b-1db94f90e39e",
    	"occupancy": 2
    }
    
    {
        "action" : "leave",
        "timestamp" : 1345549797,
        "uuid" : "175c2c67-b2a9-470d-8f4b-1db94f90e39e",
        "occupancy" : 1
    }
    
    {
    	"action": "timeout",
    	"timestamp": 1345549797,
    	"uuid": "76c2c571-9a2b-d074-b4f8-e93e09f49bd",
    	"occupancy": 0
    }
    
    
    {
    	"action": "state-change",
    	"uuid": "76c2c571-9a2b-d074-b4f8-e93e09f49bd",
    	"timestamp": 1345549797,
    	"data": {
    		"isTyping": true
    	}
    }
    
    
    {
    	"action":"interval",
    	"timestamp":1474396578,
    	"occupancy":2
    }
    

    When a channel is in interval mode with presence_deltas pnconfig flag enabled, the interval message may also include the following fields which contain an array of changed UUIDs since the last interval message.

    • joined
    • left
    • timedout

    For example, this interval message indicates there were 2 new UUIDs that joined and 1 timed out UUID since the last interval:

    
    {
        "action" : "interval",
        "occupancy" : <# users in channel>,
        "timestamp" : <unix timestamp>,
        "joined" : ["uuid2", "uuid3"],
        "timedout" : ["uuid1"]
    }
    

    If the full interval message is greater than 30KB (since the max publish payload is ∼32KB), none of the extra fields will be present. Instead there will be a here_now_refresh boolean field set to true. This indicates to the user that they should do a hereNow request to get the complete list of users present in the channel.

    
    {
        "action" : "interval",
        "occupancy" : <# users in channel>,
        "timestamp" : <unix timestamp>,
        "here_now_refresh" : true
    }
    
  3. Requires Stream Controller add-on XRequires that the Stream Controller add-on is enabled for your key. See this page on enabling add-on features on your keys:

    http://www.pubnub.com/knowledge-base/discussion/644/how-do-i-enable-add-on-features-for-my-keys.
    // Sync
    enum pubnub_res res;
     
    for (;;) {
      res = pubnub_subscribe(pn, NULL,  channel_group);
      read_response(pn, res);
    }
  4. Requires Stream Controller and Presence add-on XRequires that both Stream Controller and Presence add-ons are enabled for your key. See this page on enabling add-on features on your keys:

    http://www.pubnub.com/knowledge-base/discussion/644/how-do-i-enable-add-on-features-for-my-keys.
    // Sync
    enum pubnub_res res;
     
    for (;;) {
      res = pubnub_subscribe(pn, NULL, "family-pnpres");
      read_response(pn, res);
    }
  5. For subscribe, you don't use the cipher key at subscribe, but, when you get the received messages after the subscribe transaction has finished:
    
    char msg[MAX_MSG_LEN];
    pbresult = pubnub_get_decrypted(pn, my_cipher_key, s, sizeof s);
    if (PNR_OK == pbresult) {
        /* Use the message in `msg` */ 
    }
    or, for a different usability/safety trade-off:
    
    pubnub_bymebl_t msg = pubnub_get_decrypted_alloc(pn, my_cipher_key);
    if (msg.ptr != NULL) {
        /* use the message in `msg.ptr` */ 
        free(msg.ptr); 
    }

Holds all the options for subscribe v2.

  1. struct pubnub_subscribe_v2_options {char const* channel_group;unsigned heartbeat;};

    MemberTypeDescription
    channel_groupchar const*Channel group (a comma-delimited list of channel group names). If NULL, will not be used.
    heartbeatunsignedThe channel activity timeout, in seconds. If below the minimum value supported by Pubnub, the minimum value will be used (instead).
    char const*filter_expr

    The filter expression to apply. It's a predicate to apply to the metadata of a message. If it returns true, message will be received, otherwise, it will be skipped (as if not published). Syntax is not trivial, but can be described as mostly Javascript on the metadata (which is JSON, thus, "integrates well" with Javascript). For example, if your metadata is: {"zec":3}, then this filter _would_ match it: zec==3, while zec==4 would _not_.

    If message doesn't have metadata, this will be ignored.

    If NULL, will not be used.

This returns the default options for subscribe transactions. Will set channel_group = NULL, heartbeat to default heartbeat value and filter_expr = NULL.

  1. struct pubnub_subscribe_v2_options pubnub_subscribe_v2_defopts(void);

    This method doesn't take any argument.

struct pubnub_subscribe_v2_options opts = pubnub_subscribe_v2_defopts();
TypeValueDescription
struct pubnub_subscribe_optionsThe default options for subscribe.

The V2 subscribe. To get messages for subscribe V2, use pubnub_get_v2() - keep in mind that it can provide you with channel and channel group info.

  1. enum pubnub_res pubnub_subscribe_v2(pubnub_t *p, const char *channel, struct pubnub_subscribe_v2_options opts);

    ParameterTypeRequiredDescription
    ppubnub_t*YesThe Pubnub context.
    channelchar const*YesThe string with the channel name (or comma-delimited list of channel names) to subscribe for.
    optsstruct pubnub_subscribe_optionsYesSubscribe V2 options.
struct pubnub_subscribe_v2_options opt = pubnub_subscribe_v2_defopts();
opt.heartbeat = 412;
pbresult = pubnub_subscribe_v2(pn, "my_channel", opt);
TypeValueDescription
enum pubnub_resPNR_STARTEDStarted.
PNR_OKFinished with a success (can only happen in the sync interface).
otherFailed, Indicates the type of error.

Pubnub V2 message has lots of data and here's how we express them for the pubnub_get_v2().

The "string fields" are expressed as "Pascal strings", that is, a pointer with string length, and don't include a NUL character. Also, these pointers are actually pointing into the full received subscribe response, so, their lifetime is tied to the message lifetime and any subsequent transaction on the same context will invalidate them.

If a (string) field is not present, it will empty, meaning its size (length) will be zero and pointer (start) should be NULL (it might not be NULL, it's best to rely on size).

  1. struct pubnub_v2_message {char const* channel_group;unsigned heartbeat;};

    MemberTypeDescription
    ttstruct pubnub_char_mem_blokThe time token of the message - when it was published.
    regionintRegion of the message - not interesting in most cases.
    flagsintMessage flags (indicators).
    channelstruct pubnub_char_mem_blokChannel that message was published to.
    match_or_groupstruct pubnub_char_mem_blokSubscription match or the channel group.
    payloadstruct pubnub_char_mem_blokThe message itself (its contents, payload).
    metadatastruct pubnub_char_mem_blokThe message metadata, as published.
    message_typeenum pubnub_message_typeThe type of the message (published, signal).

Parse and return the next V2 message, if any.

Do keep in mind that you can use pubnub_get() to get the full message array in JSON, but, then you'll have to parse it yourself, and pubnub_channel() and pubnub_channel_group() would not work. On the other hand, this function will fail if you use it on subscribe v1 response.

If there are no more messages, this will return a struct "memset to zero". It's best to check if payload is empty, as there has to be a payload, while other fields/attributes are mostly optional.

  1. struct pubnub_v2_message pubnub_get_v2(pubnub_t* pbp);

    ParameterTypeRequiredDescription
    pbppubnub_t*YesThe Pubnub context from which to get the next message.
struct pubnub_v2_message msg;
for (msg = pubnub_get_v2(pbp); msg.payload.size > 0; msg = pubnub_get_v2(pbp)) {
    puts("Received message:");
    printf("  Channel    = '%.*s'\n", (int)msg.channel.size, msg.channel.ptr);
    printf("  Timetoken  = '%.*s'\n", (int)msg.tt.size, msg.tt.ptr);
    printf("  Metadata   = '%.*s'\n", (int)msg.metadata.size, msg.metadata.ptr);
    printf("  Payload    = '%.*s'\n", (int)msg.payload.size, msg.payload.ptr);
}
TypeValueDescription
struct pubnub_v2_messageThe V2 message structure describing the next V2 message in the subscribe response. If there is no next message (response is empty, or subscribe V1, or some other transaction), the payload of the message will be empty.
 

To unsubscribe, you need to cancel a subscribe transaction.

If you configured SDK to be thread-safe, you can cancel at any time, but, the cancelling may actually fail - i.e., your thread may wait for another thread to finish working with the context, and by the time your cancel request gets processed, the transaction may finish.

If you configured SDK to not be thread-safe, the only safe way to do it is to use the sync interface and:

  1. Set the context to use non-blocking I/O
  2. Wait for the outcome in a loop, checking for pubnub_last_result() - rather than calling pubnub_await()
  3. If a condition occurs that prompts you to unsubscribe, call pubnub_cancel()
  4. Wait for the cancellation to finish (here you can call pubnub_await(), unless you want to do other stuff while you wait)
 Unsubscribing from all the channel(s) and then subscribing to a new channel Y is not the same as Subscribing to channel Y and then unsubscribing from the previously subscribed channel(s). Unsubscribing from all the channels resets the timetoken and thus, there could be some gaps in the subscription that may lead to a message loss.
To Unsubscribe from a channel you can use the following method(s) in the FreeRTOS SDK:
  1. ParameterTypeRequiredDescription
    p pubnub_t* YesPointer to Pubnub Client Context.
Unsubscribe from a channel:
pbresult = pubnub_subscribe(ctx, "my_channel", NULL);
/* If we don't set non-blocking I/O, we can't get out of a blocked read */
pubnub_set_non_blocking_io(ctx);
/* Can't use pubnub_await() here, it will block */
while (PNR_STARTED == pbresult) {
    pbresult = pubnub_last_result(ctx);
    /* Somehow decide we want to quit / unsubscribe */
    if (should_stop()) {
        pubnub_cancel(ctx);
        /* If we don't have anything else to do, it's OK to await now,
        but you could again have a loop "against" pubnub_last_result()
        */
        pbresult = pubnub_await(ctx);
        break;
    }
}
if (PNR_CANCELLED == pbresult) {
    puts("Subscribe cancelled - unsubscribed!");
}
The output below demonstrates the response to a successful call:
{
	"action" : "leave"
}
  1. pbresult = pubnub_subscribe(ctx, NULL, "my_channel_group");
    /* If we don't set non-blocking I/O, we can't get out of a blocked read */
    pubnub_set_non_blocking_io(ctx);
    /* Can't use pubnub_await() here, it will block */
    while (PNR_STARTED == pbresult) {
         pbresult = pubnub_last_result(ctx);
         /* Somehow decide we want to quit / unsubscribe */
         if (should_stop()) {
             pubnub_cancel(ctx);
             /* If we don't have anything else to do, it's OK to await now,
              * but you could again have a loop "against" pubnub_last_result()
              */
             pbresult = pubnub_await(ctx);
             break;
         }
    }
    if (PNR_CANCELLED == pbresult) {
        puts("Subscribe cancelled - unsubscribed!");
    }