使用标准的 TURN 协议。TURN 协议是 STUN 协议的一个扩展,它利用一个中继服务器,使得无法建立 P2P 连接的客户端(NAT 严格限制导致)也能实现通讯。
TURN 协议的工作流程如下:
客户端发送 Allocate request 到 server,server 返回 401 未授权错误(带有 realm 和 nonce),客户端再发送带上认证信息的
Allocate request,server 返回成功分配的 relay address。分配成功后,客户端需要通过发送机制(Send Mechanism)或信道机制(Channels)
在 server 上配置和其他 peer 的转发信息。此外 allocation 和 channel 都需要保活。
WebRTC 使用的是信道机制,因为这一机制的数据开销更低。
收集 TURN relay candidate 时也可以复用收集 host candidate 时创建的 socket 对象,这一逻辑通过
PORTALLOCATOR_ENABLE_SHARED_SOCKET flag 控制,前面我们就已经知道,默认情况下它是开启的。
/
收集 relay candidate 流程
第一次响应
SocketDispatcher::OnEvent(uint32_t ff, int err) ===> SignalReadEvent(this);
===> AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) ===> SignalReadPacket(this, buf_, static_cast<size_t>(len), remote_addr,
(timestamp > -1 ? timestamp : TimeMicros()));
===> AllocationSequence::OnReadPacket(rtc::AsyncPacketSocket* socket,
const char* data,
size_t size,
const rtc::SocketAddress& remote_addr,
const int64_t& packet_time_us) ===> port->HandleIncomingPacket(socket, data, size, remote_addr, / TurnPort::HandleIncomingPacket
packet_time_us)
===> TurnPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
const char* data,
size_t size,
const rtc::SocketAddress& remote_addr,
int64_t packet_time_us) ===> equest_manager_.CheckResponse(data, size);
===> StunRequestManager::CheckResponse(const char* data, size_t size) ===> return CheckResponse(response.get());
===> StunRequestManager::CheckResponse(StunMessage* msg) ===> request->OnErrorResponse(msg); // 401 未授权 TurnAllocateRequest::OnErrorResponse
===> TurnAllocateRequest::OnErrorResponse(StunMessage* response) ===> OnAuthChallenge(response, error_code); // TurnAllocateRequest::OnAuthChallenge
===> TurnAllocateRequest::OnAuthChallenge(StunMessage* response, int code) ===> port_->SendRequest(new TurnAllocateRequest(port_), 0); 重新发送一个带有 realm nonce 参数的 TurnAllocateRequest
第二次响应,这里直接到达和第一次处理 TurnAllocateRequest 响应不同的地方
StunRequestManager::CheckResponse(StunMessage* msg) ===> request->OnResponse(msg);
===> TurnAllocateRequest::OnResponse(StunMessage* response) ===> port_->OnAllocateSuccess(relayed_attr->GetAddress(),
mapped_attr->GetAddress()); /// TurnPort::OnAllocateSuccess
===> TurnPort::OnAllocateSuccess(const rtc::SocketAddress& address,
const rtc::SocketAddress& stun_address) ===> AddAddress(address, // Candidate address.
address, // Base address.
related_address, // Related address.
UDP_PROTOCOL_NAME,
ProtoToString(server_address_.proto), // The first hop protocol.
"", // TCP canddiate type, empty for turn candidates.
RELAY_PORT_TYPE, GetRelayPreference(server_address_.proto),
server_priority_, ReconstructedServerUrl(false /* use_hostname */),
true); /// 注意注意注意,这里 is_final 传递的是 true /
===> Port::AddAddress(const rtc::SocketAddress& address,
const rtc::SocketAddress& base_address,
const rtc::SocketAddress& related_address,
const std::string& protocol,
const std::string& relay_protocol,
const std::string& tcptype,
const std::string& type,
uint32_t type_preference,
uint32_t relay_preference,
const std::string& url,
bool is_final) { // 这里 is_final 传递的是 true // ===> FinishAddingAddress(c, is_final); // 这里 is_final 传递的是 true /
===> Port::FinishAddingAddress(const Candidate& c, bool is_final) ===> SignalCandidateReady(this, c);
===> BasicPortAllocatorSession::OnCandidateReady ===> SignalCandidatesReady(this, candidates);
===> P2PTransportChannel::OnCandidatesReady ===> SignalCandidateGathered(this, candidates[i]);
===> JsepTransportController::OnTransportCandidateGathered_n ===> SignalIceCandidatesGathered(transport_name, {candidate});
===> PeerConnection::OnTransportControllerCandidatesGathered ===> OnIceCandidate(std::move(candidate));
===> PeerConnection::OnIceCandidate ===> Observer()->OnIceCandidate(candidate.get());
===> Conductor::OnIceCandidate ===> SendMessage(writer.write(jmessage));