基础建立DataChannel的步骤
Description | Detail | |
1 | SingalConnection类中创建DataChannel | PeerConnection Create DataChannel |
2 | SignalConnection实现了webrtc:PeerConnectionObserver | 有很多函数在OnDataChannel(rtc::scoped_refptr<webrtc:DataChannelInterface> InDataChannel)中 注册当前类为Observer InDataChannel→RegisterObserver(this);以便于后续消息函数等都可以在本类的实现中. |
3 | SignalConnection实现了webrtc:DataChannelObserver接口 | 实现主要函数void OnStateChange();void OnBufferedAmountChange(unit64_t PreviousAmount) override;void OnMessage(const webrtc:DataBuffer& inBuffer) override; |
4 | 第二部获取到的Handler后续可以在任何需要发送数据的地方调用mDataChannel→send 方法 | 发送文本内容 |
5 | 获取的数据,后续在Onmessage函数中可以得到。这里我们解析了JSON格式的。 |
如何准备一个DataBuffer数据,代码示例
rtc::CopyOnWriteBuffer myBuffer; myBuffer.SetData(myData,myDataSize); //其中myData是一个 char* , myDataSize是一个int 或者size_t mDataChannel->Send(webrtc::DataBuffer(myBuffer, true ); |
更多的代码示例
/// <summary>
/// 传送文本数据
/// </summary>
/// <param name="text"></param>
void QLSignalConnection::SendTextViaDataChannel(const std::string& text)
{
webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(text.c_str(), text.size()), false);
mDataChannel->Send(buffer);
}
/// <summary>
/// 传送二进制数据
///
/// </summary>
/// <param name="myFinalPngBinaryData"></param>
/// <param name="sizeOfData"></param>
void QLSignalConnection::SetBinaryDataViaDataChannel(unsigned char* myFinalPngBinaryData,size_t sizeOfData)
{
//Just send via data channel handler
//进行分片传输,确保大数据没问题
rtc::CopyOnWriteBuffer myBuffer;
//myBuffer.SetData(myData, myDataSize);
myBuffer.SetData(myFinalPngBinaryData, sizeOfData);
////byte,分块大小,我们可以自己定义,但是貌似100K一个比较合理
//单次发送数据长度大于256*1024字节时,会引发2中提到的崩溃,此时send_result=SDR_ERROR,即发送错误
unsigned int sliceSize = 100*1024;
unsigned int totalBufferSize = myBuffer.size();
unsigned int totalSteps = (totalBufferSize - 1) / sliceSize + 1;
Json::Value sendMessage;
sendMessage["sendDataType"] = "bmpFileBuffer";
sendMessage["dataSize"] = sizeOfData;
sendMessage["totalSendTimes"] = totalSteps;
//这里发送一个指令,告诉前端后面是一个什么样的数据,多长,你要怎么接受。
std::string sendMessageString = QLJsonHelper::json_2_string(sendMessage);
SendTextViaDataChannel(sendMessageString);
//unsigned int totalSteps = (totalBufferSize-1)/sliceSize + 1;
rtc::CopyOnWriteBuffer temp;
//DataBuffer第二个参数必须为true,确保接受顺序
for (unsigned int i = 0; i < totalSteps; i++)
{
if (i == totalSteps - 1)
{
temp = myBuffer.Slice((i * sliceSize), (myBuffer.size() - (i * sliceSize)));
mDataChannel->Send(webrtc::DataBuffer(temp, true));
}
else
{
temp = myBuffer.Slice((i * sliceSize), sliceSize);
mDataChannel->Send(webrtc::DataBuffer(temp, true));
}
}
}
代码讲解
上述的代码中,第一种我们纯文本内容的发送直接调用发送函数即可。
第二种发送二进制数据的时候,我们采用了一种分片方式。因为数据通道在不同的浏览器中有具体数据长度的大小限制。
我们因此采用每次发送一小片的方式,多次发送,直到完成。
一般的,上述中的100*1024是我们的一个经验值,太小可能会有问题,因为发送次数太多,太大会丢包或奇怪的问题。
关于参数大小的核心原理
对于在网络上传输的任何数据,都存在大小限制。从基本层面上说,单个网络数据包的大小不能超过一定的值(确切的数值取决于网络和所使用的传输层)。在应用程序级别——即,在你的代码运行的 WebRTC 实现中——WebRTC 实现了支持比网络传输层的最大数据包大小更大的消息的特性。
这可能会使事情变得复杂,因为你不一定知道各种用户代理的大小限制,以及它们在发送或接收较大消息时的响应方式。即使用户代理共享相同的底层库来处理流控制传输协议(SCTP)数据,仍然可能因为库的使用方式而产生差异。例如,Firefox 和 Google Chrome 都使用 usrsctp
库来实现 SCTP,但仍然会出现因它们调用库的方式以及对其返回的错误的反应方式不同而导致在 RTCDataChannel
上的数据传输失败的情况。
当两个运行 Firefox 的用户在数据通道上通信时,消息大小限制要比 Firefox 和 Chrome 通信时要大得多,因为 Firefox 实现了一种现已弃用的技术,用于将大消息分成多个 SCTP 消息发送,而 Chrome 没有。Chrome 将看到一系列它认为完整的消息,并将它们作为多个消息传递给接收的 RTCDataChannel
。
可以毫无问题地发送小于 16 KiB 的消息,因为所有主要用户代理都以相同的方式处理它们。超出这个大小的消息则变得更加复杂。
大型消息相关问题
目前,对于大于 64 KiB 的消息(如果要支持跨浏览器交换数据,则为 16 KiB),实际上不太实用使用 RTCDataChannel
。问题出在 SCTP——用于在 RTCDataChannel
上发送和接收数据的协议——最初是设计用作信令协议的。人们预计消息会相对较小。对于大于网络层 MTU 的消息的支持几乎是作为一种事后补充添加的,以防信令消息需要大于 MTU。此特性要求消息的每个片段具有连续的序列号,因此它们必须一个接一个地传输,而不能在它们之间交错其他数据。
这最终成为一个问题。随着时间的推移,各种应用程序(包括实现 WebRTC 的应用程序)开始使用 SCTP 传输越来越大的消息。最终人们意识到,当消息变得太大时,传输大消息可能会阻塞该数据通道上的所有其他数据传输——包括关键的信令消息。
当浏览器正确支持支持较大消息的当前标准——表示消息是应该被视为单个有效负载中的最后一个的结束记录(EOR)标志时,这将成为一个问题。这在 Firefox 57 中已实现,但尚未在 Chrome 中实现(参见 Chromium Bug 7774)。有了 EOR 支持,RTCDataChannel
的有效载荷可以更大(官方上限为 256 KiB,但 Firefox 的实现将它们限制在了高达 1 GiB 的惊人大小)。即使在 256 KiB 大小,这已经足够大到可能导致处理紧急流量时出现明显的延迟。如果消息变得更大,延迟可能会变得难以忍受,除非你对你的操作条件非常确定。
为了解决这个问题,设计了一种新的流调度器(通常称为“SCTP ndata 规范“),使得可以在不同流上交错发送的消息,包括用于实现 WebRTC 数据通道的流。这个提案目前仍处于 IETF 草案形式,但一旦实施,它将使得发送几乎没有大小限制的消息成为可能,因为 SCTP 层将自动交错底层的子消息,以确保每个通道的数据都有机会传递。
Firefox 对 ndata 的支持正在实现中;查看 Firefox bug 1381145 以跟踪它何时可以用于普通用途。Chrome 团队在 Chrome Bug 5696 中跟踪他们对 ndata 支持的实现。
使用DataChannel传递键盘鼠标事件
同时,我们也通过数据通道传递很多其余的参数,比如前端的鼠标和键盘信息
代码如下,我们主要在DataChannel建立后的OnMessage回调函数中处理收到的数据。
数据是二进制的,然而我们的数据可能是Json双方约定的格式,后续自行解析即可。
/// <summary>
/// 這個是DataChannel創建后,注冊的Observer的回調函數
/// 準備接受傳進來的數據
///
///这里实现具体的收到消息后的解析
/// </summary>
/// <param name="Buffer"></param>
void QLSignalConnection::OnMessage(const webrtc::DataBuffer& inBufferData)
{
//QL_LOG("DataChannel::OnMessage Entered.");
if (inBufferData.size() == 0)
{
return;
}
//Buffer中返回的是CopyOnWriteBuffer,它的data()函數獲取uint8_t*的數據
const uint8_t* myData = inBufferData.data.data();
size_t dataLength = inBufferData.size();
//过滤非必要数据
if (dataLength > 1)
{
// 有部分行为是不需要键盘鼠标处理对象的
if (mKeyboardAndMouseOwner)
{
//QL_LOG("DataChannel::ProcessKeyboardAndMouseMessage");
mKeyboardAndMouseOwner->ProcessKeyboardAndMouseMessage(myData, inBufferData.size(), inBufferData.binary);
}
}
}
RA/SD 衍生者AI训练营。发布者:chris,转载请注明出处:https://www.shxcj.com/archives/6598