Publish Messages
The foundation of all our PubNub offering is the ability to send a message and it will be delivered anywhere in less than 100ms. Below we will walk step by step to understand different ways of sending a message.
Receiving messages
We hope you already know you need to add a listener to receive and handle incoming messages, signals, and events.
If you're new to PubNub, make sure to check out how to set up your account as well.
Sending Messages
The primary means for sending messages is using the Publish API. You may only send a message to one channel at a time. PubNub's network supports sending unlimited publishes without waiting for response on the same TCP socket connection.
pubnub.publish(
{
channel: "my_channel",
message: {"text": "Hello World!"}
},
function(status, response) {
console.log(status);
console.log(response);
}
);
pubnub.publish(
channel: "my_channel",
message: ["text": "Hello World!"]
){ result in
switch result {
case let .success(response):
print("succeeded: \(response)")
case let .failure(error):
print("failed: \(error.localizedDescription)")
}
}
[self publish: @{@"text" : @"Hello World!"} toChannel:@"my_channel"
completion:^(PNPublishStatus *status) {
NSLog(@"%@status '%@'", status);
}];
JsonObject data = new JsonObject();
data.addProperty("text", "Hello World!");
pubnub.publish()
.channel("my_channel")
.message(data)
.async(new PNCallback<PNPublishResult>() {
@Override
public void onResponse(PNPublishResult result, PNStatus status) {
if (status.isError()) {
System.out.println("status code: " + status.getStatusCode());
}
else System.out.println("timetoken: " + result.getTimetoken());
}
});
Dictionary<string, string> data = new Dictionary<string, string>();
data.Add("text", "Hello World!");
pubnub.Publish()
.Channel("my_channel")
.Message(data)
.Execute(new PNPublishResultExt(
(result, status) => {
if (status.isError) {
Console.WriteLine("status code: " + status.ErrorData.Information);
}
else {
Console.WriteLine("timetoken: " + result.Timetoken.ToString());
}
}
));
def publish_callback(result, status):
if status.isError:
print status.statusCode
elif
print result.timetoken
pubnub.publish()\
.channel("my_channel") \
.message({"text": "Hello World!"})\
.pn_async(publish_callback)
When you publish a message to a channel, you're asking PubNub Platform to deliver that message to everyone who is subscribed to that channel.
As you can see, in the above publish method we specify the channel on which to send the message, the message content we want to send and a way to capture the server response, which in most languages is an asynchronous callback. A successful response looks like this:
[1,"Sent","14375189629170609"]
The response has three parts:
- success flag: 1 = success, 0 = failed
- response message: Sent or Failed
- publish timetoken: exact timestamp of the message which is a 17 digit value to the nearest 10th nanosecond (can be converted to UNIX epoch by dividing by 10000).
The publish timetoken can be used later to retrieve (or augment) that message using PubNub Storage.
HTTP Streaming and Pipelining
For more information about streaming and pipelining, refer to this gist.
Sending announcements to all users
You can send announcements to all users, or a limited set of users, in your application by using a channel to which the announcer has read/write access, and to which all other users have read-only access.
Once you have the channel set up, you can display any messages sent on the channel as announcements. Users with read access can subscribe to the channel to receive announcements, but can't send messages on the same channel.
pubnub.publish({
message: {
type: 'announcement',
text: 'Hello, this is an announcement'
},
channel: 'ch-1',
}, (status, response) => {
// handle status, response
});
pubnub.publish(
channel: "ch-1",
message: ["type": "announcement", "text": "Announcement message to all users"]
) { result in
switch result {
case let .success(response):
print("Successful Publish Response: \(response)")
case let .failure(error):
print("Failed Publish Response: \(error.localizedDescription)")
}
}
JsonObject messagePayload = new JsonObject();
messagePayload.addProperty("type", "announcement");
messagePayload.addProperty("text", "Hello, this is an announcement");
pubNub.publish()
.message(messagePayload)
.channel("ch-1")
.async(new PNCallback<PNPublishResult>() {
@Override
public void onResponse(PNPublishResult result, PNStatus status) {
// handle status, response
}
});
Dictionary<string, string> payload = new Dictionary<string, string>();
payload.Add("type", "announcement");
payload.Add("text", "Hello, this is an announcement");
pubnub.Publish()
.Channel("ch-1")
.Message(payload)
.Async((result, status) => {
if (!status.Error) {
Debug.Log(result.Timetoken);
} else {
Debug.Log(status.Error);
Debug.Log(status.ErrorData.Info);
}
});
Message Specifications
The message
property can contain any JSON serializable data (JSON isn't required, just recommended and is typical in most use cases), including: Objects, Arrays, Integers and Strings. String content can include any single-byte or multi-byte UTF-8 character.
JSON Serialization
There is no need to serialize your JSON object when sending messages via PubNub Platform. PubNub SDKs does that for you.
Delivery
Though we provide as much information about the delivery as possible, PubNub is not a guaranteed message delivery service, as there is no way to guarantee that the network connection between the subscribers and PubNub has not been interrupted.
For example, while a Sent status code returned to the message publisher does not guarantee that the message is delivered, it does mean that the PubNub Network has received the message and has already sent it along to all currently connected subscribers.
We are constantly adding features which improve deliverability but the quality of service of the last mile of connectivity is not within PubNub's control.
Subscribers with troubled connections or that were offline by intention can still retrieve missed messages. For more information about status codes, and retrieving missed and historical messages, refer to Connection Management and Storage.
Order
As long as the publisher sends messages at a slower rate than the consumer can receive them, you can assume with high confidence that messages will be kept in order.
However, PubNub does not guarantee that messages are stored or sent in the exact same order in which they are published. If your application requires messages to be processed in the same order they were published, we recommend adding a sequence field to the message payload and use it to order messages by.
Message Size Limit
The maximum number of characters per message is 32 KiB. The maximum message size is based on the final escaped character count, including the channel name.
If the message you publish exceeds the configured size, you'll receive the following message:
["PUBLISHED",[0,"Message Too Large","13524237335750949"]]
If you expect your messages will be pushing the size limit, check out the PubNub KB article, Calculating Message Payload Size Before Publish.
Receiving Messages
To receive a message, your client should implement a message
listener and subscribe to a channel in which the message is being published.
Sending Signals
Signals are ideal for high-volume, low-cost use cases; they're intended for sending small bits of data where the last sent data is the only important piece of information. For example, chat typing indicators, live GPS location updates, and sensor updates from IoT devices.
Messages vs Signals
Signals are similar to regular messages, with the following exceptions:
- The message size is limited to 30 bytes or less
- Signals cost less
- A signal's data can't be persisted in storage, and hence you can't access previous signals
- There are no SLAs, even though signals generally provide the same reliability and latency as published messages
- Signals can't invoke Push Notifications
Sending a signal is similar to publishing a message:
let gps = ["35.9296","-78.9482"];
pubnub.signal(
{
channel: "locations.route1",
message: gps
},
function(status, response) {
console.log(status, response);
}
);
let gps = ["35.9296","-78.9482"]
pubnub.signal(
channel: "locations.route1",
message: gps
) { result in
switch result {
case let .success(response):
print("succeeded: \(response)")
case let .failure(error):
print("failed: \(error.localizedDescription)")
}
}
NSArray *gps = [NSArray arrayWithObjects:@"35.9296,@"-78.9482",nil];
[self signal: gps toChannel: @"locations.route1"
completion:^(PNPublishStatus *status) {
NSLog(@"%@status '%@'", status);
}];
String[] gps = new String[]{"35.9296","-78.9482"};
pubnub.signal()
.message(gps)
.channel("locations.route1")
.async(new PNCallback<PNPublishResult>() {
@Override
public void onResponse(PNPublishResult result, PNStatus status) {
if (status.isError()) {
System.out.println("status code: " + status.getStatusCode());
}
else System.out.println("timetoken: " + result.getTimetoken());
}
});
string[] arrayMessage = new string[] {"35.9296","-78.9482"};
pubnub.Signal()
.Message(data)
.Channel("locations.route1")
.Execute(new PNPublishResultExt(
(result, status) => {
if (status.isError) {
Console.WriteLine("status code: " + status.ErrorData.Information);
}
else {
Console.WriteLine("timetoken: " + result.Timetoken);
}
}
));
def signal_callback(result, status):
if status.isError:
print status.statusCode
elif
print result.timetoken
pubnub.signal()\
.channel("locations.route1") \
.message({"msg": "Hello, my friend!"})\
.pn_async(signal_callback)
The Signal response may not be as important to your application and could be omitted, but is dependent on your use case.
Receiving Signals
To receive a signal, your client should implement a signal
listener and subscribe to a channel in which the signal is being sent.
Publishing with Filter
Message Filters allow a client to send Messages to only those clients that satisfy the conditions of the filter. To use Message Filters, it's important to include filtering conditions when publishing a new message. The meta
parameter isn't a part of the message data and allows subscribing clients to filter based on the included information.
To include metadata that can be used for filters, prepare the data and pass it to the publishing function.
pubnub.publish(
{
channel: "chats.room1",
message: "Hello to everyone (except me)",
meta: "uuid": pubnub.getUUID()
},
function(status, response) {console.log(status, response)};
);
pubnub.publish(
channel: "chats.room1",
message: "Hello to everyone (except me)",
meta: ["uuid": pubnub.configuration.uuid]
) { result in
switch result {
case let .success(response):
print("Successful Publish Response: \(response)")
case let .failure(error):
print("Failed Publish Response: \(error.localizedDescription)")
}
}
NSDictionary *meta = @{@"uuid": self.pubnub.uuid};
[self.pubnub publish:@"Hello to everyone (except me)"
toChannel:@"chats.room1" withMetadata:meta
completion:^(PNPublishStatus *status) {
NSLog(@"%@status '%@'", status);
}];
Map<String, Object> meta = new HashMap<>();
meta.put("uuid", pubnub.getUuid());
pubnub.publish().channel("chats.room1").meta(meta)
.message("Hello to everyone (except me)")
.async(new PNCallback<PNPublishResult>() {
@Override
public void onResponse(PNPublishResult result, PNStatus status) {
System.out.println(status);
System.out.println(result);
}
});
Dictionary<string, object> meta = new Dictionary<string, object>();
meta.Add("uuid", pubnub.GetUuid());
pubnub.Publish().Channel("chats.room1").Meta(meta)
.Message("Hello to everyone (except me)")
.Execute(new DemoPublishResult());
meta = {"uuid": pubnub.get_uuid()}
pubnub.publish()\
.channel("chats.room1")\
.meta(meta)\
.message("Hello to everyone (except me)")\
.sync()
In the above example, the metadata will allow the client to filter messages based on the UUID of the sender.
Filter Language Definition
The filtering language is extensive and supports many advanced use-cases. Here are some common filter examples:
Expression | Meaning |
---|---|
string == 'match' | Exact match |
string LIKE 'match*' | Asterisk wildcarding, case insensitive |
string LIKE 'match\*' | Literal match with string containing asterisk character |
('Anne','anna','Ann') LIKE 'ann*' | Any of the three set members would be a sufficient match |
('a','b','c') CONTAINS string | Compare against a list of values |
otherstring CONTAINS string | Check for a substring match |
(3,5,9) contains numValue | Compare number to a list of values |
!((3,5,9) contains numValue) | Negation |
string contains numValue | str(numValue) in string |
numValue > (numA + numB - numC) | Compare number to an arithmetic expression |
(numA ^ numB) != (numValue * 10) | Compare two expressions |
(~numA / numB) | numValue |
Filter Expression Language Specification
compound_expression
is root.
Expression | Comment |
---|---|
<compound_expression> | <expression> | <expression> <binary_logical_op> <expression> |
<binary_logical_op> | && | || |
<expression> | (<expression>) | <operand> <comparison_operator> <operand> | <unary_logical_op> <operand> |
<numeric_comparison_operator> | {==, !=, <, >, <=, >=} |
<string_comparison_operator> | {contains, like} |
<unary_logical_op> | ! |
<operand> | (<operand>) | <unary_op> <operand> | <literals> <binary_op> <literals> |
<unary_op> | ~ |
<binary_op> | |, &, ^, +, -, /, * |