Posix C++Posix C++Windows C++C++Posix C++ Publish & Subscribe API Reference for Realtime Apps

Go to Configuration


The 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 Posix C++ 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 Posix C++ SDK:
  1. ParameterTypeRequiredDescription
    channelstd::string const &YesSpecifies channel name to publish messages to.
    messagestd::string const &YesThe message.
  2. ParameterTypeRequiredDescription
    channelstd::string const &YesSpecifies channel name to publish messages to.
    messagestd::string const &YesThe message to publish.
    optionspubnub::pubv2_optYesOptions for Publish v2. These are designed to be used as bit-masks, for which purpose there are overloaded & and |(bit-and and bit-or) operators. There are 2 options available to use:
    • store_in_history
    • eat_after_reading

Publish a message to a channel:

// Sync
void publish(pubnub::context &pn) {
  enum pubnub_res res;

  res = pn.publish("my_channel", "\"Hello from the PubNub C++ SDK!\"").await();

  if (PNR_OK == res) {
    std::cout << pn.last_publish_result() << std::endl;
  } else {
    std::cout << "Publish request failed" << std::endl;
  }
}

// Lambdas
void publish(pubnub::context &pn) {
  pn.publish("my_channel", "\"Hello from the PubNub C++ SDK!\"").
    then([=](pubnub::context &pn, pubnub_res res) {
      if (PNR_OK == res) {
        std::cout << pn.last_publish_result() << std::endl;
      } else {
        std::cout << "Publish request failed" << std::endl;
      }
    });
}



// Functions
static void on_publish(pubnub::context &pn, pubnub_res res) {
  if (PNR_OK == res) {
    std::cout << pn.last_publish_result() << std::endl;
  } else {
    std::cout << "Publish request failed" << std::endl;
  }
}

void publish(pubnub::context &pn) {
  pn.publish("my_channel", "\"Hello from the PubNub C++ SDK!\"").then(on_publish);
}
 
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. // Sync
    void publish(pubnub::context &pn) {
      enum pubnub_res res;
     
      res = pn.publish("my_channel", "{\"msg\": \"Hello from the PubNub C++ SDK!\"}").await();
     
      if (PNR_OK == res) {
        std::cout << pn.last_publish_result() << std::endl;
      } else {
        std::cout << "Publish request failed" << std::endl;
      }
    }
     
    // Lambdas
    void publish(pubnub::context &pn) {
      pn.publish("my_channel", "{\"msg\": \"Hello from the PubNub C++ SDK!\"}").
        then([=](pubnub::context &pn, pubnub_res res) {
          if (PNR_OK == res) {
            std::cout << pn.last_publish_result() << std::endl;
          } else {
            std::cout << "Publish request failed" << std::endl;
          }
        });
    }
     
     
     
    // Functions
    static void on_publish(pubnub::context &pn, pubnub_res res) {
      if (PNR_OK == res) {
        std::cout << pn.last_publish_result() << std::endl;
      } else {
        std::cout << "Publish request failed" << std::endl;
      }
    }
     
    void publish(pubnub::context &pn) {
      pn.publish("my_channel", "{\"msg\": \"Hello from the PubNub C++ SDK!\"}").then(on_publish);
    }
  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);
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 subscribe() call completes.
 
Typically, you will want two separate contexts for publish and subscribe anyway. If you are changing the set of channels you subscribe to, you should first call leave() on the old set.

The subscribe() interface is essentially a transaction to start listening on the channel for arrival of next message. This has to be followed by a call to the method get() or get_all( ) 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 than Subscribing to the channel Y and then unsubscribing from the previously subscribe 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 Posix C++ SDK:
  1. ParameterTypeRequiredDescription
    channelsstd::string const &YesSpecifies channel to which to subscribe
    channel_groupstd::string const &YesSpecifies channel_group to which to subscribe
  2. ParameterTypeRequiredDescription
    channelstd::vector<std::string> const &YesSpecifies channel(s) to which to subscribe
    channel_groupstd::vector<std::string> const &YesSpecifies channel_groups to which to subscribe
Subscribe to a channel:
// Sync
void subscribe(pubnub::context &pn) {
  enum pubnub_res res;
 
  for (;;) {
    res = pn.subscribe("my_channel").await();
 
    if (PNR_OK == res) {
      std::vector<std::string> msg = pn.get_all();

      for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
       std::cout << *it << std::endl;
      }
    } else {
      std::cout << "Request failed" << std::endl;
      break;
    }
  }
}
 
 
// Lambdas
void subscribe(pubnub::context &ipn) {
  ipn.subscribe("my_channel").then([=](pubnub::context &pn, pubnub_res res) {
    auto msg = pn.get_all();
 
    if (PNR_OK == res) {
      for (auto &&m: msg) {
        std::cout << m << std::endl;
      }
 
    } else {
      std::cout << "Request failed" << std::endl;
    }
  });
}
 
// Functions
void on_subscribe(pubnub::context &pn, pubnub_res res) {
  if (PNR_OK == res) {
    std::vector<std::string> msg = pn.get_all();

    for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
      std::cout << *it << std::endl;
    }
  } else {
    std::cout << "Request failed" << std::endl;
  }
}
 
void subscribe(pubnub::context &pn) {
  pn.subscribe("my_channel").then(on_subscribe);
}
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.
    //Sync
    enum pubnub_res res;
    res = pn.subscribe("my_channel1,my_channel2").await();
    
    //Lambdas
    enum pubnub_res res;
    pn.subscribe("my_channel1,my_channel2").then(...);
    
    //Functions
    enum pubnub_res res;
    pn.subscribe("my_channel1,my_channel2").then(on_connect);
  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
    void presence(pubnub::context &pn) {
      enum pubnub_res res;
      bool done = false;
    
      while (!done) {
        res = pn.subscribe("my_channel-pnpres").await();
    
        if (PNR_OK == res) {
          std::vector<std::string> msg = pn.get_all();
    
          for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
            std::cout << *it << std::endl;
          }
    
          if (msg.size() > 0) {
            done = true;
          }
        } else {
          std::cout << "Error" << std::endl;
          break;
        }
      }
    }
    
    // Lambdas
    void presence(pubnub::context &ipn) {
      bool done = false;
    
      while (!done) {
        ipn.subscribe("my_channel-pnpres").then([&](pubnub::context &pn, pubnub_res res) {
          auto msg = pn.get_all();
    
          if (PNR_OK == res) {
            for (auto &&m: msg) {
              std::cout << m << std::endl;
            }
    
            if (msg.size() > 0) {
              done = true;
            }
          } else {
            std::cout << "Request failed" << std::endl;
          }
        });
      }
    }
    
    // Functions
    void on_presence(pubnub::context &pn, pubnub_res res) {
      if (PNR_OK == res) {
        std::vector<std::string> msg = pn.get_all();
    
        for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
          std::cout << *it << std::endl;
        }
    
        if (msg.size() > 0) {
          done = true;
        }
      } else {
        std::cout << "Error" << std::endl;
      }
    }
    
    void presence(pubnub::context &pn) {
      while (!done) {
        pn.subscribe("my_channel-pnpres").then(on_presence);
      }
    }
    
    {
    	"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
    static void subscribe_to_group(pubnub::context &pn) {
      enum pubnub_res res;
    
      for (;;) {
        try {
          res = pn.subscribe("", channel_group).await();
    
          if (PNR_OK == res) {
            std::vector<std::string> msg = pn.get_all();
    
            for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
              std::cout << *it << std::endl;
            }
    
          } else {
            std::cout << "Failed with code " << res << std::endl;
          }
        } catch (std::exception &ex) {
          std::cout << "Exception: " << ex.what() << std::endl;
          break;
        }
      }
    }
    
    //Lambdas
    static void subscribe_to_group(pubnub::context &ipn) {
      ipn.subscribe("", channel_group)
        .then([=](pubnub::context &pn, pubnub_res res) {
          if (PNR_OK == res) {
            std::cout << "Susbcribed!" << std::endl;
    
            pn.subscribe("", channel_group)
              .then([=](pubnub::context &pn, pubnub_res res) {
                if (PNR_OK == res) {
                  auto msg = pn.get_all();
                  for (auto &&x: msg) {
                    std::cout << x << std::endl;
                  }
                } else {
                  std::cout << "Failed with code " << res << std::endl;
                }
            });
          } else {
            std::cout << "Failed with code " << res << std::endl;
          }
      });
    }
    
    //Functions
    static void on_subscribe(pubnub::context &pn, pubnub_res res) {
      if (PNR_OK == res) {
        std::vector<std::string> msg = pn.get_all();
    
        for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
          std::cout << *it << std::endl;
        }
    
      } else {
        std::cout << "Failed with code " << res << std::endl;
      }
    }
    
    static void on_first_subscribe(pubnub::context &pb, pubnub_res res)
    {
      if (PNR_OK ==  res) {
        std::cout << "Subscribed!" << std::endl;
      } else {
        std::cout << "Subscribe failed!" << std::endl;
      }
    
      pb.subscribe("", channel_group).then(on_subscribe);
    }
    
    
    static void subscribe_to_group(pubnub::context &ipn) {
      ipn.subscribe("", channel_group).then(on_first_subscribe);
    }
  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
    static void presence_of_group(pubnub::context &pn) {
      enum pubnub_res res;
    
      for (;;) {
        try {
          res = pn.subscribe("", channel_group + "-pnpres").await();
    
          if (PNR_OK == res) {
            std::vector<std::string> msg = pn.get_all();
    
            for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
              std::cout << *it << std::endl;
            }
    
          } else {
            std::cout << "Failed with code " << res << std::endl;
          }
        } catch (std::exception &ex) {
          std::cout << "Exception: " << ex.what() << std::endl;
          break;
        }
      }
    }
    
    //Lambdas
    static void presence_of_group(pubnub::context &ipn) {
      ipn.subscribe("", channel_group + "-pnpres")
        .then([=](pubnub::context &pn, pubnub_res res) {
          if (PNR_OK == res) {
            std::cout << "Susbcribed!" << std::endl;
    
            pn.subscribe("", channel_group + "-pnpres")
              .then([=](pubnub::context &pn, pubnub_res res) {
                if (PNR_OK == res) {
                  auto msg = pn.get_all();
                  for (auto &&x: msg) {
                    std::cout << x << std::endl;
                  }
                } else {
                  std::cout << "Failed with code " << res << std::endl;
                }
            });
          } else {
            std::cout << "Failed with code " << res << std::endl;
          }
      });
    }
    
    //Functions
    static void on_subscribe(pubnub::context &pn, pubnub_res res) {
      if (PNR_OK == res) {
        std::vector<std::string> msg = pn.get_all();
    
        for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
          std::cout << *it << std::endl;
        }
    
      } else {
        std::cout << "Failed with code " << res << std::endl;
      }
    }
    
    static void on_first_subscribe(pubnub::context &pb, pubnub_res res)
    {
      if (PNR_OK ==  res) {
        std::cout << "Subscribed!" << std::endl;
      } else {
        std::cout << "Subscribe failed!" << std::endl;
      }
    
      pb.subscribe("", channel_group + "-pnpres").then(on_subscribe);
    }
    
    
    static void subscribe_to_group(pubnub::context &ipn) {
      ipn.subscribe("", channel_group + "-pnpres").then(on_first_subscribe);
    }
  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); 
    }
 

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 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 than Subscribing to the channel Y and then unsubscribing from the previously subscribe 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 Posix C++ SDK:
  1. Go to PubNub Leave Method
Unsubscribe from a channel:
auto futr = ctx.subscribe( "my_channel");
/* If we don't set non-blocking I/O, we can't get out of a blocked read */
ctx.set_blocking_io(pubnub::blocking);
/* Can't use await() here, it will block */
auto pbresult = PNR_STARTED;
while (PNR_STARTED == pbresult) {
     pbresult = futr.last_result();
     /* Somehow decide we want to quit / unsubscribe */
     if (should_stop()) {
         ctx.cancel();
         /* 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 = futr.await();
         break;
     }
}
if (PNR_CANCELLED == pbresult) {
    std::cout << "Subscribe cancelled - unsubscribed!" << std::endl;
}
The output below demonstrates the response to a successful call:
{
	"action" : "leave"
}
  1. pn.set_blocking_io(pubnub::non_blocking);
     
    pubnub::futres futres = pn.subscribe("", "my_channel_group");
     
    pubnub_res res = futres.last_result();
     
    while (res == PNR_STARTED) {
      res = futres.last_result();
     
      if (should_stop()) {
        pn.cancel();
        res = futres.await();
        break;
      }
    }
     
    if (res == PNR_CANCELLED) {
      std::cout << "Unsubscribed" << std::endl;
    }

class subscribe_options;

A wrapper class for subscribe options, enabling a nicer usage.

	pn.subscribe(chan, subscribe_options().heartbeat(412));

Sets the channel group to @p chgroup.

  1. subscribe_options& channel_group(std::string const& chgroup);

    MemberTypeDescription
    chgroupstd::string const&Channel group to set.
		subopts.channel_group("my_group");
TypeValueDescription
subscribe_options& *thisThe subscribe options object (reference).

Sets the channel groups to @p chgroup.

  1. subscribe_options& channel_group(std::vector<std::string> const& chgroup);

    ParameterTypeDescription
    chgroupstd::vector<std::string> const&Vector of channel groups to set.
		subopts.channel_group({"my_group"}); // C++11
TypeValueDescription
subscribe_options& *thisThe subscribe options object (reference).

Sets the heartbeat interval to @p hb_interval

  1. subscribe_options& heartbeat(unsigned hb_interval);

    ParameterTypeDescription
    hb_intervalunsignedHeartbeat interval, in seconds.
		subopts.heartbeat(100);
TypeValueDescription
subscribe_options& *thisThe subscribe options object (reference).

Starts a Subscribe transaction to @p channel with extended (full) options in @p opt.

  1. futres subscribe(std::string const &channel, subscribe_options opt);

    ParameterTypeRequiredDescription
    channelstd::string const&YesThe channel to subscribe to.
    optsubscribe_optionsYesOptions (extended/full) for subscribe.
		futres ftr = pn.subscribe(chan, subscribe_options().heartbeat(412));
TypeValueDescription
pubnub::futres The future result object to use to get the outcome of the transaction.

Go to Presence