# Topic (/zh-CN/api-reference/rtm/unity/topic)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

**Topic** 是 Stream Channel 中的数据流管理机制。用户可以在 Stream Channel 中利用此特性进行数据流的订阅、分发、事件通知等。

:::info[信息]
Topic 只存在于 Stream Channel。所以用户在使用相关特性之前，需要先创建一个 `IStreamChannel` 对象实例。
:::

想要了解 Topic 的更多特性，可以查看：

- [Topic](/zh-CN/realtime-media/rtm/build/channels-and-topics/topics/topic-basic)

## JoinTopicAsync [#jointopicasync]

### 接口描述 [#接口描述]

加入 Topic 操作让用户注册成为此 Topic 的消息发布者（publisher）之一，让其具备发送消息的能力。执行此操作与否并不影响此用户是否成为此 Topic 的订阅者。

:::info[信息]
- 当前 RTM 支持单客户端在同一个 Stream Channel 中同时加入 8 个 Topic。
- 在执行此操作之前，用户需要首先创建 `IStreamChannel` 对象实例，并调用 `JoinAsync` 方法加入频道，详见[频道](/zh-CN/realtime-media/rtm/build/channels-and-topics/channel-basic)。
:::

成功加入 Topic 后，订阅该 Topic 并添加事件监听的用户会收到 `REMOTE_JOIN` 类型的 `OnTopicEvent` 事件通知。

### 接口方法 [#接口方法]

你可以通过以下方式调用 `JoinTopicAsync` 方法：

```csharp
RtmResult<JoinTopicResult> JoinTopicAsync(string topic, JoinTopicOptions options);
```

|   参数    |     类型      | 是否必填 | 默认值 |       描述        |
| ------ | --------------- | ----- | ------ | -------------- |
|  `topic`  |    string     |   必填   | -      |   Topic 名称。    |
| `options` | `JoinTopicOptions` |   选填   | -      | 加入 Topic 选项。 |

`JoinTopicOptions` 包含以下属性：

|      属性       |     类型     | 是否必填 | 默认值             |    描述    |
| ------------ | -------------- | ----- | -------------- | ---------- |
|      `qos`      | `RTM_MESSAGE_QOS` |   选填   | `ORDERED` |                  定义此 Topic 中传输数据是否保序。详见 [`RTM_MESSAGE_QOS`](/zh-CN/api-reference/rtm/unity/enumv#messageqos)。                  |
|   `priority`    |  `RTM_MESSAGE_PRIORITY`  |   选填   | `NORMAL`  | 定义此 Topic 中传输数据的优先级（相比于同频道中的其他 Topic）。详见 [`RTM_MESSAGE_PRIORITY`](/zh-CN/api-reference/rtm/unity/enumv#messagepriority)。 |
|     `meta`      |    string    |   选填   | -                  |         加入 Topic 时携带的其他辅助信息。          |
| `syncWithMedia` |     Bool     |   选填   | `false`            |        此 Topic 中发送的数据是否与共频道的 RTC 音视频数据流实现数据同步（时间戳对齐）。        |

### 基本用法 [#基本用法]

```cs
var options = new JoinTopicOptions();
options.qos = RTM_MESSAGE_QOS.ORDERED;

var (status,response) = await streamChannel.JoinTopicAsync(topic, options);
if (status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, because of: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
    Debug.Log(string.Format("User:{0} Join Topic:{1} success! at Channel:{2}", response.UserId, response.Topic, response.ChannelName));
}
```

### 返回值 [#返回值]

`JoinTopicAsync` 操作返回一个 `RtmResult<JoinTopicResult>` 类型数据结构的返回值，其中包含：

|    属性    |     类型     | 描述    |
| ------- | -------------- | ------- |
|  `Status`  | `RtmStatus` | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。      |
| `Response` | `JoinTopicResult` | 操作成功后，返回一个 `JoinTopicResult` 类型的数据结构，其中包含本次操作的执行结果。 |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

`JoinTopicResult` 包含以下属性：

|     属性      |  类型  | 描述                     |
| ---------- | --- | --------- |
| `ChannelName` | string | 频道名称。               |
|   `UserId`    | string | 用户 ID。                |
|    `Topic`    | string | Topic 名称。             |
|    `Meta`     | string | 本次操作设置的附加信息。 |

## PublishTopicMessageAsync [#publishtopicmessageasync]

### 接口描述 [#接口描述-1]

`PublishTopicMessageAsync` 方法用于向 Stream Channel 中的 Topic 发送消息，频道中订阅此 Topic 及此消息发布者的用户会在 100 毫秒内收到此消息。调用 `PublishTopicMessageAsync` 方法需要用户首先加入 Stream Channel 并且通过调用 `JoinTopicAsync` 方法注册成为该 Topic 的消息发布者。

用户发送的消息在传输的过程中会被 TLS 加密，数据链路加密默认开启且无法关闭。用户也可以在出初始化的时候开启端侧加密，以获得更高的数据安全等级，详见[初始配置](/zh-CN/api-reference/rtm/unity/configuration)。

### 接口方法 [#接口方法-1]

你可以通过以下方式调用 `PublishTopicMessageAsync`[1/2] 和 `PublishTopicMessageAsync`[2/2] 方法：

```csharp
// PublishTopicMessageAsync[1/2]
RtmResult<PublishTopicMessageResult> PublishTopicMessageAsync(string topic, byte[] message, TopicMessageOptions option);
```

```csharp
// PublishTopicMessageAsync[2/2]
RtmResult<PublishTopicMessageResult> PublishTopicMessageAsync(string topic, string message, TopicMessageOptions option);
```

|   参数    |       类型       | 是否必填 | 默认值 | 描述       |
| ------ | ---- | ----- | --- | ------------ |
|  `topic`  |      string      |   必填   |   -    | Topic 名称。                     |
| `message` |  string/byte[]   |   必填   |   -    | 消息负载。你需要在 `PublishTopicMessageAsync`[1/2] 中填入字节数组类型的消息，或在 `PublishTopicMessageAsync`[2/2] 中填入字符串类型的消息。 |
| `options` | `TopicMessageOptions` |   选填   |   -    | 发布消息的选项。                 |

<a id="options"></a>
`TopicMessageOptions` 包含以下属性：

|     属性     |  类型  | 是否必填 | 默认值 |           描述            |
| --------- | --- | ----- | --- | ---- |
|   `sendTs`   | UInt64 |   选填   |   0    | 消息端侧发送时间戳，配合 `syncwithMedia` 功能一起使用，当开启音画同步功能时，如果用户此值填 `0`，SDK 将自动帮助用户填写正确时间戳，如果不填 `0`，则按照用户填写的时间戳作为端侧对齐基准。 |
| `customType` | string |   选填   |   `null`    | 用户自定义字段。仅支持字符串型。                |

### 基本用法 [#基本用法-1]

```cs
var message = "Hello World";
var topic = "Motion";
var options = new TopicMessageOptions();
options.customType = "PlainText";
var (status,response) = await streamChannel.PublishTopicMessageAsync(topic, message, options);
if (status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}

```

### 返回值 [#返回值-1]

`PublishTopicMessageAsync` 操作返回一个 `RtmResult<PublishTopicMessageResult>` 类型数据结构的返回值，其中包含：

|    属性    |      类型       | 描述                   |
| ------- | --- | ----- |
|  `Status`  |  `RtmStatus`   | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。                     |
| `Response` | `PublishTopicMessageResult` | 操作成功后，返回一个 `PublishTopicMessageResult` 类型的数据结构，其中包含本次操作的执行结果。当前没有任何数据，用户可以忽略。 |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

## LeaveTopicAsync [#leavetopicasync]

### 接口描述 [#接口描述-2]

当你不再需要向某个 Topic 发布消息，你可以调用 `LeaveTopicAsync` 方法取消注册为该 Topic 的消息发布者，从而释放资源。该方法不影响你是否订阅该 Topic，也不影响其他用户对该 Topic 的任何操作。

成功调用该方法后，订阅该频道并开启事件监听的用户会收到 `REMOTE_LEAVE` 类型的 `OnTopicEvent` 事件通知，详见 [事件监听](/zh-CN/api-reference/rtm/unity/configuration#事件监听)。

### 接口方法 [#接口方法-2]

你可以通过以下方式调用 `LeaveTopicAsync` 方法：

```cs
RtmResult<LeaveTopicResult> LeaveTopicAsync(string topic);
```

|  参数   |  类型  | 是否必填 | 默认值 |     描述     |
| ---- | --- | ----- | --- | --------- |
| `topic` | string |   必填   |   -    | Topic 名称。 |

### 接口方法 [#接口方法-3]

```cs
var (status,response) = await streamChannel.LeaveTopicAsync("Motion");
if (status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
    Debug.Log(string.Format("User:{0} Join Topic:{1} success! at Channel:{2}", response.UserId, response.Topic, response.ChannelName));
}

```

### 返回值 [#返回值-2]

`LeaveTopicAsync` 操作返回一个 `RtmResult<LeaveTopicResult>` 类型数据结构的返回值，其中包含：

|    属性    |     类型      | 描述     |
| ------- | --------------- | -------- |
|  `Status`  | `RtmStatus`  | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。       |
| `Response` | `LeaveTopicResult` | 操作成功后，返回一个 `LeaveTopicResult` 类型的数据结构，其中包含本次操作的执行结果。 |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

`LeaveTopicResult` 包含以下属性：

|     属性      |  类型  | 描述                     |
| ---------- | --- | --------- |
| `ChannelName` | string | 频道名称                 |
|   `UserId`    | string | 用户 ID                  |
|    `Topic`    | string | Topic 名称。             |
|    `Meta`     | string | 本次操作设置的附加信息。 |

## SubscribeTopicAsync [#subscribetopicasync]

### 接口描述 [#接口描述-3]

加入频道后，你可以调用 `SubscribeTopicAsync` 方法订阅该频道中 Topic 的消息发布者。

`SubscribeTopicAsync` 为增量方法。例如，第一次调用该方法时，订阅消息发布者列表为 `[UserA, UserB]`，第二次调用该方法时，订阅消息发布者列表为 `[UserB, UserC]`，则最后成功订阅的结果是 `[UserA, UserB, UserC]`。

同一个用户在同一个频道中最多只能同时订阅 50 个 Topic，且每个 Topic 中最多只能订阅 64 个消息发布者。详见 [API 调用限制](/zh-CN/realtime-media/rtm/reference/usage-limits-and-errors/api-limits)。

### 接口方法 [#接口方法-4]

你可以通过以下方式调用 `SubscribeTopicAsync` 方法：

```csharp
RtmResult<SubscribeTopicResult> SubscribeTopicAsync(string topic, TopicOptions options);
```

|   参数    |      类型      | 是否必填 | 默认值 |       描述        |
| ------ | ---------------- | ----- | --- | -------------- |
|  `topic`  |     string     |   必填   |   -    |   Topic 名称。    |
| `options` | `TopicOptions` |   选填   |   -    | 订阅 Topic 选项。 |

`TopicOptions` 中包含以下属性：

|  属性   |   类型   | 是否必填 | 默认值 |           描述            |
| ---- | ----- | ----- | --- | ------ |
| `users` | string[] |   选填   |   -    | 需要订阅的消息发布者（Publisher）的 `UserId` 列表，如果不设置，则默认随机订阅不超过 64 个用户。 |

### 基本用法 [#基本用法-2]

```cs
List<string> userList = new List<string>();
userList.Add("Tony");
userList.Add("Marry");

var options = new TopicOptions();
options.users = userList.ToArray();

var (status,response) = await streamChannel.SubscribeTopicAsync(subtopic, options);
if (status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
    Debug.Log(string.Format("User:{0} Join Topic:{1} success! at Channel:{2}", response.UserId, response.Topic, response.ChannelName));
}
```

### 返回值 [#返回值-3]

`SubscribeTopicAsync` 操作返回一个 `RtmResult<SubscribeTopicResult>` 类型数据结构的返回值，其中包含：

|    属性    |       类型        | 描述         |
| ------- | ----- | ------------ |
|  `Status`  |   `RtmStatus`    | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。           |
| `Response` | `SubscribeTopicResult` | 操作成功后，返回一个 `SubscribeTopicResult` 类型的数据结构，其中包含本次操作的执行结果。 |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

`SubscribeTopicResult` 包含以下属性：

|      属性      |    类型    | 描述                     |
| ----------- | ------- | --------- |
| `ChannelName`  |   string   | 频道名称。               |
|    `UserId`    |   string   | 用户 ID。                |
|    `Topic`     |   string   | Topic 名称。             |
| `SucceedUsers` | string[] | 本次订阅成功的用户列表。 |
| `FailedUsers`  | string[] | 本次订阅失败的用户列表。 |

## UnsubscribeTopicAsync [#unsubscribetopicasync]

### 接口描述 [#接口描述-4]

如果你对一个或多个 Topic 不再感兴趣，或者不再需要订阅 Topic 中的一个或多个消息发布者，你可以调用 `UnsubscribeTopicAsync` 方法取消订阅该 Topic 或取消订阅该 Topic 中指定的消息发布者。

### 接口方法 [#接口方法-5]

你可以通过以下方式调用 `UnsubscribeTopicAsync` 方法：

```csharp
RtmResult<UnsubscribeTopicResult> UnsubscribeTopicAsync(string topic, TopicOptions options);
```

|   参数    |      类型      | 是否必填 | 默认值 |          描述          |
| ------ | ---------------- | ----- | --- | ----- |
|  `topic`  |     string     |   必填   |   -    |      Topic 名称。      |
| `options` | `TopicOptions` |   选填   |   -    | 取消订阅 Topic  选项。 |

`TopicOptions` 中包含以下属性：

|  属性   |   类型   | 是否必填 | 默认值 |  描述   |
| ---- | ----- | ----- | --- | ---- |
| `users` | string[] |   选填   |   -    | 需要取消订阅的消息发布者的用户 ID 列表。如果不设置，则取消整个 Topic 的订阅。 |

### 基本用法 [#基本用法-3]

```cs
List<string> userList = new List<string>();
userList.Add("Tony");
userList.Add("Marry");

var options = new TopicOptions();
topicOptions.users = userList.ToArray();

var (status,response) = await streamChannel.UnsubscribeTopicAsync(subtopic, options);
if (status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
```

### 返回值 [#返回值-4]

`UnsubscribeTopicAsync` 操作返回一个 `RtmResult<UnsubscribeTopicResult>` 类型数据结构的返回值，其中包含：

|    属性    |        类型         | 描述                       |
| ------- | ---- | --------- |
|  `Status`  |    `RtmStatus`     | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。   |
| `Response` | `UnsubscribeTopicResult` | 操作成功后，返回一个 `UnsubscribeTopicResult` 类型的数据结构，其中包含本次操作的执行结果。当前没有任何数据，用户可以忽略。 |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。
