美文网首页WebRTC
webrtc中对连接的排序过程

webrtc中对连接的排序过程

作者: 古则 | 来源:发表于2017-05-19 11:39 被阅读746次

1、ICEROLE_CONTROLLING ICEROLE_CONTROLLED等相关概念

ICE-CONTROLLED和ICE-CONTROLLING
在每次会话中,每个终端都有一个身份,有两种身份,即受控方(controlled role)和主控方(controlling role).
主控方负责选择最终用来通讯的候选地址对,受控方被告知哪个候选地址对用来进行哪次媒体流传输,
并且不生成更新过的offer来提示此次告知.发起ICE处理进程(即生成offer)的一方必须是主控方,而另一方则是受控方.
如果终端是受控方,那么在request中就必须加上ICE-CONTROLLED属性,同样,如果终端是主控方,就需要ICE-CONTROLLING属性.

2、具体的排序过程

通过调用peerconnection::AddIceCandidate,webrtc内部会一直调用至P2PTransportChannel::AddRemoteCandidate中,该方法源代码

void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate){
    ASSERT(worker_thread_ == rtc::Thread::Current());
    uint32_t generation = GetRemoteCandidateGeneration(candidate);
    // If a remote candidate with a previous generation arrives, drop it.
  if (generation < remote_ice_generation()) {
    LOG(LS_WARNING) << "Dropping a remote candidate because its ufrag "
                    << candidate.username()
                    << " indicates it was for a previous generation.";
    return;
  }

  Candidate new_remote_candidate(candidate);
  new_remote_candidate.set_generation(generation);
  if (remote_ice()) {
    if (candidate.username().empty()) {
      new_remote_candidate.set_username(remote_ice()->ufrag);
    }
    if (new_remote_candidate.username() == remote_ice()->ufrag) {
      if (candidate.password().empty()) {
        new_remote_candidate.set_password(remote_ice()->pwd);
      }
    } else {
      LOG(LS_WARNING) << "A remote candidate arrives with an unknown ufrag: "
                      << candidate.username();
    }
  }

  //创建connections
  CreateConnections(new_remote_candidate, NULL);

  // 对所有conections进行排序
  SortConnections();
}

下面是排序的具体过程

void P2PTransportChannel::SortConnections() {
  ASSERT(worker_thread_ == rtc::Thread::Current());
  // Make sure the connection states are up-to-date since this affects how they
  // will be sorted.
  UpdateConnectionStates();

  // Any changes after this point will require a re-sort.
  sort_dirty_ = false;

  //找出最优的连接
  ConnectionCompare cmp;
  std::stable_sort(connections_.begin(), connections_.end(), cmp);
  LOG(LS_VERBOSE) << "Sorting " << connections_.size()
                  << " available connections:";
..................
}

排序是通过<algorithm> 进行的,具体排序过程如下

class ConnectionCompare {
 public:
  bool operator()(const cricket::Connection *ca,
                  const cricket::Connection *cb) {
    cricket::Connection* a = const_cast<cricket::Connection*>(ca);
    cricket::Connection* b = const_cast<cricket::Connection*>(cb);
    int cmp = CompareConnections(a, b);
    if (cmp > 0)
      return true;
    if (cmp < 0)
      return false;

    // Otherwise, sort based on latency estimate.
    return a->rtt() < b->rtt();
  }
};

int CompareConnections(cricket::Connection* a, cricket::Connection* b) {
  int state_cmp = CompareConnectionStates(a, b);
  if (state_cmp != 0) {
    return state_cmp;
  }
  // Compare the candidate information.
  return CompareConnectionCandidates(a, b);
}

int CompareConnectionStates(cricket::Connection* a, cricket::Connection* b) {
  // Sort based on write-state.  Better states have lower values.
  if (a->write_state() < b->write_state())
    return 1;
  if (a->write_state() > b->write_state())
    return -1;

  if (a->receiving() && !b->receiving())
    return 1;
  if (!a->receiving() && b->receiving())
    return -1;

  
  if (a->write_state() == cricket::Connection::STATE_WRITABLE &&
      b->write_state() == cricket::Connection::STATE_WRITABLE) {
    if (a->connected() && !b->connected()) {
      return 1;
    }
    if (!a->connected() && b->connected()) {
      return -1;
    }
  }
  return 0;
}

int CompareConnectionCandidates(cricket::Connection* a,
                                cricket::Connection* b) {
  uint32_t a_cost = a->ComputeNetworkCost();
  uint32_t b_cost = b->ComputeNetworkCost();
  // Smaller cost is better.
  if (a_cost < b_cost) {
    return 1;
  }
  if (a_cost > b_cost) {
    return -1;
  }

  // Compare connection priority. Lower values get sorted last.
  if (a->priority() > b->priority())
    return 1;
  if (a->priority() < b->priority())
    return -1;

  // If we're still tied at this point, prefer a younger generation.
  return (a->remote_candidate().generation() + a->port()->generation()) -
         (b->remote_candidate().generation() + b->port()->generation());
}

CompareConnections的流程概括为

  1. 比较连接状态
    1.1 比较写状态
    1.2 比较读状态
    1.3 连接状态
  2. 比较连接的候选地址
    2.1 比较cost
    2.2 比较优先级
    2.3 比较candidate的generation

对于连接成功的连接来说,因为cost在大部分情况下为0,所以一般比较得是candidate的priority,首先来看一个candidate的格式

 candidate:499412426 2 udp 2122260222 192.168.0.136 54143 typ host generation 0 ufrag KQbng0ClMLl6gk2B network-id 3

499412426       //foundation
1             //component-id
udp               //transport
2122260223    //priority
192.168.0.136    //connection-address
54135           //port
typ host           //candidate-types
generation 0    //generation
ufrag KQbng0ClMLl6gk2B network-id 3

connection priority的计算过程

uint64_t Connection::priority() const {
  uint64_t priority = 0;
  // RFC 5245 - 5.7.2.  Computing Pair Priority and Ordering Pairs
  // Let G be the priority for the candidate provided by the controlling
  // agent.  Let D be the priority for the candidate provided by the
  // controlled agent.
  // pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
  IceRole role = port_->GetIceRole();
  if (role != ICEROLE_UNKNOWN) {
    uint32_t g = 0;
    uint32_t d = 0;
    if (role == ICEROLE_CONTROLLING) {
      g = local_candidate().priority();
      d = remote_candidate_.priority();
    } else {
      g = remote_candidate_.priority();
      d = local_candidate().priority();
    }
    priority = std::min(g, d);
    priority = priority << 32;
    priority += 2 * std::max(g, d) + (g > d ? 1 : 0);
  }
  return priority;
}

connection的rtt_(网络延时)的计算过程

//port.cc 787
//初始值
rtt_(DEFAULT_RTT), //DEFAULT_RTT = 3000


//port.cc 1222L
void Connection::OnConnectionRequestResponse(ConnectionRequest* request,
                                             StunMessage* response) {
  // Log at LS_INFO if we receive a ping response on an unwritable
  // connection.
  rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;

  int rtt = request->Elapsed();

  ReceivedPingResponse();

  if (LOG_CHECK_LEVEL_V(sev)) {
    bool use_candidate = (
        response->GetByteString(STUN_ATTR_USE_CANDIDATE) != nullptr);
    std::string pings;
    PrintPingsSinceLastResponse(&pings, 5);
    LOG_JV(sev, this) << "Received STUN ping response"
                      << ", id=" << rtc::hex_encode(request->id())
                      << ", code=0"  // Makes logging easier to parse.
                      << ", rtt=" << rtt
                      << ", use_candidate=" << use_candidate
                      << ", pings_since_last_response=" << pings;
  }

  rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1);

  MaybeAddPrflxCandidate(request, response);
}

//Elapsed 相应跟请求的时间差
int StunRequest::Elapsed() const {
  return static_cast<int>(rtc::Time64() - tstamp_);
}

相关文章

网友评论

    本文标题:webrtc中对连接的排序过程

    本文链接:https://www.haomeiwen.com/subject/fvhyxxtx.html