通过 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 频道时,你可以通过 ChannelOptions
中的 autoConnectRdt
参数来设置是否主动建立 RDT 连接。SDK 会根据 onUserJoined
、onUserOffline
回调和你在 autoConnectRdt
参数中的设置来处理 RDT 连接:
- 如果你设置
autoConnectRdt
参数为true
,则 SDK 会在收到onUserJoined
回调后主动与远端用户建立 RDT 连接,在收到onUserOffline
回调后主动与远端用户断开 RDT 连接。 - 如果你设置
autoConnectRdt
参数为false
,则 SDK 不会主动与远端用户建立 RDT 连接,但会接受远端用户的连接邀请。
只有用户角色为主播时,SDK 才会触发 onUserJoined
和 onUserOffline
回调。RTSA SDK 中默认所有用户都是主播,无需额外设置用户角色。如果你需要将 RTSA SDK 搭配其他 SDK(例如 RTC SDK)一起使用,请确保你在其他 SDK 中设置用户角色为主播。
不同场景对可靠传输的需求不同,你需要根据实际需求设置 autoConnectRdt
参数,例如:
- 在智能摄像头、智能门铃等 IOT 场景中,可能只需要让所有客户端和设备端收发 RDT 消息,但无需让客户端之间收发 RDT 消息。在这种情况下,你需要在设备端加入频道时设置
autoConnectRdt
为true
,让设备端与每一个客户端都建立 RDT 连接;在客户端加入频道时设置autoConnectRdt
为false
,让客户端之间不建立 RDT 连接。 - 在视频会议、实时协作等办公场景中,可能需要让所有用户之间都能收发数据。在这种情况下,你需要在所有用户加入频道时设置
autoConnectRdt
为true
,让所有用户都相互建立 RDT 连接。
int ret = 0;
int connId = 0;
connId = mRtcService.CreateConnection();
if (ret != 0) {
System.out.printf("error info: " + mRtcService.errToStr(ret));
return -1;
}
ChannelOptions chnlOption = new ChannelOptions();
// 设置是否主动建立 RDT 连接。true:主动建立连接;false:不主动建立连接。
chnlOption .autoConnectRdt = true;
mRtcService.joinChannel(connId, "channel-xxx", uid, token, chnlOption)
if (ret != 0) {
System.out.printf("error info: " + mRtcService.errToStr(ret));
return -1;
}
2. 监听 RDT 通道的状态
监听 onRdtState
回调,获取 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 通道中发送和接收缓冲区中的历史数据。
RdtState g_rdt_state_for_uid_xxx = RdtState.RDT_STATE_CLOSED;
@Override
public void onRdtState(int connId, int uid, int state) {
g_rdt_state_for_uid_xxx = state;
}
此外,你也可以调用 getRdtStatusInfo
方法主动获取 RDT 通道的状态。
3. 通过 RDT 通道发送消息
当 RDT 通道的状态为 RDT_STATE_OPENED
时,用户之间才真正地成功建立了 RDT 通道,你可以调用 sendRdtMsg
方法在 RDT 通道中发送消息。
- 发送控制消息
- 发送数据消息
if (g_rdt_state_for_uid_xxx == RdtState.RDT_STATE_OPENED || g_rdt_state_for_uid_xxx == RdtState.RDT_STATE_BLOCKED) {
byte cmd = new byte[256];
ret = mRtcService.sendRdtMsg(connId, peerId, RdtStreamType.RDT_STREAM_CMD, cmd);
if (ret != 0) {
System.out.printf("error info: " + mRtcService.errToStr(ret));
return -1;
}
}
if (g_rdt_state_for_uid_xxx == RdtState.RDT_STATE_OPENED) {
byte[] msg = new byte[1024];
msg[0] = 1;
ret = mRtcService.sendRdtMsg(connId, peerId, RdtStreamType.RDT_STREAM_DATA, msg);
if (ret != 0) {
System.out.printf("error info: " + mRtcService.errToStr(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 消息,你可以监听 onRdtMsg
回调。
@Override
public void onRdtMsg(int connId, int uid, int type, byte[] msg)
{
System.out.printf("received rdt msg from uid=" + uid + "type=" + type + "msg_len=" + len);
}
5. 断开 RDT 连接
离开 RTC 频道后,用户在该频道内创建的 RDT 连接都会自动断开。为避免正在传输中的数据丢失,声网推荐你先调用 getRdtStatusInfo
方法查询发送缓冲区长度 (sendQueueSize
),确保远端用户已接收完 RDT 消息 (sendQueueSize = 0
) 后再离开 RTC 频道。
相关信息
在实现功能的过程中,你可能需要参考如下 API 文档: