---
source_url: https://www.pubnub.com/docs/sdks/windows-c/api-reference/publish-and-subscribe
title: Publish/Subscribe API for Windows C SDK
updated_at: 2026-06-11T11:37:59.167Z
sdk_name: PubNub Windows C SDK
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Publish/Subscribe API for Windows C SDK

PubNub Windows C SDK

## Publish

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's 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. `data` should not contain special Windows C classes or functions as these will not serialize. String content can include any single-byte or multi-byte UTF-8 character.

:::warning JSON serialize
It is important to note that you should JSON serialize when sending signals/messages via PUBNUB.
:::

#### Message size

The maximum number of characters per message is 32 KiB 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.5 KiB) providing optimal network performance.

If the message you publish exceeds the configured size, you will receive the following message:

#### Message too large error

```c
["PUBLISHED",[0,"Message Too Large","13524237335750949"]]
```

For further details please check: [https://support.pubnub.com/hc/en-us/articles/360051495932-Calculating-Message-Payload-Size-Before-Publish](https://support.pubnub.com/hc/en-us/articles/360051495932-Calculating-Message-Payload-Size-Before-Publish)

:::tip Need larger messages?
Our platform is optimized for payloads up to 32 KiB. PubNub supports larger messages, but increasing the limit requires a verification of compatibility with your use case.
Talk to [our team](https://www.pubnub.com/company/contact-sales/) to discuss increasing the message size limit for your use case.
:::

#### 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 can't 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 (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.

#### Publishing compressed messages

Message compression lets you send the message payload as the compressed body of an HTTP POST call.

Every PubNub SDK supports sending messages using the `publish()` call in one or more of the following ways:

* Uncompressed, using HTTP GET (message sent in the URI)
* Uncompressed, using HTTP POST (message sent in the body)
* Compressed, using HTTP POST (compressed message sent in the body)

This section outlines what compressing a message means, and how to use compressed messages to your advantage.

:::note Compressed messages support
Currently, the C and Objective-C SDKs support compressed messages.
:::

Message compression can be helpful if you want to send data exceeding the default 32 KiB message size limit, or use bandwidth more efficiently. Compressing messages is useful for scenarios that include high channel occupancy and quick exchange of information like ride hailing apps or multiplayer games.

#### Compression trade-offs

**Small messages can expand** Compressed messages generally have a smaller size, and can be delivered faster, but only if the original message is over 1 KiB. If you compress a signal (whose size is limited to 64 bytes), the compressed payload exceeds the signal's initial uncompressed size.

**CPU overhead can increase** While a smaller payload size is an advantage, working with compressed messages uses more CPU time than working with uncompressed messages. CPU time is required to compress the message on the sending client, and again to decompress the message on the receiving client. Efficient resource management is especially important on mobile devices, where increased usage affects battery life. Carefully consider the balance of lower bandwidth and higher speed versus any increased CPU usage.

**Using Compression** Compression methods and support vary between SDKs. If the receiving SDK doesn't support the sender's compression method, or even if it doesn't support it at all, the PubNub server automatically changes the compressed message's format so that it is understandable to the recipient. No action is necessary from you.

Messages are not compressed by default; you must always explicitly specify that you want to use message compression. Refer to the code below for an example of sending a compressed message.

```c
struct pubnub_publish_options opt = pubnub_publish_defopts();
opt.method = pubnubSendViaPOSTwithGZIP;
pbresult = pubnub_publish_ex(pn, "channel_name", "{\"message\":\"This message will be compressed\"}", opt);
}]
```

### Method(s)

To `Publish a message` you can use the following method(s) in the Windows C SDK:

```c
enum pubnub_res pubnub_publish (pubnub_t *p, const char *channel, const char *message)
```

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| p | pubnub_t* | Yes |  | Pointer to pubnub context. Can't be NULL |
| channel | const | Yes |  | Pointer to `string` with the `channel` ID (or comma-delimited list of `channel` IDs) to publish to. |
| message | const | Yes |  | Pointer to `string` containing `message` to `publish` in JSON format. |

```c
enum pubnub_res pubnub_publishv2 (pubnub_t *p, const char *channel, const char *message, bool store_in_history, bool eat_after_reading)
```

| Parameter | Description |
| --- | --- |
| `p` *Type: pubnub_t* | Pointer to Pubnub Client Context |
| `channel` *Type: const char* | Pointer to `string` with the `channel` ID (or comma-delimited list of `channel` IDs) to `publish` to. |
| `message` *Type: const char* | Pointer to string containing `message` to publish in JSON format. |
| `store_in_history` *Type: bool | If false, `message` will not be stored in `history` of the `channel` ID |
| `eat_after_reading` *Type: const char* | If true, `message` will not be stored for delayed or repeated retrieval or display |

### Sample code

#### Publish a message to a channel

```c
pubnub_publish(ctx, "my_channel", "\"message\"");
pbresult = pubnub_await(ctx);
if (PNR_OK == pbresult) {
    /* Published successfully */
}
```

:::note Subscribe to the channel
Before running the above publish example, either using the [Debug Console](https://www.pubnub.com/docs/console/) or in a separate script running in a separate terminal window, [subscribe to the same channel](#subscribe) that is being published to.
:::

### Rest response from server

The function returns the following formatted response:

```json
[1, "Sent", "13769558699541401"]
```

### Other examples

#### Publish a JSON serialized message

```c
pubnub_t *ctx = pubnub_alloc();
if (NULL == ctx) {
    puts("Couldn't allocate a Pubnub context");
    return -1;
}
pubnub_init(ctx, "demo", "demo");
pubnub_publish(ctx, "hello_world", "{\"msg\": \"Hello from Pubnub C-core docs!\"}");
pubnub_cancel(ctx);
```

#### Publish with cipher key

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

#### Publish with cipher key using pubnub_publish_ex

Using this method you can reuse the `cipherKey` from the options.

```c
struct pubnub_publish_options opt = pubnub_publish_defopts();
opt.cipher_key = my_cipher_key;
pbresult = pubnub_publish_ex(pn, "my_channel", "42", opt);
```

## Extended publish

### Extended publish options structure

Holds all the options for `extended` publish.

### Method(s)

#### Declaration

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

#### Members

| Member | Type | Description |
| --- | --- | --- |
| `store` | bool | If `true`, the message is stored in history. If `false`, the message is *not* stored in history. |
| `cipher_key` | char 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). |
| `replicate` | bool | If `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`). |
| `meta` | char const* | An optional `JSON` object, used to send additional (`meta`) data about the message, which can be used for `stream filtering`. |
| `method` | enum pubnub_publish_method | Defines the method by which publish transaction will be performed. Can be `HTTP GET` or `POST`. If using `POST`, content can be `GZIP` compressed. |

### Initialize extended publish options

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

| Parameter |
| --- |
| `store`Default: `true` |
| `cipher_key`Default: `NULL` |
| `replicate`Default: `true` |
| `meta`Default: `NULL` |
| `method`Default: `pubnubPublishViaGET` |

### Method(s)

#### Declaration

```c
struct pubnub_publish_options pubnub_publish_defopts(void);
```

#### Parameters

This method doesn't take any argument.

### Sample code

```c
struct pubnub_publish_options opts = pubnub_publish_defopts();
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| struct `pubnub_publish_options` |  | The default options for `publish`. |

### Extended publish

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

### Method(s)

#### Declaration

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

#### Parameters

| Parameter | Description |
| --- | --- |
| `p` *Type: pubnub_t* | The Pubnub context. |
| `channel` *Type: char const* | The string with the channel ID to `publish` to. |
| `message` *Type: char const* | The message to `publish`. It needs to be `JSON` encoded. |
| `opts` *Type: struct pubnub_publish_options | The Publish options. |

### Sample code

```c
struct pubnub_publish_options opt = pubnub_publish_defopts();
    opt.store = false;
    pbresult = pubnub_publish_ex(pn, "my_channel", "42", opt);
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `enum pubnub_res` | PNR_STARTED | Success. |
|  | other | Indicates the type of error. |

## Signal

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 timetoken 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 `64` 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](https://www.pubnub.com/docs/mailto:support@pubnub.com).

### Method(s)

#### Declaration

```c
enum pubnub_res pubnub_signal(pubnub_t* pb, char const* channel, char const* message);
```

#### Parameters

| Parameter | Description |
| --- | --- |
| `pb` *Type: pubnub_t* | The Pubnub context in which to parse the response. |
| `channel` *Type: char const* | The string with the channel ID to signal to. |
| `message` *Type: char const* | The string with the signal, expected to be in JSON format. |

### Sample code

#### Signal a message to a channel

```c
enum pubnub_res = pubnub_signal(pb, "my_channel", "\"signalling\"");
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `enum pubnub_res` | PNR_OK | Transaction finished (signal sent). |
|  | PNR_STARTED | Transaction started (started to send signal), will finish later. |
|  | otherwise | Error, value indicates the reason for failure. |

### Message type enumeration (enum pubnub_message_type)

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

| Symbol | Value | Description |
| --- | --- | --- |
| `pbsbPublished` | 0 | Published message (sent via Publish transaction) |
| `pbsbSignal` | 1 | A signal message (sent via Signal transaction) |

## Subscribe

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` ID. To subscribe to a `channel` ID 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.

:::note Context usage
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.
:::

:::warning Unsubscribing from all channels
**Unsubscribing** from all channels, 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 channels resets the last-received `timetoken` and thus, there could be some gaps in the subscription that may lead to message loss.
:::

### Method(s)

To `Subscribe to a channel` you can use the following method(s) in the Windows C SDK:

```c
enum pubnub_res pubnub_subscribe (pubnub_t *p, const char *channel, const char *channel_group)
```

| Parameter | Description |
| --- | --- |
| `p` *Type: pubnub_t* | Pointer to Pubnub client context. Can't be NULL. |
| `channel`Type: const char* | The `string` with the `channel` ID (or comma-delimited list of `channel` IDs) to subscribe to. |
| `channel_group`Type: const char* | The `string` with the channel `group` name (or comma-delimited list of channel `group` names) to subscribe to. |

### Sample code

Subscribe to a channel:

```c
pubnub_subscribe(ctx, "my_channel", NULL);
pbresult = pubnub_await(ctx);
if (PNR_OK == pbresult) {
    char const *message = pubnub_get(ctx);
    while (message != NULL) {
        message = pubnub_get(ctx);
    }
}
```

### Rest response from server

The output below demonstrates the response format to a successful call:

```json
[[], "Time Token"]
```

### Other examples

#### Subscribing to multiple channels

It's possible to subscribe to more than one channel using the [Multiplexing](https://www.pubnub.com/docs/general/channels/subscribe#channel-multiplexing) feature. The example shows how to do that using an array to specify the channel names.

:::note Alternative subscription methods
You can also use [Wildcard Subscribe](https://www.pubnub.com/docs/general/channels/subscribe#wildcard-subscribe) and [Channel Groups](https://www.pubnub.com/docs/general/channels/subscribe#channel-groups) to subscribe to multiple channels at a time. To use these features, the *Stream Controller* add-on must be enabled on your keyset in the [Admin Portal](https://admin.pubnub.com).
:::

```c
pubnub_t *ctx = pubnub_alloc();
if (NULL == ctx) {
    puts("Couldn't allocate a Pubnub context");
    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) {
    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) {
        printf("Got message: %s on channel %s\n", msg, (NULL == channel) ? "" : channel );
        msg = pubnub_get(ctx);
        channel = pubnub_get_channel(ctx);
    }
}
pubnub_free(ctx);
```

#### Subscribing to a Presence channel

:::warning Requires Presence
This method requires that the Presence add-on is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). For information on how to receive presence events and what those events are, refer to [Presence Events](https://www.pubnub.com/docs/general/presence/presence-events#subscribe-to-presence-channel).
:::

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`.

```c
// 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
int listen_for_presence_events(pubnub_t *pn) {
  char const *chan = "my_channel-pnpres";
  char const *msg;
  enum pubnub_res res;

  for (;;) {
    pubnub_subscribe(pn, chan, NULL);

    res = await(&amp;user_data);
    if (res == PNR_STARTED) {
      printf("pubnub_last_result() returned unexpected: PNR_STARTED(%d)\n", res);
      return -1;
    }

    if (PNR_OK == res) {
      puts("Presence event:");
      for (;;) {
        msg = pubnub_get(pn);
        if (NULL == msg) {
          break;
        }
        puts(msg);
      }
    } else {
      printf("Subscribing failed with code: %d\n", res);
      return -1;
    }
  }
}
```

### Sample Responses

#### Join event

```json
{
    "action": "join",
    "timestamp": 1345546797,
    "uuid": "175c2c67-b2a9-470d-8f4b-1db94f90e39e",
    "occupancy": 2
}
```

#### Leave event

```json
{
    "action" : "leave",
    "timestamp" : 1345549797,
    "uuid" : "175c2c67-b2a9-470d-8f4b-1db94f90e39e",
    "occupancy" : 1
}
```

#### Timeout event

```json
{
    "action": "timeout",
    "timestamp": 1345549797,
    "uuid": "76c2c571-9a2b-d074-b4f8-e93e09f49bd",
    "occupancy": 0
}
```

#### State change event

```json
{
    "action": "state-change",
    "uuid": "76c2c571-9a2b-d074-b4f8-e93e09f49bd",
    "timestamp": 1345549797,
    "data": {
        "isTyping": true
    }
}
```

#### Interval event

```json
{
    "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:

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

If the full interval message is greater than `30 KB` (since the max publish payload is `∼32 KiB`), 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.

```json
{
    "action" : "interval",
    "occupancy" : <# users in channel>,
    "timestamp" : <unix timestamp>,
    "here_now_refresh" : true
}
```

#### Subscribe to a channel group

:::note Requires Stream Controller add-on
This method requires that the *Stream Controller* add-on is enabled for your key in the [Admin Portal](https://admin.pubnub.com/). Read the [support page](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) on enabling add-on features on your keys.
:::

```c
enum pubnub_res res;

for (;;) {
  res = pubnub_subscribe(pn, NULL,  channel_group);
  read_response(pn, res);
}
```

#### Subscribe to the Presence channel of a channel group

:::note Requires Stream Controller and Presence add-ons
This method requires both the *Stream Controller* and *Presence* add-ons are enabled for your key in the [Admin Portal](https://admin.pubnub.com/). Read the [support page](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) on enabling add-on features on your keys.
:::

```c
enum pubnub_res res;

for (;;) {
  res = pubnub_subscribe(pn, NULL, "family-pnpres");
  read_response(pn, res);
}
```

#### Using cipherKey with subscribe

For subscribe, you don't use the cipher key at subscribe, but, when you get the received messages after the subscribe transaction has finished:

```cpp
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:

```cpp
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);
}
```

## Extended subscribe

### Extended subscribe options structure

Holds all the options for `extended` subscribe.

### Method(s)

#### Declaration

```c
struct pubnub_subscribe_options {char const* channel_group;unsigned heartbeat;};
```

#### Members

| Member | Type | Description |
| --- | --- | --- |
| `channel_group` | char const* | `Channel group` (a comma-delimited list of channel group names). If `NULL`, will not be used. |
| `cipher_key` | char const* | The channel activity `timeout`, in seconds. If below the minimum value supported by Pubnub, the minimum value will be used (instead). |

### Initialize extended subscribe options

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

### Method(s)

#### Declaration

```c
struct pubnub_subscribe_options pubnub_subscribe_defopts(void);
```

#### Parameter

This method doesn't take any argument.

### Sample code

```c
struct pubnub_subscribe_options opts = pubnub_subscribe_defopts();
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| struct `pubnub_subscribe_options` |  | The default options for subscribe. |

### Extended subscribe

The extended subscribe. Basically the same as `pubnub_subscribe()` but with options except `@p` channel given in `@p` opts.

### Method(s)

#### Declaration

```c
enum pubnub_res pubnub_subscribe_ex(pubnub_t *p, const char *channel, struct pubnub_subscribe_options opts);
```

#### Parameter

| Parameter | Description |
| --- | --- |
| `p` *Type: pubnub_t* | The Pubnub context. |
| `channel` *Type: char const* | The string with the channel ID (or comma-delimited list of channel IDs) to subscribe for. |
| `opts` *Type: struct pubnub_subscribe_options | Subscribe options. |

### Sample code

```c
struct pubnub_subscribe_options opt = pubnub_subscribe_defopts();
opt.heartbeat = 412;
pbresult = pubnub_subscribe_ex(pn, "my_channel", opt);
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `enum pubnub_res` | PNR_STARTED | Success. |
|  | other | Indicates the type of error. |

## Subscribe v2

### Subscribe v2 options structure

Holds all the options for subscribe v2.

### Method(s)

#### Declaration

```c
struct pubnub_subscribe_v2_options {char const* channel_group;unsigned heartbeat;};
```

#### Members

| Member | Type | Description |
| --- | --- | --- |
| `channel_group` | char const* | Channel group (a comma-delimited list of channel group names). If `NULL`, will not be used. |
| `heartbeat` | unsigned | The 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. |

### Initialize extended subscribe v2 options

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

### Method(s)

#### Declaration

```c
struct pubnub_subscribe_v2_options pubnub_subscribe_v2_defopts(void);
```

#### Parameter

This method doesn't take any argument.

### Sample code

```c
struct pubnub_subscribe_v2_options opts = pubnub_subscribe_v2_defopts();
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| struct `pubnub_subscribe_options` |  | The default options for subscribe. |

### Subscribe v2

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.

### Method(s)

#### Declaration

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

#### Parameter

| Parameter | Description |
| --- | --- |
| `p` *Type: pubnub_t* | The Pubnub context. |
| `channel` *Type: char const* | The string with the channel ID (or comma-delimited list of channel IDs) to subscribe for. |
| `opts` *Type: struct pubnub_subscribe_options | Subscribe V2 options. |

### Sample code

```c
struct pubnub_subscribe_v2_options opt = pubnub_subscribe_v2_defopts();
opt.heartbeat = 412;
pbresult = pubnub_subscribe_v2(pn, "my_channel", opt);
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `enum pubnub_res` | PNR_STARTED | Started. |
|  | PNR_OK | Finished with a success (can only happen in the sync interface). |
|  | other | Failed, Indicates the type of error. |

### V2 message structure

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).

### Method(s)

#### Declaration

```c
struct pubnub_v2_message {char const* channel_group;unsigned heartbeat;};
```

#### Parameter

| Member | Type | Description |
| --- | --- | --- |
| `tt` | struct pubnub_char_mem_blok | The timetoken of the message - when it was published. |
| `region` | int | Region of the message - not interesting in most cases. |
| `flags` | int | Message flags (indicators). |
| `channel` | struct pubnub_char_mem_blok | Channel ID that message was published to. |
| `match_or_group` | struct pubnub_char_mem_blok | Subscription match or the channel group. |
| `payload` | struct pubnub_char_mem_blok | The message itself (its contents, payload). |
| `metadata` | struct pubnub_char_mem_blok | The message metadata, as published. |
| `message_type` | enum pubnub_message_type | The type of the message (`published`, `signal`). |

### Get V2 message

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.

### Method(s)

#### Declaration

```c
struct pubnub_v2_message pubnub_get_v2(pubnub_t* pbp);
```

#### Parameter

| Parameter | Description |
| --- | --- |
| `pbp` *Type: pubnub_t* | The Pubnub context from which to get the next message. |

### Sample code

```c
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);
}
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `struct pubnub_v2_message` |  | The 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. |

## Unsubscribe

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).

:::warning Unsubscribing from all channels
**Unsubscribing** from all channels, 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 channels resets the last-received `timetoken` and thus, there could be some gaps in the subscription that may lead to message loss.
:::

### Method(s)

To `Unsubscribe from a channel` you can use the following method(s) in the Windows C SDK:

| Parameter | Description |
| --- | --- |
| `p` *Type: pubnub_t* | Pointer to Pubnub Client Context. |

### Sample code

```c
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!");
}
```

### Rest response from server

The output below demonstrates the response to a successful call:

```json
{
    "action" : "leave"
}
```

### Other examples

#### Unsubscribing from a channel group

```c
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!");
}
```