目录
前言
大家都知道WebRTC集各种协议于一身,今天就来谈谈SCTP协议在WebRTC中的使用。
正文
SCTP的全称是Stream Control Transmission Protocol,它是一种传输协议,默认使用5000端口,在TCP/IP协议栈中所处的位置和TCP、UDP类似,同时具备TCP和UDP的特征。
编辑
通过上图,我们可以知道SCTP协议基本上集成了TCP和UDP的全部优点,属于介于二者之间的产物。在流量控制、拥塞控制、可靠性传输和包的顺序传输方面都和TCP对齐,在传输机制方面和UDP一致。同时,可靠性和包的顺序传输控制选项是支持可配置的。
可靠性传输对于Web开发人员非常的重要,特别是类似于信令传输的场景,如果是关键的控制信令,那么丢包将面临巨大的成本问题。SCTP尽管有对应的配置选项,但是如果设定了最大重传时间或者最大重传次数,那么这个特性将无法保证可靠性。
无序传输可以避免TCP的“线头阻塞”问题,当某段数据丢失后,SCTP能够继续传输其他数据信息。另外,SCTP还提供了关联多个流的功能,在必要的时候可以单独处理每一路流。同时还能保证流与流之间存在“线头阻塞”问题。不同的流由流ID标识,在一定程度上提供了多路复用功能。
编辑
TCP是面向流的,而SCTP是面向消息的。因此在传输数据时,SCTP无需管理自己的帧。SCTP 还有其他一些未被 WebRTC 利用的属性,例如多宿主,此属性允许在多个 IP 地址之间建立 SCTP 关联,从而实现回退和冗余。由于数据通道是 WebRTC 对等连接的一部分。因此,它始终是两个浏览器之间或浏览器与其他设备之间的点对点连接。
与 TCP 一样, SCTP 内置有拥塞控制机制。在任何协议中,拥塞控制都十分重要,有助于 Intemet 的不同用户合理分享有限的带宽,并以缓解而非加重拥塞的方式应对拥塞情形。目前,IETF 的 RMCAT 工作组正在制定标准,打算在向 RTP 中加入避免拥塞和相应的控制机制,使其在设计上能够与 TCP 和 SCTP 拥塞控制实现有效配合。
SCTP协议在WebRTC的DataChannel中使用,并且是基于DTLS协议。DTLS是用来控制信令和数据传输的密钥交换的,SRTP基于交换的密钥,传输相应的流媒体信息数据。而SCTP则基于DTLS完成DataChannel数据通道内的数据传输。
编辑
在WebRTC中,SCTP协议是默认开启的:rtc_enable_sctp = !build_with_mozilla。
webrtc.gni中进行了声明:
declare_args() { # Enables the use of protocol buffers for debug recordings. rtc_enable_protobuf = !build_with_mozilla # Set this to disable building with support for SCTP data channels. rtc_enable_sctp = !build_with_mozilla # Disable these to not build components which can be externally provided. rtc_build_json = !build_with_mozilla rtc_build_libsrtp = !build_with_mozilla rtc_build_libvpx = !build_with_mozilla rtc_libvpx_build_vp9 = !build_with_mozilla rtc_build_opus = !build_with_mozilla rtc_build_ssl = !build_with_mozilla rtc_build_usrsctp = !build_with_mozilla 。。。 }
其中,build_with_mozilla = false。
参数开关rtc_enable_sctp会在BUILD.gn中起作用,在编译配置中增加“HAVE_SCTP”的宏声明,从而影响代码编译区块。
config("common_config") { cflags = [] cflags_c = [] cflags_cc = [] cflags_objc = [] defines = [] if (rtc_enable_protobuf) { defines += [ "WEBRTC_ENABLE_PROTOBUF=1" ] } else { defines += [ "WEBRTC_ENABLE_PROTOBUF=0" ] } if (rtc_include_internal_audio_device) { defines += [ "WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE" ] } if (rtc_libvpx_build_vp9) { defines += [ "RTC_ENABLE_VP9" ] } if (rtc_enable_sctp) { defines += [ "HAVE_SCTP" ] } 。。。 }
这里以MaybeCreateSctpFactory()方法为例进行说明:
std::unique_ptr<SctpTransportFactoryInterface> MaybeCreateSctpFactory( std::unique_ptr<SctpTransportFactoryInterface> factory, rtc::Thread* network_thread) { if (factory) { return factory; } #ifdef HAVE_SCTP return std::make_unique<cricket::SctpTransportFactory>(network_thread); #else return nullptr; #endif }
对于SCTP协议的参数控制可以查看如下头文件。
src/media/sctp/sctp_transport_internal.h
#ifndef MEDIA_SCTP_SCTP_TRANSPORT_INTERNAL_H_ #define MEDIA_SCTP_SCTP_TRANSPORT_INTERNAL_H_ // TODO(deadbeef): Move SCTP code out of media/, and make it not depend on // anything in media/. #include <memory> #include <string> #include <vector> #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/thread.h" // For SendDataParams/ReceiveDataParams. // TODO(deadbeef): Use something else for SCTP. It's confusing that we use an // SSRC field for SID. #include "media/base/media_channel.h" #include "p2p/base/packet_transport_internal.h" namespace cricket { // Constants that are important to API users // The size of the SCTP association send buffer. 256kB, the usrsctp default. constexpr int kSctpSendBufferSize = 256 * 1024; // The number of outgoing streams that we'll negotiate. S***ream IDs (SIDs) // are 0-based, the highest usable SID is 1023. // // It's recommended to use the maximum of 65535 in: // https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.2 // However, we use 1024 in order to save memory. usrsctp allocates 104 bytes // for each pair of incoming/outgoing streams (on a