# 通过 RDT 通道收发消息 (/zh-CN/realtime-media/rtsa/build/data-communication/send-message-through-rdt-channel)

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

在文件传输、文件下载等业务场景中，通常需要可靠的数据传输来保证数据的完整性和有序性，从而避免因网络等问题造成的数据丢失。自 1.9.2 版本起，声网提供 RDT（Reliable Data Transmission, 可靠数据传输）功能，允许同一频道中的用户之间建立 RDT 通道并进行可靠的数据传输。

本文主要介绍如何在 RDT 通道中收发消息。

## 实现原理 [#实现原理]

RDT 通道是一种基于 RTC Connection 的端到端可靠数据传输通道，如下图所示，RDT 通道与音视频通道相互独立。在一个 RTC Connection 中，你可以同时发送音频数据、视频数据和 RDT 消息。

<DocsImage alt="RDT 通道与 RTC Connection 的关系" src="https://web-cdn.agora.io/docs-portal/img/rtsa/rdt-tech.png" displayWidth="50%" />

RDT 消息分为控制消息和数据消息，区别如下：

- 控制消息 (`RDT_STREAM_CMD`)：发送优先级高，不受拥塞控制策略影响，不会阻塞发送。每秒至多发送 100 个消息包，每个消息包的长度至多为 256 B。
- 数据消息 (`RDT_STREAM_DATA`)：发送优先级低，受拥塞控制策略影响。如果缓冲区已满，则消息发送失败。对发送的消息包数量没有限制，每个消息包的长度至多为 128 KB。

下图展示通过 RDT 通道收发消息的基本流程：

<DocsImage alt="基本流程" src="https://web-cdn.agora.io/docs-portal/img/rtsa/rdt-basic-workflow.png" displayWidth="60%" />

## 前提条件 [#前提条件]

开始前，请确保已完成 SDK 的初始化并创建 RTC Connection。参考 [实现媒体流和信令传输](/zh-CN/realtime-media/rtsa/build/media-and-signaling-transmission/implement-transmission)。

## 实现发送和接收 RDT 消息 [#实现发送和接收-rdt-消息]

本节介绍如何实现在 RDT 通道中收发消息。

### 1. 加入 RTC 频道并建立 RDT 连接 [#1-加入-rtc-频道并建立-rdt-连接]

:::info[信息]
本节仅适用于 v1.9.2 之后、v1.9.4 之前版本的 SDK。对于 v1.9.4 及之后的版本，`auto_connect_rdt` 在 SDK 内部默认设置为 `true`，不再需要额外设置；你只需要在调用 `agora_rtc_join_channel` 加入 RTC 频道时，将 `rtc_channel_options_t` 中的 `enable_rdt` 设为 `true` 即可使用 RDT 功能。如果你不想启用该功能，则将 `enable_rdt` 设为 `false`。
:::

加入 RTC 频道时，你可以通过 `rtc_channel_options_t` 中的 `auto_connect_rdt` 参数来设置是否主动建立 RDT 连接。SDK 会根据 `on_user_joined`、`on_user_offline` 回调和你在 `auto_connect_rdt` 参数中的设置来处理 RDT 连接：

- 如果你设置 `auto_connect_rdt` 参数为 `true`，则 SDK 会在收到 `on_user_joined` 回调后主动与远端用户建立 RDT 连接，在收到 `on_user_offline` 回调后主动与远端用户断开 RDT 连接。
- 如果你设置 `auto_connect_rdt` 参数为 `false`，则 SDK 不会主动与远端用户建立 RDT 连接，但会接受远端用户的连接邀请。

:::warning[注意]
只有用户角色为主播时，SDK 才会触发 `on_user_joined` 和 `on_user_offline` 回调。RTSA SDK 中默认所有用户都是主播，无需额外设置用户角色。如果你需要将 RTSA SDK 搭配其他 SDK（例如 RTC SDK）一起使用，请确保你在其他 SDK 中设置用户角色为主播。
:::

不同场景对可靠传输的需求不同，你需要根据实际需求设置 `auto_connect_rdt` 参数，例如：

- 在智能摄像头、智能门铃等 IoT 场景中，可能只需要让所有客户端和设备端收发 RDT 消息，但无需让客户端之间收发 RDT 消息。在这种情况下，你需要在设备端加入频道时设置 `auto_connect_rdt` 为 `true`，让设备端与每一个客户端都建立 RDT 连接；在客户端加入频道时设置 `auto_connect_rdt` 为 `false`，让客户端之间不建立 RDT 连接。
- 在视频会议、实时协作等办公场景中，可能需要让所有用户之间都能收发数据。在这种情况下，你需要在所有用户加入频道时设置 `auto_connect_rdt` 为 `true`，让所有用户都相互建立 RDT 连接。

```c
int ret = 0;
connection_id_t conn_id = 0;

ret = agora_rtc_create_connection(&conn_id);
if (ret != 0) {
    printf("error info: %s", agora_rtc_err_2_str(ret));
    return -1;
}

rtc_channel_options_t channel_opt = {0};
// 设置是否主动建立 RDT 连接。true：主动建立连接；false：不主动建立连接
channel_opt.auto_connect_rdt = true;
ret = agora_rtc_join_channel(conn_id, "channel-xxx", uid, token, &channel_opt);
if (ret != 0) {
    printf("error info: %s", agora_rtc_err_2_str(ret));
    return -1;
}
```

### 2. 监听 RDT 通道的状态 [#2-监听-rdt-通道的状态]

监听 `on_rdt_state` 回调，获取 RDT 通道的状态。你需要根据该状态判断能否收发 RDT 消息。

- `RDT_STATE_CLOSED`：初始状态，未连接 RDT 通道。该状态下用户不可以收发 RDT 消息。
- `RDT_STATE_OPENED`：已连接 RDT 通道。该状态下用户可以收发 RDT 消息。
- `RDT_STATE_BLOCKED`：RDT 通道中的发送缓冲区已满。该状态下用户不可以发送数据消息 (`RDT_STREAM_DATA`)，但可以发送控制消息 (`RDT_STREAM_CMD`)。
- `RDT_STATE_PENDING`：正在重新连接 RDT 通道。
  - 如果重新连接成功，则连接状态会变为 `RDT_STATE_OPENED`，SDK 会保留 RDT 通道中发送和接收缓冲区中的历史数据。
  - 如果重新连接失败，则连接状态会变为 `RDT_STATE_CLOSED`，SDK 会清除 RDT 通道中发送和接收缓冲区中的历史数据。
- `RDT_STATE_BROKEN`：与 RDT 通道的连接已损坏，SDK 会尝试重新连接。SDK 不论能否成功连接，都会清空 RDT 通道中发送和接收缓冲区中的历史数据。

```c
rdt_state_e g_rdt_state_for_uid_xxx = RDT_STATE_CLOSED;

void __on_rdt_state(connection_id_t conn_id, uint32_t uid, rdt_state_e state)
{
   g_rdt_state_for_uid_xxx = state;
}
```

此外，你也可以调用 `agora_rtc_get_rdt_status_info` 方法主动获取 RDT 通道的状态。

### 3. 通过 RDT 通道发送消息 [#3-通过-rdt-通道发送消息]

当 RDT 通道的状态为 `RDT_STATE_OPENED` 时，用户之间才真正地成功建立了 RDT 通道，你可以调用 `agora_rtc_send_rdt_msg` 方法在 RDT 通道中发送消息。

```c tab="发送控制消息" tabGroup="datatype"
if (g_rdt_state_for_uid_xxx == RDT_STATE_OPENED || g_rdt_state_for_uid_xxx == RDT_STATE_BLOCKED) {
    uint8_t cmd[256] = {xxxxx};
    ret = agora_rtc_send_rdt_msg(conn_id, peer_id, RDT_STREAM_CMD, cmd, 256);
    if (ret != 0) {
        printf("error info: %s", agora_rtc_err_2_str(ret));
        return -1;
    }
}
```

```c tab="发送数据消息" tabGroup="datatype"
if (g_rdt_state_for_uid_xxx == RDT_STATE_OPENED) {
    uint8_t msg[1024] = {xxxxx};
    ret = agora_rtc_send_rdt_msg(conn_id, peer_id, RDT_STREAM_DATA, msg, 1024);
    if (ret != 0) {
        printf("error info: %s", agora_rtc_err_2_str(ret));
        return -1;
    }
}
```

如果 RDT 消息发送失败，SDK 会报告如下错误码，你可以根据错误码判断失败原因并处理：

- `RTC_ERR_RDT_USER_NOT_EXIST` (-8): 频道内不存在该远端用户。
- `RTC_ERR_RDT_USER_NOT_READY` (-9): 未成功与远端用户建立连接。你需要等 RDT 通道的连接状态变为 `RDT_STATE_OPENED` 后重新调用该方法。
- `RTC_ERR_RDT_DATA_BLOCKED` (-10): 发送缓冲区已满。你需要等 RDT 通道的连接状态变为 `RDT_STATE_OPENED` 后重新调用该方法。
- `RTC_ERR_RDT_CMD_EXCEED_LIMIT` (-11): 控制消息的数量超过限制。每秒发送的控制消息包不能超过 100 个。

### 4. 通过 RDT 通道接收消息 [#4-通过-rdt-通道接收消息]

如需接收 RDT 消息，你可以监听 `on_rdt_msg` 回调。

```c
void __on_rdt_msg(connection_id_t conn_id, uint32_t uid, rdt_stream_type_e type, const void *msg, size_t len)
{
    printf("received rdt msg from uid=%u type=%d msg len=%zu\n", uid, type, len);
}
```

### 5. 断开 RDT 连接 [#5-断开-rdt-连接]

离开 RTC 频道后，用户在该频道内创建的 RDT 连接都会自动断开。为避免正在传输中的数据丢失，声网推荐你先调用 `agora_rtc_get_rdt_status_info` 方法查询发送缓冲区长度 (`send_queue_size`)，确保远端用户已接收完 RDT 消息 (`send_queue_size = 0`) 后再离开 RTC 频道。

## 相关信息 [#相关信息]

本节提供了在实现 RDT 功能时可能需要的参考信息。

### 示例代码 [#示例代码]

声网在 SDK 包中提供了以下示例代码供你参考：

```shell
├── agora_rtsa_sdk  # 声网的 SDK 库文件和头文件
└── example  # 示例代码
    └── hello_rdt
        ├── hello_rdt.c  #在单个频道中使用 RDT 通道收发消息
        └── hello_rdt_multi.c  #在多频道中使用 RDT 通道收发消息
```

### API 参考 [#api-参考]

在实现功能的过程中，你可能需要参考如下 API 文档：

- [`agora_rtc_join_channel`](/zh-CN/api-reference/rtsa/c/agora-rtc-api-8h#a6c29ff27f04623526a164cf6e5dcd738)
- [`agora_rtc_send_rdt_msg`](/zh-CN/api-reference/rtsa/c/agora-rtc-api-8h#a056de38235f67ad1ea91cfd695ffaf61)
- [`agora_rtc_get_rdt_status_info`](/zh-CN/api-reference/rtsa/c/agora-rtc-api-8h#a80f16bf3cf9bcdf8a6cecc51f8e584aa)
- [`on_rdt_state`](/zh-CN/api-reference/rtsa/c/structagora-rtc-event-handler-t#afe6c35e916473a1424f28d3fefbdda69)
- [`on_rdt_msg`](/zh-CN/api-reference/rtsa/c/structagora-rtc-event-handler-t#a7b5b9ba5f3acaf9b336040e4edd282a5)
