三、mediasoup之WebRtcTransport 创建流程(2)

        上节说过WebRtcTransport实例创建成功后,客户端通过服务端的nodejs主进程发送"transport.connect" 指令到c++子进程,进行建立连接的。连接的建立过程主要是完全ICE,DTLS协议等规定的相关内容,下面简单说明一下相关概念。

        ICE是一种标准穿透协议,利用STUN和TURN服务器来帮助端点建立连接 ,客户端通过ice-ufrags, ice-passwords与服务器进行身份验证并建立连接。并互换fingerprint,用于双方证书的合法性。

        DTLS用于保障传输数据的安全,是对TLS协议进行了扩展。

        SRTP为通过IP网络交付音频和视频定义了标准的分组格式。SRTP本身并不对传输数据的及时性、可靠性或数据恢复提供任何保证机制,它只负责把数字化的音频采样和视频帧用一些元数据封装起来,以辅助接收方处理这些流。

        SCTP是一种面向消息的可靠传输协议,与 TCP 一样,SCTP 提供可靠的、面向连接的数据传输和拥塞控制。 与 TCP 不同,SCTP 还提供消息边界保留、有序和无序消息传递、多流和多归属。 数据损坏、数据丢失和数据重复的检测是通过使用校验和和序列号来实现的。 应用选择性重传机制来纠正数据的丢失或损坏。对于WebRTC,SCTP是在一个安全的DTLS信道中运行,而这个信道又运行在UDP之上。由于WebRTC支持通过DataChannel API在端与端之间传输任意应用数据,而DataChannel就依赖于SCTP

        首先在main.cpp中对相关的库进行初始化:

DepOpenSSL::ClassInit();//初始化一些加密相关的算法
DepLibSRTP::ClassInit(); //libSRTP库初始化,libSRTP提供了保护 RTP 和 RTCP 的功能
DepUsrSCTP::ClassInit();//一个用户级的sctp的初始化
DepLibWebRTC::ClassInit();//只是初始化webrtc试用特性 “WebRTC-Bwe-AlrLimitedBackoff/Enabled/” 暂不做了解
Utils::Crypto::ClassInit();//创建一个HMAC SHA1的HMAC_CTX上下文
RTC::DtlsTransport::ClassInit();//创建加密上下文,证书,私钥,证书指纹(摘要),与srtp绑定
RTC::SrtpSession::ClassInit();//监听会话事件,打印相关信息

我们重点看DtlsTransport的初始化代码

void DtlsTransport::ClassInit()
{
		MS_TRACE();

		// Generate a X509 certificate and private key (unless PEM files are provided).
        // 先生成x509的证书和密钥
		if (
		  Settings::configuration.dtlsCertificateFile.empty() ||
		  Settings::configuration.dtlsPrivateKeyFile.empty())
		{
            // 没有证书配置信息,就通过代码创建,流程与openssl命令创建过程一样
			GenerateCertificateAndPrivateKey();
		}
		else
		{
            // 已经有证书配置信息,直接从配置文件指定的路径读取
			ReadCertificateAndPrivateKeyFromFiles();
		}

		// Create a global SSL_CTX.
        // 创建全局的ssl 上下文 主要使用的是一些openssl的api,初始化证书、私钥并绑定上下文.
        // 与srtp绑定,设置采用srtp做为数据的传输协议
		CreateSslCtx();

		// Generate certificate fingerprints.
        // 创建证书指纹,不同的算法可以产生不同的指纹
		GenerateFingerprints();
}

相关内容初始化后,看一下Router在收到ROUTER_CREATE_WEBRTC_TRANSPORT时,创建 WebRtcTransport实例代码:

WebRtcTransport::WebRtcTransport(const std::string& id, RTC::Transport::Listener* listener, json& data)
	  : RTC::Transport::Transport(id, listener, data)
{
    ............省略............

        try
		{
			uint16_t iceLocalPreferenceDecrement{ 0 };

			if (enableUdp && enableTcp)
				this->iceCandidates.reserve(2 * jsonListenIpsIt->size());
			else
				this->iceCandidates.reserve(jsonListenIpsIt->size());

			for (auto& listenIp : listenIps)
			{
				if (enableUdp)//启动upd传输
				{
					uint16_t iceLocalPreference =
					  IceCandidateDefaultLocalPriority - iceLocalPreferenceDecrement;

					if (preferUdp)
						iceLocalPreference += 1000;

					uint32_t icePriority = generateIceCandidatePriority(iceLocalPreference);

					// This may throw.
					RTC::UdpSocket* udpSocket;
					if (port != 0)
						udpSocket = new RTC::UdpSocket(this, listenIp.ip, port);
					else
						udpSocket = new RTC::UdpSocket(this, listenIp.ip);

					this->udpSockets[udpSocket] = listenIp.announcedIp;

					if (listenIp.announcedIp.empty())
						this->iceCandidates.emplace_back(udpSocket, icePriority);
					else
						this->iceCandidates.emplace_back(udpSocket, icePriority, listenIp.announcedIp);
				}

				if (enableTcp)//启动tcp传输
				{
					uint16_t iceLocalPreference =
					  IceCandidateDefaultLocalPriority - iceLocalPreferenceDecrement;

					if (preferTcp)
						iceLocalPreference += 1000;

					uint32_t icePriority = generateIceCandidatePriority(iceLocalPreference);

					// This may throw.
					RTC::TcpServer* tcpServer;
					if (port != 0)
						tcpServer = new RTC::TcpServer(this, this, listenIp.ip, port);
					else
						tcpServer = new RTC::TcpServer(this, this, listenIp.ip);

					this->tcpServers[tcpServer] = listenIp.announcedIp;

					if (listenIp.announcedIp.empty())
						this->iceCandidates.emplace_back(tcpServer, icePriority);
					else
						this->iceCandidates.emplace_back(tcpServer, icePriority, listenIp.announcedIp);
				}

				// Decrement initial ICE local preference for next IP.
				iceLocalPreferenceDecrement += 100;
			}

			// Create a ICE server.
			this->iceServer = new RTC::IceServer(
			  this, Utils::Crypto::GetRandomString(16), Utils::Crypto::GetRandomString(32));

			// Create a DTLS transport.
			this->dtlsTransport = new RTC::DtlsTransport(this);
		}



    ...........省略............
}

       WebRtcTransport实例创建成功后,会通过WebRtcTransport::FillJson填充要返回给客户端的数据,包括iceRole(默认是controlled),用户名usernameFragment,密码password,iceLite说明不是完全的遵循ice标准,iceCandidates,dtlsParameters(包含fingerprints,role)。

void WebRtcTransport::FillJson(json& jsonObject) const
{
		MS_TRACE();

		// Call the parent method.
		RTC::Transport::FillJson(jsonObject);

		// Add iceRole (we are always "controlled").
		jsonObject["iceRole"] = "controlled";

		// Add iceParameters.
		jsonObject["iceParameters"] = json::object();
		auto jsonIceParametersIt    = jsonObject.find("iceParameters");

		(*jsonIceParametersIt)["usernameFragment"] = this->iceServer->GetUsernameFragment();
		(*jsonIceParametersIt)["password"]         = this->iceServer->GetPassword();
		(*jsonIceParametersIt)["iceLite"]          = true;

		// Add iceCandidates.
		jsonObject["iceCandidates"] = json::array();
		auto jsonIceCandidatesIt    = jsonObject.find("iceCandidates");

		for (size_t i{ 0 }; i < this->iceCandidates.size(); ++i)
		{
			jsonIceCandidatesIt->emplace_back(json::value_t::object);

			auto& jsonEntry    = (*jsonIceCandidatesIt)[i];
			auto& iceCandidate = this->iceCandidates[i];

			iceCandidate.FillJson(jsonEntry);
		}

		// Add iceState.
		switch (this->iceServer->GetState())
		{
			case RTC::IceServer::IceState::NEW:
				jsonObject["iceState"] = "new";
				break;
			case RTC::IceServer::IceState::CONNECTED:
				jsonObject["iceState"] = "connected";
				break;
			case RTC::IceServer::IceState::COMPLETED:
				jsonObject["iceState"] = "completed";
				break;
			case RTC::IceServer::IceState::DISCONNECTED:
				jsonObject["iceState"] = "disconnected";
				break;
		}

		// Add iceSelectedTuple.
		if (this->iceServer->GetSelectedTuple())
			this->iceServer->GetSelectedTuple()->FillJson(jsonObject["iceSelectedTuple"]);

		// Add dtlsParameters.
		jsonObject["dtlsParameters"] = json::object();
		auto jsonDtlsParametersIt    = jsonObject.find("dtlsParameters");

		// Add dtlsParameters.fingerprints.
		(*jsonDtlsParametersIt)["fingerprints"] = json::array();
		auto jsonDtlsParametersFingerprintsIt   = jsonDtlsParametersIt->find("fingerprints");
		auto& fingerprints                      = this->dtlsTransport->GetLocalFingerprints();

		for (size_t i{ 0 }; i < fingerprints.size(); ++i)
		{
			jsonDtlsParametersFingerprintsIt->emplace_back(json::value_t::object);

			auto& jsonEntry   = (*jsonDtlsParametersFingerprintsIt)[i];
			auto& fingerprint = fingerprints[i];

			jsonEntry["algorithm"] =
			  RTC::DtlsTransport::GetFingerprintAlgorithmString(fingerprint.algorithm);
			jsonEntry["value"] = fingerprint.value;
		}

		// Add dtlsParameters.role.
		switch (this->dtlsRole)
		{
			case RTC::DtlsTransport::Role::NONE:
				(*jsonDtlsParametersIt)["role"] = "none";
				break;
			case RTC::DtlsTransport::Role::AUTO:
				(*jsonDtlsParametersIt)["role"] = "auto";
				break;
			case RTC::DtlsTransport::Role::CLIENT:
				(*jsonDtlsParametersIt)["role"] = "client";
				break;
			case RTC::DtlsTransport::Role::SERVER:
				(*jsonDtlsParametersIt)["role"] = "server";
				break;
		}

		// Add dtlsState.
		switch (this->dtlsTransport->GetState())
		{
			case RTC::DtlsTransport::DtlsState::NEW:
				jsonObject["dtlsState"] = "new";
				break;
			case RTC::DtlsTransport::DtlsState::CONNECTING:
				jsonObject["dtlsState"] = "connecting";
				break;
			case RTC::DtlsTransport::DtlsState::CONNECTED:
				jsonObject["dtlsState"] = "connected";
				break;
			case RTC::DtlsTransport::DtlsState::FAILED:
				jsonObject["dtlsState"] = "failed";
				break;
			case RTC::DtlsTransport::DtlsState::CLOSED:
				jsonObject["dtlsState"] = "closed";
				break;
		}
}

       客户端收到回应后,接下来就是客户端发送connect指令给服务端了,connect指令主要是包括dtlsParameters的信息(fingerprints和role)。

        另外,WebRtcTransport实例的创建过程,通过generateIceCandidatePriority,会生成相应的服务端的ice candidate并传给客户端(双方进行通信需要交换candidate), 在客户端收到服务端的candidate,并将candidate加入到peerconnection连接对象中去后,底层就会发送一个stun binding request消息(包含ice-ufrags, ice-passwords),服务端收到消息后取出ice-ufrags, ice-passwords,然后进行验证是不是由之前WebRtcTransport为客户端生成的用户名和密码。如果是就返回OK,否则返回错误。如果返回的是OK,接下来就是进行DTLS握手消息处理,DTLS握手成功后,客户端与服务端就确认的加密算法key。然后dtls把拿到的key交给SRTP,再创建一个SRTP Session,后面数据传输都采用SRTP进行加密解密。

inline void WebRtcTransport::OnPacketReceived(
	  RTC::TransportTuple* tuple, const uint8_t* data, size_t len)
	{
		MS_TRACE();

		// Increase receive transmission.
		RTC::Transport::DataReceived(len);

		// Check if it's STUN.
		if (RTC::StunPacket::IsStun(data, len))
		{
			OnStunDataReceived(tuple, data, len);
		}
		// Check if it's RTCP.
		else if (RTC::RTCP::Packet::IsRtcp(data, len))
		{
			OnRtcpDataReceived(tuple, data, len);
		}
		// Check if it's RTP.
		else if (RTC::RtpPacket::IsRtp(data, len))
		{
			OnRtpDataReceived(tuple, data, len);
		}
		// Check if it's DTLS.
		else if (RTC::DtlsTransport::IsDtls(data, len))
		{
			OnDtlsDataReceived(tuple, data, len);
		}
		else
		{
			MS_WARN_DEV("ignoring received packet of unknown type");
		}
	}

从 WebRtcTransport::OnPacketReceived中可以看出针对不同类型的消息进行了分别的处理。WebRtcTransport实例创建时,同时创建了iceServer,dtlsTransport。

stun消息就是通过iceServer进行处理的:

void IceServer::ProcessStunPacket(RTC::StunPacket* packet, RTC::TransportTuple* tuple)
{
		MS_TRACE();

		// Must be a Binding method.
		if (packet->GetMethod() != RTC::StunPacket::Method::BINDING)
		{
            .............这边就是对 stun binding消息进行处理
        }

}

然后dtls握手过程就是通过 dtlsTransport进行处理的:

void DtlsTransport::ProcessDtlsData(const uint8_t* data, size_t len)
{
		MS_TRACE();

		int written;
		int read;

		if (!IsRunning())
		{
			MS_ERROR("cannot process data while not running");

			return;
		}

		// Write the received DTLS data into the sslBioFromNetwork.
		written =
		  BIO_write(this->sslBioFromNetwork, static_cast<const void*>(data), static_cast<int>(len));

		if (written != static_cast<int>(len))
		{
			MS_WARN_TAG(
			  dtls,
			  "OpenSSL BIO_write() wrote less (%zu bytes) than given data (%zu bytes)",
			  static_cast<size_t>(written),
			  len);
		}

		// Must call SSL_read() to process received DTLS data.
		read = SSL_read(this->ssl, static_cast<void*>(DtlsTransport::sslReadBuffer), SslReadBufferSize);

		// Send data if it's ready.
		SendPendingOutgoingDtlsData();

		// Check SSL status and return if it is bad/closed.
        // 真正处理握手的地方
		if (!CheckStatus(read))
			return;

		// Set/update the DTLS timeout.
		if (!SetTimeout())
			return;

		// Application data received. Notify to the listener.
		if (read > 0)
		{
			// It is allowed to receive DTLS data even before validating remote fingerprint.
			if (!this->handshakeDone)
			{
				MS_WARN_TAG(dtls, "ignoring application data received while DTLS handshake not done");

				return;
			}

			// Notify the listener.
			this->listener->OnDtlsTransportApplicationDataReceived(
			  this, (uint8_t*)DtlsTransport::sslReadBuffer, static_cast<size_t>(read));
		}
}

另外对于RTCP,RTP类型的数据就是实际的音视频数据。

每个类型的具体的处理细节都比较多,后续会慢慢逐个分析。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值