通过 RDT 通道收发消息
在文件传输、文件下载等业务场景中,通常需要可靠的数据传输来保证数据的完整性和有序性,从而避免因网络等问题造成的数据丢失。自 1.9.2 版本起,声网提供 RDT(Reliable Data Transmission, 可靠数据传输)功能,允许同一频道中的用户之间建立 RDT 通道并进行可靠的数据传输。
本文主要介绍如何在 RDT 通道中收发消息。
实现原理
RDT 通道是一种基于 RTC Connection 的端到端可靠数据传输通道,如下图所示,RDT 通道与音视频通道相互独立。在一个 RTC Connection 中,你可以同时发送音频数据、视频数据和 RDT 消息。
RDT 消息分为控制消息和数据消息,区别如下:
- 控制消息 (
RDT_STREAM_CMD
):发送优先级高,不受拥塞控制策略影响,不会阻塞发送。每秒至多发送 100 个消息包,每个消息包的长度至多为 256 B。 - 数据消息 (
RDT_STREAM_DATA
):发送优先级低,受拥塞控制策略影响。如果缓冲区已满,则消息发送失败。对发送的消息包数量没有限制,每个消息包的长度至多为 128 KB。
下图展示通过 RDT 通道收发消息的基本流程:
前提条件
开始前,请确保已完成 SDK 的初始化并创建 RTC Connection。参考实现媒体流和信令传输。
实现发送和接收 RDT 消息
本节介绍如何实现在 RDT 通道中收发消息。
1. 加入 RTC 频道并建立 RDT 连接
加入 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 连接,但会接受远端用户的连接邀请。
只有用户角色为主播时,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 连接。
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 通道的状态
监听 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 通道中发送和接收缓冲区中的历史数据。
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 通道发送消息
当 RDT 通道的状态为 RDT_STATE_OPENED
时,用户之间才真正地成功建立了 RDT 通道,你可以调用 agora_rtc_send_rdt_msg
方法在 RDT 通道中发送消息。
- 发送控制消息
- 发送数据消息
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;
}
}
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 通道接收消息
如需接收 RDT 消息,你可以监听 on_rdt_msg
回调。
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 连接
离开 RTC 频道后,用户在该频道内创建的 RDT 连接都会自动断开。为避免正在传输中的数据丢失,声网推荐你先调用 agora_rtc_get_rdt_status_info
方法查询发送缓冲区长度 (send_queue_size
),确保远端用户已接收完 RDT 消息 (send_queue_size = 0
) 后再离开 RTC 频道。
相关信息
本节提供了在实现 RDT 功能时可能需要的参考信息。
示例代码
声网在 SDK 包中提供了以下示例代码供你参考:
├── agora_rtsa_sdk # 声网的 SDK 库文件和头文件
└── example # 示例代码
└── hello_rdt
├── hello_rdt.c #在单个频道中使用 RDT 通道收发消息
└── hello_rdt_multi.c #在多频道中使用 RDT 通道收发消息
API 参考
在实现功能的过程中,你可能需要参考如下 API 文档: