WebRtc連接狀態變化
- ICE收集完成的條件
- 連接狀態的變化
ComputeState:一旦ICE收集完成,至少應該存在一個可用的連接
1.如果had_connection_ 爲false,則狀態爲STATE_INIT
2.檢查所有的Connection,看是否有active的
3.該函數應該是在ICE收集完成時纔會調用
4.如果檢查所有Connection,沒有active的連接,則返回STATE_FAILED
// A channel is considered ICE completed once there is at most one active
// connection per network and at least one active connection.
IceTransportState P2PTransportChannel::ComputeState() const {
if (!had_connection_) {
return IceTransportState::STATE_INIT;
}
std::vector<Connection*> active_connections;
for (Connection* connection : connections_) {
if (connection->active()) {
active_connections.push_back(connection);
}
}
if (active_connections.empty()) {
return IceTransportState::STATE_FAILED;
}
std::set<rtc::Network*> networks;
for (Connection* connection : active_connections) {
rtc::Network* network = connection->port()->Network();
if (networks.find(network) == networks.end()) {
networks.insert(network);
} else {
LOG_J(LS_VERBOSE, this) << "Ice not completed yet for this channel as "
<< network->ToString()
<< " has more than 1 connection.";
return IceTransportState::STATE_CONNECTING;
}
}
return IceTransportState::STATE_COMPLETED;
}
UpdateState :當一個連接被添加、刪除或者任何連接改變了write state時纔會被調用,以至於transport controller能夠得到最新的狀態。
然後,該函數不應該被調用太頻繁,以防出現多個狀態的改變,該函數應該在所有的狀態改變後纔會被調用。例如,我媽在SortConnectionsAndUpdate的最後調用。
1.首先得到當前的狀態,如果當前狀態和之前的狀態不一樣,則先判斷狀態轉變是否合理,即不能從某種狀態轉變成另外一種狀態
2.SingalStateChanged是產生狀態改變的信號???
3.set_writable設置寫的狀態?應該是判斷是否可以向對方發送數據???
4.set_receiving設置接受的狀態?應該是判斷是否可以接收對方的數據???
// Warning: UpdateState should eventually be called whenever a connection
// is added, deleted, or the write state of any connection changes so that the
// transport controller will get the up-to-date channel state. However it
// should not be called too often; in the case that multiple connection states
// change, it should be called after all the connection states have changed. For
// example, we call this at the end of SortConnectionsAndUpdateState.
void P2PTransportChannel::UpdateState() {
IceTransportState state = ComputeState();
if (state_ != state) {
LOG_J(LS_INFO, this) << "Transport channel state changed from "
<< static_cast<int>(state_) << " to "
<< static_cast<int>(state);
// Check that the requested transition is allowed. Note that
// P2PTransportChannel does not (yet) implement a direct mapping of the ICE
// states from the standard; the difference is covered by
// TransportController and PeerConnection.
switch (state_) {
case IceTransportState::STATE_INIT:
// TODO(deadbeef): Once we implement end-of-candidates signaling,
// we shouldn't go from INIT to COMPLETED.
RTC_DCHECK(state == IceTransportState::STATE_CONNECTING ||
state == IceTransportState::STATE_COMPLETED);
break;
case IceTransportState::STATE_CONNECTING:
RTC_DCHECK(state == IceTransportState::STATE_COMPLETED ||
state == IceTransportState::STATE_FAILED);
break;
case IceTransportState::STATE_COMPLETED:
// TODO(deadbeef): Once we implement end-of-candidates signaling,
// we shouldn't go from COMPLETED to CONNECTING.
// Though we *can* go from COMPlETED to FAILED, if consent expires.
RTC_DCHECK(state == IceTransportState::STATE_CONNECTING ||
state == IceTransportState::STATE_FAILED);
break;
case IceTransportState::STATE_FAILED:
// TODO(deadbeef): Once we implement end-of-candidates signaling,
// we shouldn't go from FAILED to CONNECTING or COMPLETED.
RTC_DCHECK(state == IceTransportState::STATE_CONNECTING ||
state == IceTransportState::STATE_COMPLETED);
break;
default:
RTC_NOTREACHED();
break;
}
state_ = state;
SignalStateChanged(this);
}
// If our selected connection is "presumed writable" (TURN-TURN with no
// CreatePermission required), act like we're already writable to the upper
// layers, so they can start media quicker.
bool writable =
selected_connection_ && (selected_connection_->writable() ||
PresumedWritable(selected_connection_));
set_writable(writable);
bool receiving = false;
for (const Connection* connection : connections_) {
if (connection->receiving()) {
receiving = true;
break;
}
}
set_receiving(receiving);
}
SortConnectionsAndUpdateState:對可用的連接進行排序,找到最合適的連接。我們會監控這個可用連接數的變化以及當前的狀態。
1.
// Sort the available connections to find the best one. We also monitor
// the number of available connections and the current state.
void P2PTransportChannel::SortConnectionsAndUpdateState() {
RTC_DCHECK(network_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;
// Find the best alternative connection by sorting. It is important to note
// that amongst equal preference, writable connections, this will choose the
// one whose estimated latency is lowest. So it is the only one that we
// need to consider switching to.
// TODO(honghaiz): Don't sort; Just use std::max_element in the right places.
std::stable_sort(connections_.begin(), connections_.end(),
[this](const Connection* a, const Connection* b) {
int cmp = CompareConnections(
a, b, rtc::Optional<int64_t>(), nullptr);
if (cmp != 0) {
return cmp > 0;
}
// Otherwise, sort based on latency estimate.
return a->rtt() < b->rtt();
});
LOG(LS_VERBOSE) << "Sorting " << connections_.size()
<< " available connections:";
for (size_t i = 0; i < connections_.size(); ++i) {
LOG(LS_VERBOSE) << connections_[i]->ToString();
}
Connection* top_connection =
(connections_.size() > 0) ? connections_[0] : nullptr;
// If necessary, switch to the new choice. Note that |top_connection| doesn't
// have to be writable to become the selected connection although it will
// have higher priority if it is writable.
MaybeSwitchSelectedConnection(top_connection, "sorting");
// The controlled side can prune only if the selected connection has been
// nominated because otherwise it may prune the connection that will be
// selected by the controlling side.
// TODO(honghaiz): This is not enough to prevent a connection from being
// pruned too early because with aggressive nomination, the controlling side
// will nominate every connection until it becomes writable.
if (ice_role_ == ICEROLE_CONTROLLING ||
(selected_connection_ && selected_connection_->nominated())) {
PruneConnections();
}
// Check if all connections are timedout.
bool all_connections_timedout = true;
for (size_t i = 0; i < connections_.size(); ++i) {
if (connections_[i]->write_state() != Connection::STATE_WRITE_TIMEOUT) {
all_connections_timedout = false;
break;
}
}
// Now update the writable state of the channel with the information we have
// so far.
if (all_connections_timedout) {
HandleAllTimedOut();
}
// Update the state of this channel.
UpdateState();
// Also possibly start pinging.
// We could start pinging if:
// * The first connection was created.
// * ICE credentials were provided.
// * A TCP connection became connected.
MaybeStartPinging();
}